/
teeny/db/
teeny/dbm/
teeny/doc/
teeny/includes/
#include <stdio.h>
#include <strings.h>
#include <ctype.h>

#include "teeny.h"
#include "match.h"

/*
Copyright(C) 1990, Andrew Molitor, All Rights Reserved.
This software may be freely used, modified, and redistributed,
as long as this copyright message is left intact, and this
software is not used to develop any commercial product, or used
in any product that is provided on a pay-for-use basis.

No warranties whatsoever. This is not guaranteed to compile, nor,
in the event that it does compile to binaries, are these binaries
guaranteed to perform any function whatsoever.
*/

/*
	Utility functions for use by the command handlers in cmds.c, speech.c,
wiz.c and buildcmds.c

*/

long random();

/*
A work buffer. Different from the one in cmds.c/buildcmds.c, though it
probably need not be.
*/

char work[BUFFSIZ+64];

/*
	Send an thing IN A CONTENTS LIST home.
*/

send_home(obj,loc)
int obj;
int loc;
{
	int home,next;

	if(get_int_elt(obj,HOME,&home) == -1){
		warning("sendhome","could not get home");
		return;
	}
	list_drop(obj,loc,1);  /* Drop it from contents list here */

	if(!exists_object(home)){
		home = 0; /* Fake it, eh? */
	}

	if(set_int_elt(obj,LOC,home) == -1){
		warning("sendhome","could not set location");
		return;
	}
	if(get_int_elt(home,CONTENTS,&next) == -1){
		warning("sendhome","could not get contents");
		return;
	}
	if(set_int_elt(obj,NEXT,next) == -1){
		warning("sendhome","could not set next");
		return;
	}
	if(set_int_elt(home,CONTENTS,obj) == -1){
		warning("sendhome","could not set contents");
		return;
	}
}

/*
	Returns 1 if the player controls the object, 0 otherwise.
*/

controls(player,object)

int player;
int object;

{
	int owner,flags;

	if(object == -3) /* Everyone controls 'home' */
		return(1);

	if(!exists_object(player)){
		warning("controls","bad player reference, no such player");
		return(0);
	}
	if(!exists_object(object)){
		warning("controls","bad object reference, no such object");
		return(0);

	}


	if(get_int_elt(player,FLAGS,&flags) == -1){
		warning("controls","bad flags ref on player");
		return(0);
	}
	if(flags & WIZARD){
		return(1);
	}

	if(get_int_elt(object,OWNER,&owner) == -1){
		warning("controls","bad object reference, no such object");
		return(0);
	}
	return(owner == player);
}

/*
	Resolves a string into an object reference, if it can. The last
parameter is a flag. Returns -1 if no matching object.

	We always check exits first and separately, since any match here is
exact, and we want (exact) matches on exits to take priority over partial
matches.

*/

int resolve_object(player,name,splat_ok)

int player;
char *name;
int splat_ok;

{
	int matched,location;
	int flags;

	/* try for things on player, exits or contents. */

	if( (matched = match_here(player,player,name,MAT_EXITS)) != -1){
		return(matched);
	}
	if( (matched = match_here(player,player,name,MAT_THINGS)) != -1){
		return(matched);
	}

	/* Try for things on the player's room. things, exits or players. */

	if(get_int_elt(player,LOC,&location) == -1){
		warning("resolve_object","bad location");
		return(-1);
	}
	if( (matched = match_here(player,location,name,MAT_EXITS)) != -1){
		return(matched);
	}
	if( (matched = match_here(player,location,name,MAT_THINGS | MAT_PLAYERS)) != -1){
		return(matched);
	}

	/* Still no luck. Try a raw object number. match_here() does this */
	/* too, but cares about location. Typically, we don't, so re-do   */

	if(*name == '#' && isdigit(name[1])){
		name++;
		matched = atoi(name);
		return(matched);
	}

	/* Maybe it's one of these. */

	if(strcmp("here",name) == 0){
		return(location);
	}

	if(strcmp("me",name) == 0){
		return(player);
	}

	if(strcmp("home",name) == 0){
		return(-3);	/* Abstract DB ref for home */
	}
	/* OK. Last ditch. If this player's a wiz or we're splat_ok,
		try *<playername> */

	if(*name == '*'){

		if(!splat_ok){  /* If not a Wiz, bag out */

			if(get_int_elt(player,FLAGS,&flags) == -1){
				warning("resolve_object"
					,"bad flags ref on player");
				return(-1);
			}
			if( !(flags & WIZARD)){
				return(-1);
			}
		}
		name++;
		return(match_player(name));
	}
	return(-1);
}
/*
	See if a player has enough pennies, and bill it if so. Returns number
of pennies billed, or -1 on error.

*/

