ToyMUD V1.0 - Documentation		Chris Gray, January 1993
[Updated for DevMUD version, December 1998]


ToyMUD began as my first test of trying to use sockets under Sun UNIX. It
grew into a project to see how little code is needed to write a reasonably
general, fully-programmable MUD. The result is a server in about 3500 lines
of C code. It is not terribly efficient, but it is quite powerful. Most of
that power comes from the built-in programming language - the rest of the
server only provides the database and client setup and communications.

Configuration options are currently hard-coded in the source files.
The location of the database file (defaults to 'devmud.db' in the
directory the server is started in) is set in world/clients.c, via
a static initialization of variable 'DbPath'. The ports and flags to
use for the telnet code are set in telnet.c, in the 'telnet_start' routine.
As shipped, the 'run' directory contains 'master.db', which is an
original copy of the simple sample world - copy it to 'devmud.db' so
the server can access it. Then, just run the devmud server 'loader'.
Hit it with a control-C to shut it down.

As shipped, the system comes with a simple adventure-style scenario. Feel
free to replace and/or mangle this as desired.

I have written a number of compilers and interpreters in the past, but this
is the first one where I did not build an internal data structure (tree) of
the program and interpret from that. Thus, it may be a bit strange, but it
seems to work OK. It is quite slow because of this style, and most errors
are not detected until run-time. Error reporting is fairly poor, but trial
and error (and likely a few debug prints) can normally find any problems.


Using the Server

When the player is fully connected, if the input line begins with a period,
then the entire line, without the period, is treated as input to the
programming language interpreter instead of being passed to the scenario's
command parsing routine. Thus, there is no concept of a 'wizard' in ToyMUD -
everyone is a wizard. It would not be difficult to add such a distinction to
the system.

Input lines received by the server are examined for a simple line-continuation
system. If an input line ends with a '+', then the '+' is deleted and the line
is not processed. Instead, it is saved in the server and successive input
lines will be appended to the end of it. This will continue up to the server's
line length limit, which is currently 4000 characters. This is useful when
doing on-line building, since longer strings can be entered. E.g.

.location(me()).desc := +
"This is the new long description for this here room. It could be fairly long +
and spread over several input lines."

Ending a line in '-' is similar, except that the '-' is replaced by a newline
and the next line is glued on after that newline. This is useful when
interactively entering code, etc. in that you can explicitly enter the
format you want them stored in. E.g. entering the following prime number
program:

	.primes := `(max) -
	    cprint(me(), "2 "); -
	    cprint(me(), "3 "); -
	    a := 5; -
	    while a <= max do -
		b := 3; -
		p := 1; -
		while b * b <= a do -
		    if a = b * b / a then -
			p := 0; -
			b := a; -
		    fi; -
		    b := b + 2; -
		od; -
		if p then -
		    cprint(me(), itos(a) + " "); -
		fi; -
		a := a + 2; -
	    od; -
	    cprint(me(), "\n")`

which can then be called by e.g.

	.primes(1000)

Entering just

	.primes

will display the contents of the 'primes' function, which is the text of
it as entered, with the '-'s removed.

Note that in the previous example of assigning to the current room's 'desc',
you would likely want to use a '-' at the end of the first line of the string,
so that there is an actual newline placed in the string.


Connecting as a Client

Using 'telnet', you can connect to the ToyMUD server using a command of the
form   telnet [hostname] port	where 'port' is the port you have the server
running on (default is 6666), and 'hostname' is the name (or internet address)
of the machine running the server (default is the local machine).


Basic Concepts Used in Programming and Building

The interpreter provides a number of builtin functions which are used to
create things, find things, retrieve and set special attributes on players,
produce output, parse input, etc. These are described fully later, but a
couple will be mentioned here.

The basic entity in ToyMUD is a "thing". This is basically a context (a set
of attribute-value pairs) which is maintained by the server and can be
stored in the database file. Each thing must have a unique name, so that
the graph of them can be read from and written to the database file in a
computable order. The 'thing' builtin, which generates new things at runtime,
will generate a name if required. Each room or object in ToyMUD is just a
thing. There is a thing associated with each player, to hold information about
that player. The name of that thing is just the player's name. There is also
a single global thing that serves as a global programming environment, and
contains some special server-called functions described below.

