dbm/
misc/
old-docs/
/* cmdutils.c */

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

#include <stdio.h>
#ifdef STRING_H
#include <string.h>
#else
#include <strings.h>
#endif				/* STRING_H */
#include <ctype.h>

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

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

#ifdef OLD_RAND
long            rand();
#else
long            random();
#endif				/* OLD_RAND */

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

char            work[LARGEBUFFSIZ];

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

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

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

  if (!exists_object(home)) {
    home = STARTING_LOC;	/* Fake it, eh? */
  }
  if (set_int_elt(obj, LOC, home) == -1) {
    warning("send_home", "could not set location");
    return;
  }
  if (get_int_elt(home, CONTENTS, &next) == -1) {
    warning("send_home", "could not get contents");
    return;
  }
  if (set_int_elt(obj, NEXT, next) == -1) {
    warning("send_home", "could not set next");
    return;
  }
  if (set_int_elt(home, CONTENTS, obj) == -1) {
    warning("send_home", "could not set contents");
    return;
  }
}

/*
 * Resolves a string into an object reference, if it can. The last parameter
 * is a flag. Returns -1 if no matching object, -2 if ambiguous. This routine
 * now *only* matches non-exits. If you want exits matched, call either
 * resolve_exit() or resolve_anything().
 */
int             resolve_object(player, name, splat_ok)
  int             player;
  char           *name;
  int             splat_ok;
{
  int             matched, location;
  int             match1, match2, match3;

  /* Check these first.  Don't want to confuse these. */
  if (get_int_elt(player, LOC, &location) == -1) {
    warning("resolve_object", "bad location");
    return (-1);
  }
  if (strcasecmp("here", name) == 0) {
    if (get_int_elt(player, LOC, &location) == -1) {
      warning("resolve_object", "couldn't get player's location");
      return (-1);		/* no match, hehe */
    }
    return (location);
  }
  if (strcasecmp("me", name) == 0) {
    return (player);
  }
  if (name[0] == '#' && isdigit(name[1])) {
    matched = atoi(name + 1);
    if (exists_object(matched) && !isexit(matched))
      return (matched);
  }
  /* if match_here tells us "tie" then we return the info immediately. */

  if ((match1 = match_here(player, player, name, MAT_THINGS)) == -2)
    return (-2);
  if ((match2 = match_here(player, location, name, MAT_THINGS)) == -2)
    return (-2);
  if ((match3 = match_here(player, location, name, MAT_PLAYERS)) == -2)
    return (-2);

  if ((match1 == -1) && (match2 == -1) && (match3 == -1)) {
    /*
     * OK. Last ditch. If this player's a wiz or we're splat_ok, try
     * *<playername>.
     */

    if (*name == '*') {
      if (!splat_ok && !iswiz(player)) {	/* If not a Wiz, bag out */
	return (-1);
      }
      name++;
      if ((match1 = match_active_player(name)) == -1 || match1 == -2) {
	if ((match1 = match_who(name)) == -1) {
	  if ((match1 = match_player(name)) == -1) {
	    return (-1);
	  }
	}
      }
      return (match1);
    }
    return (-1);
  }
  return (best_match(name, match1, match2, match3));
}

/*
 * This routine tries to match a player name every way possible. It should be
 * used more often.
 */
int             resolve_player(player, name, splat_ok)
  int             player;
  char           *name;
  int             splat_ok;
{
  int             matched, loc;

  if (get_int_elt(player, LOC, &loc) == -1) {
    warning("resolve_player", "bad loc ref on player");
    return -1;
  }
  matched = match_here(player, loc, name, MAT_PLAYERS);
  if (exists_object(matched) && isplayer(matched))
    return (matched);

  if (!strcmp(name, "me"))
    return (player);

  if (!splat_ok && !iswiz(player))
    return (-1);

  if (name[0] == '*')
    name++;
  if (name[0] == '#') {
    matched = atoi(name + 1);
    if (exists_object(matched) && isplayer(matched))
      return (matched);
    else
      return (-1);
  }
  if ((matched = match_active_player(name)) == -1 || matched == -2) {
    if ((matched = match_who(name)) == -1) {
      if ((matched = match_player(name)) == -1) {
	return (-1);
      }
    }
  }
  return (matched);
}

