[comp.sources.games] v11i011: 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 11
Archive-name: tinymud2/Part07
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 7 (of 10)."
# Contents:  db.c game.c restart-cmu tinymud.tex
# Wrapped by billr@saab on Fri Jul 27 15:27:48 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'db.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'db.c'\"
else
echo shar: Extracting \"'db.c'\" \(9028 characters\)
sed "s/^X//" >'db.c' <<'END_OF_FILE'
X#include "copyright.h"
X
X#include <stdio.h>
X#include <ctype.h>
X
X#include "db.h"
X#include "config.h"
X
Xstruct object *db = 0;
Xdbref db_top = 0;
X
X#ifdef TEST_MALLOC
Xint malloc_count = 0;
X#endif /* TEST_MALLOC */
X
X#ifndef DB_INITIAL_SIZE
X#define DB_INITIAL_SIZE 10000
X#endif /* DB_INITIAL_SIZE */
X
X#ifdef DB_DOUBLING
X
Xdbref db_size = DB_INITIAL_SIZE;
X#endif /* DB_DOUBLING */
X
Xconst char *alloc_string(const char *string)
X{
X    char *s;
X
X    /* NULL, "" -> NULL */
X    if(string == 0 || *string == '\0') return 0;
X
X    if((s = (char *) malloc(strlen(string)+1)) == 0) {
X	abort();
X    }
X    strcpy(s, string);
X    return s;
X}
X
X#ifdef DB_DOUBLING
X
Xstatic void db_grow(dbref newtop)
X{
X    struct object *newdb;
X
X    if(newtop > db_top) {
X	db_top = newtop;
X	if(!db) {
X	    /* make the initial one */
X	    db_size = DB_INITIAL_SIZE;
X	    if((db = (struct object *)
X		malloc(db_size * sizeof(struct object))) == 0) {
X		abort();
X	    }
X	}
X	
X	/* maybe grow it */
X	if(db_top > db_size) {
X	    /* make sure it's big enough */
X	    while(db_top > db_size) db_size *= 2;
X	    if((newdb = (struct object *)
X		realloc((void *) db,
X			db_size * sizeof(struct object))) == 0) {
X		abort();
X	    } 
X	    db = newdb;
X	}
X    }
X}
X
X#else /* DB_DOUBLING */
X
Xstatic void db_grow(dbref newtop)
X{
X    struct object *newdb;
X
X    if(newtop > db_top) {
X	db_top = newtop;
X	if(db) {
X	    if((newdb = (struct object *)
X		realloc((void *) db,
X			db_top * sizeof(struct object))) == 0) {
X		abort();
X	    } 
X	    db = newdb;
X	} else {
X	    /* make the initial one */
X	    if((db = (struct object *)
X		malloc(DB_INITIAL_SIZE * sizeof(struct object))) == 0) {
X		abort();
X	    }
X	}
X    }
X}
X#endif /* DB_DOUBLING */
X
Xdbref new_object(void)
X{
X    dbref newobj;
X    struct object *o;
X
X    newobj = db_top;
X    db_grow(db_top + 1);
X
X    /* clear it out */
X    o = db+newobj;
X    o->name = 0;
X    o->description = 0;
X    o->location = NOTHING;
X    o->contents = NOTHING;
X    o->exits = NOTHING;
X    o->next = NOTHING;
X    o->key = TRUE_BOOLEXP;
X    o->fail_message = 0;
X    o->succ_message = 0;
X    o->ofail = 0;
X    o->osuccess = 0;
X    o->owner = NOTHING;
X    o->pennies = 0;
X    /* flags you must initialize yourself */
X    o->password = 0;
X
X    return newobj;
X}
X	
X#define DB_MSGLEN 512
X
Xvoid putref(FILE *f, dbref ref)
X{
X    fprintf(f, "%d\n", ref);
X}
X
Xstatic void putstring(FILE *f, const char *s)
X{
X    if(s) {
X	fputs(s, f);
X    } 
X    putc('\n', f);
X}
X	
Xstatic void putbool_subexp(FILE *f, struct boolexp *b)
X{
X    switch(b->type) {
X      case BOOLEXP_AND:
X	putc('(', f);
X	putbool_subexp(f, b->sub1);
X	putc(AND_TOKEN, f);
X	putbool_subexp(f, b->sub2);
X	putc(')', f);
X	break;
X      case BOOLEXP_OR:
X	putc('(', f);
X	putbool_subexp(f, b->sub1);
X	putc(OR_TOKEN, f);
X	putbool_subexp(f, b->sub2);
X	putc(')', f);
X	break;
X      case BOOLEXP_NOT:
X	putc('(', f);
X	putc(NOT_TOKEN, f);
X	putbool_subexp(f, b->sub1);
X	putc(')', f);
X	break;
X      case BOOLEXP_CONST:
X	fprintf(f, "%d", b->thing);
X	break;
X      default:
X	break;
X    }
X}
X
Xvoid putboolexp(FILE *f, struct boolexp *b)
X{
X    if(b != TRUE_BOOLEXP) {
X	putbool_subexp(f, b);
X    }
X    putc('\n', f);
X}
X	
Xint db_write_object(FILE *f, dbref i)
X{
X    struct object *o;
X
X    o = db + i;
X    putstring(f, o->name);
X    putstring(f, o->description);
X    putref(f, o->location);
X    putref(f, o->contents);
X    putref(f, o->exits);
X    putref(f, o->next);
X    putboolexp(f, o->key);
X    putstring(f, o->fail_message);
X    putstring(f, o->succ_message);
X    putstring(f, o->ofail);
X    putstring(f, o->osuccess);
X    putref(f, o->owner);
X    putref(f, o->pennies);
X    putref(f, o->flags);
X    putstring(f, o->password);
X
X    return 0;
X}
X
Xdbref db_write(FILE *f)
X{
X    dbref i;
X
X    for(i = 0; i < db_top; i++) {
X	fprintf(f, "#%d\n", i);
X	db_write_object(f, i);
X    }
X    fputs("***END OF DUMP***\n", f);
X    fflush(f);
X    return(db_top);
X}
X
Xdbref parse_dbref(const char *s)
X{
X    const char *p;
X    long x;
X
X    x = atol(s);
X    if(x > 0) {
X	return x;
X    } else if(x == 0) {
X	/* check for 0 */
X	for(p = s; *p; p++) {
X	    if(*p == '0') return 0;
X	    if(!isspace(*p)) break;
X	}
X    }
X
X    /* else x < 0 or s != 0 */
X    return NOTHING;
X}
X	    
Xdbref getref(FILE *f)
X{
X    static char buf[DB_MSGLEN];
X
X    fgets(buf, sizeof(buf), f);
X    return(atol(buf));
X}
X
Xstatic const char *getstring_noalloc(FILE *f)
X{
X    static char buf[DB_MSGLEN];
X    char *p;
X
X    fgets(buf, sizeof(buf), f);
X    for(p = buf; *p; p++) {
X	if(*p == '\n') {
X	    *p = '\0';
X	    break;
X	}
X    }
X
X    return buf;
X}
X
X#define getstring(x) alloc_string(getstring_noalloc(x))
X
X#ifdef COMPRESS
Xextern const char *compress(const char *);
X#define getstring_compress(x) alloc_string(compress(getstring_noalloc(x)));
X#else
X#define getstring_compress(x) getstring(x)
X#endif /* COMPRESS */
X
Xstatic struct boolexp *negate_boolexp(struct boolexp *b)
X{
X    struct boolexp *n;
X
X    /* Obscure fact: !NOTHING == NOTHING in old-format databases! */
X    if(b == TRUE_BOOLEXP) return TRUE_BOOLEXP;
X
X    n = (struct boolexp *) malloc(sizeof(struct boolexp));
X    n->type = BOOLEXP_NOT;
X    n->sub1 = b;
X
X    return n;
X}
X
Xstatic struct boolexp *getboolexp1(FILE *f)
X{
X    struct boolexp *b;
X    int c;
X
X    c = getc(f);
X    switch(c) {
X      case '\n':
X	ungetc(c, f);
X	return TRUE_BOOLEXP;
X	/* break; */
X      case EOF:
X	abort();		/* unexpected EOF in boolexp */
X	break;
X      case '(':
X	b = (struct boolexp *) malloc(sizeof(struct boolexp));
X	if((c = getc(f)) == '!') {
X	    b->type = BOOLEXP_NOT;
X	    b->sub1 = getboolexp1(f);
X	    if(getc(f) != ')') goto error;
X	    return b;
X	} else {
X	    ungetc(c, f);
X	    b->sub1 = getboolexp1(f);
X	    switch(c = getc(f)) {
X	      case AND_TOKEN:
X		b->type = BOOLEXP_AND;
X		break;
X	      case OR_TOKEN:
X		b->type = BOOLEXP_OR;
X		break;
X	      default:
X		goto error;
X		/* break */
X	    }
X	    b->sub2 = getboolexp1(f);
X	    if(getc(f) != ')') goto error;
X	    return b;
X	}
X	/* break; */
X      case '-':
X	/* obsolete NOTHING key */
X	/* eat it */
X	while((c = getc(f)) != '\n') if(c == EOF) abort(); /* unexp EOF */
X	ungetc(c, f);
X	return TRUE_BOOLEXP;
X	/* break */
X      default:
X	/* better be a dbref */
X	ungetc(c, f);
X	b = (struct boolexp *) malloc(sizeof(struct boolexp));
X	b->type = BOOLEXP_CONST;
X	b->thing = 0;
X	    
X	/* NOTE possibly non-portable code */
X	/* Will need to be changed if putref/getref change */
X	while(isdigit(c = getc(f))) {
X	    b->thing = b->thing * 10 + c - '0';
X	}
X	ungetc(c, f);
X	return b;
X    }
X
X  error:
X    abort();			/* bomb out */
X    return TRUE_BOOLEXP;
X}
X
Xstruct boolexp *getboolexp(FILE *f)
X{
X    struct boolexp *b;
X
X    b = getboolexp1(f);
X    if(getc(f) != '\n') abort(); /* parse error, we lose */
X    return b;
X}
X
Xvoid free_boolexp(struct boolexp *b)
X{
X    if(b != TRUE_BOOLEXP) {
X	switch(b->type) {
X	  case BOOLEXP_AND:
X	  case BOOLEXP_OR:
X	    free_boolexp(b->sub1);
X	    free_boolexp(b->sub2);
X	    free((void *) b);
X	    break;
X	  case BOOLEXP_NOT:
X	    free_boolexp(b->sub1);
X	    free((void *) b);
X	    break;
X	  case BOOLEXP_CONST:
X	    free((void *) b);
X	    break;
X	}
X    }
X}
X
Xvoid db_free(void)
X{
X    dbref i;
X    struct object *o;
X
X    if(db) {
X	for(i = 0; i < db_top; i++) {
X	    o = &db[i];
X	    if(o->name) free((void *) o->name);
X	    if(o->description) free((void *) o->description);
X	    if(o->succ_message) free((void *) o->succ_message);
X	    if(o->fail_message) free((void *) o->fail_message);
X	    if(o->ofail) free((void *) o->ofail);
X	    if(o->osuccess) free((void *) o->osuccess);
X	    if(o->password) free((void *) o->password);
X	    if(o->key) free_boolexp(o->key);
X	}
X	free((void *) db);
X	db = 0;
X	db_top = 0;
X    }
X}
X
Xdbref db_read(FILE *f)
X{
X    dbref i;
X    struct object *o;
X    const char *end;
X
X#ifdef PLAYER_LIST
X    clear_players();
X#endif
X    
X    db_free();
X    for(i = 0;; i++) {
X	switch(getc(f)) {
X	  case '#':
X	    /* another entry, yawn */
X	    if(i != getref(f)) {
X		/* we blew it */
X		return -1;
X	    }
X	    /* make space */
X	    db_grow(i+1);
X	    
X	    /* read it in */
X	    o = db+i;
X	    o->name = getstring(f);
X	    o->description = getstring_compress(f);
X	    o->location = getref(f);
X	    o->contents = getref(f);
X	    o->exits = getref(f);
X	    o->next = getref(f);
X	    o->key = getboolexp(f);
X	    o->fail_message = getstring_compress(f);
X	    o->succ_message = getstring_compress(f);
X	    o->ofail = getstring_compress(f);
X	    o->osuccess = getstring_compress(f);
X	    o->owner = getref(f);
X	    o->pennies = getref(f);
X	    o->flags = getref(f);
X	    o->password = getstring(f);
X	    /* For downward compatibility with databases using the */
X	    /* obsolete ANTILOCK flag. */ 
X	    if(o->flags & ANTILOCK) {
X	      o->key = negate_boolexp(o->key);
X	      o->flags &= ~ANTILOCK;
X	    }
X#ifdef PLAYER_LIST	    
X	    if(Typeof(i) == TYPE_PLAYER) {
X		add_player(i);
X	    }
X#endif PLAYER_LIST	    
X	    break;
X	  case '*':
X	    end = getstring(f);
X	    if(strcmp(end, "**END OF DUMP***")) {
X		free((void *) end);
X		return -1;
X	    } else {
X		free((void *) end);
X		return db_top;
X	    }
X	  default:
X	    return -1;
X	    /* break; */
X	}
X    }
X}
X		
END_OF_FILE
if test 9028 -ne `wc -c <'db.c'`; then
    echo shar: \"'db.c'\" unpacked with wrong size!