Property values (and hence variables and parameters) can be of the following
types:

    int - signed integer (an 'int' in the C code of the host machine)
    string - a sequence of characters - string constants look like those
	in C
    proc - the same as a string, but in a standardized form that the
	interpreter understands. Represented as a sequence of characters
	surrounded by back-quotes (`)
    thing - a pointer to another thing

Type 'void' also exists - it represents the concept of "no value". The
thing value 'nil' represents a pointer to no other thing.

The builtin 'describe' takes a thing as argument and will dump it out to
the current client. E.g. here is the output for a room in the supplied
scenario:

.describe(location(me()))
thing r_entrance:
east ==> <CONTEXT>
west ==> <CONTEXT>
north ==> <CONTEXT>
name ==> "in the entrance"
desc ==>
    "The main door is to the south, and other doors lead north, east and west."
south ==> <CONTEXT>
out ==> <CONTEXT>
contents ==> nil
scenery ==> "doors door"

Builtin 'me' returns the thing for the current client, and builtin 'location'
returns the current room thing for a client corresponding to the passed thing.
In the example output, the thing is named 'r_entrance', and has properties
named 'east', 'west', 'north', 'name', 'desc', 'south', 'out', 'contents' and
'scenery'. By convention, every room in this scenario has properties 'name'
and 'contents'. Similarly, every object has properties 'name' and 'next' (a
pointer to the next object in any list).

The interpreter is "dynamically typed" and does not use true declarations.
Whenever a symbol is referenced, it is searched for in the context of the
current function call (if any), and then in the global context. If it is
not found, an error message is printed. When a symbol is assigned to, any
already existing definition in the current call context or the global context
will be reused, and the reuse must use the same type of value as the already
existing definition. If the symbol being assigned to does not exist, then
it is created in the current function call context. The '.' syntax can
be used to explicitly reference or assign to a property in a context.


Machines

Machines are similar to "robots" or "bots" in other MUDS. In ToyMUD, they
will normally run right in the MUD, rather than being an external program.
This gives them full access to the data structures of the scenario. A machine
is in most respects indistinguishable from a player. It should have whatever
standard properties the scenario gives to players. The 'forEachPlayer' builtin
will call its parameter for machines as well as for players. Whenever the
server is restarted, a property named 'MACHINE_RESTART_ACTION' is looked for
on each machine. If it exists and is a proc, it will be called. This is how
machines can be operational on a server startup without any intervention. This
routine will normally use the 'after' builtin to reschedule itself at a later
time, thus allowing the machine to perform actions over time, much like a
player does.

Whenever the scenario does an 'aprint' (sending a message to all players in
the same location as the passed entity), every machine in that same room
will have it's 'LISTEN_ACTION' (if any) called with the printed string as its
parameter. This is how machines can listen to and observe what is happening
around them, and react to those events.


Server Conventions

The server will magically call global interpreted routines as follows:

NEW_PLAYER_ACTION(player) - this is called, with the active player context
    as the argument, whenever a new player enters the game. This is a player
    who has never played before. This routine will typically do appropriate
    initialization of the player context and move the player to the start
    location.

RESTART_ACTION(player) - this is called when a previously seen player
    re-enters the game. It will typically do a 'look around'.

QUIT_ACTION(player) - this is called when a player is leaving the game,
    either as a result of the code calling the builtin 'quit', or as a
    result of the player entering an end-of-file or killing the client.

PARSE_ACTION(string) - this is called with normal input lines from the player.
    The entire input line is passed in a string argument. This routine should
    "parse" the input command and execute it.


The Programming Language

The ToyMUD programming language doesn't really have a name, so I'll just
call it 'TM'. It is not a C-like language, but resembles languages I have
developed for other purposes - that is simply my own preference. TM is
very minimal. It has no I/O (builtins are used), no logical operators,
no goto (hurray!), continue, break or return, no unary negation (use
subtraction from 0), etc. It is quite usable however. Those interested
should be able to easily add things like logical operators, unary negation,
etc. Gotos would be much harder, so don't try!

Basic values:

    integer-constant		[e.g. 1397]
    string-constant		[e.g. "Hello there world!\n"]
    proc-constant		[e.g. `(a, b) a * 2 + b * 5)`]
    function-parameter-name
    variable-name		[this includes functions]
    context.prop-name		[property name fixed]
    context.{string-expr}	[property name determined at run-time]
    {string-expr}		[kind-of allows read-only pointers]
    proc-expr(proc-parameters)	[can yield void or any type]

    The various forms can be combined arbitrarily. Property selection and
    procedure calling have the same precedence, so

	th.action(args)     ==	    (th.action)(args)
	thf(args).prop	    ==	    (thf(args)).prop

    More complex uses are possible, but are likely hard to read. E.g.

	th.func(args1)(args2).{"p" + p1 + "_"}.p2(args3).p3

Assignment statements:

    variable := expression
    context.prop-name := expression
    context.{string-expr} := expression

    Precedence and combinations are as with basic values.

Sequence expression:

    statement; statement ... statement; expression

Sequence statement:

    statement; statement ... statement

    The forms 'sequence expression' and 'sequence statement' are identical.
    They are separated out since some constructs require void values.

If construct (statement or expression):

    if <int-expression> then
	<sequence-expression>
    {elif <int-expression> then <sequence-expression>}
    [else <sequence-expression>]
    fi

    An 'if' construct can be either a statement or expression, depending on
    the type returned. A result type of 'void' yields an 'if' statement. The
    various alternatives in an 'if' should all yield the same type, but this
    is not checked, so strange abuses are possible. An 'if' consists of one
    'if' part, zero or more 'elif' parts, an optional 'else' part and a 'fi'.
    TM has no boolean type, but the supplied scenario provides global
    variables 'false' and 'true' with values 0 and 1. Note that TM has no
    boolean 'and', 'or' or 'not' operators, so if these are needed, they must
    be constructed out of 'if's.

    e.g.:

	if a < b then a else b fi

	if flag1 then
	    statements1
	elif flag2 then
	    statements2
	else
	    statements3
	fi

While statement:

    while <int-sequence-expression> do
	<sequence-statement>
    od;

    'while' statements are standard, except that the condition can be a
    sequence of statements followed by the condition expression. This allows
    center and tail-exit loops to be built from the same construct.

    e.g.:

	while i ~= 10 do
	    cprint(me(), itos(i) + " ");
	    i := i + 1;
	od;
	while
	    th := getAThingValue();
	    th ~= nil
	do
	    processTheThing(th);
	od;

Proc call:

    proc-expression([par1 {, parI}])

    The correctness of a given procedure is not checked until it is first
    called. Syntactically, a procedure, represented as characters inside a
    pair of back-quotes, consists of a parenthesized list of parameter names
    followed by the body of the procedure. The parameters are not typed and
    hence their type can vary from call to call, although this is not
    recommended. If a procedure is to return a result, then put that result
    after the last statement of the procedure's body. There is no 'return'
    statement.

    e.g.:

	sum := `(a, b) a + b`
	show := `(th)
	    cprint(me(), "The thing you passed contains:\n");
	    describe(th);
	    cprint(me(), "Wasn't that interesting?\n")`
	...
	sum(1, 2)
	show(me()) - call show on the active player
	show(location(me())) - call show on the location of the active player
	th.{stringFunc(3, 4)}.actor(th) -
	    call 'stringFunc' with parameters 3 and 4. It must return a
	    string, which is then looked up as a property name on thing 'th'.
	    That property must exist and be another thing. Property 'actor'
	    is looked up on the second thing, and must yield a function,
	    which is then called with 'th' as parameter.
	fred.joe.sam("mary", ellen, susan + 2)
	    Thing 'fred' must have a property 'joe' which is another thing.
	    That thing must have property 'sam', which is a proc. That
	    proc is called with 3 parameters - the string "mary", the
	    current value of parameter, local or global variable 'ellen',
	    and the similar value of 'susan' + 2. (Hence 'susan' must be
	    an int variable.)

Operators (in order of groups of decreasing precedence):

    * - integer multiplication
    / - integer division
    % - integer remainder

    + - integer addition
    + - string concatenation
    - - integer subtraction

    =, ~= - integer, string, thing, action equality comparison
    <, <=, >, >= - integer or string sorting comparison

Parentheses are used as normal to alter precedence.

Builtins:
    cprint(who, what) - print string 'what' to client 'who'. 'who' is a
	thing value, and should be one for an active player.
    aprint(who, what) - print string 'what' to all clients in the same
	room as 'who', except for 'who'
    global() - return a pointer to the global context
    itos(n) - return string form of integer 'n'
    length(str) - return length in characters of string 'st'
    substr(str, pos, len) - return a substring of string 'st' starting at
	position 'pos' (first is position 0) of length 'len'. The result will
	just get truncated for out-of-range values.
    strtoproc(str) - return a proc whose body is just the passed string 'str'.
	Note: strings are dynamic objects which are allocated and freed as
	needed. Procs are not allocated/copied/freed when used. Thus, the use
	of this builtin is likely to result in lost memory in the server.
    random() - return a random signed integer
    word() - return the next "word" (whitespace terminated) from the
	current command-line-tail. When 'PARSE_ACTION' is called, the
	command-line-tail is set to the entire command that the user entered.
	The "current position" in the command-line-tail is moved up to after
	the returned word, so successive calls to 'word' will return
	successive "words" from the command-line-tail. 'substr' can do
	this splitting apart, but it would do it slowly.
    gettail() - return the current command-line-tail.
    settail(str) - set the command-line-tail to 'str'.
    me() - return the thing for the current player
    location(who) - return the current location thing for player 'who'.
    name(who) - return the name of the passed thing. For players, this will
	be the player name. Do not confuse this with the 'name' property
	explicitly used on rooms and objects by the supplied scenario.
    move(who, where) - move the player whose thing is passed as 'who' to
	the room whose thing is passed as 'where'. This value is saved
	in the database, can be returned by 'location', and is used by
	'aprint'. Note that for 'location', 'name' and 'move', "player"
	can be a machine.
    typeof(th, name) - look up the name given by string 'name' in the
	context represented by thing 'th'. If the name is not a property
	in that context, return -1, otherwise return:
	    0 - property is void (should not be possible)
	    1 - property is an integer
	    2 - property is a string
	    3 - property is a proc
	    4 - property is a thing
	This is often used to find out if a given thing has a property. The
	supplied scenario also uses it to retrieve a string to print or a
	proc to call when a given action is applied to an object.
    describe(th) - print to the current client a textual representation
	of the name and properties of the given thing.
    quit() - arrange that the current client will terminate when the
	running code eventually returns to the main server code.
    thing(str) - create and return a new thing with the given name. It is
	an error if a thing by that name already exists. If the passed
	string is empty, then a new, unique name will be constructed.
    destroy(th) - destroy thing 'th'. NO CHECK IS MADE TO SEE IF SOMETHING
	IS STILL POINTING AT THE THING.
    find(str) - find and return the thing with the given name. If none is
	found, return nil.
    findPlayer(str, loc) - find and return the thing for the player with name
	'str' who is in location 'loc'. If 'loc' is nil, then any location is
	acceptable. If no such player exists or is not currently active, then
	return nil.
    forEachPlayer(pr) - the given proc is called once for each active
	player in the world. The proc is passed that player as its
	parameter. 'me()' can still be used to find the current player.
    createMachine(name) - create a new machine with the given name. The
	context (thing) for the new machine will be returned. If this call
	is used to create a new machine dynamically, it is likely that 'force'
	would be used to start the machine operating. Machines can also be
	created simply by defining them in the initial database given to the
	server. See the section above on Machines.
    destroyMachine(thing) - destroy the indicated machine. Dynamically
	creating and destroying machines would be done, for example, to handle
	randomly created monsters in a scenario with fighting.
    after(time, proc) - after roughly 'time' seconds has elapsed, the proc
	is called for the client or machine who is calling 'after'. This is
	often used at the end of the normal step action for a machine, in
	order to trigger the next call of the same action.
    force(who, proc) - force player or machine 'who' to call the given proc,
	with thing 'who' as parameter. The call is done immediately. This can
	be used for a variety of purposes, some useful, others merely annoying.


Online Building

Online building is possible, but no commands have been provided in the
scenario to make it easier. This could certainly be done. Building can be
done by directly using the programming language. E.g., using the
conventions of the supplied scenario, one could enter:

    .FredRoom := thing("r_fredRoom1")
    .FredRoom.name := "in Fred's first room"
    .FredRoom.desc := "This room is magnificent! ... "
    .FredThing := thing("o_fredVase")
    .FredThing.name := "vase"
    .FredThing.adjectives := "pretty china"
    .FredThing.desc := "The china vase is very pretty. ... "
    .FredThing.drop := FredBreakableRoutine
    .FredThing.next := nil
    .FredRoom.contents := FredThing
    .FredRoom.south := location(me())
    .location(me()).north := FredRoom
    go north

Note that it is a good idea to not link to the new room until it is fully
setup, so that no-one can wander into it prematurely (and get errors from
the code looking for properties that aren't there yet!) The global variables
used here (FredRoom and FredThing) can be reused when the room and object
are finished. These assignments, which are convenient, can easily be
re-established later, e.g. by using  '.FredRoom := location(me())'  or
'.FredThing := location(me()).contents'.


Database Format

The database format is a simple, text-only representation of the world. The
format is human-readable, so it is quite possible to build a scenario by
simply editing a copy of the initial database and trying it out, much like
the edit-compile-link-run cycle of compiler-based programming. On-line
building can be done also, and the newly built stuff will be saved to the
database file when the server exits, but the database writing routines do
not insert blank lines, comments, etc. Also, when creating by editing
the database, the builder will likely choose meaningful names for the
various things in the scenario, whereas with on-line building they may
have only numeric names generated by the system. Thus, it may be wise to
do the initial development of a scenario using an edit/test cycle.

Blanks lines in the database are skipped when reading it. Lines starting
with a '#' are similarly skipped as comments.

The first significant line of the file contains the decimal number of the
next available numeric value for a thing name. For an empty database,
100 is a suitable value. When building a large scenario, however, you may
want to up the value to 1000 or even 10000. When the system writes out the
database file, the value used is that of the next free unnamed-thing name.

Next in the database come the things in the scenario. Each thing is
terminated by a line containing just a single period, and the set of all
things is terminated by a line containing just a pair of periods (..).
Each thing entry starts out with a line containing the name of the thing.
This name must be unique within the entire database. Following the name
are lines describing the properties of the thing (variables in its context).
Each starts with the name of the property and continues with the value of
the property. The property may actually start on the next line - this is
useful for string properties, so that newlines end up in the right places.
The various property types are represented as follows:

    integer value - a decimal number, perhaps with leading minus sign
    proc value - a back-quote (`), followed by the text of the proc (which
	may span several lines), followed by a closing back-quote.
    string value - a quoted string, which may span several lines.
	Each newline in the string is kept as a real newline. Also,
	backslash escapes for \n, and \" are accepted.
    thing pointer - the value is simply the name of another thing. That
	thing must already have been defined. As a special case, the
	property name for a thing property can be a pair of names with a
	slash (/) separating them. In that case the first name in the
	pair is entered in the current thing as pointing to the thing
	named by the property value, and the second name is entered into
	the thing named by the property value as pointing to the current
	thing. This is how the lack of forward references is handled.