/*
 * This routine tries to match its argument to an exit, exact matches only.
 * #<objnum> is not allowed. Returns -1 on no match, -2 if ambiguous, or the
 * object number.
 */
int             resolve_exit(player, name)
  int             player;
  char           *name;
{
  int             matched, location;
  int             match1, match2;

  if (get_int_elt(player, LOC, &location) == -1) {
    warning("resolve_exit", "bad location ref on player");
    return (-1);
  }
  match1 = match_here(player, location, name, MAT_EXITS);
  match2 = match_here(player, player, name, MAT_EXITS);

  if (((match1 > -1) && (match2 > -1)) || match1 == -2 || match2 == -2)
    return (-2);
  if (match1 != -1) {
    matched = match1;
  } else if (match2 != -1) {
    matched = match2;
  } else {
    /* go ahead and *carefully* parse an object number */
    matched = -1;

    if (*name && (*name == '#') && *(name + 1) && isdigit(name[1])) {
      matched = atoi(name + 1);
      if (!exists_object(matched) || !isexit(matched))
	matched = -1;
    }
  }

  return (matched);
}

/*
 * This simply calls resolve_object() and then resolve_exit(). Use this if
 * you really, really don't give a damn about match order.
 */
int             resolve_anything(player, name, splat_ok)
  int             player;
  char           *name;
  int             splat_ok;
{
  int             matched, match1, match2;

  match1 = resolve_object(player, name, splat_ok);
  match2 = resolve_exit(player, name);

  if ((match1 == -2) || (match2 == -2)) {
    matched = -2;
  } else if ((match1 != -1) && (match2 != -1)) {
    matched = -2;
  } else if (match1 != -1) {
    matched = match1;
  } else if (match2 != -1) {
    matched = match2;
  } else
    matched = -1;

  return (matched);
}

/*
 * 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.
 */

char           *stuff_name(player, thing)
  int             player;
  int             thing;
{
  char           *name, *p;
  static char     buff[BUFFSIZ + 32];
  int             flags;

  if (!exists_object(thing)) {
    (void) strcpy(buff, "<nothing>");
    return (buff);
  }
  if (get_str_elt(thing, NAME, &name) == -1
      || get_int_elt(thing, FLAGS, &flags) == -1) {
    log_error("stuff_name: couldn't get name or flags for #%d\n", thing);
    (void) strcpy(buff, "<spammed name>");
    return (buff);
  }
  for (p = buff; name && *name && (p - buff) < BUFFSIZ; *p++ = *name++);
  *p = 0;

  if (controls(player, thing) || islinkok(thing)
      || isjumpok(thing) || isabode(thing)) {

    (void) sprintf(p, "(#%d", thing);

    /* Flags */

    switch (flags & TYPE_MASK) {
    case TYP_PLAYER:
      (void) strcat(buff, "P");
      break;
    case TYP_ROOM:
      (void) strcat(buff, "R");
      break;
    case TYP_EXIT:
      (void) strcat(buff, "E");
    }
    if (flags & GOD)
      (void) strcat(buff, "G");
    if (flags & WIZARD)
      (void) strcat(buff, "W");
    if ((flags & ROBOT) && (flags & TYPE_MASK) == TYP_PLAYER)
      (void) strcat(buff, "R");
    if (flags & STICKY)
      (void) strcat(buff, "S");
    if (flags & LINK_OK)
      (void) strcat(buff, "L");
    if (flags & JUMP_OK)
      (void) strcat(buff, "J");
    if ((flags & ABODE) && (flags & TYPE_MASK) != TYP_EXIT)
      (void) strcat(buff, "A");
    else if (flags & ABODE)
      (void) strcat(buff, "O");
    if (flags & HAVEN)
      (void) strcat(buff, "H");
    if (flags & DARK)
      (void) strcat(buff, "D");
    if (flags & BUILDER)
      (void) strcat(buff, "B");
    if ((flags & ACTION) && (flags & TYPE_MASK) == TYP_EXIT)
      (void) strcat(buff, "a");
    if (flags & ENTER_OK)
      (void) strcat(buff, "e");
    if (flags & GUEST)
      (void) strcat(buff, "g");
    if ((flags & GENDER_MALE) && (flags & TYPE_MASK) == TYP_PLAYER)
      (void) strcat(buff, "m");
    if ((flags & GENDER_FEMALE) && (flags & TYPE_MASK) == TYP_PLAYER)
      (void) strcat(buff, "f");
    if ((flags & GENDER_NEUTER) && (flags & TYPE_MASK) == TYP_PLAYER)
      (void) strcat(buff, "n");
    if (isalive(thing) && iswiz(player))
      (void) strcat(buff, "c");
    (void) strcat(buff, ")");
  }
  return (buff);
}

