/* $Header: look.c,v 2.0 90/05/05 12:45:36 lachesis Exp $
 * $Log:    look.c,v $
 * Revision 2.0  90/05/05  12:45:36  lachesis
 * Incorporated ABODE and HAVEN flags (remembering to convert FireFoot's
 * usage of those flags to ours).
 * Added Examine of objects that don't belong to you, added GOD_PRIV.
 *
 * Revision 1.3  90/04/21  17:20:46  lachesis
 * Added property lists.
 *
 * Revision 1.2  90/04/20  14:06:44  lachesis
 * Added @odrop && @drop.
 *
 * Revision 1.1  90/04/14  14:56:46  lachesis
 * Initial revision
 *
 */
#include "copyright.h"

/* commands which look at things */

#include "os.h"
#include "db.h"
#include "config.h"
#include "interface.h"
#include "match.h"
#include "externs.h"

char buf[BUFFER_LEN];

/* prints owner of something */
static void print_owner (dbref player, dbref thing)
{
  char buf[BUFFER_LEN];

  switch (Typeof (thing)) {
  case TYPE_ROOM:
    sprintf (buf, "Owner: %s", db[db[thing].sp.room.owner].name);
    break;
  case TYPE_THING:
    sprintf (buf, "Owner: %s", db[db[thing].sp.thing.owner].name);
    break;
  case TYPE_PLAYER:
    sprintf (buf, "%s is a player", db[thing].name);
    break;
  case TYPE_EXIT:
    sprintf (buf, "Owner: %s", db[db[thing].sp.exit.owner].name);
    break;
#ifdef RECYCLE
  case TYPE_GARBAGE:
    sprintf (buf, "%s is garbage", db[thing].name);
    break;
#endif
  }

  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) || controls (player, loc));

  /* check to see if there is anything there */
  DOLIST (thing, db[loc].contents) {
    if (can_see (player, thing, can_see_loc)) {
      /* something exists!  show him everything */
      notify (player, contents_name);
      DOLIST (thing, db[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 (db[thing].description) {
    notify (player, db[thing].description);
  } else {
    notify (player, "You see nothing special.");
  }
}

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 (db[loc].description) {
    notify (player, db[loc].description);
  }

  /* tell him the appropriate messages if he has the key */
  can_doit (player, loc, 0);

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

  if (*name == '\0') {
    if ((thing = getloc (player)) != NOTHING) {
      look_room (player, thing);
    }
  } else {
    /* look at a thing here */
    init_match (player, name, NOTYPE);
    match_all_exits ();
    match_neighbor ();
    match_possession ();
    if (Wizard (player)) {
      match_absolute ();
      match_player ();
    }
    match_here ();
    match_me ();

    if ((thing = noisy_match_result ()) != NOTHING) {
      switch (Typeof (thing)) {
      case TYPE_ROOM:
        look_room (player, thing);
        break;
      case TYPE_PLAYER:
        look_simple (player, thing);
        look_contents (player, thing, "Carrying:");
        break;
      default:
        look_simple (player, thing);
        break;
      }
    }
  }
}

#ifdef VERBOSE_EXAMINE
static const char *flag_description (dbref thing)
{
  static char buf[BUFFER_LEN];

  strcpy (buf, "Type: ");
  switch (Typeof (thing)) {
  case TYPE_ROOM:
    strcat (buf, "ROOM");
    break;
  case TYPE_EXIT:
    strcat (buf, "EXIT/ACTION");
    break;
  case TYPE_THING:
    strcat (buf, "THING");
    break;
  case TYPE_PLAYER:
    strcat (buf, "PLAYER");
    break;
#ifdef RECYCLE
  case TYPE_GARBAGE:
    strcat (buf, "GARBAGE");
    break;
#endif
  default:
    strcat (buf, "***UNKNOWN TYPE***");
    break;
  }

  if (db[thing].flags & ~TYPE_MASK) {
    /* print flags */
    strcat (buf, "  Flags:");
    if (db[thing].flags & WIZARD)
      strcat (buf, " WIZARD");
    if (db[thing].flags & STICKY)
      strcat (buf, " STICKY");
    if (db[thing].flags & DARK)
      strcat (buf, " DARK");
    if (db[thing].flags & LINK_OK)
      strcat (buf, " LINK_OK");
    if (db[thing].flags & TEMPLE)
      strcat (buf, " TEMPLE");
#ifdef RESTRICTED_BUILDING
    if (db[thing].flags & BUILDER)
      strcat (buf, " BUILDER");
#endif /* RESTRICTED_BUILDING */
#ifdef PLAYER_CHOWN
    if (db[thing].flags & CHOWN_OK)
      strcat (buf, " CHOWN_OK");
#endif /* PLAYER_CHOWN */
    if (db[thing].flags & JUMP_OK)
      strcat (buf, " JUMP_OK");
#ifdef HAVEN
    if (db[thing].flags & HAVEN)
      strcat (buf, " HAVEN");
#endif /* HAVEN */
#ifdef ABODE
    if (db[thing].flags & ABODE)
      strcat (buf, " ABODE");
#endif /* ABODE */
  }

  return buf;
}
#endif /* VERBOSE_EXAMINE */

void do_examine (dbref player, const char *name)
{
  dbref thing;
  char buf[BUFFER_LEN];
  dbref content;
  dbref exit;
  int i;

  if (*name == '\0') {
    if ((thing = getloc (player)) == NOTHING)
      return;
  } else {
    /* look it up */
    init_match (player, name, NOTYPE);
    match_all_exits ();
    match_neighbor ();
    match_possession ();
    match_absolute ();
    /* only Wizards can examine other players */
    if (Wizard (player))
      match_player ();
    match_here ();
    match_me ();

    /* get result */
    if ((thing = noisy_match_result ()) == NOTHING)
      return;
  }

  if (!can_link (player, thing)) {
    print_owner (player, thing);
    do_look_at (player, name);
    return;
  }

  switch (Typeof (thing)) {
  case TYPE_ROOM:
    sprintf (buf, "%s  Owner: %s",
      unparse_object (player, thing), db[db[thing].sp.room.owner].name);
    break;
  case TYPE_THING:
    sprintf (buf, "%s  Owner: %s  Value: %d",
      unparse_object (player, thing),
      db[db[thing].sp.thing.owner].name, db[thing].sp.thing.value);
    break;
  case TYPE_PLAYER:
    sprintf (buf, "%s  Cookies: %d  ",
      unparse_object (player, thing), db[thing].sp.player.pennies);
    break;
  case TYPE_EXIT:
    sprintf (buf, "%s  Owner: %s",
      unparse_object (player, thing), db[db[thing].sp.exit.owner].name);
    break;
#ifdef RECYCLE
  case TYPE_GARBAGE:
    strcpy (buf, unparse_object (player, thing));
    break;
#endif
  }
  notify (player, buf);
#ifdef VERBOSE_EXAMINE
  notify (player, flag_description (thing));
#endif /* VERBOSE_EXAMINE */
  if (db[thing].description)
    notify (player, db[thing].description);
  sprintf (buf, "Key: %s", unparse_boolexp (player, db[thing].key));
  notify (player, buf);

  if (db[thing].fail_message) {
    sprintf (buf, "Fail: %s", db[thing].fail_message);
    notify (player, buf);
  }
  if (db[thing].succ_message) {
    sprintf (buf, "Success: %s", db[thing].succ_message);
    notify (player, buf);
  }
  if (db[thing].drop_message) {
    sprintf (buf, "Drop: %s", db[thing].drop_message);
    notify (player, buf);
  }
  if (db[thing].ofail) {
    sprintf (buf, "Ofail: %s", db[thing].ofail);
    notify (player, buf);
  }
  if (db[thing].osuccess) {
    sprintf (buf, "Osuccess: %s", db[thing].osuccess);
    notify (player, buf);
  }
  if (db[thing].odrop) {
    sprintf (buf, "Odrop: %s", db[thing].odrop);
    notify (player, buf);
  }

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

    notify (player, "Properties:");
    for (p = db[thing].properties; p; p = p->next) {
      strncpy (buf, p->type, BUFFER_LEN - 1);
      strncat (buf, ": ", BUFFER_LEN - 1);
      strncat (buf, p->class, BUFFER_LEN - 1);
      notify (player, buf);
    }
  }

  /* show him the contents */
  if (db[thing].contents != NOTHING) {
    if (Typeof (thing) == TYPE_PLAYER)
      notify (player, "Carrying:");
    else
      notify (player, "Contents:");
    DOLIST (content, db[thing].contents) {
      notify (player, unparse_object (player, content));
    }
  }

  switch (Typeof (thing)) {
  case TYPE_ROOM:
    /* tell him about exits */
    if (db[thing].sp.room.exits != NOTHING) {
      notify (player, "Exits:");
      DOLIST (exit, db[thing].sp.room.exits) {
        notify (player, unparse_object (player, exit));
      }
    } else {
      notify (player, "No exits.");
    }

    /* print dropto if present */
    if (db[thing].sp.room.dropto != NOTHING) {
      sprintf (buf, "Dropped objects go to: %s",
        unparse_object (player, db[thing].sp.room.dropto));
      notify (player, buf);
    }
    break;
  case TYPE_THING:
    /* print home */
    sprintf (buf, "Home: %s", unparse_object (player, db[thing].sp.thing.home));        /* home */
    notify (player, buf);
    /* print location if player can link to it */
    if (db[thing].location != NOTHING
      && (controls (player, db[thing].location)
        || can_link_to (player, db[thing].location))) {
      sprintf (buf, "Location: %s",
        unparse_object (player, db[thing].location));
      notify (player, buf);
    }
    /* print thing's actions, if any */
    if (db[thing].sp.thing.actions != NOTHING) {
      notify (player, "Actions/exits:");
      DOLIST (exit, db[thing].sp.thing.actions) {
        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, db[thing].sp.player.home));       /* home */
    notify (player, buf);

    /* print location if player can link to it */
    if (db[thing].location != NOTHING
      && (controls (player, db[thing].location)
        || can_link_to (player, db[thing].location))) {
      sprintf (buf, "Location: %s",
        unparse_object (player, db[thing].location));
      notify (player, buf);
    }

    /* print player's actions, if any */
    if (db[thing].sp.player.actions != NOTHING) {
      notify (player, "Actions/exits:");
      DOLIST (exit, db[thing].sp.player.actions) {
        notify (player, unparse_object (player, exit));
      }
    } else {
      notify (player, "No actions attached.");
    }
    break;
  case TYPE_EXIT:
    if (db[thing].location != NOTHING) {
      sprintf (buf, "Source: %s",
        unparse_object (player, db[thing].location));
      notify (player, buf);
    }
    /* print destinations */
    if (db[thing].sp.exit.ndest == 0)
      break;
    for (i = 0; i < db[thing].sp.exit.ndest; i++) {
      switch ((db[thing].sp.exit.dest)[i]) {
      case NOTHING:
        break;
      case HOME:
        notify (player, "Destination: *HOME*");
        break;
      default:
        sprintf (buf, "Destination: %s",
          unparse_object (player, (db[thing].sp.exit.dest)[i]));
        notify (player, buf);
        break;
      }
    }
    break;
  default:
    /* do nothing */
    break;
  }
}

void do_score (dbref player)
{
  char buf[BUFFER_LEN];

  sprintf (buf, "You have %d %s.",
    db[player].sp.player.pennies,
    db[player].sp.player.pennies == 1 ? "cookie" : "cookies");
  notify (player, buf);
}

void do_inventory (dbref player)
{
  dbref thing;

  if ((thing = db[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_find (dbref player, const char *name)
{
  dbref i;

  if (!payfor (player, LOOKUP_COST)) {
    notify (player, "You don't have enough cookies.");
  } else {
    for (i = 0; i < db_top; i++) {
      if (Typeof (i) != TYPE_EXIT && controls (player, i)
        && (!*name || string_match (db[i].name, name))) {
        notify (player, unparse_object (player, i));
      }
    }
    notify (player, "***End of List***");
  }
}