Note that the database writing code in the server may write the things in
the database in a different order than they were read in. Some effort is
made to prevent this, however.

After the things in the database comes the global context. It is exactly
like a single thing, except that it does not have a thing name. It will
usually have many more properties (variables) than true things do.

After the global context comes the player values. Each player value consists
of a line containing the player's name, a line containing the player's
password (not encrypted in any way!), a line containing the name of the
thing which is the player's current location and then the context for the
player, again just like a thing but without the thing name. The set of
players is terminated by a line containing a double dot (..).

After the players come any machines in the scenario. These appear exactly
the same as players, but they will almost always have a
'MACHINE_RESTART_ACTION' proc property, and often a 'LISTEN_ACTION' one as
well. The password for a machine is irrelevant but must be present.


The Provided Scenario

The reader is encouraged to examine the provided scenario - it is not very
complicated. Some "standards" within that scenario:

    thing 'THING_PROCS' is used to hold all of the utility routines in
	the scenario. This is abbreviated 'P', so that utility routines
	are called as in 'P.formatName(...)'. For a larger scenario,
	more similar things could be used in order to modularize the
	code and perhaps speed up run-time searches.

    thing 'THING_COMMANDS' is used to store all of the commands in the
	scenario. Each is stored under the command name or abbreviation,
	and its value is a parmeterless routine to execute to do the
	command. This thing is abbreviated as 'C', although the only
	references to it are from 'PARSE_ACTION'.

    when 'PARSE_ACTION' is trying to do a command, it checks for the
	command verb as a property of the player and of the room that
	the player is in. If found, the property is either a string to
	print (with added newline) as the entire effect of the command,
	or is a proc to call to do the command. This allows properties
	on players and in rooms to override the normal interpretation
	of commands, and also allows special commands to be attached to
	players and rooms that are not otherwise available. If the command
	is not found on the player or the room, then 'PARSE_ACTION' will
	call P.getNounPhrase to get an object for the command. If that
	object is either being carried or in the current room, then the
	command is similarly checked for on the object. Only if all of
	these checks fail is the command looked up in the global command
	table, where it must be a proc to call. The command line tail is
	put back before calling such a routine. A command found on an
	object is passed the object itself as a parameter. This allows the
	the use of generic actions on several objects.

    global variables 'true' and 'false' are provided - it is suggested
	that you use them for all boolean values.

    objects are linked in lists (room contents, player carrying) through
	explicit 'next' properties that the scenario must maintain properly
    objects have a 'name' property, which is the single noun of their name
    objects may have an 'invisible' property, in which case they will not
	normally show up in a room contents or player carrying list
    objects may have an 'adjectives' property, which is a blank-separated
	list of the relevant adjectives for the thing
    objects may have a 'desc' property, which can be either a string
	describing the object, or a routine which is passed the object as
	parameter and which returns a string describing the object. Both
	such strings should include proper punctuation, but should not
	include a terminating newline.
    objects may have a 'get' property, which can be either a string to be
	printed if someone tries to pick up the object (the pickup fails)
	or a proc which is called to see if the pickup succeeds. The proc
	will be called with the object as parameter (so generic 'get'
	routines can be used), and the get will only succeed if the proc
	returns a nonzero value (e.g. 'true').
    objects may have a 'drop' routine, which is handled exactly analagous to
	a 'get' property.

    rooms have a 'name' property which is a string which makes sense to
	print in the format  "You are " + room.name + ".\n"
    rooms have a 'contents' property, which heads a list of the objects
	in the room
    rooms may have a 'desc' property for the long description of the room.
	It can be a simple string, which will be printed along with a final
	newline, or a proc which will be called to return a string which is
	the description of the room.
    rooms are linked with properties north, south, east, west, northeast,
	northwest, southeast, southwest, up, down, in and out.

    players have a 'carrying' property which is a list of the objects they
	are carrying
    players have a 'verbose' flag, which is either 1 or 0, controlling
	whether or not they get the full description when they walk into
	a room.
    players may have a 'desc' property, which can be either a simple string
	which will be printed with a newline when someone looks at them,
	or it can be a proc which should yield a string which will be
	printed with a trailing newline.

