fbmuck-6.01/contrib/jresolver/
fbmuck-6.01/contrib/jresolver/org/
fbmuck-6.01/contrib/jresolver/org/fuzzball/
fbmuck-6.01/docs/devel/
fbmuck-6.01/game/
fbmuck-6.01/game/logs/
fbmuck-6.01/game/muf/
fbmuck-6.01/scripts/
fbmuck-6.01/src_docs/
/* $Header: /cvsroot/fbmuck/fbmuck/src/match.c,v 1.6 2003/04/07 18:37:02 cutevixy Exp $ */


#include "copyright.h"
#include "config.h"

/* Routines for parsing arguments */
#include <ctype.h>

#include "db.h"
#include "props.h"
#include "params.h"
#include "tune.h"
#include "match.h"
#include "interface.h"
#include "externs.h"


#define DOWNCASE(x) (tolower(x))

char match_cmdname[BUFFER_LEN];	/* triggering command */
char match_args[BUFFER_LEN];	/* remaining text */

void
init_match(int descr, dbref player, const char *name, int type, struct match_data *md)
{
	md->exact_match = md->last_match = NOTHING;
	md->match_count = 0;
	md->match_who = player;
	md->match_from = player;
	md->match_descr = descr;
	md->match_name = name;
	md->check_keys = 0;
	md->preferred_type = type;
	md->longest_match = 0;
	md->match_level = 0;
	md->block_equals = 0;
	md->partial_exits = (TYPE_EXIT == type);
}

void
init_match_check_keys(int descr, dbref player, const char *name, int type,
					  struct match_data *md)
{
	init_match(descr, player, name, type, md);
	md->check_keys = 1;
}

void
init_match_remote(int descr, dbref player, dbref what, const char *name, int type,
				  struct match_data *md)
{
	init_match(descr, player, name, type, md);
	md->match_from = what;
}

static dbref
choose_thing(int descr, dbref thing1, dbref thing2, struct match_data *md)
{
	int has1;
	int has2;
	int preferred = md->preferred_type;

	if (thing1 == NOTHING) {
		return thing2;
	} else if (thing2 == NOTHING) {
		return thing1;
	}
	if (preferred != NOTYPE) {
		if (Typeof(thing1) == preferred) {
			if (Typeof(thing2) != preferred) {
				return thing1;
			}
		} else if (Typeof(thing2) == preferred) {
			return thing2;
		}
	}
	if (md->check_keys) {
		has1 = could_doit(descr, md->match_who, thing1);
		has2 = could_doit(descr, md->match_who, thing2);

		if (has1 && !has2) {
			return thing1;
		} else if (has2 && !has1) {
			return thing2;
		}
		/* else fall through */
	}
	return (RANDOM() % 2 ? thing1 : thing2);
}

void
match_player(struct match_data *md)
{
	dbref match;
	const char *p;

	if (*(md->match_name) == LOOKUP_TOKEN && payfor(OWNER(md->match_from), tp_lookup_cost)) {
		for (p = (md->match_name) + 1; isspace(*p); p++) ;
		if ((match = lookup_player(p)) != NOTHING) {
			md->exact_match = match;
		}
	}
}

/* returns dbref if registered object found for name, else NOTHING */
dbref
find_registered_obj(dbref player, const char *name)
{
	dbref match;
	const char *p;
	char buf[BUFFER_LEN];
	PropPtr ptr;

	if (*name != REGISTERED_TOKEN)
		return (NOTHING);
	for (p = name + 1; *p && isspace(*p); p++) ;
	if (!*p)
		return (NOTHING);
	snprintf(buf, sizeof(buf), "_reg/%s", p);
	ptr = envprop(&player, buf, 0);
	if (!ptr)
		return NOTHING;
#ifdef DISKBASE
	propfetch(player, ptr);		/* DISKBASE PROPVALS */
#endif
	switch (PropType(ptr)) {
	case PROP_STRTYP:
		p = PropDataStr(ptr);
#ifdef COMPRESS
		p = uncompress(p);
#endif
		if (*p == NUMBER_TOKEN)
			p++;
		if (number(p)) {
			match = (dbref) atoi(p);
			if ((match >= 0) && (match < db_top) && (Typeof(match) != TYPE_GARBAGE))
				return (match);
		}
		break;
	case PROP_REFTYP:
		match = PropDataRef(ptr);
		if ((match >= 0) && (match < db_top) && (Typeof(match) != TYPE_GARBAGE))
			return (match);
		break;
	case PROP_INTTYP:
		match = (dbref) PropDataVal(ptr);
		if ((match > 0) && (match < db_top) && (Typeof(match) != TYPE_GARBAGE))
			return (match);
		break;
	case PROP_LOKTYP:
		return (NOTHING);
		break;
	}
	return (NOTHING);
}