int bill_player(player,pennies)
int player;
int pennies;

{
	int flags;
	int wealth;

	if(get_int_elt(player,FLAGS,&flags) == -1){
		return(-1);
	}
	if(flags & WIZARD)
		return(pennies);
	if(get_int_elt(player,PENNIES,&wealth) == -1){
		return(-1);
	}

	if(wealth >= pennies){
		wealth -= pennies;
		if(set_int_elt(player,PENNIES,wealth) == -1){
			return(-1);
		} else {
			return(pennies);
		}
	}
	notify_player(player,"You don't have enough pennies.\n");
	return(0);
}

/*
	Drops an element from a list. You'd better be sure the element is
actually on the list. If code == 0, exits list, if code == 1, things list.

*/

list_drop(elt,place,code)
int elt,place,code;
{
	int current,last,next;
	int listcode;

	if(code == 1){
		listcode = CONTENTS;
	} else {
		listcode = EXITS;
	}

	if(get_int_elt(place,listcode,&current) == -1){
		warning("list_drop","bad list reference.");
		return;
	}

	last = -1;
	while(current != elt && current != -1){
		last = current;
		if(get_int_elt(current,NEXT,&current) == -1){
			warning("list_drop","bad ref inside list");
			return;
		}
	}

	/* Post Mortem. */

	/* grab the next thing on the list anyway. */

	if(get_int_elt(current,NEXT,&next) == -1){
		warning("list_drop","bad ref inside list");
		return;
	}

	if(last == -1){ /* Was 1st thing on list */
		if(set_int_elt(place,listcode,next) == -1){
			warning("list_drop","bad list reference.");
			return;
		}
	} else {
		if(set_int_elt(last,NEXT,next) == -1){
			warning("list_drop","bad list reference.");
			return;
		}
	}
}

/*
	Adds a thing to the top of the contents or exits list of the specified
place.

*/

list_add(thing,place,code)
int thing,place,code;

{
	int list;
	int listcode;

	if(code == 1){
		listcode = CONTENTS;
	} else {
		listcode = EXITS;
	}

	if(get_int_elt(place,listcode,&list) == -1){
		warning("list_add","can't get contents");
		return;
	}
	if(set_int_elt(thing,NEXT,list) == -1){
		warning("list_add","bad can't set NEXT");
		return;
	}
	if(set_int_elt(place,listcode,thing) == -1){
		warning("list_add","can't set contents");
		return;
	}
}

/*
	Crams the name of a thing into a buffer. If the player controls the
thing, or if the thing is link_ok, we show the number.

*/

stuff_name(player,thing,buff,siz)
int player;
int thing;
char *buff;
int siz;

{
	char *name,*oldbuff;
	int flags;

	if(!exists_object(thing)){
		name = "<nothing>";
		for(; *name && siz > 0 ; siz--){
			*buff++ = *name++;
		}
		return(9);
	}
	oldbuff = buff;
	if(get_str_elt(thing,NAME,&name) == -1
			|| get_int_elt(thing,FLAGS,&flags) == -1){

		warning("stuff_name","bad object name or flags ref");
		return(0); /* Pretend it was a blank name. */
	}
	if(name == NULL)
		return(0);
	if((TYPE_MASK & flags) == TYP_PLAYER){
		while(!isspace(*name) && *name && siz){
			*buff++ = *name++;
			siz--;
		}
		if(!isspace(*name) && *name){
			return(-1);	/* Failed to fit */
		}
	} else {
		while(*name && siz){
			*buff++ = *name++;
			siz--;
		}
		if(*name){
			return(-1);	/* Failed to fit */
		}
	}

	/* Name fit OK. Do a number? */

	if(controls(player,thing) || (flags & LINK_OK)){
		if(siz < 12){
			return(-1);  /* Frob it. */
		}

		*buff++ = '(';
		*buff++ = '#';
		buff = ty_itoa(buff,thing);

		/* Flags */

		switch(flags & TYPE_MASK){
		case TYP_PLAYER:
			*buff++ = 'P';
			break;
		case TYP_ROOM:
			*buff++ = 'R';
			break;
		case TYP_EXIT:
			*buff++ = 'E';
		}
		if(flags & WIZARD) *buff++ = 'W';
		if(flags & LINK_OK) *buff++ = 'L';
		if(flags & STICKY) *buff++ = 'S';
		if(flags & TEMPLE) *buff++ = 'T';
		if(flags & DARK) *buff++ = 'D';
		*buff++ = ')';
	}
	return(buff - oldbuff);
}