Some utility routines provided in the scenario:

formatName(obj) - passed an object, returns a properly formatted name of
    that object, containing the object's name and any adjectives

showObjects(desc, list) - if the list is empty or none of the objects in it
    are visible, do nothing and return 'true', otherwise print 'desc' and
    then use formatName to print the name of each visible object in the list,
    indented by two spaces, and return 'false'.

playerIsHere(who) - used only as part of 'showPlayersAndStuff'

showPlayersAndStuff() - print a one-line message saying someone is here
    for each player in the same room as the current player, and also
    use 'showObjects' to print the objects in the player's location.

showHere - print a long description of the player's location

lookAround - print either a long or a short description of what the player
    can see, depending on the player's 'verbose' flag

showOneObject(object) - print a long description of the object

showPlayer(player) - print a long description of the player

tryMove(dir) - attempt to move in the given direction. 'dir' is a string
    which is a standard direction name. The property is looked up in the
    current location. Any result must either be a thing which is the room
    in that direction, a string to print to explain why going in that
    direction doesn't work, or a proc to call to do the entirety of
    going in that direction.

getNounPhrase() - pull a noun phrase out of the current contents of the tail
    buffer. The returned string will have the noun (last word in the tail
    buffer) first, followed by any adjectives given. There will be a single
    space between the words, and one at the end.