void
match_registered(struct match_data *md)
{
	dbref match;

	match = find_registered_obj(md->match_from, md->match_name);
	if (match != NOTHING)
		md->exact_match = match;
}


/* returns nnn if name = #nnn, else NOTHING */
static dbref
absolute_name(struct match_data *md)
{
	dbref match;

	if (*(md->match_name) == NUMBER_TOKEN) {
		match = parse_dbref((md->match_name) + 1);
		if (match < 0 || match >= db_top) {
			return NOTHING;
		} else {
			return match;
		}
	} else {
		return NOTHING;
	}
}

void
match_absolute(struct match_data *md)
{
	dbref match;

	if ((match = absolute_name(md)) != NOTHING) {
		md->exact_match = match;
	}
}

void
match_me(struct match_data *md)
{
	if (!string_compare(md->match_name, "me")) {
		md->exact_match = md->match_who;
	}
}

void
match_here(struct match_data *md)
{
	if (!string_compare(md->match_name, "here")
		&& DBFETCH(md->match_who)->location != NOTHING) {
		md->exact_match = DBFETCH(md->match_who)->location;
	}
}

void
match_home(struct match_data *md)
{
	if (!string_compare(md->match_name, "home"))
		md->exact_match = HOME;
}


static void
match_list(dbref first, struct match_data *md)
{
	dbref absolute;

	absolute = absolute_name(md);
	if (!controls(OWNER(md->match_from), absolute))
		absolute = NOTHING;

	DOLIST(first, first) {
		if (first == absolute) {
			md->exact_match = first;
			return;
		} else if (!string_compare(RNAME(first), md->match_name)) {
			/* if there are multiple exact matches, randomly choose one */
			md->exact_match = choose_thing(md->match_descr, md->exact_match, first, md);
		} else if (string_match(RNAME(first), md->match_name)) {
			md->last_match = first;
			(md->match_count)++;
		}
	}
}

void
match_possession(struct match_data *md)
{
	match_list(DBFETCH(md->match_from)->contents, md);
}

void
match_neighbor(struct match_data *md)
{
	dbref loc;

	if ((loc = DBFETCH(md->match_from)->location) != NOTHING) {
		match_list(DBFETCH(loc)->contents, md);
	}
}

/*
 * match_exits matches a list of exits, starting with 'first'.
 * It will match exits of players, rooms, or things.
 */
