/* $Header: /belch_a/users/rearl/tinymuck/src/RCS/look.c,v 1.17 90/10/06 19:04:30 rearl Exp $ */

/*
 * $Log:	look.c,v $
 * Revision 1.17  90/10/06  19:04:30  rearl
 * Fixed a typo in @# handling.
 * 
 * Revision 1.16  90/10/06  16:32:49  rearl
 * Fixes for 2.2 distribution.
 * 
 * Revision 1.15  90/09/28  12:24:13  rearl
 * Fixed small bug with disappearing descriptions...
 * 
 * Revision 1.14  90/09/18  08:00:02  rearl
 * Took out FILTER.  Fixed @find.
 * 
 * Revision 1.13  90/09/16  04:42:25  rearl
 * Preparation code added for disk-based MUCK.
 * 
 * Revision 1.12  90/09/15  22:25:59  rearl
 * Added argument passing to MUF from @#.  COMPRESS fix for this as well.
 * 
 * Revision 1.11  90/09/13  06:27:38  rearl
 * Argument passing with @# added.
 * 
 * Revision 1.10  90/08/27  03:29:34  rearl
 * Added environment support.
 * 
 * Revision 1.9  90/08/11  04:05:21  rearl
 * *** empty log message ***
 * 
 * Revision 1.8  90/08/06  02:35:27  rearl
 * Oopsie, changed controls() back to can_link().
 * 
 * Revision 1.7  90/08/05  03:19:33  rearl
 * Redid matching routines.
 * 
 * Revision 1.6  90/08/02  18:53:09  rearl
 * Changed can_link() to controls().  Everyone controls an unlinked exit.
 * 
 * Revision 1.5  90/07/30  00:09:55  rearl
 * Added @owned command, lists everything you own, or for wizards,
 * everything owned by the specified player (including exits)
 * 
 * Revision 1.4  90/07/29  21:19:17  rearl
 * Fixed examine-description brain damage.
 * 
 * Revision 1.3  90/07/29  17:39:30  rearl
 * Fixed various things such as ROOM programs, examine bug, examine
 * of programs now shows location, etc.
 * 
 * Revision 1.2  90/07/23  03:12:10  casie
 * Cleaned up various gcc warnings.
 * 
 * Revision 1.1  90/07/19  23:03:52  casie
 * Initial revision
 * 
 *
 */

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

/* commands which look at things */

#include <ctype.h>
#include "db.h"
#include "params.h"
#include "interface.h"
#include "match.h"
#include "externs.h"
#include "strings.h"

#define EXEC_SIGNAL '@'    /* Symbol which tells us what we're looking at
			    * is an execution order and not a message.    */

/* prints owner of something */
static void
  print_owner(dbref player, dbref thing)
{
  char buf[BUFFER_LEN];
  
  switch(Typeof(thing))
    {
    case TYPE_PLAYER:
      sprintf(buf, "%s is a player.", NAME(thing));
      break;
    case TYPE_ROOM:
    case TYPE_THING:
    case TYPE_EXIT:
    case TYPE_PROGRAM:
      sprintf(buf, "Owner: %s", NAME(OWNER(thing)));
      break;
#ifdef RECYCLE
    case TYPE_GARBAGE:
      sprintf(buf, "%s is garbage.", NAME(thing));
      break;
#endif      
    }
  notify(player, buf);
}  

void exec_or_notify(dbref player, dbref thing, const char *message)
{
  const char *p;
  p = get_uncompress(message);

  if (*p == EXEC_SIGNAL) {
    int i;

    i = atoi(++p);
    for (; *p && !isspace(*p); p++)
      ;
    if (*p) p++;
    if ((Typeof(i) != TYPE_PROGRAM) || DBFETCH(player)->run) { 
      if (*p) {
	if(index(p, '\\')) {
	  char buff[BUFFER_LEN];
	  strcpy(buff, p);
	  p = buff;
	  while(index(p, '\\') && (*(1 + index(p, '\\')) == 'n')) {
	    *index(p, '\\') = '\0';
	    notify(player, p);
	    p += strlen(p);
	    p += 2;
	  };
	  if(*p)
	    notify (player, p);
	} else
	  notify(player, p);
      } else notify(player, "You see nothing special.");
    } else {
      strcpy(match_args, p);
      (void) interp(player, i, thing);
      if (!(DBFETCH(player)->run -> pc)) {
	free((void *) DBFETCH(player)->run);
	DBSTORE(player, run, 0);
      }
    }
  } else {
    if(index(p, '\\')) {
      char buff[BUFFER_LEN];
      strcpy(buff, p);
      p = buff;
      while(index(p, '\\') && (*(1 + index(p, '\\')) == 'n')) {
	*index(p,'\\') = '\0';
	notify(player,p);
	p += strlen(p);
	p += 2;
      };
      if(*p)
	notify(player, p);
    } else
      notify(player, p);
  }
}