fi
# end of 'db.c'
fi
if test -f 'game.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'game.c'\"
else
echo shar: Extracting \"'game.c'\" \(14143 characters\)
sed "s/^X//" >'game.c' <<'END_OF_FILE'
X#include "copyright.h"
X
X#include <stdio.h>
X#include <ctype.h>
X#include <signal.h>
X#include <sys/wait.h>
X#include <time.h>
X#include <stdarg.h>
X    
X#include "db.h"
X#include "config.h"
X#include "interface.h"
X#include "match.h"
X#include "externs.h"
X
X/* declarations */
Xstatic const char *dumpfile = 0;
Xstatic int epoch = 0;
Xstatic int alarm_triggered = 0;
Xstatic int alarm_block = 0;
X
Xstatic void fork_and_dump(void);
Xvoid dump_database(void);
X
Xvoid do_dump(dbref player)
X{
X    if(Wizard(player)) {
X	alarm_triggered = 1;
X	notify(player, "Dumping...");
X    } else {
X	notify(player, "Sorry, you are in a no dumping zone.");
X    }
X}
X
Xvoid do_shutdown(dbref player)
X{
X    if(Wizard(player)) {
X	writelog("SHUTDOWN: by %s\n", unparse_object(player, player));
X	fflush(stderr);
X	shutdown_flag = 1;
X    } else {
X	notify(player, "Your delusions of grandeur have been duly noted.");
X    }
X}
X	
X/* should be void, but it's defined as int */
Xstatic int alarm_handler(void)
X{
X    alarm_triggered = 1;
X    if(!alarm_block) {
X        fork_and_dump();
X    }
X    return 0;
X}
X
Xstatic void dump_database_internal(void)
X{
X    char tmpfile[2048];
X    FILE *f;
X
X    sprintf(tmpfile, "%s.#%d#", dumpfile, epoch - 1);
X    unlink(tmpfile);		/* nuke our predecessor */
X
X    sprintf(tmpfile, "%s.#%d#", dumpfile, epoch);
X
X    if((f = fopen(tmpfile, "w")) != NULL) {
X	db_write(f);
X	fclose(f);
X	if(rename(tmpfile, dumpfile) < 0) perror(tmpfile);
X    } else {
X	perror(tmpfile);
X    }
X}
X
Xvoid panic(const char *message)
X{
X    char panicfile[2048];
X    FILE *f;
X    int i;
X
X    writelog("PANIC: %s\n", message);
X
X    /* turn off signals */
X    for(i = 0; i < NSIG; i++) {
X	signal(i, SIG_IGN);
X    }
X
X    /* shut down interface */
X    emergency_shutdown();
X
X    /* dump panic file */
X    sprintf(panicfile, "%s.PANIC", dumpfile);
X    if((f = fopen(panicfile, "w")) == NULL) {
X	perror("CANNOT OPEN PANIC FILE, YOU LOSE:");
X#ifndef NODUMPCORE
X	signal(SIGILL, SIG_DFL);
X	abort();
X#endif NODUMPCORE
X	_exit(135);
X    } else {
X	writelog("DUMPING: %s\n", panicfile);
X	db_write(f);
X	fclose(f);
X	writelog("DUMPING: %s (done)\n", panicfile);
X#ifndef NODUMPCORE
X	signal(SIGILL, SIG_DFL);
X	abort();
X#endif NODUMPCORE
X	_exit(136);
X    }
X}
X
Xvoid writelog(const char *fmt, ...)
X{
X    va_list list;
X    struct tm *tm;
X    long t;
X    char buffer[2048];
X
X    va_start(list, fmt);
X    vsprintf(buffer, fmt, list);
X    t = time(NULL);
X    tm = localtime(&t);
X    fprintf(stderr, "%d/%02d %02d:%02d:%02d %s",
X	    tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
X	    buffer);
X    fflush(stderr);
X    va_end(list);
X}
X
Xvoid dump_database(void)
X{
X    epoch++;
X
X    writelog("DUMPING: %s.#%d#\n", dumpfile, epoch);
X    dump_database_internal();
X    writelog("DUMPING: %s.#%d# (done)\n", dumpfile, epoch);
X}
X
Xstatic void fork_and_dump(void)
X{
X    int child;
X
X    epoch++;
X
X    writelog("CHECKPOINTING: %s.#%d#\n", dumpfile, epoch);
X#ifdef USE_VFORK
X    child = vfork();
X#else /* USE_VFORK */
X    child = fork();
X#endif /* USE_VFORK */
X    if(child == 0) {
X	/* in the child */
X	close(0);		/* get that file descriptor back */
X	dump_database_internal();
X	_exit(0);		/* !!! */
X    } else if(child < 0) {
X	perror("fork_and_dump: fork()");
X    }
X	
X    /* in the parent */
X    /* reset alarm */
X    alarm_triggered = 0;
X    alarm(DUMP_INTERVAL);
X}
X
Xstatic int reaper(void)
X{
X    union wait stat;
X
X    while(wait3(&stat, WNOHANG, 0) > 0);
X    return 0;
X}
X
Xint init_game(const char *infile, const char *outfile)
X{
X   FILE *f;
X
X   if((f = fopen(infile, "r")) == NULL) return -1;
X   
X   /* ok, read it in */
X   writelog("LOADING: %s\n", infile);
X   if(db_read(f) < 0) return -1;
X   writelog("LOADING: %s (done)\n", infile);
X
X   /* everything ok */
X   fclose(f);
X
X   /* initialize random number generator */
X   srandom(getpid());
X
X   /* set up dumper */
X   if(dumpfile) free((void *) dumpfile);
X   dumpfile = alloc_string(outfile);
X   signal(SIGALRM, (void (*)) alarm_handler);
X   signal(SIGHUP, (void (*)) alarm_handler);
X   signal(SIGCHLD, (void (*)) reaper);
X   alarm_triggered = 0;
X   alarm(DUMP_INTERVAL);
X   
X   return 0;
X}
X
X/* use this only in process_command */
X#define Matched(string) { if(!string_prefix((string), command)) goto bad; }
X
Xvoid process_command(dbref player, char *command)
X{
X    char *arg1;
X    char *arg2;
X    char *q;			/* utility */
X    char *p;			/* utility */
X
X    char *index(char *, char);
X
X    if(command == 0) abort();
X
X    /* robustify player */
X    if(player < 0 || player >= db_top || Typeof(player) != TYPE_PLAYER) {
X	writelog("process_command: bad player %d\n", player);
X	return;
X    }
X
X#ifdef LOG_COMMANDS
X    writelog("COMMAND from %s(%d) in %s(%d): %s\n",
X	    db[player].name, player,
X	    db[db[player].location].name, db[player].location,
X	    command);
X#endif /* LOG_COMMANDS */
X
X    /* eat leading whitespace */
X    while(*command && isspace(*command)) command++;
X
X    /* eat extra white space */
X    q = p = command;
X    while(*p) {
X	/* scan over word */
X	while(*p && !isspace(*p)) *q++ = *p++;
X	/* smash spaces */
X	while(*p && isspace(*++p));
X	if(*p) *q++ = ' '; /* add a space to separate next word */
X    }
X    /* terminate */
X    *q = '\0';
X
X    /* block dump to prevent db inconsistencies from showing up */
X    alarm_block = 1;
X
X    /* check for single-character commands */
X    if(*command == SAY_TOKEN) {
X	do_say(player, command+1, NULL);
X    } else if(*command == POSE_TOKEN) {
X	do_pose(player, command+1, NULL);
X    } else if(can_move(player, command)) {
X	/* command is an exact match for an exit */
X	do_move(player, command);
X    } else {
X	/* parse arguments */
X
X	/* find arg1 */
X	/* move over command word */
X	for(arg1 = command; *arg1 && !isspace(*arg1); arg1++);
X	/* truncate command */
X	if(*arg1) *arg1++ = '\0';
X
X	/* move over spaces */
X	while(*arg1 && isspace(*arg1)) arg1++;
X
X	/* find end of arg1, start of arg2 */
X	for(arg2 = arg1; *arg2 && *arg2 != ARG_DELIMITER; arg2++);
X
X	/* truncate arg1 */
X	for(p = arg2 - 1; p >= arg1 && isspace(*p); p--) *p = '\0';
X
X	/* go past delimiter if present */
X	if(*arg2) *arg2++ = '\0';
X	while(*arg2 && isspace(*arg2)) arg2++;
X
X	switch(command[0]) {
X	  case '@':
X	    switch(command[1]) {
X	      case 'b':
X	      case 'B':
X	      	switch (command[2]) {
X		  case 'o':
X		  case 'O':
X		    switch (command[3]) {
X		      case 'o':
X		      case 'O':	Matched("@boot");
X				do_boot(player, arg1);
X		      		break;
X		      case 'b':
X		      case 'B':	if(string_compare(command, "@bobble")) goto bad;
X				do_bobble(player, arg1, arg2);
X				break;
X		     default:	goto bad;
X		    }
X		    break;
X		  default: goto bad;
X		}
X		break;
X	      case 'c':
X	      case 'C':
X		/* chown, create */
X		switch(command[2]) {
X		  case 'h':
X		  case 'H':
X		    Matched("@chown");
X		    do_chown(player, arg1, arg2);
X		    break;
X#ifdef RECYCLE
X		  case 'o':
X		  case 'O':
X		    Matched("@count");
X		    do_count(player, arg1);
X		    break;
X#endif RECYCLE
X		  case 'r':
X		  case 'R':
X		    Matched("@create");
X		    do_create(player, arg1, atol(arg2));
X		    break;
X		  default:
X		    goto bad;
X		}
X		break;
X	      case 'd':
X	      case 'D':
X		/* describe, dig, or dump */
X		switch(command[2]) {
X		  case 'e':
X		  case 'E':
X		    Matched("@describe");
X		    do_describe(player, arg1, arg2);
X		    break;
X		  case 'i':
X		  case 'I':
X		    Matched("@dig");
X		    do_dig(player, arg1);
X		    break;
X		  case 'u':
X		  case 'U':
X		    Matched("@dump");
X		    do_dump(player);
X		    break;
X		  default:
X		    goto bad;
X		}
X		break;
X	      case 'f':
X		/* fail, find, or force */
X		switch(command[2]) {
X		  case 'a':
X		  case 'A':
X		    Matched("@fail");
X		    do_fail(player, arg1, arg2);
X		    break;
X		  case 'i':
X		  case 'I':
X		    Matched("@find");
X		    do_find(player, arg1);
X		    break;
X		  case 'o':
X		  case 'O':
X		    Matched("@force");
X		    do_force(player, arg1, arg2);
X		    break;
X		  default:
X		    goto bad;
X		}
X		break;
X	      case 'l':
X	      case 'L':
X		/* lock or link */
X		switch(command[2]) {
X		  case 'i':
X		  case 'I':
X		    Matched("@link");
X		    do_link(player, arg1, arg2);
X		    break;
X		  case 'o':
X		  case 'O':
X		    Matched("@lock");
X		    do_lock(player, arg1, arg2);
X		    break;
X		  default:
X		    goto bad;
X		}
X		break;
X	      case 'm':
X	      case 'M':
X	      	Matched("@mass_teleport");
X		do_mass_teleport(player, arg1);
X	      	break;
X	      case 'n':
X	      case 'N':
X		/* @name or @newpassword */
X		switch(command[2]) {
X		  case 'a':
X		  case 'A':
X		    Matched("@name");
X		    do_name(player, arg1, arg2);
X		    break;
X		  case 'e':
X		    if(strcmp(command, "@newpassword")) goto bad;
X		    do_newpassword(player, arg1, arg2);
X		    break;
X		  default:
X		    goto bad;
X		}
X		break;
X	      case 'o':
X	      case 'O':
X		switch(command[2]) {
X		  case 'f':
X		  case 'F':
X		    Matched("@ofail");
X		    do_ofail(player, arg1, arg2);
X		    break;
X		  case 'p':
X		  case 'P':
X		    Matched("@open");
X		    do_open(player, arg1, arg2);
X		    break;
X		  case 's':
X		  case 'S':
X		    Matched("@osuccess");
X		    do_osuccess(player, arg1, arg2);
X		    break;
X		  case 'w':
X		  case 'W':
X		    Matched("@owned");
X		    do_owned(player, arg1);
X		    break;
X		  default:
X		    goto bad;
X		}
X		break;
X	      case 'p':
X	      case 'P':
X 		switch(command[2]) {
X 		  case 'a':
X 		  case 'A':
X 		    Matched("@password");
X 		    do_password(player, arg1, arg2);
X 		    break;
X#ifdef REGISTRATION		    
X 		  case 'c':
X 		  case 'C':
X 		    Matched("@pcreate");
X 		    do_pcreate(player, arg1, arg2);
X 		    break;
X#endif REGISTRATION
X	          default: goto bad;
X 		}
X		break;
X#ifdef RECYCLE
X	      case 'r':
X	      case 'R':
X	      	/* Recycle */
X		Matched("@recycle");
X		do_recycle(player, arg1);
X		break;
X#endif RECYCLE
X	      case 's':
X	      case 'S':
X		/* set, shutdown, success */
X		switch(command[2]) {
X		  case 'e':
X		  case 'E':
X		    Matched("@set");
X		    do_set(player, arg1, arg2);
X		    break;
X		  case 'h':
X		    if(strcmp(command, "@shutdown")) goto bad;
X		    do_shutdown(player);
X		    break;
X		  case 't':
X		  case 'T':
X		    Matched("@stats");
X		    do_stats(player, arg1);
X		    break;
X		  case 'u':
X		  case 'U':
X		    Matched("@success");
X		    do_success(player, arg1, arg2);
X		    break;
X		  default:
X		    goto bad;
X		}
X		break;
X	      case 't':
X	      case 'T':
X		switch(command[2]) {
X		  case 'e':
X		  case 'E':
X		    Matched("@teleport");
X		    do_teleport(player, arg1, arg2);
X		    break;
X		  case 'o':
X		  case 'O':
X		    if(string_compare(command, "@toad")) goto bad;
X		    do_bobble(player, arg1, arg2);
X		    break;
X		  default:
X		    goto bad;
X		}
X		break;
X	      case 'u':
X	      case 'U':
X		if(string_prefix(command, "@unli")) {
X		    Matched("@unlink");
X		    do_unlink(player, arg1);
X		} else if(string_prefix(command, "@unlo")) {
X		    Matched("@unlock");
X		    do_unlock(player, arg1);
X		} else if(string_prefix(command, "@unb")) {
X		    Matched("@unbobble");
X		    do_unbobble(player, arg1, arg2);
X		} else if(string_prefix(command, "@unt")) {
X		    Matched("@untoad");
X		    do_unbobble(player, arg1, arg2);
X		} else {
X		    goto bad;
X		}
X		break;
X	      case 'w':
X		if(strcmp(command, "@wall")) goto bad;
X		do_wall(player, arg1, arg2);
X		break;
X	      default:
X		goto bad;
X	    }
X	    break;
X	  case 'd':
X	  case 'D':
X	    Matched("drop");
X	    do_drop(player, arg1);
X	    break;
X	  case 'e':
X	  case 'E':
X	    if (command[1] == 'x' || command[1] == 'X') {
X		Matched("examine");
X		do_examine(player, arg1);
X		break;
X	    } else {
X		goto bad;
X	    }
X	  case 'g':
X	  case 'G':
X	    /* get, give, go, or gripe */
X	    switch(command[1]) {
X	      case 'e':
X	      case 'E':
X		Matched("get");
X		do_get(player, arg1);
X		break;
X	      case 'i':
X	      case 'I':
X		Matched("give");
X		do_give(player, arg1, atol(arg2));
X		break;
X	      case 'o':
X	      case 'O':
X		Matched("goto");
X		do_move(player, arg1);
X		break;
X	      case 'r':
X	      case 'R':
X		Matched("gripe");
X		do_gripe(player, arg1, arg2);
X		break;
X	      default:
X		goto bad;
X	    }
X	    break;
X	  case 'h':
X	  case 'H':
X	    Matched("help");
X	    do_help(player);
X	    break;
X	  case 'i':
X	  case 'I':
X	    Matched("inventory");
X	    do_inventory(player);
X	    break;
X	  case 'k':
X	  case 'K':
X	    Matched("kill");
X	    do_kill(player, arg1, atol(arg2));
X	    break;
X	  case 'l':
X	  case 'L':
X	    Matched("look");
X	    do_look_at(player, arg1);
X	    break;
X	  case 'm':
X	  case 'M':
X	    Matched("move");
X	    do_move(player, arg1);
X	    break;
X	  case 'n':
X	  case 'N':
X	    /* news */
X	    if(string_compare(command, "news")) goto bad;
X	    do_news(player);
X	    break;
X	  case 'p':
X	  case 'P':
X	    Matched("page");
X	    do_page(player, arg1, arg2);
X	    break;
X	  case 'r':
X	  case 'R':
X	    switch(command[1]) {
X	      case 'e':
X	      case 'E':
X		Matched("read"); /* undocumented alias for look at */
X		do_look_at(player, arg1);
X		break;
X	      case 'o':
X	      case 'O':
X		Matched("rob");
X		do_rob(player, arg1);
X		break;
X	      default:
X		goto bad;
X	    }
X	    break;
X	  case 's':
X	  case 'S':
X	    /* say, "score" */
X	    switch(command[1]) {
X	      case 'a':
X	      case 'A':
X		Matched("say");
X		do_say(player, arg1, arg2);
X		break;
X	      case 'c':
X	      case 'C':
X		Matched("score");
X		do_score(player);
X		break;
X	      default:
X		goto bad;
X	    }
X	    break;
X	  case 't':
X	  case 'T':
X	    switch(command[1]) {
X	      case 'a':
X	      case 'A':
X		Matched("take");
X		do_get(player, arg1);
X		break;
X	      case 'h':
X	      case 'H':
X		Matched("throw");
X		do_drop(player, arg1);
X		break;
X	      default:
X		goto bad;
X	    }
X	    break;
X	  case 'w':
X	  case 'W':
X	    Matched("whisper");
X	    do_whisper(player, arg1, arg2);
X	    break;
X	  default:
X	  bad:
X	    notify(player, "Huh?  (Type \"help\" for help.)");
X#ifdef LOG_FAILED_COMMANDS
X	    if(!controls(player, db[player].location)) {
X		writelog("HUH from %s(%d) in %s(%d)[%s]: %s %s\n",
X			db[player].name, player,
X			db[db[player].location].name,
X			db[player].location,
X			db[db[db[player].location].owner].name,
X			command,
X			reconstruct_message(arg1, arg2));
X	    }
X#endif /* LOG_FAILED_COMMANDS */
X	    break;
X	}
X    }
X
X    /* unblock alarms */
X    alarm_block = 0;
X    if(alarm_triggered) {
X	fork_and_dump();
X    }
X}
X
X#undef Matched
END_OF_FILE
if test 14143 -ne `wc -c <'game.c'`; then
    echo shar: \"'game.c'\" unpacked with wrong size!