/*
	Given a non-empty matchlist of exits, this will try to get the player
through one of them. It takes an unlocked exit in preference to a locked one.

*/

do_go_attempt(player,here,exlist)
int player;
int here;
struct match *exlist;

{
	struct match *current,*locklist,*next;
	int locked;
	int total_locked,total_unlocked,total;
	int theex,dest,flags,wealth;
	int list;
	char *p,*q,*name;
	int count;

	/* loop over the list once, to count it. This is clumsy. Cope. */

	current = exlist;
	for(count = 1;current->fwd != exlist && count < 1000;count++){
		current = current->fwd;
	}

	current = exlist;
	locklist = NULL;
	total_locked = total_unlocked = 0;

	/* Loop over the list, putting locked exits on the locklist */

	do {


		next = current->fwd; /* Guard this with your LIFE. */

		if(get_int_elt(current->obj,DESTINATION,&dest) == -1){
			warning("do_go_attempt","bed dest ref on exit");
			return;
		}
		/* Exits that are unlinked, or have dests that don't exist */
		/* Are considered locked. home (-3) is OK.  */

		if( islocked(player,current->obj)
				|| (dest != -3 && !exists_object(dest))){
			total_locked++;
			/* Remove this match from exlist */

			if(current->fwd == current){
				exlist = NULL;
			} else {
				(current->back)->fwd = current->fwd;
				(current->fwd)->back = current->back;
			}
			/* Stuff it in to locklist */

			if(locklist == NULL){
				locklist = current->back = current->fwd
					= current;
			} else {
				current->back = locklist->back;
				current->fwd = locklist;
				(locklist->back)->fwd = current;
				locklist->back = current;
			}
		} else {
			total_unlocked++;
		}
		current = next;
		count--;
	} while(count > 0);

	/* If there are no unlocked exits, then go with a locked one. */

	if(exlist == NULL){
		exlist = locklist;
		total = total_locked;
		locked = 1;
	} else {
		free_match_list(locklist);
		total = total_unlocked;
		locked = 0;
	}

	/* Now choose an exit from exlist */

	current = exlist;
	while(total){
		if(random() & 0x0010L) /* Break with 50% probability */
			break;
		current = current->fwd;
		total--;
	}

	/* Actually, current points one too far ahead. */

	theex = (current->back)->obj;
	free_match_list(exlist);

	/* Now do the exit */

	if(locked){
		fail_object(player,theex,"You can't go that way.");
		return;
	}

	/* Grab the destination */

	if(get_int_elt(theex,DESTINATION,&dest) == -1){
		warning("do_go_attempt","bad dest ref");
		return;
	}

	/* dest will be an existing object, or -3, otherwise 'locked' */

	if(dest == -3){ /* home */
		if(get_int_elt(player,HOME,&dest) == -1){
			warning("do_go_attempt","bad home ref on player");
			return;
		}
		if(!exists_object(dest)){
			notify_player(player,"Your home does not exist!\n");
			dest = 0;
		}
	}
	succeed_object(player,theex,(char *)NULL);

	/* Tell people here that the player has left */

	if(get_str_elt(player,NAME,&name) == -1){
		warning("do_go_attempt","bad player name reference");
		return;
	}
	p = work;
	q = name;
	for(count = 0 ; !isspace(*q) && *q && count < BUFFSIZ-32 ; count++){
		*p++ = *q++;
	}
	strcpy(p," has left.\n");
	notify_oall(player,work);

	/* Get the player out of here. */

	list_drop(player,here,1); /* From the things list */

	/* stuff player in at destination. */

	list_add(player,dest,1); /* Into contents list */
	if(set_int_elt(player,LOC,dest) == -1){
		warning("do_go_attempt","could not set player location");
		return;
	}

	/* Tell folks the player has arrived. p is still correct, really. */

	strcpy(p," has arrived.\n");
	notify_oall(player,work);
	do_look(player,(char *) NULL);

	/* Maybe we found a penny? */

	if(random() % 10L == 0){
		notify_player(player,"You found a penny!\n");
		if(get_int_elt(player,PENNIES,&wealth) == -1){
			warning("do_go_attempt","bad pennies reference");
			return;
		}
		wealth++;
		if(set_int_elt(player,PENNIES,wealth) == -1){
			warning("do_go_attempt","could not set pennies");
			return;
		}
	}
	/* Check for sticky droptos */

	if(get_int_elt(here,FLAGS,&flags) == -1){
		warning("do_go_attempt","bad flags ref on 'here'");
		return;
	}