static void look_exits(dbref player, dbref loc, const char *exits_name)
{
  dbref thing;
  int flag = 1;
  char buf[BUFFER_LEN];
  strcpy(buf, exits_name);

  DOLIST(thing, DBFETCH(loc)->exits) {
    if(!(FLAGS(thing)&DARK)) {
      if (flag == 0)
	strcat(buf, ", ");  /* don't put a comma before the first exit */
      strcat(buf, NAME(thing));
      flag = 0;
      if(index(buf, ';'))
	*index(buf, ';') ='\0';
    }
  }
  if(strcmp(buf, exits_name))
    notify(player, buf);
}

static void look_contents(dbref player, dbref loc, const char *contents_name)
{
  dbref thing;
  dbref can_see_loc;
  
  /* check to see if he can see the location */
  can_see_loc = (!(Dark(loc) || Murky(loc)) || controls(player, loc));

  /* check to see if there is anything there */
  DOLIST(thing, DBFETCH(loc)->contents) {
    if(can_see(player, thing, can_see_loc)) {
      /* something exists!  show him everything */
      notify(player, contents_name);
      DOLIST(thing, DBFETCH(loc)->contents) {
	if(can_see(player, thing, can_see_loc)) {
	  notify(player, unparse_object(player, thing));
	}
      }
      break;		/* we're done */
    }
  }
}

static void look_simple(dbref player, dbref thing)
{
  if(get_attr(thing, "Desc")) {
    exec_or_notify(player, thing, get_attr(thing, "Desc"));
  } else {
    notify(player, "You see nothing special.");
  }
  if(get_attr(thing, "Odesc")) {
    sprintf(buf,"%s %s", NAME(player), get_attr(thing, "Odesc"));
    notify_except(DBFETCH(getloc(player))->contents,
		  player, pronoun_substitute(player, buf, thing));
  }
  if(get_attr(thing, "Adesc")) {
    trigobj(thing, get_attr(thing, "Adesc"), player);
  }
  look_contents(player, thing, "Contents:");
}

void look_room(dbref player, dbref loc)
{
  /* tell him the name, and the number if he can link to it */
  notify(player, unparse_object(player, loc));
  
  /* tell him the description */
  if(get_attr(loc, "Idesc")) {
    exec_or_notify(player, loc, get_attr(loc,"idesc"));
  } else
    if(get_attr(loc, "Desc")) {
      exec_or_notify(player, loc, get_attr(loc, "Desc"));
    }
 if(get_attr(loc, "Odesc")) {
    sprintf(buf,"%s %s", NAME(player), get_attr(loc, "Odesc"));
    notify_except(DBFETCH(getloc(player))->contents, player,
		  pronoun_substitute(player, buf, loc));
  }
  if(get_attr(loc, "Adesc")) {
    trigobj(loc, get_attr(loc, "Adesc"), player);
  }
  
  /* tell him the appropriate messages if he has the key */
  if(Typeof(loc) == TYPE_ROOM) { /* we don't wanna see the osucc on an object. */
    can_doit(player, loc, 0);
  if (!(FLAGS(loc)&DARK))
    look_exits(player,loc,"Obvious exits: ");
  }
  /* tell him the contents */
  look_contents(player, loc, "Contents:");
}

void do_look_around(dbref player)
{
  dbref loc;
  
  if((loc = getloc(player)) == NOTHING) return;
  look_room(player, loc);
}