void
match_exits(dbref first, struct match_data *md)
{
	dbref exit, absolute;
	const char *exitname, *p;
	int i, exitprog, lev, partial;

	if (first == NOTHING)
		return;					/* Easy fail match */
	if ((DBFETCH(md->match_from)->location) == NOTHING)
		return;

	absolute = absolute_name(md);	/* parse #nnn entries */
	if (!controls(OWNER(md->match_from), absolute))
		absolute = NOTHING;

	DOLIST(exit, first) {
		if (exit == absolute) {
			md->exact_match = exit;
			continue;
		}
		exitprog = 0;
		if (FLAGS(exit) & HAVEN) {
			exitprog = 1;
		} else if (DBFETCH(exit)->sp.exit.dest) {
			for (i = 0; i < DBFETCH(exit)->sp.exit.ndest; i++)
				if (Typeof((DBFETCH(exit)->sp.exit.dest)[i]) == TYPE_PROGRAM)
					exitprog = 1;
		}
		if (tp_enable_prefix && exitprog && md->partial_exits &&
			(FLAGS(exit) & XFORCIBLE) && FLAGS(OWNER(exit)) & WIZARD) {
			partial = 1;
		} else {
			partial = 0;
		}
		exitname = NAME(exit);
		while (*exitname) {		/* for all exit aliases */
			int notnull = 0;
			for (p = md->match_name;	/* check out 1 alias */
				 *p &&
				 DOWNCASE(*p) == DOWNCASE(*exitname) &&
				 *exitname != EXIT_DELIMITER;
				 p++, exitname++)
			{
				if (!isspace(*p)) {
					notnull = 1;
				}
			}
			/* did we get a match on this alias? */
			if ((partial && notnull) || ((*p == '\0') || (*p == ' ' && exitprog))) {
				/* make sure there's nothing afterwards */
				while (isspace(*exitname))
					exitname++;
				lev = PLevel(exit);
				if (tp_compatible_priorities && (lev == 1) &&
					(DBFETCH(exit)->location == NOTHING ||
					 Typeof(DBFETCH(exit)->location) != TYPE_THING ||
					 controls(OWNER(exit), getloc(md->match_from))))
					lev = 2;
				if (*exitname == '\0' || *exitname == EXIT_DELIMITER) {
					/* we got a match on this alias */
					if (lev >= md->match_level) {
						if (strlen(md->match_name) - strlen(p) > md->longest_match) {
							if (lev > md->match_level) {
								md->match_level = lev;
								md->block_equals = 0;
							}
							md->exact_match = exit;
							md->longest_match = strlen(md->match_name) - strlen(p);
							if ((*p == ' ') || (partial && notnull)) {
								strcpy(match_args, (partial && notnull)? p : (p + 1));
								{
									char *pp;
									int ip;

									for (ip = 0, pp = (char *) md->match_name;
										 *pp && (pp != p); pp++)
										match_cmdname[ip++] = *pp;
									match_cmdname[ip] = '\0';
								}
							} else {
								*match_args = '\0';
								strcpy(match_cmdname, (char *) md->match_name);
							}
						} else if ((strlen(md->match_name) - strlen(p) ==
									md->longest_match) && !((lev == md->match_level) &&
															(md->block_equals))) {
							if (lev > md->match_level) {
								md->exact_match = exit;
								md->match_level = lev;
								md->block_equals = 0;
							} else {
								md->exact_match =
										choose_thing(md->match_descr, md->exact_match, exit,
													 md);
							}
							if (md->exact_match == exit) {
								if ((*p == ' ') || (partial && notnull)) {
									strcpy(match_args, (partial && notnull) ? p : (p + 1));
									{
										char *pp;
										int ip;

										for (ip = 0, pp = (char *) md->match_name;
											 *pp && (pp != p); pp++)
											match_cmdname[ip++] = *pp;
										match_cmdname[ip] = '\0';
									}
								} else {
									*match_args = '\0';
									strcpy(match_cmdname, (char *) md->match_name);
								}
							}
						}
					}
					goto next_exit;
				}
			}
			/* we didn't get it, go on to next alias */
			while (*exitname && *exitname++ != EXIT_DELIMITER) ;
			while (isspace(*exitname))
				exitname++;
		}						/* end of while alias string matches */
	  next_exit:
		;
	}
}

/*
 * match_invobj_actions
 * matches actions attached to objects in inventory
 */
void
match_invobj_actions(struct match_data *md)
{
	dbref thing;

	if (DBFETCH(md->match_from)->contents == NOTHING)
		return;
	DOLIST(thing, DBFETCH(md->match_from)->contents) {
		if (Typeof(thing) == TYPE_THING && DBFETCH(thing)->exits != NOTHING) {
			match_exits(DBFETCH(thing)->exits, md);
		}
	}
}

/*
 * match_roomobj_actions
 * matches actions attached to objects in the room
 */
void
match_roomobj_actions(struct match_data *md)
{
	dbref thing, loc;

	if ((loc = DBFETCH(md->match_from)->location) == NOTHING)
		return;
	if (DBFETCH(loc)->contents == NOTHING)
		return;
	DOLIST(thing, DBFETCH(loc)->contents) {
		if (Typeof(thing) == TYPE_THING && DBFETCH(thing)->exits != NOTHING) {
			match_exits(DBFETCH(thing)->exits, md);
		}
	}
}