/*
 * 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.
 */
void            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;
  char           *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, LOCK) || (dest != -3 && !exists_object(dest))) {
      total_locked++;
      /* Remove this match from exlist */

      if (current->fwd == current) {
	exlist = NULL;
      } else {
	if (exlist == current) {
	  exlist = next;
	}
	(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) {
#ifndef OLD_RAND
    if (random() & 0x0010L)	/* Break with 50% probability */
#else
    if (rand() & 0x0010L)	/* Break with 50% probability */
#endif				/* OLD_RAND */
      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) {
    act_object(player, theex, FAIL, OFAIL, -1, "You can't go that way.",
	       (char *) NULL);
    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!\r\n");
      dest = STARTING_LOC;
    }
  }
  act_object(player, theex, SUC, (isdark(player) || isdark(here)) ? -1 :
	     OSUC, -1, (char *) NULL, (char *) NULL);
  /* Tell people here that the player has left */

  stamp(theex);

  if (get_str_elt(player, NAME, &name) == -1) {
    warning("do_go_attempt", "bad player name reference");
    return;
  }
  if (!isdark(theex) && !isdark(player) && !isdark(here)) {
    sprintf(work, "%s has left.\r\n", name);
    notify_oall(player, work);
  }
  act_object(player, theex, DROP, (isdark(player) || isdark(dest)) ? -1 :
	     ODROP, dest, (char *) NULL, (char *) NULL);

  /* Get the player out of here. */

  list_drop(player, here, CONTENTS_LIST);

  /* stuff player in at destination. */

  list_add(player, dest, 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. */

  if (!isdark(theex) && !isdark(player) && !isdark(dest)) {
    sprintf(work, "%s has arrived.\r\n", name);
    notify_oall(player, work);
  }
  do_look(player, (char *) NULL);

  flush_room(here);
}

void            flush_room(room)
  int             room;
{
  int             flags, dest, list;

  /* Check for sticky droptos */

  if (get_int_elt(room, FLAGS, &flags) == -1) {
    warning("flush_room", "bad flags ref on room");
    return;
  }
  if ((flags & TYPE_MASK) != TYP_ROOM)
    return;
  if (flags & STICKY) {

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

    if (get_int_elt(room, DROPTO, &dest) == -1) {
      warning("flush_room", "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(room, CONTENTS, &list) == -1) {
      warning("flush_room", "bad contents list ref");
      return;
    }
    while (list != -1) {
      if (get_int_elt(list, FLAGS, &flags) == -1) {
	warning("flush_room", "bad flags ref in contents list");
	return;
      }
      if ((TYPE_MASK & flags) == TYP_PLAYER) {
	return;
      }
      if (get_int_elt(list, NEXT, &list) == -1) {
	warning("flush_room", "bad NEXT ref in contents list");
	return;
      }
    }

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

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

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

/*
 * This replaces fail_object(), succeed_object(), and much other kooky
 * coding.
 */
void            act_object(player, thing, code, ocode, oloc, def, odef)
  int             player;
  int             thing;	/* things they're acting upon */
  int             code;		/* code of the "success" string */
  int             ocode;	/* code of the "osuccess" string */
  int             oloc;		/* location for the o-string, optional. the
				 * player must not be here! */
  char           *def;		/* Default suc/fail/etc string. */
  char           *odef;
{
  char           *str, *ostr, *name;

  if (code != -1) {
    if (get_str_elt(thing, code, &str) == -1) {
      warning("act_object", "bad string reference");
      return;
    }
    if (str == NULL) {
      str = def;
    }
    if (str != NULL) {
      sprintf(work, "%s\r\n", str);
      notify_player(player, work);
    }
  }
  if (ocode != -1) {
    if (get_str_elt(thing, ocode, &ostr) == -1) {
      warning("act_object", "bad ostring reference");
      return;
    }
    if (ostr == NULL)
      ostr = odef;
    if (ostr != NULL && !isdark(player)) {
      name = getname(player);
      sprintf(work, "%s%s%s\r\n", name, (ostr[0] == '\'' || ostr[0] == ',')
	      ? "" : " ", pronoun_sub(player, ostr));
      if (oloc == -1)
	notify_oall(player, work);
      else
	notify_contents(oloc, work);
    }
  }
}

/*
 * Spits a file in the cwd to the player.
 */
void            spit_file(player, name)
  int             player;
  char           *name;
{
  FILE           *in;
  char            filebuff[128];

  if ((in = fopen(name, "r")) == NULL) {
    extern char     cmdwork[];

    sprintf(cmdwork, "Sorry, %s is broken. Your wizards are no doubt toiling over it now.\r\n");
    notify_player(player, cmdwork);
    return;
  }
  /* Grab thing outta the file and shove 'em */

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

  (void) fclose(in);
}

/*
 * Returns 1 if the player can see anything, 0 otherwise.
 */
int             can_see_anything(player, location)
  int             player;
  int             location;
{
  int             list, contents;

  if (location == -1) {
    /* get their location */
    if (get_int_elt(player, LOC, &location) == -1) {
      warning("can_see_anything", "bad player loc ref");
      notify_bad(player);
      return 0;
    }
  }
  if (get_int_elt(location, CONTENTS, &contents) == -1) {
    warning("can_see_anything", "bad location contents ref");
    notify_bad(player);
    return 0;
  }
  if (contents == player) {
    int             foo;

    if (get_int_elt(player, NEXT, &foo) == -1) {
      warning("can_see_anything", "bad player next ref");
      notify_bad(player);
      return 0;
    }
    if (foo == -1)
      return 0;
  }
  /* still here... hmm... loop through list. */
  list = contents;
  while (list != -1) {
    if (can_see(player, list))
      return 1;
    if (get_int_elt(list, NEXT, &list) == -1) {
      warning("can_see_anything", "bad next ref in contents list");
      notify_bad(player);
      return 0;
    }
  }
  /* they cannot see a damn thing! */
  return 0;
}

/*
 * Returns a 1 if player can see that specific object, 0 otherwise.
 */
int             can_see(player, obj)
  int             player;
  int             obj;
{
  int             loc, owner;

  if (player == obj)
    return 0;
#ifdef DARK_SLEEP
  if (isplayer(obj) && !isalive(obj))
    return 0;
#endif				/* DARK_SLEEP */
  if (isroom(obj))
    return 0;			/* wee... */
  if (get_int_elt(obj, LOC, &loc) == -1) {
    warning("can_see", "couldn't get object's location");
    return (0);
  }
  if (get_int_elt(obj, OWNER, &owner) == -1) {
    warning("can_see", "couldn't get object's owner");
    return (0);
  }
  /* this is so a wizz won't see so much junk a dark room. */
  if (isdark(loc) && (owner != player))
    return 0;
  if (!isdark(obj))
    return 1;
  if (isdark(obj) && controls(player, obj))
    return 1;

  return 0;
}
int             legal_parent_check(source, test)
  int             source, test;
{
  int             parent;

  if (source == test)
    return 0;
  if (!exists_object(test) || !isroom(test))
    return 0;
  if (test == ROOT_PARENT)
    return 1;
  if (get_int_elt(test, LOC, &parent) == -1)
    return 0;
  return (legal_parent_check(source, parent));
}

#ifdef VERBOSE_FLAGS
char           *display_flags(obj)
  int             obj;
{
  static char     buffer[1024];
  int             flags;

  if (get_int_elt(obj, FLAGS, &flags) == -1) {
    strcpy(buffer, "<spammed flags>");
    return buffer;
  } else {
    strcpy(buffer, "Type: ");
    switch (flags & TYPE_MASK) {
    case TYP_ROOM:
      strcat(buffer, "Room");
      break;
    case TYP_EXIT:
      strcat(buffer, "Exit");
      break;
    case TYP_PLAYER:
      strcat(buffer, "Player");
      break;
    case TYP_THING:
      strcat(buffer, "Thing");
      break;
    default:
      strcat(buffer, "*UNKNOWN TYPE*");
    }
    if (flags & ~(TYPE_MASK | INTERNAL_FLAGS)) {
      strcat(buffer, "  Flags: ");
      if (flags & GOD)
	strcat(buffer, "GOD ");
      if (flags & WIZARD)
	strcat(buffer, "WIZARD ");
      if ((flags & ROBOT) && (flags & TYPE_MASK) == TYP_PLAYER)
	strcat(buffer, "ROBOT ");
      if (flags & STICKY)
	strcat(buffer, "STICKY ");
      if (flags & LINK_OK)
	strcat(buffer, "LINK_OK ");
      if (flags & JUMP_OK)
	strcat(buffer, "JUMP_OK ");
      if (flags & ABODE) {
	if ((flags & TYPE_MASK) == TYP_EXIT)
	  strcat(buffer, "OBVIOUS ");
	else
	  strcat(buffer, "ABODE ");
      }
      if (flags & HAVEN)
	strcat(buffer, "HAVEN ");
      if (flags & DARK)
	strcat(buffer, "DARK ");
#ifdef BUILDING_OK
      if (flags & BUILDER) {
	if ((flags & TYPE_MASK) != TYP_PLAYER)
	  strcat(buffer, "BUILDING_OK ");
	else
	  strcat(buffer, "BUILDER ");
      }
#else
      if (flags & BUILDER)
	strcat(buffer, "BUILDER ");
#endif				/* BUILDING_OK */
      if ((flags & ACTION) && (flags & TYPE_MASK) == TYP_EXIT)
	strcat(buffer, "ACTION ");
      if ((flags & ENTER_OK) && (flags & TYPE_MASK) != TYP_EXIT)
	strcat(buffer, "ENTER_OK ");
      else if (flags & ENTER_OK)
	strcat(buffer, "EXTERNAL ");
      if (flags & GUEST)
	strcat(buffer, "GUEST ");
      if ((flags & GENDER_MALE) && (flags & TYPE_MASK) == TYP_PLAYER)
	strcat(buffer, "MALE ");
      if ((flags & GENDER_FEMALE) && (flags & TYPE_MASK) == TYP_PLAYER)
	strcat(buffer, "FEMALE ");
      if ((flags & GENDER_NEUTER) && (flags & TYPE_MASK) == TYP_PLAYER)
	strcat(buffer, "NEUTER ");
    }
    if (buffer[strlen(buffer) - 1] == ' ')
      buffer[strlen(buffer) - 1] = 0;
    strcat(buffer, "\r\n");
    return buffer;
  }
}

#endif				/* VERBOSE_FLAGS */