fi
# end of 'game.c'
fi
if test -f 'restart-cmu' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'restart-cmu'\"
else
echo shar: Extracting \"'restart-cmu'\" \(576 characters\)
sed "s/^X//" >'restart-cmu' <<'END_OF_FILE'
X#!/bin/csh -f
X#
X# This script assumes you have the following structure:
X#
X#	$HOME/bin	executable MUD programs
X#	$HOME/lib	database files, daily logs
X#	$HOME/src	source code for netmud
X#
X
Xcd /usr/tinymud/lib
Xmv tinymud.db tinymud.db.old
Xif(-r tinymud.db.new) then
X	mv tinymud.db.new tinymud.db
Xelse
X	cp tinymud.db.old tinymud.db
Xendif
X
X# Use netmud instead of netmud.conc if you don't want the port concentrator
Xcp ../bin/netmud.conc netmud.exe
Xcp ../bin/concentrate concentrate
X
Xecho RESTARTED AT `date` >> tinymud.log
X./netmud.exe tinymud.db tinymud.db.new >>& tinymud.log &
END_OF_FILE
if test 576 -ne `wc -c <'restart-cmu'`; then
    echo shar: \"'restart-cmu'\" unpacked with wrong size!
fi
chmod +x 'restart-cmu'
# end of 'restart-cmu'
fi
if test -f 'tinymud.tex' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'tinymud.tex'\"
else
echo shar: Extracting \"'tinymud.tex'\" \(28102 characters\)
sed "s/^X//" >'tinymud.tex' <<'END_OF_FILE'
X\documentstyle[11pt,titlepage,makeidx,ragright]{article}
X
X\newcommand{\bs}{\char '134}	% backslash character for \tt font
X\newcommand{\ub}{\char '137}	% underbar character for \tt font
X\newcommand{\ua}{\char '136}	% up arrow character for \tt font
X\newcommand{\qt}{\char '175}	% quotes character for \tt font
X\newcommand{\tl}{\char '176}	% tilde character for \tt font
X\newcommand{\sh}{\char '043}	% sharp character for \tt font
X
X\newcommand{\tinymud}{{\small Tiny}{MUD}}
X\newcommand{\type}[1]{{\tt #1\/}}
X
X\newenvironment{simple}{\begin{list}%
X{\relax}%
X{\setlength{\labelwidth}{0pt}%
X \setlength{\labelsep}{0pt}%
X \setlength{\leftmargin}{0pt}%
X \setlength{\listparindent}{0pt}}}%
X{\end{list}}
X
X%\newenvironment{simple}{\begin{trivlist}}{\end{trivlist}}
X
X%\newlength{\boxwidth}
X%\setlength{\boxwidth}{\textwidth}
X%\addtolength{\boxwidth}{-7pt}
X
X\newlength{\rulewidth}
X\setlength{\rulewidth}{\textwidth}
X%\divide\rulewidth by 3
X
X\newcommand{\dorule}{\begin{center}
X\rule{\rulewidth}{1pt}
X\end{center}}
X
X\makeindex
X
X\title{{\bf A brief guide to {\large\bf Tiny}MUD}}
X
X\author{Jennifer Stone (aka Chrysalis) \\
X{\tt jennifer@uokmax.ecn.uoknor.edu}
X\and
XRusty C. Wright \\
X{\tt rusty@garnet.berkeley.edu}}
X
X\begin{document}
X
X% uncomment the commented lines if you want a more formal document
X% with a separate title page and table of contents.
X
X\ragright
X
X\pagenumbering{roman}
X
X\maketitle
X
X\tableofcontents
X\clearpage
X
X\pagenumbering{arabic}
X
XMuch of this is from the {\tinymud} source code and the {\tt small.db}
Xexample.
X
X\section{Ordinary commands}
X\label{sec:ordinary-commands}
X
X\begin{simple}
X
X\item[]
X\begin{flushleft}
X{\tt drop} $<${\em object\/}$>$ \\
X{\tt throw} $<${\em object\/}$>$
X\index{drop@\type{drop}}
X\index{throw@\type{throw}}
X\end{flushleft}
XDrops the specified object.  $<${\em object\/}$>$ can be either a
Xthing or exit.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt examine} $<${\em name\/}$>$ \\
X{\tt examine} {\tt \#}$<${\em number\/}$>$
X\index{examine@\type{examine}}
X\end{flushleft}
XPrints a detailed description of object specified by $<${\em
Xname\/}$>$ or by $<${\em number\/}$>$ giving name, description, owner,
Xkeys, pennies, failure message, success message, others failure
Xmessage, others success message, and exits.  The location will also be
Xdisplayed if you control the object's location (that is, if it's not
Xbeing carried by someone else or in a room you don't control).
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt get} $<${\em object\/}$>$ \\
X{\tt take} $<${\em object\/}$>$
X\index{get@\type{get}}
X\index{take@\type{take}}
X\end{flushleft}
XGets the specified object.  $<${\em object\/}$>$ can be either a thing
Xor exit.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt give} $<${\em player\/}$>$ \verb|=| $<${\em amount\/}$>$
X\index{give@\type{give}}
X\end{flushleft}
XGives $<${\em player\/}$>$ the specified number of pennies.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt go} $<${\em direction\/}$>$ \\
X{\tt go home} \\
X{\tt move} $<${\em direction\/}$>$ \\
X{\tt move home}
X\index{go@\type{go}}
X\index{move@\type{move}}
X\index{home@\type{home}}
X\end{flushleft}
X
XMoves in the specified direction.  {\tt go home} is a special command
Xthat returns you to your home (initially Limbo).  If the direction is
Xfully specified, the {\tt go} may be omitted.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt gripe} $<${\em message\/}$>$
X\index{gripe@\type{gripe}}
X\end{flushleft}
XSends $<${\em message\/}$>$ to the system maintainer.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt help}
X\index{help@\type{help}}
X\end{flushleft}
XPrints a short help message.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt inventory}
X\index{inventory@\type{inventory}}
X\end{flushleft}
XLists what you are carrying.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt kill} $<${\em player\/}$>$ $[$ \verb|=| $<${\em cost\/}$>$ $]$
X\index{kill@\type{kill}}
X\end{flushleft}
XKills the specified player.  Killing costs either $<${\em cost\/}$>$
Xpennies or 10 pennies, whichever is greater.  The probability of
Xsuccess is proportional to the cost.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt look} $<${\em object\/}$>$ \\
X{\tt read} $<${\em object\/}$>$
X\index{look@\type{look}}
X\index{read@\type{read}}
X\end{flushleft}
X$<${\em object\/}$>$ can be a room, thing, player, or direction.  Prints
Xa description of $<${\em object\/}$>$.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt page} $<${\em player\/}$>$
X\index{page@\type{page}}
X\end{flushleft}
XUsed to inform an active player that you are looking for them.  The
Xtargeted player will get a message telling them your name and
Xlocation.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt rob} $<${\em player\/}$>$
X\index{rob@\type{rob}}
X\end{flushleft}
XAttempt to steal a penny from $<${\em player\/}$>$.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt say} $<${\em message\/}$>$ \\
X\verb|"|$<${\em message\/}$>$ \\
X\verb|:|$<${\em message\/}$>$
X\index{say@\type{say}}
X\index{""@\type{""}}
X\index{:@\type{:}}
X\end{flushleft}
XThe first two forms display the $<${\em message\/}$>$ with the
Xnotification that you said it.  For example, if your player's name is
XBetty the other players in the same room will see
X\begin{flushleft}
X{\tt Betty says} ``$<${\em message\/}$>$''
X\end{flushleft}
XThe third form {\sl poses} the message, preceded by your name, with no
Xquotes, as in
X\begin{flushleft}
X{\tt Betty} $<${\em message\/}$>$
X\end{flushleft}
XFor both the second and third forms, do not put a space after
Xthe double quotes or colon as it will be included in the message.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt score}
X\index{score@\type{score}}
X\end{flushleft}
XPrints how many pennies you have.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt whisper} $<${\em player\/}$>$ \verb|=| $<${\em message\/}$>$
X\index{whisper@\type{whisper}}
X\end{flushleft}
X$<${\em player\/}$>$ is presented with $<${\em message\/}$>$ saying
Xthat you whispered it.  The other players only see the message
X\begin{flushleft}
X{\tt Betty whispers something to} $<${\em player\/}$>$.
X\end{flushleft}
X
X\end{simple}
X
X\section{Commands for modifying the dungeon}
X\label{sec:commands-for-modifying-the-dungeon}
X
X\begin{simple}
X
X\item[]
X\begin{flushleft}
X{\tt @create\/} $<${\em name\/}$>$ $[$ \verb|=| $<${\em cost\/}$>$ $]$
X\index{create@\type{"@create}}
X\end{flushleft}
XCreates a thing with the specified name.  Creation costs either
X$<${\em cost\/}$>$ pennies or 10 pennies, whichever is greater.  The
Xvalue of a thing is proportional to its cost.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @describe} $<${\em object\/}$>$ \verb|=| $<${\em description\/}$>$
X\index{describe@\type{"@describe}}
X\end{flushleft}
X$<${\em object\/}$>$ can be a room, thing, player, or direction.  Sets
Xthe description a player sees when they use the command {\tt look}
X$<${\em object\/}$>$.  If $<${\em object\/}$>$ is {\tt
Xhere}\index{here@\type{here}} it sets the description for the current
Xroom that is displayed when the room is entered.  If $<${\em
Xobject\/}$>$ is {\tt me}\index{me@\type{me}} it sets the description for
Xyour character.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @dig} $<${\em name\/}$>$
X\index{dig@\type{"@dig}}
X\end{flushleft}
XCreates a new room with the specified name, and prints the room's
Xnumber.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @fail} $<${\em object\/}$>$ $[$ \verb|=| $<${\em message\/}$>$
X$]$
X\index{fail@\type{"@fail}}
X\end{flushleft}
XWithout a message argument, clears the failure message on $<${\em
Xobject\/}$>$, otherwise sets it.  The failure message is printed when
Xa player unsuccessfully attempts to use the object.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @find} $<${\em name\/}$>$
X\index{find@\type{"@find}}
X\end{flushleft}
XPrints the name and object number of every room, thing, or player that
Xyou control whose name matches $<${\em name\/}$>$.  Because the {\tt
X@find} command is computationally expensive, there is a small charge
Xfor using it.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @link} $<${\em direction\/}$>$ \verb|=| $<${\em room number\/}$>$ \\
X{\tt @link} $<${\em thing\/}$>$ \verb|=| $<${\em room number\/}$>$ \\
X{\tt @link} $<${\em room\/}$>$ \verb|=| $<${\em room number\/}$>$
X\index{link@\type{"@link}}
X\end{flushleft}
XIn the first form links the exit specified by $<${\em direction\/}$>$
Xto the room specified by $<${\em room number\/}$>$.  The exit must be
Xunlinked, and you must own the target room if its
X\verb|link_ok|\index{linkok@\verb,link_ok,} attribute is not set.  If
Xyou don't already own the exit its ownership is transferred to you.
XThe second form sets the home for $<${\em thing\/}$>$\index{home}.  If
X$<${\em thing\/}$>$ is {\tt me}\index{me@\type{me}} it sets your home.
XThe third form sets the dropto; see section~\ref{sec:droptos} for an
Xexplanation of dropto's.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @lock} $<${\em object\/}$>$ \verb|=| $<${\em key\/}$>$
X\index{lock@\type{"@lock}}
X\end{flushleft}
XSets a key (another object) for an object.  If $<${\em key\/}$>$
Xstarts with \verb|*| then it must be a player's name.  $<${\em
Xkey\/}$>$ can contain the Boolean operators \verb|!| (not or
Xnegation), \verb|&| (and), and \verb:|: (or), and use parentheses for
Xgrouping.
X
XIn order to use $<${\em object\/}$>$ you must satisfy the requirements
Xof $<${\em key\/}$>$.  In the simplest case you must simply have
X$<${\em key\/}$>$.  If $<${\em key\/}$>$ is preceded by \verb|!| then
Xyou must not have $<${\em key\/}$>$ in order to use $<${\em
Xobject\/}$>$.  See section~\ref{sec:lock-key-boolean-examples} for
Xmore complicated examples.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @name} $<${\em object\/}$>$ \verb|=| $<${\em name\/}$>$ \\
X{\tt @name} $<${\em player\/}$>$ \verb|=| $<${\em name\/}$>$ $<${\em
Xpassword\/}$>$
X\index{name@\type{"@name}}
X\end{flushleft}
XChanges the name of the specified object.  This can also be used to
Xspecify a new direction list for an exit (see for example {\tt
X@open\/}).  For a player, it requires the player's password.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @ofail} $<${\em object\/}$>$ $[$ \verb|=| $<${\em message\/}$>$
X$]$
X\index{ofail@\type{"@ofail}}
X\end{flushleft}
XWithout a message argument, clears the others failure message on
X$<${\em object\/}$>$, otherwise sets it.  The others failure message,
Xprefixed by the player's name, is shown to others when the player
Xfails to use $<${\em object\/}$>$.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @open} $<${\em direction\/}$>$ $[$ {\tt;} $<${\em other-dir\/}$>$
X$]*$ $[$ {\tt =} $<${\em destination\/}$>$ $]$
X\index{open@\type{"@open}}
X\end{flushleft}
XCreates an unlinked exit in the specified direction(s).  You can also
Xspecify an exit to link the exit to.  Once created, you (or any other
Xplayer) may use the {\tt @link} command to specify the room to which
Xthe exit leads.  See also {\tt @name}.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @osuccess} $<${\em object\/}$>$ $[$ \verb|=| $<${\em
Xmessage\/}$>$ $]$
X\index{osuccess@\type{"@osuccess}}
X\end{flushleft}
XWithout a message argument, clears the others success message on
X$<${\em object\/}$>$, otherwise sets it.  The others success message,
Xprefixed by the player's name, is shown to others when the player
Xsuccessfully uses $<${\em object\/}$>$.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @password} $<${\em old\/}$>$ \verb|=| $<${\em new\/}$>$
X\index{password@\type{"@password}}
X\end{flushleft}
XSets a new password; you must specify your old password to verify your
Xidentity.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @set} $<${\em object\/}$>$ \verb|=| $<${\em flag\/}$>$ \\
X{\tt @set} $<${\em object\/}$>$ \verb|=| {\tt !}$<${\em flag\/}$>$
X\index{set@\type{"@set}}
X\end{flushleft}
XSets (first form) or resets (second form) $<${\em flag\/}$>$ on
X$<${\em object\/}$>$.  The current flags are \verb|dark|,
X\verb|link_ok|, \verb|sticky|, \verb|temple|, and \verb|wizard|.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @success} $<${\em object\/}$>$ \verb|=| $<${\em message\/}$>$
X\index{success@\type{"@success}}
X\end{flushleft}
XWithout a message argument, clears the success message on $<${\em
Xobject\/}$>$, otherwise sets it.  The success message is printed when
Xa player successfully uses $<${\em object\/}$>$.  Without $<${\em
Xmessage}$>$ it clears the success message.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @teleport} $<${\em object\/}$>$ {\tt =} $<${\em destination\/}$>$
X\index{teleport@\type{"@teleport}}
X\end{flushleft}
XTeleports the object to the specified destination.  You must either
Xcontrol the object or it's current location, and you must be able to
Xlink to the destination. You can only teleport objects from room to
Xroom, not into or out of someone's hand.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @unlink} $<${\em direction\/}$>$
X\index{unlink@\type{"@unlink}}
X\end{flushleft}
XRemoves the link on the exit in the specified $<${\em direction\/}$>$.
XYou must own the exit.  The exit may then be relinked by any player
Xusing the {\tt @link} command and ownership of the exit transfers to
Xthat player.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @unlock} $<${\em object\/}$>$
X\index{unlock@\type{"@unlock}}
X\end{flushleft}
XRemoves the lock on an object.
X
X\end{simple}
X
X\section{Miscellaneous commands}
X\label{sec:miscellaneous-commands}
X
X\begin{simple}
X
X\item[]
X\begin{flushleft}
X{\tt @stats}
X\index{stats@\type{"@stats}}
XShows current total of players, rooms, objects, exits.
X\end{flushleft}
X
X\end{simple}
X
X\section{Money}
X\label{sec:money}
X
XSome actions have an associated cost:
X\begin{center}
X\begin{tabular}{|l|p{4in}|}
X\hline
X\multicolumn{1}{|c|}{{\bf Action}} &
X\multicolumn{1}{c|}{{\bf Cost}} \\
X\hline
X\hline
X\hline
X{\tt kill} & 10 pennies or more \\
X\hline
X{\tt page} & 1 penny \\
X\hline
X{\tt @open} & 1 penny \\
X\hline
X{\tt @dig} & 10 pennies \\
X\hline
X{\tt @create} & 10 pennies (or more) \\
X\hline
X{\tt @link} & 1 penny, plus 1 penny to the previous owner if you
Xdidn't already own the exit. \\
X\hline
X\end{tabular}
X\index{kill@\type{kill}}
X\index{page@\type{page}}
X\index{open@\type{"@open}}
X\index{dig@\type{"@dig}}
X\index{create@\type{"@create}}
X\index{link@\type{"@link}}
X\end{center}
X
XYou get money by finding pennies, getting killed, having someone give
Xyou money, or by sacrificing a thing.  The sacrifice value of a thing
Xis $(\mbox{\rm cost of the thing} / 5 ) - 1$.
X
X\section{TinyMUD concepts}
X\label{sec:tinymud-concepts}
X
XAn {\em object} is either a player, room, thing, or exit.
X
XIn addition to the commands listed above there are some built in words
Xin {\tinymud}; {\tt me}\index{me@\type{me}} and {\tt
Xhere}\index{here@\type{here}}.  {\tt me} refers to your character or
Xplayer, and {\tt here} refers to the room you are in.  For example,
Xyou can use the \verb|@describe| command to give yourself a
Xdescription; as another example, in order to prevent yourself from
Xbeing robbed use {\tt @lock me = me\/}.
X
X\subsection{Success and the lack thereof}
X\label{sec:success-and-the-lack-thereof}
X
XWhen you can {\tt take} a thing, use an exit, or rob a player you are
Xsuccessfull in using that object.  The converse is true for failure.
XThe {\tt @success\/}, {\tt @osuccess\/}, {\tt @fail\/}, and {\tt
X@ofail} commands set the success and failure messages on objects.%
X\index{success@\type{"@success}}%
X\index{osuccess@\type{"@osuccess}}%
X\index{fail@\type{"@fail}}%
X\index{ofail@\type{"@ofail}}
X
X\subsection{Object strings}
X\label{sec:object-strings}
X
XEvery object has six strings:
X\begin{enumerate}
X\item
XName.  This is what you use with {\tt drop\/}, {\tt examine\/}, {\tt
Xget\/}, and so on.  You can also use the object's number (for example,
Xwhen two objects have the same name).
X
X\item
XDescription.  This is what is given when you use the {\tt look}
Xcommand.
X
X\item
XSuccess message.  This is what you see when you successfully use the
Xobject.
X
X\item
XOthers success message.  This is what the other players see when you
Xsuccessfully use the object.
X
X\item
XFailure message.  This is what you see when you fail to use an object.
X
X\item
XOthers failure message.  This is what the other players see when you
Xfail to use an object.
X\end{enumerate}
XThe maximum length of each string is 512 characters.
X
X\subsection{Object properties}
X\label{sec:object-properties}
X
XAs listed in the \verb|@set| command, objects can have any of the
Xfollowing properties:
X\begin{description}
X\item[{\tt dark}]
X\index{dark@\type{dark}}
XWhen a room has its {\tt dark} flag set you can't see things in it
Xwith the {\tt look} command.  When a thing or player has its {\tt
Xdark} flag set it can't be seen.  Only a wizard can set the {\tt dark}
Xflag on a thing or player.  Setting the {\tt dark} flag on exits
Xcurrently has no effect.
X
X\item[{\tt link{\ub}ok}]
X\index{linkok@\verb,link_ok,}
XYou can link to a room if you control it, or if the room has its
X\verb|link_ok| flag set.  Being able to link to a room means that you
Xcan set the homes\index{home} of things (or yourself) to that room,
Xand you can set the destination of exits to that room.  See also the
X{\tt @link} command for additional information on linking and
Xsection~\ref{sec:droptos} for droptos.  Setting the
X\verb|link_ok| flag on players, things, and exits currently has no
Xeffect.
X
X\item[{\tt sticky}]
X\index{sticky@\type{sticky}}
XWhen an object that has its {\tt sticky} flag set is dropped it
Ximmediately goes home.  When a room has its {\tt sticky} flag set its
Xdropto is delayed until the last person leaves the room.  Setting the
X{\tt sticky} flag on players and exits currently has no effect.
X
X\item[{\tt temple}]
X\index{temple@\type{temple}}
XWhen a room has its {\tt temple} flag set you can sacrifice things
Xthere and receive pennies for your sacrifices.  (See
Xsection~\ref{sec:money} for how many pennies you receive for your
Xsacrifices.)  Only a wizard can set this flag.  Setting this flag on
Xplayers, things, and exits currently has no effect.
X
X\item[{\tt wizard}]
X\index{wizard@\type{wizard}}
XWhen a player has its {\tt wizard} flag set they are a wizard.  Only a
Xwizard can set this flag.  Setting this flag on things, rooms, and
Xexits currently has no effect.
X
X\end{description}
X
XThe flags {\tt player\/}, {\tt room\/}, and {\tt exit} are set
Xautomatically when a player, room, or exit is created.  They cannot be
Xsubsequently unset or set with the \verb|@set| command.
X
X\subsection{Control}
X\label{sec:control}
X
XThere are three rules for determining control:
X\begin{enumerate}
X\item
XYou can control anything you own.
X\item
XA wizard can control anything.
X\item\label{item:unlinked-exit}
XAnybody can control an unlinked exit (even if it is locked).
X\end{enumerate}
XBuilders should watch out for item~\ref{item:unlinked-exit}.
X
X\subsection{Dropto's}
X\label{sec:droptos}
X
X\index{droptos}
XWhen the {\tt @link} command is used on a room, it sets a dropto
Xlocation for that room.  Any thing dropped in the room (if it is not
X{\tt sticky\/}; see above) will go to that location.  If the room has
Xits {\tt sticky\/} flag set the effect of the dropto will be delayed
Xuntil the last player leaves the room.  The special location {\tt
Xhome} may be used as a dropto, as in {\tt @link here = home\/}; in
Xthat case things dropped in the room will go to their
Xhomes\index{home@\type{home}}.  To remove the dropto on a room go into
Xthat room and use {\tt @unlink here\/}\index{here@\type{here}}.
X
X\subsection{Homes}
X\label{sec:homes}
X
X\index{home}
XEvery thing or player has a home.  For things, this is the location
Xthe thing returns to when sacrificed, when a player carrying it goes
Xhome, or when (if its {\tt sticky\/} flag is set) it is dropped.  For
Xplayers, this is where the player goes when they issue the {\tt home}
Xcommand.  Homes may be set using the {\tt @link} command; for example,
X{\tt @link donut =} $<${\em room-number\/}$>$ or {\tt @link me =}
X$<${\em room-number\/}$>$\index{me@\type{me}}.  Exits may also be
Xlinked to the special location {\tt home\/}; for example, {\tt @link
Xnorth = home\/}\index{home@\type{home}}.
X
X\subsection{Recycling}
X\label{sec:recycling}
X
XNothing can be destroyed in {\tinymud}, but it is possible to recycle
Xjust about anything.  The {\tt @name} command can be used to rename
Xobjects, making it easy to turn a silk purse into a sow's ear or vice
Xversa.  Extra exits can be unlinked and picked up by their owner using
Xthe {\tt get} command, and dropped like things using the {\tt drop}
Xcommand in any room controlled by the dropper.
X
X\subsection{Being killed}
X\label{sec:being-killed}
X
XWhen you are killed you return to your home and any items you were
Xcarrying return to their homes.  As a consolation you receive 50
Xpennies from the {\tinymud} Total Life Indemnity insurance
Xcompany.
X
X\section{Examples}
X\label{sec:examples}
X
XHere we present examples to demonstrate some of the features of
X{\tinymud}.
X
X\subsection{Success and failure messages}
X\label{sec:success-and-failure-messages}
X
XSuccess and failure messages are fairly straightforward.  Just
Xremember that for the messages set with {\tt
X@osuccess}\index{osuccess@\type{"@osuccess}} and {\tt
X@ofail}\index{ofail@\type{"@ofail}} the player's name is prefixed onto
Xthe message when it is printed, while the messages set with {\tt
X@success}\index{success@\type{"@success}} and {\tt
X@fail}\index{fail@\type{"@fail}} are printed as-is.
X
XPreviously we saw that you can use {\tt say}, \verb|"|, and \verb|:|
Xto display messages.  An older method for posing non sequiturs is to
Xset the others success message on yourself and then rob yourself.  For
Xexample, if your character is Betty and you do
X\index{say@\type{say}}
X\index{""@\type{""}}
X\index{:@\type{:}}
X\begin{verbatim}
X@osuccess me = starts picking her nose.
Xrob me
X\end{verbatim}
Xon your screen you'll see
X\begin{verbatim}
XYou stole a penny.
XBetty stole one of your pennies!
X\end{verbatim}
Xwhile the other players will see
X\begin{verbatim}
XBetty starts picking her nose.
X\end{verbatim}
XAn easier way to accomplish this is to simply do
X\begin{verbatim}
X:starts picking her nose.
X\end{verbatim}
Xthen both you and the others see
X\begin{verbatim}
XBetty starts picking her nose.
X\end{verbatim}
XWhen using \verb|"| and \verb|:| don't follow them with a space
Xbecause it will be included in the output; put the message right up
Xagainst the quotes or colon.
X
X\subsection{Making your home}
X\label{sec:making-your-home}
X
X\index{home}
XThe minimal steps for making your home are
X\begin{enumerate}
X\item
XMake the room for your home with the {\tt @dig} command.  Write down
Xthe room number in case the following step takes a long time.
X
X\item
X\label{item:exit}
XMake or acquire an exit.  In order to use the {\tt @open} command you
Xmust own the room that you are doing the {\tt @open} in.  The
Xalternative is to find a room with an exit that isn't linked and use
Xit.
X
X\item
XMake a link to your home.  Once you've made or found an unlinked exit
Xsimply use the {\tt @link} command to link the exit to your room.
X
X\item
X\label{item:exit-room}
XFind a room to which you can make a link in order to have an exit from
Xyour room (this is a room with the \verb|link_ok| flag set).  For the
Xsake of example we'll pretend the number of this room is 711; we'll be
Xusing it in step~\ref{item:exit-link}.  Without this you'd be able to
Xgo to your home but you wouldn't have any way to get out of it.
X
X\item
XSet the link from you to your home.  Go into your room and do
X\index{here@\type{here}}
X\index{link@\type{"@link}}
X\begin{verbatim}
X@link me = here
X\end{verbatim}
X
X\item
X\label{item:exit-link}
XMake the exit and link it to the destination
X\begin{verbatim}
X@open out
X@link out = #711
X\end{verbatim}
X(The \verb|#| isn't mandatory.)
X\end{enumerate}
X
XOf course there are probably various details that you would want to
Xtake care of in addition to the above steps.  For example, if you're
Xantisocial and want to prevent other people from using your home room
Xyou'd do
X\index{lock@\type{"@lock}}
X\begin{verbatim}
X@lock down = me
X\end{verbatim}
Xassuming that {\tt down} is the exit you made in step~\ref{item:exit}.
XAlong a similar vein you might not want other people linking to your
Xroom in which case you'd turn off the \verb|link_ok| flag on your
Xroom.  You might also set the description of your home room.  If you
Xown the exit you could also set the success, others success, fail, and
Xothers fail messages on the exit to your home.  Without the
Xdescriptions places and things are boring and uninteresting.
X
X\subsection{Lock key boolean examples}
X\label{sec:lock-key-boolean-examples}
X
XWhen using the {\tt @lock} command the key is either another object or
Xsome boolean combination of other objects.  If the key starts with a
X\verb|*| then that object must be a player.
X
XFor example, if a room has a direction {\tt out} and you want to
Xprevent players from carrying the object {\tt xyz} when they go out,
Xyou would use
X\begin{verbatim}
X@lock out = !xyz
X\end{verbatim}
Xor if you want to prevent the player Julia from using the {\tt out}
Xexit you would use
X\begin{verbatim}
X@lock out = !*Julia
X\end{verbatim}
XIf you want to prevent only Julia from going out with {\tt xyz} you
Xwould use
X\begin{verbatim}
X@lock out = ( *Julia & !xyz )
X\end{verbatim}
X
X\subsection{Ersatz commands}
X\label{sec:ersatz-commands}
X
XYou can make new commands by making an exit and then locking it to
Xsomething impossible to have and then assigning the failure and others
Xfailure messages to it.  For example, assume the following commands
Xhave been used
X\begin{verbatim}
X@open eat
X@link eat = here
X@lock eat = something_impossible
X@fail eat = You try to eat but only gag
X@ofail eat = tries to eat but instead gags
X\end{verbatim}
XThen when you use the command {\tt eat} the others in the room will
Xsee {\tt Betty tries to eat but instead gags} and you'll see {\tt You
Xtry to eat but only gag\/}.
X
XNote that this ``new command'' will only work in the room that you
Xmade it in.
X
X\clearpage
X
X\part*{Appendix}
X
X\appendix
X
X\section{Wizard commands}
X\label{sec:wizard-commands}
X
X\begin{simple}
X
X\item[]
X\begin{flushleft}
X{\tt @chown} $<${\em object\/}$>$ \verb|=| $<${\em player\/}$>$
X\index{chown@\type{"@chown}}
X\end{flushleft}
XChanges the ownership of $<${\em object\/}$>$ to $<${\em player\/}$>$.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @dump}
X\index{dump@\type{"@dump}}
X\end{flushleft}
XForces a dump of the database.  This command isn't really necessary
Xsince {\tt @shutdown} does one as well as the regular periodic dumps.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt examine} $<${\em name\/}$>$ \\
X{\tt examine} {\tt \#}$<${\em number\/}$>$
X\index{examine@\type{examine}}
X\end{flushleft}
XPrint a detailed description of object specified by $<${\em name\/}$>$
Xor by $<${\em number\/}$>$.  All six strings listed in
Xsection~\ref{sec:object-strings} are printed.
X
XWhen $<${\em name\/}$>$ is a room or {\tt
Xhere}\index{here@\type{here}} it lists the owner, key, number of
Xpennies, the description, contents, and exits.  When $<${\em
Xname\/}$>$ is a direction lists the direction number, owner, key,
Xpennies, and destination.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @force} $<${\em victim\/}$>$ \verb|=| $<${\em command\/}$>$
X\index{force@\type{"@force}}
X\end{flushleft}
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @newpassword} $<${\em player\/}$>$ \verb|=| $<${\em
Xpassword\/}$>$
X\index{newpassword@\type{"@newpassword}}
X\end{flushleft}
XChanges the password for $<${\em player\/}$>$.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @shutdown}
X\index{shutdown@\type{"@shutdown}}
X\end{flushleft}
XShuts down the {\tinymud} server.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @stats}
X\index{stats@\type{"@stats}}
X\end{flushleft}
XLists the total number of objects, which is the sum of the rooms,
Xexits, things, and players, giving the count for each one.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @teleport} $[$ $<${\em victim\/}$>$ \verb|=| $]$ $<${\em
Xdestination\/}$>$
X\index{teleport@\type{"@teleport}}
X\end{flushleft}
XTeleports the object to the specified destination.  Object can also be
X{\tt me\/}.\index{me@\type{me}}
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @toad} $<${\em player\/}$>$
X\index{toad@\type{"@toad}}
X\end{flushleft}
XTurns $<${\em player\/}$>$ into an object.
X
X\dorule
X
X\item[]
X\begin{flushleft}
X{\tt @wall} $<${\em message\/}$>$
X\index{wall@\type{"@wall}}
X\end{flushleft}
XSend $<${\em message\/}$>$ to all players.
X
X\end{simple}
X
X\addcontentsline{toc}{part}{Index}
X
X\printindex
X
X\end{document}
END_OF_FILE
if test 28102 -ne `wc -c <'tinymud.tex'`; then
    echo shar: \"'tinymud.tex'\" unpacked with wrong size!
fi
# end of 'tinymud.tex'
fi
echo shar: End of archive 7 \(of 10\).
cp /dev/null ark7isdone
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