[comp.sources.games] v11i013: tinymud2 - user-extendible multi-user adventure

billr@saab.CNA.TEK.COM (Bill Randle) (07/30/90)

Submitted-by: James Aspnes <asp@cs.cmu.edu>
Posting-number: Volume 11, Issue 13
Archive-name: tinymud2/Part09
Supersedes: tinymud: Volume 8, Issue 80-83



#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 9 (of 10)."
# Contents:  CHANGES compress.c create.c db.h match.c move.c
#   predicates.c speech.c
# Wrapped by billr@saab on Fri Jul 27 15:27:50 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'CHANGES' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'CHANGES'\"
else
echo shar: Extracting \"'CHANGES'\" \(7507 characters\)
sed "s/^X//" >'CHANGES' <<'END_OF_FILE'
X------------------------------------------------------------------------
X
XChanges in Islandia Version (1.5.4) by Fuzzy (Michael Mauldin)
X
X25-Jun-90	Allow people to drop exits they are carrying that
X		have location -1 (this fixes a problem with exits
X		that get recycled while they are carried).
X
X9-Jun-90	Added failure message for whispers to objects
X		Made DUMPCORE on error the default (writes DB,
X		  then calls abort() with SIGILL enabled).  Set
X		  -DNODUMPCORE to inhibit core dumps.
X		Unified Islandia & TinyHELL sources, with switch
X		  in config.h
X		Added Virus (Robert Hood's) port concentrator.
X		Added connect.txt, for messages before the connect.
X		
X		There are now 5 text files associated with TinyMUD:
X			connect.txt	before logging in
X			motd.txt	after logging in
X			tinker.txt	after logging in for tinkers
X			news.txt	News command
X			help.txt	Help command
X
X17-Jun-90	Added Virus's concentrator code.
X		Fixed extract to allow player names instead of numbers,
X		  and added special commands: reachable, players,
X		  norecycle a<num> b<num>
X		
X------------------------------------------------------------------------
X
XChanges in Islandia/Hell Version (1.5.3C) by Random (Russ Smith)
X
X5-Jun-90	Made ABODE govern dropto
X		Allow players to DARKen objects that they own and are
X		  carrying, but not let them drop DARK objects in an
X		  area they couldn't link to.
X		Added UNWANTED flag, which allows people to @chown
X		  objects to themselves, subject to locking;
X		Made @chown understand 'me'
X		Made @recycle set its object to be UNWANTED
X		Made page and whisper check the WHO list and
X		  respond appropriately.
X
X------------------------------------------------------------------------
X
XChanges in Islandia Version (1.5.3B) by Fuzzy (Michael Mauldin)
X
X31-May-90	Added casts to signal arguments to get rid of
X		annoying warnings from gcc.
X
X28-May-90	Added @count & @recycle...@count is redundant with
X		@stat for players, but it can also be run by
X		non-tinkers, and @count of a room summarizes
X		contents: a new feature.  Note that @count is
X		less work than @find, because only the db array
X		is used, no strings are referenced.
X
X		Also fixed bug in extract:
X
X			extract all -<player>
X
X		now extracts player and his contents...before,
X		a bug/feature/omission caused only the player
X		to be extracted.
X
X		Syntax:
X
X		@count <player>
X		@count <room>
X
X		@recycle <thing> = <recipient>
X		@recycle <thing>		defaults to Recycler.
X
X		Recycling an object destroys its strings, and chowns
X		the object to a player called Recycler.  The idea is
X		that periodically you can "extract all -<recycler-num>"
X		to remove the recycled objects.
X
X		Added code to extract to check for isolated rooms
X		(no entrances, or no entrances, exits, or contents).
X
X		Fixed bug/feature in extract that caused it to complain
X		aboute unlinked exits being carried by players.  It no
X		longer complains about them.
X
X28-May-90	Added NOFAKES switch to forbid use of names that
X		are really first words from important system messages
X		(A, An, The, You, Your, Going, Huh?)
X
X25-May-90:	Added code to @bobble to chown all the players
X		objects to another user.
X
X20-May-90:	Added ROBOT flag...enables OUTPUTPREFIX/SUFFIX,
X		prevents ROBOT players from getting objects,
X		using exits, or entering rooms with ROBOT set.
X
X		Changed Wizards to Tinkers, Temples to Junkpiles,
X		and 'sacrifice' to 'donate'.  Added special
X		MOTD for Tinkers (file ../lib/tinker.txt).
X
X		Also changed @toad to @bobble.
X
X		Fixed problem with @link on links that are
X		set to *home*.
X
X		Fixed security hole that allowed a Wizard to
X		@force God...thus preventing
X
X		    @force Wizard = @set *Apprentice = WIZARD
X
X		Changed game.c so that on errors both a database
X		dump and a regular core dump are written.
X
X17-May-90:	Fixed bug where @name guest = foo bar caused
X		a crash because guest has no password (file set.c)
X
X		Added MOTD function, file ../lib/motd.txt
X		(files help.c, interface.c)
X
X		Added caching of hostnames so that Tinkers can do a
X		WHO without bogging down the server.
X
X11-May-90	Added Random's "page <name> = <msg>" command, to allow
X		sending messages simply...people otherwise used
X		
X		   @name me = <msg> / @page <name> / @name me = <desc>
X
X		anyway, so why not make it easy.
X
X------------------------------------------------------------------------
X
XChanges in Firefoot version (1.5.3A) (Scott Goehring):
X
XAdded code to detach netmud from the terminal on startup (-DDETACH).
X
XNetmud now writes to a log file instead of stderr when detached.
XSIGUSR2 will close and reopen the log file.
X
XSIGTERM and SIGINT now cause netmud to shutdown cleanly instead of
Xpanicking (good for when your system is /etc/shutdown'd).
X
XAdded parallel compile support to the makefile (if you're on a
XSequent, compile with 'make P=\&').
X
XLog file entries are now preceded by the time of day.
X
XThe status dumper (SIGUSR1) has been removed.
X
XAdded Random's preregistration support (disabled create, @pcreate).
X
XAdded OJ's examine hack.
X
XAdded Random's @owned command.
X
XPanics now dump the contents of the sigcontext struct in hex to the
Xlogfile for debugging purposes.
X
X
XChanges in version 1.5.3-FF.1 (Scott Goehring):
X
XWHO can take an argument; only names with the same prefix as the
Xargument are displayed.
X
XLINK_OK code modified; ABODE flag added.
X
XPlayer may select format and order of WHO listing.
X
X!-bug fixed properly.
X
X------------------------------------------------------------------------
X
XChanges in version 1.5.3:
X
XGripes now get sent immediately to GOD when GOD_PRIV is defined.  They
Xcan be blocked by setting HAVEN.
X
XLimited @newpassword command to GOD when GOD_PRIV is defined.  Otherwise
Xany WIZARD could subvert GOD.
X
XAdded special case to do_name for changing the case of a player name.
X
XAdded interface changes, GOD_PRIV hacks, and HAVEN bit. [These Changes
Xfrom Random at TinyHELL]
X
XGot rid of LOOKUP_COST; replaced it with special FIND_COST and PAGE_COST.  
Xfind defaults to being very expensive.
X
XAdded hash table for player name lookups.  This change should
Xeliminate a lot of thrashing.
X
XFixed ok_name test so that ! can appear after the beginning of a name.
X
X
XChanges in version 1.5.2:
X
XAdded Stephen White's pronoun substitution code under #ifdef GENDER.
X
XChanged test for setting a created object's home to current room to
Xrequire control of the room instead of linkability.
X
XObjects sent to rooms with @teleport go through dropto's.
X
XBoolean operators can no longer appear in names.
X
XAdded rudimentary string compression under #ifdef COMPRESS.
X
XFixed missing OUTPUTPREFIX and OUTPUTSUFFIX on WHO command in interface.
X
XFixed various small bugs not caught in 1.5.1.
X
X
XChanges in version 1.5.1:
X
X@newpassword command added.  Password checking added.  1p charge for
Xchanging your own name eliminated to prevent confusion.
X
XDatabase structure modified to allow for boolean expressions as keys.
XUSE_VFORK, DB_INITIAL_SIZE, and DB_DOUBLING #defines added to allow
Xfor greater system-dependent configuration.
X
XWhisper command added.
X
XName formatting centralized.  Names now include flags.
X
Xok_player_name() modified to reject names longer than
XPLAYER_NAME_LIMIT.
X
XBoolean lock code added.
X
X
XChanges in version 1.4.2:
X
XSmall bug fixes; okname() modified to reject names containing
XARG_DELIMITER, democratized teleport added under #ifndef
XRESTRICTED_TELEPORT.
X
X
XChanges in version 1.4.1:
X
XRadically rewritten interface.c with fair command processing, command limits.
X
X------------------------------------------------------------------------
END_OF_FILE
if test 7507 -ne `wc -c <'CHANGES'`; then
    echo shar: \"'CHANGES'\" unpacked with wrong size!
fi
# end of 'CHANGES'
fi
if test -f 'compress.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'compress.c'\"
else
echo shar: Extracting \"'compress.c'\" \(2725 characters\)
sed "s/^X//" >'compress.c' <<'END_OF_FILE'
X/* Compression routines */
X
X/* These use a pathetically simple encoding that takes advantage of the */
X/* eighth bit on a char; if you are using an international character set, */
X/* they may need substantial patching. */
X
X#ifdef COMPRESS
X
X#define BUFFER_LEN 16384	/* nice big buffer */
X
X#define TOKEN_BIT 0x80		/* if on, it's a token */
X#define TOKEN_MASK 0x7f		/* for stripping out token value */
X#define NUM_TOKENS (128)
X#define MAX_CHAR (128)
X
X/* Top 128 bigrams in the CMU TinyMUD database as of 2/13/90 */ 
Xstatic const char *tokens[NUM_TOKENS] = {
X    "e ", " t", "th", "he", "s ", " a", "ou", "in",
X    "t ", " s", "er", "d ", "re", "an", "n ", " i",
X    " o", "es", "st", "to", "or", "nd", "o ", "ar",
X    "r ", ", ", "on", " b", "ea", "it", "u ", " w",
X    "ng", "le", "is", "te", "en", "at", " c", "y ",
X    "ro", " f", "oo", "al", ". ", "a ", " d", "ut",
X    " h", "se", "nt", "ll", "g ", "yo", " l", " y",
X    " p", "ve", "f ", "as", "om", "of", "ha", "ed",
X    "h ", "hi", " r", "lo", "Yo", " m", "ne", "l ",
X    "li", "de", "el", "ta", "wa", "ri", "ee", "ti",
X    "no", "do", "Th", " e", "ck", "ur", "ow", "la",
X    "ac", "et", "me", "il", " g", "ra", "co", "ch",
X    "ma", "un", "so", "rt", "ai", "ce", "ic", "be",
X    " n", "k ", "ge", "ot", "si", "pe", "tr", "wi",
X    "e.", "ca", "rs", "ly", "ad", "we", "bo", "ho",
X    "ir", "fo", "ke", "us", "m ", " T", "di", ".." };
X
Xstatic char token_table[MAX_CHAR][MAX_CHAR];
Xstatic int table_initialized = 0;
X
Xstatic void init_compress(void)
X{
X    int i;
X    int j;
X
X    for(i = 0; i < MAX_CHAR; i++) {
X	for(j = 0; j < MAX_CHAR; j++) {
X	    token_table[i][j] = 0;
X	}
X    }
X
X    for(i = 0; i < NUM_TOKENS; i++) {
X	token_table[tokens[i][0]][tokens[i][1]] = i | TOKEN_BIT;
X    }
X
X    table_initialized = 1;
X}
X
Xstatic int compressed(const char *s)
X{
X    while(*s) {
X	if(*s++ & TOKEN_BIT) return 1;
X    }
X    return 0;
X}
X
Xconst char *compress(const char *s)
X{
X    static char buf[BUFFER_LEN];
X    char *to;
X    char token;
X
X    if(!table_initialized) init_compress();
X
X    if(compressed(s)) return s;	/* already compressed */
X
X    /* tokenize the first characters */
X    for(to = buf; s[0] && s[1]; to++) {
X	if(token = token_table[s[0]][s[1]]) {
X	    *to = token;
X	    s += 2;
X	} else {
X	    *to = s[0];
X	    s++;
X	}
X    }
X
X    /* copy the last character (if any) and null */
X    while(*to++ = *s++);
X
X    return buf;
X}
X    
Xconst char *uncompress(const char *s)
X{
X    static char buf[BUFFER_LEN];
X    char *to;
X    const char *token;
X
X    for(to = buf; *s; s++) {
X	if(*s & TOKEN_BIT) {
X	    token = tokens[*s & TOKEN_MASK];
X	    *to++ = *token++;
X	    *to++ = *token;
X	} else {
X	    *to++ = *s;
X	}
X    }
X
X    *to++ = *s;
X
X    return buf;
X}
X
X#endif /* COMPRESS */
X	    
END_OF_FILE
if test 2725 -ne `wc -c <'compress.c'`; then
    echo shar: \"'compress.c'\" unpacked with wrong size!
fi
# end of 'compress.c'
fi
if test -f 'create.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'create.c'\"
else
echo shar: Extracting \"'create.c'\" \(8647 characters\)
sed "s/^X//" >'create.c' <<'END_OF_FILE'
X#include "copyright.h"
X
X/* Commands that create new objects */
X
X#include "db.h"
X#include "config.h"
X#include "interface.h"
X#include "externs.h"
X
X/* utility for open and link */
Xstatic dbref parse_linkable_room(dbref player, object_flag_type thing,
X				 const char *room_name)
X{
X    dbref room;
X
X    /* skip leading NUMBER_TOKEN if any */
X    if(*room_name == NUMBER_TOKEN) room_name++;
X
X    /* parse room */
X    if(!string_compare(room_name, "here")) {
X	room = db[player].location;
X    } else if(!string_compare(room_name, "home")) {
X	return HOME;		/* HOME is always linkable */
X    } else {
X	room = parse_dbref(room_name);
X    }
X
X    /* check room */
X    if(room < 0 || room >= db_top
X       || Typeof(room) != TYPE_ROOM) {
X	notify(player, "That's not a room!");
X	return NOTHING;
X    } else if(!can_link_to(player, thing, room)) {
X	notify(player, "You can't link to that.");
X	return NOTHING;
X    } else {
X	return room;
X    }
X}
X
X/* use this to create an exit */
Xvoid do_open(dbref player, const char *direction, const char *linkto)
X{
X    dbref loc;
X    dbref exit;
X
X#ifdef RESTRICTED_BUILDING
X    if(!Builder(player)) {
X	notify(player, "That command is restricted to authorized builders.");
X	return;
X    }
X#endif /* RESTRICTED_BUILDING */
X
X    if((loc = getloc(player)) == NOTHING) return;
X    if(!*direction) {
X	notify(player, "Open where?");
X	return;
X    } else if(!ok_name(direction)) {
X	notify(player, "That's a strange name for an exit!");
X	return;
X    }
X
X    if(!controls(player, loc)) {
X	notify(player, "Permission denied.");
X    } else if(!payfor(player, EXIT_COST)) {
X	notify(player,
X	       "Sorry, you don't have enough pennies to open an exit.");
X    } else {
X	/* create the exit */
X	exit = new_object();
X
X	/* initialize everything */
X	db[exit].name = alloc_string(direction);
X	db[exit].owner = player;
X	db[exit].flags = TYPE_EXIT;
X
X	/* link it in */
X	PUSH(exit, db[loc].exits);
X
X	/* and we're done */
X	notify(player, "Opened.");
X
X	/* check second arg to see if we should do a link */
X	if(*linkto != '\0') {
X	    notify(player, "Trying to link...");
X	    if((loc = parse_linkable_room(player, TYPE_EXIT, linkto)) !=
X	       NOTHING) {
X		if(!payfor(player, LINK_COST)) {
X		    notify(player, "You don't have enough pennies to link.");
X		} else {
X		    /* it's ok, link it */
X		    db[exit].location = loc;
X		    notify(player, "Linked.");
X		}
X	    }
X	}
X    }
X}
X
X/* use this to link to a room that you own */
X/* it seizes ownership of the exit */
X/* costs 1 penny */
X/* plus a penny transferred to the exit owner if they aren't you */
X/* you must own the linked-to room AND specify it by room number */
Xvoid do_link(dbref player, const char *name, const char *room_name)
X{
X    dbref thing;
X    dbref room;
X
X    init_match(player, name, TYPE_EXIT);
X    match_exit();
X    match_neighbor();
X    match_possession();
X    match_me();
X    match_here();
X    if(Wizard(player)) {
X	match_absolute();
X	match_player();
X    }
X
X    if((thing = noisy_match_result()) != NOTHING) {
X	if((room = parse_linkable_room(player, Typeof(thing), room_name)) ==
X	   NOTHING)
X	    return;
X	switch(Typeof(thing)) {
X	  case TYPE_EXIT:
X	    /* we're ok, check the usual stuff */
X	    if(db[thing].location != NOTHING) {
X		if(controls(player, thing)) {
X
X		    /*
X		     * Changed 5/18/90 Fuzzy - exits linked to *home*
X		     * break 'Typeof() call'
X		     */
X
X		    if(db[thing].location >= 0 &&
X		       Typeof(db[thing].location) == TYPE_PLAYER) {
X			notify(player, "That exit is being carried.");
X		    } else {
X			notify(player, "That exit is already linked.");
X		    }
X		} else {
X		    notify(player, "Permission denied.");
X		}
X	    } else {
X		/* handle costs */
X		if(db[thing].owner == player) {
X		    if(!payfor(player, LINK_COST)) {
X			notify(player,
X			       "It costs a penny to link this exit.");
X			return;
X		    }
X		} else {
X		    if(!payfor(player, LINK_COST + EXIT_COST)) {
X			notify(player,
X			       "It costs two pennies to link this exit.");
X			return;
X#ifdef RESTRICTED_BUILDING
X		    } else if(!Builder(player)) {
X			notify(player,
X			       "Only authorized builders may seize exits.");
X#endif /* RESTRICTED_BUILDING */			
X		    } else {
X			/* pay the owner for his loss */
X			db[db[thing].owner].pennies += EXIT_COST;
X		    }
X		}
X
X		/* link has been validated and paid for; do it */
X		db[thing].owner = player;
X		db[thing].location = room;
X
X		/* notify the player */
X		notify(player, "Linked.");
X	    }
X	    break;
X	  case TYPE_PLAYER:
X	  case TYPE_THING:
X	    if(!controls(player, thing)) {
X		notify(player, "Permission denied.");
X	    } else if(room == HOME) {
X		notify(player, "Can't set home to home.");
X	    } else {
X		/* do the link */
X		db[thing].exits = room; /* home */
X		notify(player, "Home set.");
X	    }
X	    break;
X	  case TYPE_ROOM:
X	    if(!controls(player, thing)) {
X		notify(player, "Permission denied.");
X	    } else {
X		/* do the link, in location */
X		db[thing].location = room; /* dropto */
X		notify(player, "Dropto set.");
X	    }
X	    break;
X	  default:
X	    notify(player, "Internal error: weird object type.");
X	    writelog("PANIC weird object: Typeof(%d) = %d\n",
X		    thing, Typeof(thing));
X	    break;
X	}
X    }
X}
X
X/* use this to create a room */
Xvoid do_dig(dbref player, const char *name)
X{
X    dbref room;
X    char buf[BUFFER_LEN];
X
X#ifdef RESTRICTED_BUILDING
X    if(!Builder(player)) {
X	notify(player, "That command is restricted to authorized builders.");
X	return;
X    }
X#endif /* RESTRICTED_BUILDING */
X
X    /* we don't need to know player's location!  hooray! */
X    if(*name == '\0') {
X	notify(player, "Dig what?");
X    } else if(!ok_name(name)) {
X	notify(player, "That's a silly name for a room!");
X    } else if(!payfor(player, ROOM_COST)) {
X	notify(player, "Sorry, you don't have enough pennies to dig a room.");
X    } else {
X	room = new_object();
X
X	/* Initialize everything */
X	db[room].name = alloc_string(name);
X	db[room].owner = player;
X	db[room].flags = TYPE_ROOM;
X
X	sprintf(buf, "%s created with room number %d.", name, room);
X	notify(player, buf);
X    }
X}
X
X/* use this to create an object */
Xvoid do_create(dbref player, char *name, int cost)
X{
X    dbref loc;
X    dbref thing;
X
X#ifdef RESTRICTED_BUILDING
X    if(!Builder(player)) {
X	notify(player, "That command is restricted to authorized builders.");
X	return;
X    }
X#endif /* RESTRICTED_BUILDING */
X
X    if(*name == '\0') {
X	notify(player, "Create what?");
X	return;
X    } else if(!ok_name(name)) {
X	notify(player, "That's a silly name for a thing!");
X	return;
X    } else if(cost < 0) {
X	notify(player, "You can't create an object for less than nothing!");
X	return;
X    } else if(cost < OBJECT_COST) {
X	cost = OBJECT_COST;
X    }
X
X    if(!payfor(player, cost)) {
X	notify(player, "Sorry, you don't have enough pennies.");
X    } else {
X	/* create the object */
X	thing = new_object();
X
X	/* initialize everything */
X	db[thing].name = alloc_string(name);
X	db[thing].location = player;
X	db[thing].owner = player;
X	db[thing].pennies = OBJECT_ENDOWMENT(cost);
X	db[thing].flags = TYPE_THING;
X
X	/* endow the object */
X	if(db[thing].pennies > MAX_OBJECT_ENDOWMENT) {
X	    db[thing].pennies = MAX_OBJECT_ENDOWMENT;
X	}
X
X	/* home is here (if we can link to it) or player's home */
X	if((loc = db[player].location) != NOTHING
X	   && controls(player, loc)) {
X	    db[thing].exits = loc;	/* home */
X	} else {
X	    db[thing].exits = db[player].exits;	/* home */
X	}
X
X	/* link it in */
X	PUSH(thing, db[player].contents);
X
X	/* and we're done */
X	notify(player, "Created.");
X    }
X}
X
X#ifdef REGISTRATION
Xvoid do_pcreate (dbref player, char *newplayer, char *newpass)
X{
X    dbref ptmp;
X
X#ifdef GOD_MODE && GOD_ONLY_PCREATE
X    if (!God(player))
X#ifndef TINKER
X        notify (player, "Only GOD can create a new player.");
X#else TINKER
X        notify (player, "Only the Master Tinker can create a new player.");
X#endif TINKER
X#else GOD_MODE && GOD_ONLY_PCREATE
X    if (!Wizard(player))
X#ifndef TINKER
X	notify (player, "Only a Wizard can create a new player.");
X#else TINKER
X	notify (player, "Only a Tinker can create a new player.");
X#endif TINKER
X#endif GOD_MODE && GOD_ONLY_PCREATE
X    else if (!*newplayer || !*newpass)
X	notify (player, "You must specify name and password.");
X    else {
X        ptmp = create_player (newplayer, newpass);
X	if (ptmp == NOTHING) {
X	    notify(player, "Either there is already a player with that name, or that name is illegal.");
X	    writelog("FAILED CREATE %s by %s\n",newplayer,db[player].name);
X	} else {
X	    char buf[512];
X	    sprintf(buf, "%s created as object #%d.",db[ptmp].name,ptmp);
X	    notify(player, buf);
X	    writelog("CREATED %s(%d) by %s\n",db[ptmp].name,ptmp,
X		     db[player].name);
X	}
X    }
X}
X#endif REGISTRATION
END_OF_FILE
if test 8647 -ne `wc -c <'create.c'`; then
    echo shar: \"'create.c'\" unpacked with wrong size!
fi
# end of 'create.c'
fi
if test -f 'db.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'db.h'\"
else
echo shar: Extracting \"'db.h'\" \(5703 characters\)
sed "s/^X//" >'db.h' <<'END_OF_FILE'
X#include "copyright.h"
X
X#ifndef __DB_H
X#define __DB_H
X#include <stdio.h>
X
Xextern void *malloc(unsigned long);
Xextern void *realloc(void *, unsigned long);
Xextern void free(void *);
X
X#ifdef TEST_MALLOC
Xextern int malloc_count;
X#define malloc(x) (malloc_count++, malloc(x))
X#define free(x) (malloc_count--, free(x))
X#endif /* TEST_MALLOC */
X
Xtypedef int dbref;		/* offset into db */
X
X#define TYPE_ROOM 	0x0
X#define TYPE_THING 	0x1
X#define TYPE_EXIT 	0x2
X#define TYPE_PLAYER 	0x3
X#define NOTYPE		0x7	/* no particular type */
X#define TYPE_MASK 	0x7	/* room for expansion */
X#define ANTILOCK	0x8	/* negates key (*OBSOLETE*) */
X#define WIZARD		0x10	/* gets automatic control */
X#define LINK_OK		0x20	/* anybody can link exits to this room */
X#define DARK		0x40	/* contents of room are not printed */
X#define TEMPLE		0x80	/* objects dropped in this room go home */
X#define STICKY		0x100	/* this object goes home when dropped */
X
X#ifdef RESTRICTED_BUILDING
X#define BUILDER		0x200	/* this player can use construction commands */
X#endif /* RESTRICTED_BUILDING */
X
X#define HAVEN           0x400   /* this room prohibits killing */
X#define ABODE           0x800   /* can link objects or players here */
X
X#ifdef GENDER
X#define GENDER_MASK	0x3000	/* 2 bits of gender */
X#define GENDER_SHIFT	12	/* 0x1000 is 12 bits over (for shifting) */
X#define GENDER_UNASSIGNED	0x0	/* unassigned - the default */
X#define GENDER_NEUTER	0x1	/* neuter */
X#define GENDER_FEMALE	0x2	/* for women */
X#define GENDER_MALE	0x3	/* for men */
X
X#ifdef ROBOT_MODE
X#define ROBOT		0x4000  /* Can set OUTPUTPREFIX */
X#endif ROBOT_MODE
X
X#define UNWANTED        0x8000  /* can be chowned */
X
X#define TABULAR_WHO     0x10000
X#define REVERSED_WHO    0x20000
X
X#define Genderof(x) ((db[(x)].flags & GENDER_MASK) >> GENDER_SHIFT)
X#endif /* GENDER */
X
Xtypedef int object_flag_type;
X
X#define Flag(x,f) ((db[(x)].flags & (f)) != 0)
X
X#define Typeof(x) (db[(x)].flags & TYPE_MASK)
X#define Wizard(x) ((db[(x)].flags & WIZARD) != 0)
X#ifdef ROBOT_MODE
X#define Robot(x) ((db[(x)].flags & ROBOT) != 0)
X#endif ROBOT_MODE
X#define Dark(x) ((db[(x)].flags & DARK) != 0)
X#ifdef GOD_PRIV
X#define GOD ((dbref)1)
X#define	God(x) ((x)==GOD)
X#endif GOD_PRIV
X
X#ifdef RECYCLE
X#define RECYCLER "Recycler"
X#endif
X
X#ifdef RESTRICTED_BUILDING
X#define Builder(x) ((db[(x)].flags & (WIZARD|BUILDER)) != 0)
X#endif /* RESTRICTED_BUILDING */
X
X/* Boolean expressions, for locks */
Xtypedef char boolexp_type;
X
X#define BOOLEXP_AND 0
X#define BOOLEXP_OR 1
X#define BOOLEXP_NOT 2
X#define BOOLEXP_CONST 3
X
Xstruct boolexp {
X  boolexp_type type;
X  struct boolexp *sub1;
X  struct boolexp *sub2;
X  dbref thing;
X};
X
X#define TRUE_BOOLEXP ((struct boolexp *) 0)
X
X/* special dbref's */
X#define NOTHING (-1)		/* null dbref */
X#define AMBIGUOUS (-2)		/* multiple possibilities, for matchers */
X#define HOME (-3)		/* virtual room, represents mover's home */
X
Xstruct object {
X    const char *name;			
X    const char *description;		
X    dbref location;		/* pointer to container */
X				/* for exits, pointer to destination */
X    dbref contents;		/* pointer to first item */
X    dbref exits;		/* pointer to first exit for rooms */
X    				/* pointer to home for things and players */
X    dbref next;			/* pointer to next in contents/exits chain */
X
X    /* the following are used for pickups for things, entry for exits */
X    struct boolexp *key;	/* if not NOTHING, must have this to do op */
X    const char *fail_message;		/* what you see if op fails */
X    const char *succ_message;		/* what you see if op succeeds */
X    /* other messages get your name prepended, so if your name is "Foo", */
X    /* and osuccess = "disappears in a blast of gamma radiation." */
X    /* then others see "Foo disappears in a blast of gamma radiation." */
X    /* (At some point I may put in Maven-style %-substitutions.) */
X    const char *ofail;		/* what others see if op fails */
X    const char *osuccess;	/* what others see if op succeeds */
X
X    dbref owner;		/* who controls this object */
X    int pennies;		/* number of pennies object contains */
X    object_flag_type flags;
X    const char *password;	/* password for players */
X};
X
Xextern struct object *db;
Xextern dbref db_top;
X
Xextern const char *alloc_string(const char *s);
X
Xextern dbref new_object();	/* return a new object */
X
Xextern dbref getref (FILE *);   /* Read a database reference from a file. */
X
Xextern void putref (FILE *, dbref); /* Write one ref to the file */
X
Xextern struct boolexp *getboolexp(FILE *); /* get a boolexp */
Xextern void putboolexp(FILE *, struct boolexp *); /* put a boolexp */
X
Xextern int db_write_object(FILE *, dbref); /* write one object to file */
X
Xextern dbref db_write(FILE *f);	/* write db to file, return # of objects */
X
Xextern dbref db_read(FILE *f);	/* read db from file, return # of objects */
X				/* Warning: destroys existing db contents! */
X
Xextern void free_boolexp(struct boolexp *);
Xextern void db_free(void);
X
Xextern dbref parse_dbref(const char *);	/* parse a dbref */
X
X#define DOLIST(var, first) \
X  for((var) = (first); (var) != NOTHING; (var) = db[(var)].next)
X#define PUSH(thing, locative) \
X    ((db[(thing)].next = (locative)), (locative) = (thing))
X#define getloc(thing) (db[thing].location)
X
X/*
X  Usage guidelines:
X
X  To refer to objects use db[object_ref].  Pointers to objects may 
X  become invalid after a call to new_object().
X
X  The programmer is responsible for managing storage for string
X  components of entries; db_read will produce malloc'd strings.  The
X  alloc_string routine is provided for generating malloc'd strings
X  duplicates of other strings.  Note that db_free and db_read will
X  attempt to free any non-NULL string that exists in db when they are
X  invoked.  
X*/
X#endif /* __DB_H */
END_OF_FILE
if test 5703 -ne `wc -c <'db.h'`; then
    echo shar: \"'db.h'\" unpacked with wrong size!
fi
# end of 'db.h'
fi
if test -f 'match.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'match.c'\"
else
echo shar: Extracting \"'match.c'\" \(5261 characters\)
sed "s/^X//" >'match.c' <<'END_OF_FILE'
X#include "copyright.h"
X
X/* Routines for parsing arguments */
X#include <ctype.h>
X
X#include "db.h"
X#include "config.h"
X#include "match.h"
X
X#define DOWNCASE(x) (isupper(x) ? tolower(x) : (x))
X
Xstatic dbref exact_match = NOTHING;	/* holds result of exact match */
Xstatic int check_keys = 0;	/* if non-zero, check for keys */
Xstatic dbref last_match = NOTHING;	/* holds result of last match */
Xstatic int match_count;		/* holds total number of inexact matches */
Xstatic dbref match_who;	/* player who is being matched around */
Xstatic const char *match_name;	/* name to match */
Xstatic int preferred_type = NOTYPE; /* preferred type */
X
Xvoid init_match(dbref player, const char *name, int type)
X{
X    exact_match = last_match = NOTHING;
X    match_count = 0;
X    match_who = player;
X    match_name = name;
X    check_keys = 0;
X    preferred_type = type;
X}
X
Xvoid init_match_check_keys(dbref player, const char *name, int type)
X{
X    init_match(player, name, type);
X    check_keys = 1;
X}
X
Xstatic dbref choose_thing(dbref thing1, dbref thing2)
X{
X    int has1;
X    int has2;
X
X    if(thing1 == NOTHING) {
X	return thing2;
X    } else if(thing2 == NOTHING) {
X	return thing1;
X    }
X
X    if(preferred_type != NOTYPE) {
X	if(Typeof(thing1) == preferred_type) {
X	    if(Typeof(thing2) != preferred_type) {
X		return thing1;
X	    }
X	} else if(Typeof(thing2) == preferred_type) {
X	    return thing2;
X	}
X    }
X
X    if(check_keys) {
X	has1 = could_doit(match_who, thing1);
X	has2 = could_doit(match_who, thing2);
X
X	if(has1 && !has2) {
X	    return thing1;
X	} else if (has2 && !has1) {
X	    return thing2;
X	}
X	/* else fall through */
X    }
X
X    return (random() % 2 ? thing1 : thing2);
X}
X
Xvoid match_player(void)
X{
X    dbref match;
X    const char *p;
X
X    if(*match_name == LOOKUP_TOKEN) {
X	for(p = match_name + 1; isspace(*p); p++);
X	if((match = lookup_player(p)) != NOTHING) {
X	    exact_match = match;
X	}
X    }
X}
X
X/* returns nnn if name = #nnn, else NOTHING */
Xstatic dbref absolute_name(void)
X{
X    dbref match;
X
X    if(*match_name == NUMBER_TOKEN) {
X	match = parse_dbref(match_name+1);
X	if(match < 0 || match >= db_top) {
X	    return NOTHING;
X	} else {
X	    return match;
X	}
X    } else {
X	return NOTHING;
X    }
X}
X
Xvoid match_absolute(void)
X{
X    dbref match;
X
X    if((match = absolute_name()) != NOTHING) {
X	exact_match = match;
X    }
X}
X
Xvoid match_me(void)
X{
X    if(!string_compare(match_name, "me")) {
X	exact_match = match_who;
X    }
X}
X
Xvoid match_here(void)
X{
X    if(!string_compare(match_name, "here")
X       && db[match_who].location != NOTHING) {
X	exact_match = db[match_who].location;
X    }
X}
X
Xstatic void match_list(dbref first)
X{
X    dbref absolute;
X
X    absolute = absolute_name();
X    if(!controls(match_who, absolute)) absolute = NOTHING;
X
X    DOLIST(first, first) {
X	if(first == absolute) {
X	    exact_match = first;
X	    return;
X	} else if(!string_compare(db[first].name, match_name)) {
X	    /* if there are multiple exact matches, randomly choose one */
X	    exact_match = choose_thing(exact_match, first);
X	} else if(string_match(db[first].name, match_name)) {
X	    last_match = first;
X	    match_count++;
X	}
X    }
X}
X    
Xvoid match_possession(void)
X{
X    match_list(db[match_who].contents);
X}
X
Xvoid match_neighbor(void)
X{
X    dbref loc;
X
X    if((loc = db[match_who].location) != NOTHING) {
X	match_list(db[loc].contents);
X    }
X}
X
Xvoid match_exit(void)
X{
X    dbref loc;
X    dbref exit;
X    dbref absolute;
X    const char *match;
X    const char *p;
X
X    if((loc = db[match_who].location) != NOTHING) {
X	absolute = absolute_name();
X	if(!controls(match_who, absolute)) absolute = NOTHING;
X
X	DOLIST(exit, db[loc].exits) {
X	    if(exit == absolute) {
X		exact_match = exit;
X	    } else {
X		match = db[exit].name;
X		while(*match) {
X		    /* check out this one */
X		    for(p = match_name;
X			(*p
X			 && DOWNCASE(*p) == DOWNCASE(*match)
X			 && *match != EXIT_DELIMITER);
X			p++, match++);
X		    /* did we get it? */
X		    if(*p == '\0') {
X			/* make sure there's nothing afterwards */
X			while(isspace(*match)) match++;
X			if(*match == '\0' || *match == EXIT_DELIMITER) {
X			    /* we got it */
X			    exact_match = choose_thing(exact_match, exit);
X			    goto next_exit;	/* got this match */
X			}
X		    }
X		    /* we didn't get it, find next match */
X		    while(*match && *match++ != EXIT_DELIMITER);
X		    while(isspace(*match)) match++;
X		}
X	    }
X	  next_exit:
X	    ;
X	}
X    }
X}
X
Xvoid match_everything(void)
X{
X    match_exit();
X    match_neighbor();
X    match_possession();
X    match_me();
X    match_here();
X    if(Wizard(match_who)) {
X	match_absolute();
X	match_player();
X    }
X}
X
Xdbref match_result(void)
X{
X    if(exact_match != NOTHING) {
X	return exact_match;
X    } else {
X	switch(match_count) {
X	  case 0:
X	    return NOTHING;
X	  case 1:
X	    return last_match;
X	  default:
X	    return AMBIGUOUS;
X	}
X    }
X}
X	   
X/* use this if you don't care about ambiguity */
Xdbref last_match_result(void)
X{
X    if(exact_match != NOTHING) {
X	return exact_match;
X    } else {
X	return last_match;
X    }
X}
X
Xdbref noisy_match_result(void)
X{
X    dbref match;
X
X    switch(match = match_result()) {
X      case NOTHING:
X	notify(match_who, NOMATCH_MESSAGE);
X	return NOTHING;
X      case AMBIGUOUS:
X	notify(match_who, AMBIGUOUS_MESSAGE);
X	return NOTHING;
X      default:
X	return match;
X    }
X}
X
END_OF_FILE
if test 5261 -ne `wc -c <'match.c'`; then
    echo shar: \"'match.c'\" unpacked with wrong size!
fi
# end of 'match.c'
fi
if test -f 'move.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'move.c'\"
else
echo shar: Extracting \"'move.c'\" \(8534 characters\)
sed "s/^X//" >'move.c' <<'END_OF_FILE'
X#include "copyright.h"
X
X#include "db.h"
X#include "config.h"
X#include "interface.h"
X#include "match.h"
X#include "externs.h"
X
Xvoid moveto(dbref what, dbref where)
X{
X    dbref loc;
X
X    /* remove what from old loc */
X    if((loc = db[what].location) != NOTHING) {
X	db[loc].contents = remove_first(db[loc].contents, what);
X    }
X
X    /* test for special cases */
X    switch(where) {
X      case NOTHING:
X	db[what].location = NOTHING;
X	return;			/* NOTHING doesn't have contents */
X      case HOME:
X	where = db[what].exits;	/* home */
X	break;
X    }
X
X    /* now put what in where */
X    PUSH(what, db[where].contents);
X
X    db[what].location = where;
X}
X
Xstatic void send_contents(dbref loc, dbref dest)
X{
X    dbref first;
X    dbref rest;
X
X    first = db[loc].contents;
X    db[loc].contents = NOTHING;
X
X    /* blast locations of everything in list */
X    DOLIST(rest, first) {
X	db[rest].location = NOTHING;
X    }
X
X    while(first != NOTHING) {
X	rest = db[first].next;
X	if(Typeof(first) != TYPE_THING) {
X	    moveto(first, loc);
X	} else {
X	    moveto(first, (db[first].flags & STICKY) ? HOME : dest);
X	}
X	first = rest;
X    }
X
X    db[loc].contents = reverse(db[loc].contents);
X}
X
Xvoid maybe_dropto(dbref loc, dbref dropto)
X{
X    dbref thing;
X
X    if(loc == dropto) return;	/* bizarre special case */
X
X    /* check for players */
X    DOLIST(thing, db[loc].contents) {
X	if(Typeof(thing) == TYPE_PLAYER) return;
X    }
X    
X    /* no players, send everything to the dropto */
X    send_contents(loc, dropto);
X}
X
Xvoid enter_room(dbref player, dbref loc)
X{
X    dbref old;
X    dbref dropto;
X    char buf[BUFFER_LEN];
X
X    /* check for room == HOME */
X    if(loc == HOME) loc = db[player].exits; /* home */
X
X    /* get old location */
X    old = db[player].location;
X
X    /* check for self-loop */
X    /* self-loops don't do move or other player notification */
X    /* but you still get autolook and penny check */
X    if(loc != old) {
X
X	if(old != NOTHING) {
X	    /* notify others unless DARK */
X	    if(!Dark(old) && !Dark(player)) {
X		sprintf(buf, "%s has left.", db[player].name);
X		notify_except(db[old].contents, player, buf);
X	    }
X	}
X
X	/* go there */
X	moveto(player, loc);
X
X	/* if old location has STICKY dropto, send stuff through it */
X	if(old != NOTHING
X	   && (dropto = db[old].location) != NOTHING
X	   && (db[old].flags & STICKY)) {
X	    maybe_dropto(old, dropto);
X	}
X
X	/* tell other folks in new location if not DARK */
X	if(!Dark(loc) && !Dark(player)) {
X	    sprintf(buf, "%s has arrived.", db[player].name);
X	    notify_except(db[loc].contents, player, buf);
X	}
X    }
X
X    /* autolook */
X    look_room(player, loc);
X
X    /* check for pennies */
X    if(!controls(player, loc)
X       && db[player].pennies <= MAX_PENNIES
X       && random() % PENNY_RATE == 0) {
X	notify(player, "You found a penny!");
X	db[player].pennies++;
X    }
X}
X	    
Xvoid send_home(dbref thing)
X{
X    switch(Typeof(thing)) {
X      case TYPE_PLAYER:
X	/* send his possessions home first! */
X	/* that way he sees them when he arrives */
X	send_contents(thing, HOME);
X	enter_room(thing, db[thing].exits); /* home */
X	break;
X      case TYPE_THING:
X	moveto(thing, db[thing].exits);	/* home */
X	break;
X      default:
X	/* no effect */
X	break;
X    }
X}
X    
Xint can_move(dbref player, const char *direction)
X{
X    if(!string_compare(direction, "home")) return 1;
X
X    /* otherwise match on exits */
X    init_match(player, direction, TYPE_EXIT);
X    match_exit();
X    return(last_match_result() != NOTHING);
X}
X
Xvoid do_move(dbref player, const char *direction)
X{
X    dbref exit;
X    dbref loc;
X    char buf[BUFFER_LEN];
X
X    if(!string_compare(direction, "home")) {
X	/* send him home */
X	/* but steal all his possessions */
X	if((loc = db[player].location) != NOTHING) {
X	    /* tell everybody else */
X	    sprintf(buf, "%s goes home.", db[player].name);
X	    notify_except(db[loc].contents, player, buf);
X	}
X	/* give the player the messages */
X	notify(player, "There's no place like home...");
X	notify(player, "There's no place like home...");
X	notify(player, "There's no place like home...");
X	notify(player, "You wake up back home, without your possessions.");
X	send_home(player);
X    } else {
X	/* find the exit */
X	init_match_check_keys(player, direction, TYPE_EXIT);
X	match_exit();
X	switch(exit = match_result()) {
X	  case NOTHING:
X	    notify(player, "You can't go that way.");
X	    break;
X	  case AMBIGUOUS:
X	    notify(player, "I don't know which way you mean!");
X	    break;
X	  default:
X	    /* we got one */
X	    /* check to see if we got through */
X	    if(can_doit(player, exit, "You can't go that way.")) {
X		enter_room(player, db[exit].location);
X	    }
X	    break;
X	}
X    }
X}
X
Xvoid do_get(dbref player, const char *what)
X{
X    dbref loc;
X    dbref thing;
X
X    init_match_check_keys(player, what, TYPE_THING);
X    match_neighbor();
X    match_exit();
X    if(Wizard(player)) match_absolute(); /* the wizard has long fingers */
X
X    if((thing = noisy_match_result()) != NOTHING) {
X	if(db[thing].location == player) {
X	    notify(player, "You already have that!");
X	    return;
X	}
X	switch(Typeof(thing)) {
X	  case TYPE_THING:
X	    if(can_doit(player, thing, "You can't pick that up.")) {
X		moveto(thing, player);
X		notify(player, "Taken.");
X	    }
X	    break;
X	  case TYPE_EXIT:
X	    if(!controls(player, thing)) {
X		notify(player, "You can't pick that up.");
X	    } else if(db[thing].location != NOTHING) {
X		notify(player, "You can't pick up a linked exit.");
X#ifdef RESTRICTED_BUILDING
X	    } else if(!Builder(player)) {
X		notify(player, "Only authorized builders may pick up exits.");
X#endif /* RESTRICTED_BUILDING */
X	    } else {
X		/* take it out of location */
X		if((loc = getloc(player)) == NOTHING) return;
X		if(!member(thing, db[loc].exits)) {
X		    notify(player,
X			   "You can't pick up an exit from another room.");
X		    return;
X		}
X		db[loc].exits = remove_first(db[loc].exits, thing);
X		PUSH(thing, db[player].contents);
X		db[thing].location = player;
X		notify(player, "Exit taken.");
X	    }
X	    break;
X	  default:
X	    notify(player, "You can't take that!");
X	    break;
X	}
X    }
X}
X
Xvoid do_drop(dbref player, const char *name)
X{
X    dbref loc;
X    dbref thing;
X    char buf[BUFFER_LEN];
X    int reward;
X
X    if((loc = getloc(player)) == NOTHING) return;    
X
X    init_match(player, name, TYPE_THING);
X    match_possession();
X
X    switch(thing = match_result()) {
X      case NOTHING:
X	notify(player, "You don't have that!");
X	break;
X      case AMBIGUOUS:
X	notify(player, "I don't know which you mean!");
X	break;
X      default:
X	if(db[thing].location != player &&
X	   !(Typeof(thing) == TYPE_EXIT) && db[thing].location == NOTHING) {
X	    /* Should not ever happen. */
X	    notify(player, "You can't drop that.");
X	} else if(Typeof(thing) == TYPE_EXIT) {
X	    /* special behavior for exits */
X	    if(!controls(player, loc)) {
X		notify(player, "You can't put an exit down here.");
X		return;
X	    }
X	    /* else we can put it down */
X	    moveto(thing, NOTHING); /* take it out of the pack */
X	    PUSH(thing, db[loc].exits);
X	    notify(player, "Exit dropped.");
X	} else if(db[loc].flags & TEMPLE) {
X	    /* sacrifice time */
X	    send_home(thing);
X	    sprintf(buf,
X		    "%s is consumed in a burst of flame!", db[thing].name);
X	    notify(player, buf);
X#ifndef TINKER
X	    sprintf(buf, "%s sacrifices %s.", db[player].name, db[thing].name);
X#else   TINKER
X	    sprintf(buf, "%s donates %s.", db[player].name, db[thing].name);
X#endif  TINKER
X	    notify_except(db[loc].contents, player, buf);
X
X	    /* check for reward */
X	    if(!controls(player, thing)) {
X		reward = db[thing].pennies;
X		if(reward < 1 || db[player].pennies > MAX_PENNIES) {
X		    reward = 1;
X		} else if(reward > MAX_OBJECT_ENDOWMENT) {
X		    reward = MAX_OBJECT_ENDOWMENT;
X		}
X
X		db[player].pennies += reward;
X		sprintf(buf,
X			"You have received %d %s for your donation.",
X			reward,
X			reward == 1 ? "penny" : "pennies");
X		notify(player, buf);
X	    }
X	} else if(db[thing].flags & STICKY) {
X	    send_home(thing);
X	    notify(player, "Dropped.");
X	} else if(db[loc].location != NOTHING && !(db[loc].flags & STICKY)) {
X	    /* location has immediate dropto */
X	    moveto(thing, db[loc].location);
X	    notify(player, "Dropped.");
X	} else if((db[thing].flags & DARK) && !can_link_to(player,Typeof(thing),loc)) {
X	    notify(player, "You cannot drop that DARK object here.");
X	} else {
X	    moveto(thing, loc);
X	    notify(player, "Dropped.");
X	    sprintf(buf, "%s dropped %s.", db[player].name, db[thing].name);
X	    notify_except(db[loc].contents, player, buf);
X	}
X	break;
X    }
X}
END_OF_FILE
if test 8534 -ne `wc -c <'move.c'`; then
    echo shar: \"'move.c'\" unpacked with wrong size!
fi
# end of 'move.c'
fi
if test -f 'predicates.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'predicates.c'\"
else
echo shar: Extracting \"'predicates.c'\" \(6557 characters\)
sed "s/^X//" >'predicates.c' <<'END_OF_FILE'
X#include "copyright.h"
X
X/* Predicates for testing various conditions */
X
X#include <ctype.h>
X
X#include "db.h"
X#include "interface.h"
X#include "config.h"
X#include "externs.h"
X
Xvoid pronoun_substitute(char *result, dbref player, const char *str);
X
Xint can_link_to(dbref who, object_flag_type what, dbref where)
X{
X    return(where >= 0 &&
X	   where < db_top &&
X	   Typeof(where) == TYPE_ROOM &&
X	   (controls(who, where) ||
X	    (what == NOTYPE &&
X	     (Flag(where,LINK_OK|ABODE))) ||
X	    (what == TYPE_ROOM &&
X	     (Flag(where,ABODE))) ||
X	    (what == TYPE_EXIT &&
X	     (Flag(where,LINK_OK))) ||
X	    ((what == TYPE_PLAYER || what == TYPE_THING) &&
X#ifdef ROBOT_MODE
X	     (what != TYPE_PLAYER || !Robot(who) || !Robot(where)) &&
X#endif ROBOT_MODE
X	     Flag(where,ABODE))));
X    }
X
X/*
X * Check whether a player can perform an action...robotic players are
X * now implicitly barred from performing actions on things with the
X * robot flag set.	5/18/90 - Fuzzy
X */
X
Xint could_doit(dbref player, dbref thing)
X{
X    if(Typeof(thing) != TYPE_ROOM && db[thing].location == NOTHING) return 0;
X
X#ifdef ROBOT_MODE
X    if(Typeof(thing) != TYPE_PLAYER && Robot(player) && Robot(thing)) return 0;
X    if(Typeof(thing) == TYPE_EXIT && Robot(player) &&
X        db[thing].location >= 0 && Robot(db[thing].location)) return 0;
X#endif ROBOT_MODE
X
X    return(eval_boolexp (player, db[thing].key));
X}
X
Xint can_doit(dbref player, dbref thing, const char *default_fail_msg)
X{
X    dbref loc;
X    char buf[BUFFER_LEN];
X
X    if((loc = getloc(player)) == NOTHING) return 0;
X
X    if(!could_doit(player, thing)) {
X	/* can't do it */
X	if(db[thing].fail_message) {
X	    notify(player, db[thing].fail_message);
X	} else if(default_fail_msg) {
X	    notify(player, default_fail_msg);
X	}
X	
X	if(db[thing].ofail && !Dark(player)) {
X#ifdef GENDER
X	    pronoun_substitute(buf, player, db[thing].ofail);
X#else	    
X	    sprintf(buf, "%s %s", db[player].name, db[thing].ofail);
X#endif /* GENDER */
X	    notify_except(db[loc].contents, player, buf);
X	}
X
X	return 0;
X    } else {
X	/* can do it */
X	if(db[thing].succ_message) {
X	    notify(player, db[thing].succ_message);
X	}
X
X	if(db[thing].osuccess && !Dark(player)) {
X#ifdef GENDER
X	    pronoun_substitute(buf, player, db[thing].osuccess);
X#else
X	    sprintf(buf, "%s %s", db[player].name, db[thing].osuccess);
X#endif /* GENDER */
X	    notify_except(db[loc].contents, player, buf);
X	}
X
X	return 1;
X    }
X}
X
Xint can_see(dbref player, dbref thing, int can_see_loc)
X{
X    if(player == thing || Typeof(thing) == TYPE_EXIT) {
X	return 0;
X    } else if(can_see_loc) {
X	return(!Dark(thing) || controls(player, thing));
X    } else {
X	/* can't see loc */
X	return(controls(player, thing));
X    }
X}
X
Xint controls(dbref who, dbref what)
X{
X    /* Wizard controls everything */
X    /* owners control their stuff */
X    return(what >= 0
X	   && what < db_top
X	   && (Wizard(who)
X	       || who == db[what].owner));
X}
X
Xint can_link(dbref who, dbref what)
X{
X    return((Typeof(what) == TYPE_EXIT && db[what].location == NOTHING)
X	   || controls(who, what));
X}
X
Xint payfor(dbref who, int cost)
X{
X    if(Wizard(who)) {
X	return 1;
X    } else if(db[who].pennies >= cost) {
X	db[who].pennies -= cost;
X	return 1;
X    } else {
X	return 0;
X    }
X}
X
Xint word_start (const char *str, const char let)
X{
X    int chk;
X
X    for (chk = 1; *str; str++) {
X	if (chk && *str == let) return 1;
X	chk = *str == ' ';
X    }
X    return 0;
X}
X
X
Xint ok_name(const char *name)
X{
X    return (name
X	    && *name
X	    && *name != LOOKUP_TOKEN
X	    && *name != NUMBER_TOKEN
X	    && !index(name, ARG_DELIMITER)
X	    && !index(name, AND_TOKEN)
X	    && !index(name, OR_TOKEN)
X	    && !word_start(name, NOT_TOKEN)
X#ifdef NOFAKES
X	    && string_compare(name, "A")
X	    && string_compare(name, "An")
X	    && string_compare(name, "The")
X	    && string_compare(name, "You")
X	    && string_compare(name, "Your")
X	    && string_compare(name, "Going")
X    	    && string_compare(name, "Huh?")
X#endif NOFAKES
X	    && string_compare(name, "me")	    
X	    && string_compare(name, "home")
X	    && string_compare(name, "here"));
X}
X
Xint ok_player_name(const char *name)
X{
X    const char *scan;
X
X    if(!ok_name(name) || strlen(name) > PLAYER_NAME_LIMIT) return 0;
X
X    for(scan = name; *scan; scan++) {
X	if(!(isprint(*scan) && !isspace(*scan))) { /* was isgraph(*scan) */
X	    return 0;
X	}
X    }
X
X    /* lookup name to avoid conflicts */
X    return (lookup_player(name) == NOTHING);
X}
X
Xint ok_password(const char *password)
X{
X    const char *scan;
X
X    if(*password == '\0') return 0;
X
X    for(scan = password; *scan; scan++) {
X	if(!(isprint(*scan) && !isspace(*scan))) {
X	    return 0;
X	}
X    }
X
X    return 1;
X}
X
X#ifdef GENDER
X/*
X * pronoun_substitute()
X *
X * %-type substitutions for pronouns
X *
X * %s/%S for subjective pronouns (he/she/it, He/She/It)
X * %o/%O for objective pronouns (him/her/it, Him/Her/It)
X * %p/%P for possessive pronouns (his/her/its, His/Her/Its)
X * %n    for the player's name.
X */
Xvoid pronoun_substitute(char *result, dbref player, const char *str)
X{
X    char c;
X
X    const static char *subjective[4] = { "", "it", "she", "he" };
X    const static char *possessive[4] = { "", "its", "her", "his" };
X    const static char *objective[4] = { "", "it", "her", "him" };
X
X#ifdef COMPRESS
X    str = uncompress(str);
X#endif /* COMPRESS */
X    strcpy(result, db[player].name);
X    result += strlen(result);
X    *result++ = ' ';
X    while (*str) {
X	if(*str == '%') {
X	    *result = '\0';
X	    c = *(++str);
X	    if (Genderof(player) == GENDER_UNASSIGNED) {
X		switch(c) {
X		  case 'n':
X		  case 'N':
X		  case 'o':
X		  case 'O':
X		  case 's':
X		  case 'S':
X		    strcat(result, db[player].name);
X		    break;
X		  case 'p':
X		  case 'P':
X		    strcat(result, db[player].name);
X		    strcat(result, "'s");
X		    break;
X		  default:
X		    result[0] = *str;
X		    result[1] = 0;
X		    break;
X		}
X		str++;
X		result += strlen(result);
X	    } else {
X		switch (c) {
X		  case 's':
X		  case 'S':
X		    strcat(result, subjective[Genderof(player)]);
X		    break;
X		  case 'p':
X		  case 'P':
X		    strcat(result, possessive[Genderof(player)]);
X		    break;
X		  case 'o':
X		  case 'O':
X		    strcat(result, objective[Genderof(player)]);
X		    break;
X		  case 'n':
X		  case 'N':
X		    strcat(result, db[player].name);
X		    break;
X		  default:
X		    *result = *str;
X		    result[1] = '\0';
X		    break;
X		} 
X		if(isupper(c) && islower(*result)) {
X		    *result = toupper(*result);
X		}
X		
X		result += strlen(result);
X		str++;
X	    }
X	} else {
X	    *result++ = *str++;
X	}
X    }
X    *result = '\0';
X} 
X
X#endif /* GENDER */
END_OF_FILE
if test 6557 -ne `wc -c <'predicates.c'`; then
    echo shar: \"'predicates.c'\" unpacked with wrong size!
fi
# end of 'predicates.c'
fi
if test -f 'speech.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'speech.c'\"
else
echo shar: Extracting \"'speech.c'\" \(5423 characters\)
sed "s/^X//" >'speech.c' <<'END_OF_FILE'
X#include "copyright.h"
X
X/* Commands which involve speaking */
X
X#include "db.h"
X#include "interface.h"
X#include "match.h"
X#include "config.h"
X#include "externs.h"
X
X/* this function is a kludge for regenerating messages split by '=' */
Xconst char *reconstruct_message(const char *arg1, const char *arg2)
X{
X    static char buf[BUFFER_LEN];
X
X    if(arg2 && *arg2) {
X	strcpy(buf, arg1);
X	strcat(buf, " = ");
X	strcat(buf, arg2);
X	return buf;
X    } else {
X	return arg1;
X    }
X}
X
Xvoid do_say(dbref player, const char *arg1, const char *arg2)
X{
X    dbref loc;
X    const char *message;
X    char buf[BUFFER_LEN];
X
X    if((loc = getloc(player)) == NOTHING) return;
X
X    message = reconstruct_message(arg1, arg2);
X
X    /* notify everybody */
X    sprintf(buf, "You say \"%s\"", message);
X    notify(player, buf);
X    sprintf(buf, "%s says \"%s\"", db[player].name, message);
X    notify_except(db[loc].contents, player, buf);
X}
X
Xvoid do_whisper(dbref player, const char *arg1, const char *arg2)
X{
X#ifndef QUIET_WHISPER
X    dbref loc;
X#endif QUIET_WHISPER
X    dbref who;
X    char buf[BUFFER_LEN];
X    char *det;
X    int result;
X
X    init_match(player, arg1, TYPE_PLAYER);
X    match_neighbor();
X    match_me();
X    if(Wizard(player)) {
X	match_absolute();
X	match_player();
X    }
X    switch(who = match_result()) {
X      case NOTHING:
X	notify(player, "Whisper to whom?");
X	break;
X      case AMBIGUOUS:
X	notify(player, "I don't know who you mean!");
X	break;
X      default:
X        if (Typeof(who) == TYPE_PLAYER) {
X	    sprintf(buf, "%s whispers \"%s\"", db[player].name, arg2);
X	    if (notify(who, buf)) {
X		sprintf(buf, "You whisper \"%s\" to %s.", arg2, db[who].name);
X	        notify(player, buf);
X#ifndef QUIET_WHISPER
X		sprintf(buf, "%s whispers something to %s.",
X			db[player].name, db[who].name);
X		if((loc = getloc(player)) != NOTHING) {
X		    notify_except2(db[loc].contents, player, who, buf);
X		}
X#endif /* QUIET_WHISPER */
X	    }
X	    else
X		notify(player, "That person is not connected.");
X	} else {
X	    if (string_prefix (db[who].name, "a ") ||
X	    	string_prefix (db[who].name, "an ") ||
X	    	string_prefix (db[who].name, "the ") ||
X	    	string_prefix (db[who].name, "some ")) {
X		det = "";
X	    } else if (lookup_player (db[who].name) != NOTHING) {
X	       	det = "the thing called ";
X    	    } else {
X		det = "the ";
X	    }
X	    sprintf(buf, "You feel silly about whispering to %s%s.",
X		    det, db[who].name);
X	    notify(player, buf);
X	}
X	break;
X    }
X}
X
Xvoid do_pose(dbref player, const char *arg1, const char *arg2)
X{
X    dbref loc;
X    const char *message;
X    char buf[BUFFER_LEN];
X
X    if((loc = getloc(player)) == NOTHING) return;
X
X    message = reconstruct_message(arg1, arg2);
X
X    /* notify everybody */
X    sprintf(buf, "%s %s", db[player].name, message);
X    notify_except(db[loc].contents, NOTHING, buf);
X}
X
Xvoid do_wall(dbref player, const char *arg1, const char *arg2)
X{
X    dbref i;
X    const char *message;
X    char buf[512];
X
X    message = reconstruct_message(arg1, arg2);
X    if(Wizard(player)) {
X	writelog("WALL from %s(%d): %s\n",
X		db[player].name, player, message);
X	sprintf(buf, "%s shouts \"%s\"", db[player].name, message);
X	for(i = 0; i < db_top; i++) {
X	    if(Typeof(i) == TYPE_PLAYER) {
X		notify(i, buf);
X	    }
X	}
X    } else {
X	notify(player, "But what do you want to do with the wall?");
X    }
X}
X
Xvoid do_gripe(dbref player, const char *arg1, const char *arg2)
X{
X    dbref loc;
X    const char *message;
X
X    loc = db[player].location;
X    message = reconstruct_message(arg1, arg2);
X    writelog("GRIPE from %s(%d) in %s(%d): %s\n",
X	    db[player].name, player,
X	    db[loc].name, loc,
X	    message);
X    fflush(stderr);
X
X#ifdef GOD_PRIV
X    /* try telling GOD about it */
X    if (!Flag(GOD,HAVEN)) {
X	char buf[BUFFER_LEN];
X	sprintf(buf, "%s gripes: \"%s\"",
X		unparse_object(GOD, player), message);
X	notify(GOD, buf);
X    }
X#endif GOD_PRIV    
X
X    notify(player, "Your complaint has been duly noted.");
X}
X
X/* doesn't really belong here, but I couldn't figure out where else */
Xvoid do_page(dbref player, const char *arg1, const char *arg2)
X{
X    char buf[BUFFER_LEN];
X    dbref target;
X    int result;
X
X    if(!payfor(player, PAGE_COST)) {
X	notify(player, "You don't have enough pennies.");
X    } else if((target = lookup_player(arg1)) == NOTHING) {
X	notify(player, "I don't recognize that name.");
X    } else if (db[target].flags & HAVEN) {
X	notify(player, "That player is not accepting pages.");
X    } else if (arg2 && *arg2) {
X	sprintf(buf, "%s pages: %s", db[player].name, arg2);
X	result = notify(target, buf);
X	if (result) 
X	    notify(player, "Your message has been sent.");
X	else
X	    notify(player, "That person is not connected.");
X    } else {
X	sprintf(buf, "You sense that %s is looking for you in %s.",
X		db[player].name, db[db[player].location].name);
X	result = notify(target, buf);
X	if (result)
X	    notify(player, "Your message has been sent.");
X	else
X	    notify(player, "That person is not connected.");
X    }
X}
X
Xvoid notify_except(dbref first, dbref exception, const char *msg)
X{
X    DOLIST (first, first) {
X	if ((db[first].flags & TYPE_MASK) == TYPE_PLAYER
X	    && first != exception) {
X	    notify (first, msg);
X	}
X    }
X}
X
Xvoid notify_except2(dbref first, dbref exc1, dbref exc2, const char *msg)
X{
X    DOLIST (first, first) {
X	if ((db[first].flags & TYPE_MASK) == TYPE_PLAYER
X	    && first != exc1
X	    && first != exc2) {
X	    notify (first, msg);
X	}
X    }
X}
END_OF_FILE
if test 5423 -ne `wc -c <'speech.c'`; then
    echo shar: \"'speech.c'\" unpacked with wrong size!
fi
# end of 'speech.c'
fi
echo shar: End of archive 9 \(of 10\).
cp /dev/null ark9isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 10 archives.
    echo ">>> now type 'sh joinspl.sh'"
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0