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