void do_look_at(dbref player, const char *name)
{
  dbref thing;
  struct match_data md;
  
  if(*name == '\0') {
    if((thing = getloc(player)) != NOTHING) {
      look_room(player, thing);
    }
  } else {
    /* look at a thing here */
    init_match(player, name, NOTYPE, &md);
    match_all_exits(&md);
    match_neighbor(&md);
    match_possession(&md);
    if(Arch(player)) {
      match_absolute(&md);
      match_player(&md);
    }
    match_here(&md);
    match_me(&md);

    if((thing = noisy_match_result(&md)) != NOTHING) {
      switch(Typeof(thing)) {
      case TYPE_ROOM:
	if (getloc(player) != thing
	    && !can_link_to(player, TYPE_ROOM, thing)) {
	  notify(player, "Permission denied.");
	} else {
	  look_room(player, thing);
	}
	break;
      default:
	look_simple(player, thing);
	break;
      }
    }
  }
}

void do_examine(dbref player, const char *name)
{
  dbref thing;
  char buf[BUFFER_LEN];
  dbref content;
  dbref exit;
  int  i;
  struct match_data md;
  if(*name == '\0') {
    if((thing = getloc(player)) == NOTHING) return;
  } else {
    /* look it up */
    init_match(player, name, NOTYPE, &md);
    match_all_exits(&md);
    match_neighbor(&md);
    match_possession(&md);
    match_absolute(&md);
    match_player(&md);
    match_here(&md);
    match_me(&md);
    /* get result */
    if((thing = noisy_match_result(&md)) == NOTHING) return;
  }
  if(!can_link(player, thing) && !(FLAGS(thing)&VISIBLE) && !Arch(player)) {
    print_owner(player, thing);
    return;
  }
  switch(Typeof(thing)) {
  case TYPE_ROOM:
    sprintf(buf, "%s  Owner: %s  Parent: ", unparse_object(player, thing),
	    NAME(OWNER(thing)));
    strcat(buf, unparse_object(player, DBFETCH(thing)->location));
    break;
  case TYPE_THING:
    sprintf(buf, "%s  Owner: %s  Value: %d", unparse_object(player, thing),
	    NAME(OWNER(thing)), DBFETCH(thing)->sp.thing.value);
    break;
  case TYPE_PLAYER:
    sprintf(buf, "%s  Owner: %s  Pennies: %d  ", unparse_object(player, thing),
	    NAME(OWNER(thing)), DBFETCH(thing)->sp.player.pennies);
    break;
  case TYPE_EXIT:
  case TYPE_PROGRAM:
#ifdef RECYCLE
  case TYPE_GARBAGE:
#endif
    sprintf(buf, "%s  Owner: %s", unparse_object(player, thing),
	    NAME(OWNER(thing)));
    break;
  }
  notify(player, buf);
#ifdef VERBOSE_EXAMINE
  sprintf(buf,"Flags: %s",unparse_flag(FLAGS(thing)));
  notify(player, buf);
#endif /* VERBOSE_EXAMINE */

  sprintf(buf, "Key: %s", unparse_boolexp(player, DBFETCH(thing)->key));
  notify(player, buf);

  /* show him the attributes */
  if (DBFETCH(thing)->attributes)
    {
      struct plist *a;
      char buf[BUFFER_LEN];

      sprintf(buf, "Attributes('%c'):", PROP_WIZARD);
      notify(player, buf);

      for (a = DBFETCH(thing) -> attributes; a; a = a -> next)
	{
	  strncpy(buf, (a -> type) + 1, BUFFER_LEN - 1);
	  strncat(buf, ": ", BUFFER_LEN - 1);
	  strncat(buf, get_uncompress(a -> class), BUFFER_LEN - 1);
	  notify(player, buf);
	}
    }

  /* show him the properties */
  if (DBFETCH(thing)->properties)
    {
      struct plist *p;
      char   buf[BUFFER_LEN];

      notify(player, "Properties:");      
      for (p = DBFETCH(thing) -> properties; p; p = p -> next)
	{
	  strncpy(buf, p -> type, BUFFER_LEN - 1);
	  strncat(buf, ": ", BUFFER_LEN - 1);
	  strncat(buf, get_uncompress(p -> class), BUFFER_LEN - 1);
	  notify(player, buf);
	}
    }
  /* show him the contents */
  if(DBFETCH(thing)->contents != NOTHING) {
    notify(player, "Contents:");
    DOLIST(content, DBFETCH(thing)->contents) {
      notify(player, unparse_object(player, content));
    }
  }
  
  switch(Typeof(thing)) {
  case TYPE_ROOM:
    /* tell him about exits */
    if(DBFETCH(thing)->exits != NOTHING) {
      notify(player, "Exits:");
      DOLIST(exit, DBFETCH(thing)->exits) {
	notify(player, unparse_object(player, exit));
      }
    } else {
      notify(player, "No exits.");
    }
    /* print dropto if present */
    if(DBFETCH(thing)->link != NOTHING) {
      sprintf(buf, "Dropped objects go to: %s",
	      unparse_object(player, DBFETCH(thing)->link));
      notify(player, buf);
    }
    break;
  case TYPE_THING:
    /* print home */
    sprintf(buf, "Home: %s",
	    unparse_object(player, DBFETCH(thing)->link)); /* home */
    notify(player, buf);
    /* print location if player can link to it */
    if(DBFETCH(thing)->location != NOTHING
       && (controls(player, DBFETCH(thing)->location)
	   || can_link_to(player, NOTYPE, DBFETCH(thing)->location))) {
      sprintf(buf, "Location: %s",
	      unparse_object(player, DBFETCH(thing)->location));
      notify(player, buf);
    }
    /* print thing's actions, if any */
    if(DBFETCH(thing)->exits != NOTHING) {
      notify(player, "Actions/exits:");
      DOLIST(exit, DBFETCH(thing)->exits) {
	notify(player, unparse_object(player, exit));
      }
    } else {
      notify(player, "No actions attached.");
    }
    break;
  case TYPE_PLAYER:
    
    /* print home */
    sprintf(buf, "Home: %s",
	    unparse_object(player, DBFETCH(thing)->link)); /* home */
    notify(player, buf);
    
    /* print location if player can link to it */
    if(DBFETCH(thing)->location != NOTHING
       && (controls(player, DBFETCH(thing)->location)
	   || can_link_to(player, NOTYPE, DBFETCH(thing)->location))) {
      sprintf(buf, "Location: %s",
	      unparse_object(player, DBFETCH(thing)->location));
      notify(player, buf);
    }
    
    /* print player's actions, if any */
    if(DBFETCH(thing)->exits != NOTHING) {
      notify(player, "Actions/exits:");
      DOLIST(exit, DBFETCH(thing)->exits) {
	notify(player, unparse_object(player, exit));
      }
    } else {
      notify(player, "No actions attached.");
    }
    break;
  case TYPE_EXIT:
    if (DBFETCH(thing)->location != NOTHING) {
      sprintf(buf, "Source: %s",
	      unparse_object(player, DBFETCH(thing)->location));
      notify(player, buf);
    }
    /* print destinations */
    if (DBFETCH(thing)->sp.exit.ndest == 0) break;
    for (i = 0; i < DBFETCH(thing)->sp.exit.ndest; i++) {
      switch( (DBFETCH(thing)->sp.exit.dest)[i]) {
      case NOTHING:
	break;
      case HOME:
	notify(player, "Destination: *HOME*");
	break;
      default:
	sprintf(buf, "Destination: %s",
		unparse_object(player, (DBFETCH(thing)->sp.exit.dest)[i]));
	notify(player, buf);
	break;
      }
    }
    break;
  case TYPE_PROGRAM:
    if (DBFETCH(thing)->sp.program.siz)
      sprintf(buf, "Program compiled size: %d",
	      DBFETCH(thing)->sp.program.siz);
    else
      sprintf(buf, "Program not compiled.");
    notify(player, buf);
    
    /* print location if player can link to it */
    if(DBFETCH(thing)->location != NOTHING
       && (controls(player, DBFETCH(thing)->location)
	   || can_link_to(player, NOTYPE, DBFETCH(thing)->location))) {
      sprintf(buf, "Location: %s",
	      unparse_object(player, DBFETCH(thing)->location));
      notify(player, buf);
    }
    break;
  default:
    /* do nothing */
    break;
  }
}