showName(objname) - takes an adjective/noun set as returned by
    'getNounPhrase' and returns it back in the normal order, with no
    extra spaces.

matchName(th, pr, objname) - takes a thing, the name of a property to look
    up on that thing, and a string in the form as returned by getNounPhrase.
    Returns 'true' if the value of the property exists, is a string, and one
    of the words in it (it is a series of blank-separated words) is the same
    as the first word in the passed 'objname' (i.e. the noun). This routine
    modifies the tail buffer during its operation.

findObject(list, objname) - look through 'list' for an object whose name
    and any adjectives match the noun/adjectives passed as 'objname'
    (in the format as returned by 'getNounPhrase'). The first matching
    object is returned, and 'nil' is returned if none is found. This
    routine modifies the tail buffer during its operation.

deleteObject(list, object) - if the object is in the list, then it is
    deleted from that list. The head of the resulting list is returned
    (which is needed since the object could be at the head of the list
    and all parameters are strictly call-by-value).

findPlayerHere(name) - name is in the 'getNounPhrase' format. If there
    is a player by that name in the same location as the current player,
    then return that player, otherwise return nil.

genericVerb(verbString, propName) - this utility can be used to easily
    implement additional simple verbs. A call to it can be used as the
    entire body for a new command in 'THING_COMMANDS'. It is passed the
    command that was used by the user (or a normalized form of it), and
    the name of a property to look up on objects. It will parse off a
    noun phrase, and attempt to find some object matching that in the
    current room or in the player's inventory. If such an object is found,
    then it will check for the property on that object. If found, the
    property must be either a string to print (with trailing newline
    added) or a proc to call with the object as parameter, as the entire
    action of applying the command to the object. If the object has no such
    property then a message saying that you can't apply the verb to the
    object is printed. If no matching object is found, a message indicating
    so is printed.