	if(flags & STICKY){

		/* Check for a dropto to get activated. */

		if(get_int_elt(here,DROPTO,&dest) == -1){
			warning("do_go_attempt","bad dropto reference");
			return;
		}

		/* If no dropto, or it's to somewhere that doesn't exist.. */

		if(dest == -1 || (dest != -3 && !exists_object(dest))){
			return;
		}

		/* OK. See if there are any players here. */

		if(get_int_elt(here,CONTENTS,&list) == -1){
			warning("do_go_attempt","bad contents list ref");
			return;
		}
		while(list != -1){
			if(get_int_elt(list,FLAGS,&flags) == -1){
				warning("do_go_attempt"
					,"bad flags ref in contents list");
				return;
			}
			if((TYPE_MASK & flags) == TYP_PLAYER){
				return;
			}
			if(get_int_elt(list,NEXT,&list) == -1){
				warning("do_go_attempt"
					,"bad NEXT ref in contents list");
				return;
			}
		}

		/* No players left, toss everything here down the dropto */

		if(get_int_elt(here,CONTENTS,&list) == -1){
			warning("do_go_attempt","bad contents list ref");
			return;
		}
		while(list != -1){
			int sendto;

			if(get_int_elt(list,FLAGS,&flags) == -1){
				warning("do_go_attempt"
					,"bad flags ref in contents list");
				return;
			}
			if((TYPE_MASK & flags) == TYP_THING){
				list_drop(list,here,1);
				if(dest == -3){
					/* send it home */
					if(get_int_elt(list,HOME,&sendto) == -1){
						warning("do_go_attempt",
						  "bad home ref in dropto");
						sendto = 0;
					}
					if(!exists_object(sendto)){
						sendto = 0;
					}
				} else {
					sendto = dest;
				}
				list_add(list,sendto,1);
				if(set_int_elt(list,LOC,sendto) == -1){
					warning("do_go_attempt"
						,"could not set LOC on dropto");
					return;
				}
			}
			if(get_int_elt(list,NEXT,&list) == -1){
				warning("do_go_attempt"
					,"bad NEXT ref in contents list");
				return;
			}
		}

	}
}

fail_object(player,thing,def)
int player;
int thing;
char *def; /* Default fail string. */
{
	char *str,*ostr;
	/* Just grab the fail/ofail and do 'em */

	if(get_str_elt(thing,FAIL,&str) == -1
			|| get_str_elt(thing,OFAIL,&ostr) == -1){
		warning("fail_object","bad fail/ofail reference");
		return;
	}
	if(str == NULL)
		str = def;

	do_strings(player,str,ostr);
}
succeed_object(player,thing,def)
int player;
int thing;
char *def; /* Default success string. */
{
	char *str,*ostr;

	/* Grab the succ and osucc and do 'em */

	if(get_str_elt(thing,SUC,&str) == -1
			|| get_str_elt(thing,OSUC,&ostr) == -1){
		warning("succeed_object","bad success/osuccess reference");
		return;
	}
	if(str == NULL)
		str = def;
	do_strings(player,str,ostr);
}
/*
	This does the a success/osuccess pair, or a fail/ofail pair.
It *will* trample on the work buffer here. Cope.

*/

do_strings(player,str,ostr)
int player;
char *str,*ostr;
{
	char *name,*p;
	int count,len;

	/* Do the str */

	if(str != NULL){
		notify_player(player,str);
		notify_player(player,"\n");
	}

	/* Do the ostring. */

	if(ostr == NULL) return;

	if(get_str_elt(player,NAME,&name) == -1){
		warning("do_strings","bad player name reference.");
		return;
	}

	p = work;
	len = strlen(ostr);
	for(count = 0;!isspace(*name) && *name && count < BUFFSIZ-len-2;count++){
		*p++ = *name++;
	}
	*p++ = ' ';
	strcpy(p,ostr);
	p[len] = '\n';
	p[len+1] = '\0';
	
	notify_oall(player,work);
}

/*
	Spits a file in the cwd to the player.

*/

spit_file(player,name)
int player;
char *name;

{
	FILE *in;
	char filebuff[128];

	if((in = fopen(name,"r")) == NULL){
		notify_player(player,"Sorry ");
		notify_player(player,name);
		notify_player(player,
		" is broken. Your wizards are no doubt toiling over it now.\n");
		return;
	}

	/* Grab thing outta the file and shove 'em */

	while(fgets(filebuff,128,in) != NULL){
		notify_player(player,filebuff);
	}

	(void)fclose(in);
}