void do_score(dbref player) 
{
  char buf[BUFFER_LEN];
  
  sprintf(buf, "You have %d %s.", DBFETCH(player)->sp.player.pennies,
	  DBFETCH(player)->sp.player.pennies == 1 ? "penny" : "pennies");
  notify(player, buf);
}

void do_inventory(dbref player)
{
  dbref thing;
  
  if((thing = DBFETCH(player)->contents) == NOTHING) {
    notify(player, "You aren't carrying anything.");
  } else {
    notify(player, "You are carrying:");
    DOLIST(thing, thing) {
      notify(player, unparse_object(player, thing));
    }
  }
  do_score(player);
}

void do_owned(dbref player, const char *name, const char *filter)
{
  dbref i, victim = 0;
  object_flag_type type_check = 0;
  object_flag_type flags_check = 0;
  int x, y, flag;
  struct match_data md;
  const char *type_codes = "R-EPF";
  char temp[2];
  
  if(!payfor(player, LOOKUP_COST)) {
    notify(player, "You don't have enough pennies.");
    return;
  }

  if(*name)
    if((victim = lookup_player(name)) == NOTHING) {
      init_match(player, name, NOTYPE, &md);
      match_me(&md);
      match_absolute(&md);
      match_player(&md);

      if((victim = match_result(&md)) == NOTHING) {
	notify(player, "I can't find that player.");
	return;
      }
    }

  if(victim && !controls(player, victim)) {
    notify(player, "Permission denied.");
    return;
  }
  
  for(x = 0; x < strlen(filter); x++) {
    flag = 0;
    for(y = 0; y < 5 && !flag; y++)
      if(filter[x] == type_codes[y]) {
	 type_check |= filter_type(y);
	 flag++;
       }
    if(!flag) {
      sprintf(temp, "%c", filter[x]);
      flags_check |= lookup_flag(temp);
    }
  }
  for(i = 0; i < db_top; i++)
    if(controls(player, i)) {
      flag = 1;

      if(victim)
	if(OWNER(i) != victim)
	  flag = 0;
      if(flags_check && flag)
	if((FLAGS(i)&flags_check) != flags_check)
	  flag = 0;
      if(type_check && flag)
	if(!(filter_type(Typeof(i))&type_check))
	  flag = 0;

      if(flag)
	notify(player, unparse_object(player, i));
    }
  notify(player, "***End of List***");
}