Things To Do

This section describes some of the many weakness of ToyMUD, and suggests
some things that you may want to do about them. Note that I do NOT plan
on doing any of these things or on being a coordinator for changes and
upgrades. My interest in ToyMUD has mainly been in how small it can be
and still do what I need it to do.

Add boolean 'and', 'or' and 'not'

    This shouldn't take more than an hour. Add the tokens to TokenType_t.
    Add the words to ReservedWords. Add a couple of routines to the
    recursive descent parser/interpreter between 'parseComparison' and
    'parseExpression', and make 'parseExpression' call the top new one.
    [See! I set it up just so you could easily do this.]

Invisibility

    Add the concept of invisible players to the scenario. This should be
    easy - just fix P.playerIsHere and P.showPlayersAndStuff, add commands
    to set/unset the flag on the current player, and modify 'say' and 'pose'
    appropriately.

Darkness

    Add the concept of darkness to the world. This would involve adding a
    flag to rooms that are to be dark, perhaps adding a flag to objects and/or
    players that give off light, modifying P.showHere to take the flags into
    account, and modifying several of the user commands to perhaps fail in
    the dark, but at least not provide visual indication of things being done.

More multiplayer stuff

    Actions that a player can do should be seen by others in that location.
    This would include actions like getting and dropping things, operating
    things, etc. Examine the code and add appropriate messages. Pay attention
    to player invisibility and darkness if you have added them.