/*
 * match_player_actions
 * matches actions attached to player
 */
void
match_player_actions(struct match_data *md)
{
	dbref obj;

	switch (Typeof(md->match_from)) {
	case TYPE_PLAYER:
	case TYPE_ROOM:
	case TYPE_THING:
		obj = DBFETCH(md->match_from)->exits;
		break;
	default:
		obj = NOTHING;
		break;
	}
	if (obj == NOTHING)
		return;
	match_exits(obj, md);
}

/*
 * match_room_exits
 * Matches exits and actions attached to player's current room.
 * Formerly 'match_exit'.
 */
void
match_room_exits(dbref loc, struct match_data *md)
{
	dbref obj;

	switch (Typeof(loc)) {
	case TYPE_PLAYER:
	case TYPE_ROOM:
	case TYPE_THING:
		obj = DBFETCH(loc)->exits;
		break;
	default:
		obj = NOTHING;
		break;
	}
	if (obj == NOTHING)
		return;
	match_exits(obj, md);
}

/*
 * match_all_exits
 * Matches actions on player, objects in room, objects in inventory,
 * and room actions/exits (in reverse order of priority order).
 */
void
match_all_exits(struct match_data *md)
{
	dbref loc;
	int limit = 88;

	strcpy(match_args, "\0");
	strcpy(match_cmdname, "\0");
	if ((loc = DBFETCH(md->match_from)->location) != NOTHING)
		match_room_exits(loc, md);

	if (md->exact_match != NOTHING)
		md->block_equals = 1;
	match_invobj_actions(md);

	if (md->exact_match != NOTHING)
		md->block_equals = 1;
	match_roomobj_actions(md);

	if (md->exact_match != NOTHING)
		md->block_equals = 1;
	match_player_actions(md);

	if (loc == NOTHING)
		return;

	/* if player is in a vehicle, use environment of vehicle's home */
	if (Typeof(loc) == TYPE_THING) {
		loc = THING_HOME(loc);
		if (loc == NOTHING)
			return;
		if (md->exact_match != NOTHING)
			md->block_equals = 1;
		match_room_exits(loc, md);
	}

	while ((loc = DBFETCH(loc)->location) != NOTHING) {
		if (md->exact_match != NOTHING)
			md->block_equals = 1;
		match_room_exits(loc, md);
		if (!limit--)
			break;
	}
}

void
match_everything(struct match_data *md)
{
	match_all_exits(md);
	match_neighbor(md);
	match_possession(md);
	match_me(md);
	match_here(md);
	match_registered(md);
	if (Wizard(OWNER(md->match_from)) || Wizard(md->match_who)) {
		match_absolute(md);
		match_player(md);
	}
}

dbref
match_result(struct match_data *md)
{
	if (md->exact_match != NOTHING) {
		return (md->exact_match);
	} else {
		switch (md->match_count) {
		case 0:
			return NOTHING;
		case 1:
			return (md->last_match);
		default:
			return AMBIGUOUS;
		}
	}
}

/* use this if you don't care about ambiguity */
dbref
last_match_result(struct match_data * md)
{
	if (md->exact_match != NOTHING) {
		return (md->exact_match);
	} else {
		return (md->last_match);
	}
}

dbref
noisy_match_result(struct match_data * md)
{
	dbref match;

	switch (match = match_result(md)) {
	case NOTHING:
		notify(md->match_who, NOMATCH_MESSAGE);
		return NOTHING;
	case AMBIGUOUS:
		notify(md->match_who, AMBIGUOUS_MESSAGE);
		return NOTHING;
	default:
		return match;
	}
}

void
match_rmatch(dbref arg1, struct match_data *md)
{
	if (arg1 == NOTHING)
		return;
	switch (Typeof(arg1)) {
	case TYPE_PLAYER:
	case TYPE_ROOM:
	case TYPE_THING:
		match_list(DBFETCH(arg1)->contents, md);
		match_exits(DBFETCH(arg1)->exits, md);
		break;
	}
}