void do_find(dbref player, const char *name, const char *who)
{
  dbref i, victim = 0;
  int flag;
  struct match_data md;

  if(!payfor(player, LOOKUP_COST)) {
    notify(player, "You don't have enough pennies.");
    return;
  }

  if(*who)
    if((victim = lookup_player(who)) == NOTHING) {
      init_match(player, who, NOTYPE, &md);
      match_me(&md);
      match_absolute(&md);
      match_player(&md);

      if((victim = match_result(&md)) == NOTHING) {
	notify(player, "I can't find that player.");
	return;
      }
    }
  
  if(victim && !controls(player, victim)) {
    notify(player, "Permission denied.");
    return;
  }
  

  for(i = 0; i < db_top; i++)
    if(controls(player, i))
      {
	flag = 1;
	if (victim)
	  if(OWNER(i) != victim)
	    flag = 0;
	if (flag && *name)
	  if(!string_match(NAME(i), name))
	    flag = 0;
	if(flag)
	  notify(player, unparse_object(player, i));
      }

  notify(player, "***End of List***");
}

void do_trace(dbref player, const char *name, int depth)
{
  dbref thing;
  int i;
  struct match_data md;

  init_match(player, name, NOTYPE, &md);
  match_absolute(&md);
  match_here(&md);
  match_me(&md);
  match_neighbor(&md);
  match_possession(&md);
  if ((thing = noisy_match_result(&md)) == NOTHING
      || thing == AMBIGUOUS) return;
  if(!depth) depth=30; /* Otherwise this'd crash the game */
  for (i = 0; (!depth || i < depth) && thing != NOTHING; i++) {
    if (controls(player, thing) || can_link_to(player, NOTYPE, thing))
      notify(player, unparse_object(player, thing));
    else
      notify(player, "**Missing**");
    thing = DBFETCH(thing)->location;
  }
  notify(player, "***End of List***");
}