untermud/DOC/
untermud/DOC/U/
untermud/DOC/U/U-examples/
untermud/DOC/internals/
untermud/DOC/wizard/
untermud/MISC/
untermud/MISC/dbchk/
untermud/RWHO/
untermud/RWHO/rwhod/
/*
    Copyright (C) 1991, Marcus J. Ranum. All rights reserved.
*/

/* configure all options BEFORE including system stuff. */
#include    "config.h"
#include    "mud.h"
#include    "match.h"
#include    "look.h"
#include    "vars.h"

static int player_destroy(char *who, char *aswho, char *ob, int wiz);
static int delete_xit(char *room, char *ex);
static int room_destroy(char *who, char *aswho, char *name, int wiz);
static int exit_destroy(char *who, char *aswho, char *name, int wiz);
static int object_destroy(char *who, char *aswho, char *name, int wiz);
static int home_object(char *who, char *what, char *msg, char *notthere);
/*
Object destruction.
*/

/* ARGSUSED */
int cmd_destroy (int ac, char *av[], char *who, char *aswho)
{
  int wiz;

  /* do help */
  if (ac == 2 && !strcmp (av[1], "help")) {
    say (who, av[0], " player player-obj\n", (char *) 0);
    say (who, av[0], " exit exit-obj\n", (char *) 0);
    say (who, av[0], " room room-obj\n", (char *) 0);
    say (who, av[0], " object obj\n", (char *) 0);
    return (UERR_NONE);
  }

  if (ac != 3) {
    say (who, "I don't understand that use of \"", av[0], "\"!\n",
      (char *) 0);
    return (UERR_BADPARM);
  }

  wiz = ut_flagged (aswho, var_wiz);

  if (!strcmp (av[1], "player"))
    return (player_destroy (who, aswho, av[2], wiz));
  else if (!strcmp (av[1], "exit"))
    return (exit_destroy (who, aswho, av[2], wiz));
  else if (!strcmp (av[1], "room"))
    return (room_destroy (who, aswho, av[2], wiz));
  else if (!strcmp (av[1], "object"))
    return (object_destroy (who, aswho, av[2], wiz));

  say (who, "I don't understand what you want me to destroy!\n", (char *) 0);
  return (UERR_BADPARM);
}



/* destroy a player */
static int player_destroy (char *who, char *aswho, char *ob, int wiz)
{
  char ply[MAXOID];
  char *loc;
  char *dp;
  char nxtu[MAXOID];

  /* start with some checks */

  /* must be a wizard! */
  if (!wiz) {
    say (who, "Only wizards can destroy players\n", (char *) 0);
    return (UERR_PERM);
  }

  /* find the player; unique, exact match required.  #num is OK. */
  if (matchplayers (who, ob, ut_loc (who), MTCH_UNIQ | MTCH_NONLOC, ply))
    return (UERR_NOMATCH);

  /* lets make sure its a player! */
  if (!ut_flagged (ply, var_isplay)) {
    say (who, ut_name (ply), " is not a player\n", (char *) 0);
    return (UERR_BADOID);
  }

  /* can't destroy yourself */
  if (!strcmp (ply, aswho))
    /*
     * gotos are only evil if used indiscriminately or
     * if they branch backwards.
     * Use for exception handling is kosher.
     */
    goto fail;

  /* ensure that the player is logged off */
  say (ply, ut_name (who), " is destroying you.  Goodbye!\n", (char *) 0);
  /*
   * note that "goodbye" must gracefully handle the
   * player object not existing
   */
  io_logoff (ply);

  /* ok.  fire away */

  /* remove the player from the player list of its location */
  loc = ut_loc (ply);

  /* move the objects in the players contents list to their homes */
  dp = ut_getatt (ply, 0, typ_list, var_cont, (char *) 0);
  if (dp != (char *) 0) {
    while ((dp = lstnext (dp, nxtu, sizeof (nxtu))) != (char *) 0)
      if (home_object (who, nxtu, (char *) 0, ply)) {
        goto fail;
      }
  }

  /* send the "use" object home */
  dp = ut_getatt (ply, 0, typ_obj, var_using, (char *) 0);
  if (dp != (char *) 0 &&
    (dp = lstnext (dp, nxtu, sizeof (nxtu))) != (char *) 0)
    /* there is a "use" object */
    if (home_object (who, nxtu, (char *) 0, ply))
      goto fail;

  if (ut_listdel (who, loc, var_ply, ply)) {
    plogf ("destroy player %s: can't remove player from room %s\n", ply, loc);
    goto fail;
  }

  /* save the name! */
  dp = ut_getatt (ply, 0, typ_str, var_nam, (char *) 0);
  if (dp != (char *) 0) {
    strncpy (nxtu, dp, MAXOID);
    nxtu[MAXOID - 1] = '\0';
  } else
    strcpy (nxtu, "An unidentifiable player");

  /* now delete the player (log and display error) */
  switch (cache_del (ply, 0)) {
  case 1:
    /* DB couldn't delete the object */
    plogf ("destroy player %s: couldn't delete the player\n", ply);
    goto fail;
  case -1:
    /* DB not initialized */
    plogf ("destroy player %s: no delete.  db not initted\n", ply);
    say (who, "couldn't delete player ", ply, "\n", (char *) 0);
    goto fail;
  case 0:
    /* either cache not initted, or object deleted ok */
    break;
  }

  plogf ("player %s destroyed by %s\n", ply, who);

  /* send a message to all where they were */
  ut_roombcast (loc, (char *) 0, nxtu, " disintegrates before you!\n",
    (char *) 0);

  if (run_level () == 0)
    say (who, "You destroyed player ", ply, "\n", (char *) 0);

  return (UERR_NONE);

fail:
  say (who, "Cannot destroy player ", ply, "\n", (char *) 0);
  return (UERR_FATAL);
}

static char ply_mesg[] =
  "You've been sent home as the room you were in was being destroyed\n";

/* utility routine to delete the exit */
static int delete_xit (char *room, char *ex)
{
  switch (cache_del (ex, 0)) {
  case 1:
    /* DB couldn't delete the object */
    plogf ("destroy room %s: couldn't delete exit %s\n", room, ex);
    return (UERR_FATAL);
  case 0:
    /* no problem */
    return (UERR_NONE);
  default:
  case -1:
    /* DB not initialized */
    plogf ("destroy room %s: no delete.  db not initted\n", room);
    return (UERR_FATAL);
  }
}




/* destroy a room */
static int room_destroy (char *who, char *aswho, char *name, int wiz)
{
  char room[MAXOID];
  char *dp;
  char nxtu[MAXOID];
  char *limbo;

  /* start with some checks */

  /* find the room; unique, exact match required.  #num is OK.
   *  Actually, #num is the *only* way to refer to a room */
  if (matchlocal (who, name, ut_loc (who),
      MTCH_UNIQ | MTCH_MEOK | MTCH_NONLOC, room))
    return (UERR_NOMATCH);

  /* check ownership */
  if (!wiz && !ut_isobjown (aswho, room)) {
    say (who, "You don't own ", name, "\n", (char *) 0);
    return (UERR_PERM);
  }

  /* ensure the object is a room */
  if (!ut_flagged (room, var_isroom)) {
    say (who, ut_name (room), " is not a room\n", (char *) 0);
    return (UERR_BADOID);
  }

  limbo = ut_getatt (system_object, 0, typ_obj, var_syslimbo, (char *) 0);

  if (limbo == (char *) 0) {
    plogf ("destroy room %s: cannot find limbo!\n", room);
    goto fail;
  } else if (!strcmp (room, limbo)) {
    say (who, "You can't destroy the system limbo!\n", (char *) 0);
    return (UERR_PERM);
  }


  /* send the players home */
  dp = ut_getatt (room, 0, typ_list, var_ply, (char *) 0);
  if (dp != (char *) 0) {
    /* there are players */
    while ((dp = lstnext (dp, nxtu, sizeof (nxtu))) != (char *) 0) {
      if (home_object (who, nxtu, ply_mesg, room)) {
        goto fail;
      }
      /* Do some messages, just for Moira */

      if (!ut_flagged (nxtu, var_isdark))
        ut_roombcast (ut_loc (nxtu), nxtu, ut_name (nxtu),
          " has arrived.\n", (char *) 0);
      lookat (nxtu, ut_loc (nxtu), LOOK_NAME | LOOK_PLAY | LOOK_CONT);
    }
  }

  /* send the contents home */
  dp = ut_getatt (room, 0, typ_list, var_cont, (char *) 0);
  if (dp != (char *) 0) {
    /* there are contents */
    while ((dp = lstnext (dp, nxtu, sizeof (nxtu))) != (char *) 0)
      if (home_object (who, nxtu, (char *) 0, room)) {
        goto fail;
      }
  }

  /* destroy the exits */
  dp = ut_getatt (room, 0, typ_list, var_xit, (char *) 0);
  if (dp != (char *) 0) {
    /* there are exits */
    while ((dp = lstnext (dp, nxtu, sizeof (nxtu))) != (char *) 0)
      /* delete the exit (log error) */
      if (delete_xit (room, nxtu)) {
        goto fail;
      } else
        /* ignore any error */
        (void) ut_listdel (who, room, var_xit, nxtu);
  }

  /* ignore error */
  (void) ut_unset (who, room, var_xit);

  /* otherwise, delete the room! */
  switch (cache_del (room, 0)) {
  case 1:
    /* DB couldn't delete the object */
    plogf ("destroy room %s: couldn't delete the room\n", room);
    goto fail;
  case -1:
    /* DB not initialized */
    plogf ("destroy room %s: no delete.  db not initted\n", room);
    goto fail;
  case 0:
    /* either cache not initted, or object deleted ok */
    break;
  }

  if (run_level () == 0)
    say (who, "You destroyed room ", room, "\n", (char *) 0);

  return (UERR_NONE);

fail:
  say (who, "Cannot destroy room ", room, "\n", (char *) 0);
  return (UERR_FATAL);
}

/* destroy an exit */
static int exit_destroy (char *who, char *aswho, char *name, int wiz)
{
  char ex[MAXOID];
  char *loc;

  /* start with some checks */

  /* find the exit; unique match required.  #num is OK. */
  if (matchexit (who, name, ut_loc (who), MTCH_UNIQ | MTCH_NONLOC, ex))
    return (UERR_NOMATCH);

  /* check ownership */
  if (!wiz && !ut_isobjown (aswho, ex)) {
    say (who, "You don't own ", name, "\n", (char *) 0);
    return (UERR_PERM);
  }

  /* ensure the object is an exit */
  if (ut_getatt (ex, 1, typ_obj, var_dest, (char *) 0) == (char *) 0) {
    say (who, ut_name (ex), " is not an exit\n", (char *) 0);
    return (UERR_BADOID);
  }

  /* where is the exit? */
  loc = ut_loc (ex);

  if (ut_listdel (who, loc, var_xit, ex)) {
    plogf ("destroy exit %s: can't find in %s's xit list\n", ex, loc);
    goto fail;
  }

  /* now delete the exit (log and display error) */
  switch (cache_del (ex, 0)) {
  case 1:
    /* DB couldn't delete the object */
    plogf ("destroy exit %s: couldn't delete the object\n", ex);
    goto fail;
  case -1:
    /* DB not initialized */
    plogf ("destroy exit %s: no delete.  db not initted\n", ex);
    goto fail;
  case 0:
    /* either cache not initted, or object deleted ok */
    break;
  }

  if (run_level () == 0)
    say (who, "You destroyed exit ", ex, "\n", (char *) 0);
  return (UERR_NONE);

fail:
  say (who, "Cannot destroy exit ", ex, "\n", (char *) 0);
  return (UERR_FATAL);
}

/* destroy a thing */
static int object_destroy (char *who, char *aswho, char *name, int wiz)
{
  char obj[MAXOID];
  char *loc;
  char *use;

  /* start with some checks */

  /* find the object; unique and exact match required.  #num is OK. */
  if (matchlocal (who, name, ut_loc (who), MTCH_UNIQ | MTCH_NONLOC, obj))
    return (UERR_NOMATCH);

  /* check ownership */
  if (!wiz && !ut_isobjown (aswho, obj)) {
    say (who, "You don't own ", name, "\n", (char *) 0);
    return (UERR_PERM);
  }

  /* ensure the object is not a player,room or exit */
  if (ut_flagged (obj, var_isplay) || ut_flagged (obj, var_isroom)
    || ut_getatt (obj, 1, typ_obj, var_dest, (char *) 0) != (char *) 0) {
    say (who, ut_name (obj), " is not an object\n", (char *) 0);
    return (UERR_BADOID);
  }

  /* where is the thing? */
  loc = ut_loc (obj);

  if (ut_flagged (loc, var_isplay) &&
    (use = ut_getatt (loc, 0, typ_obj, var_using, (char *) 0)) != (char *) 0
    && !strcmp (use, obj)) {
    /* if object is used by a player, unset the using attr */
    if (ut_unset (who, loc, var_using))
      goto fail;
  } else if (ut_listdel (who, loc, var_cont, obj))
    /* if object is carried by a player or in a room,
     * remove from contents */
    goto fail;

  /*
   * if objects can be inside objects, then you have to
   * send the contents of this object home.
   *
   * Not currently implemented.
   */

  /* now delete the thing (log and display error) */
  switch (cache_del (obj, 0)) {
  case 1:
    /* DB couldn't delete the object */
    plogf ("destroy object %s: couldn't delete the object\n", obj);
    goto fail;
  case -1:
    /* DB not initialized */
    plogf ("destroy object %s: no delete.  db not initted\n", obj);
    goto fail;
  case 0:
    /* either cache not initted, or object deleted ok */
    break;
  }

  if (run_level () == 0)
    say (who, "You destroyed object ", obj, "\n", (char *) 0);
  return (UERR_NONE);
fail:
  say (who, "Cannot destroy object ", obj, "\n", (char *) 0);
  return (UERR_FATAL);
}


static int home_object (char *who, char *what, char *msg, char *notthere)
{
  char hm[MAXOID];
  char *where;
  char *dstlist = NULL;
  char *p;

  if (!ut_home (what, hm))
    return (1);

  /* Use limbo if home == notthere */

  if (!strcmp (hm, notthere)) {
    p = ut_getatt (system_object, 0, typ_obj, var_syslimbo, (char *) 0);
    if (p == (char *) 0)
      return (1);
    strcpy (hm, p);
  }

  /* Figure out where it is, and get it out of there */

  if ((where = ut_loc (what)) != (char *) 0) {
    if (ut_flagged (what, var_isplay)) {
      /* It's a player */

      dstlist = var_ply;
      if (ut_listdel (what, where, var_ply, what))
        return (1);
    } else {
      /* It must be an object */

      dstlist = var_cont;
      if (ut_getatt (where, 0, typ_obj, var_using, (char *) 0) == what) {
        /* Used object */

        if (ut_unset (who, where, var_using))
          return (1);
      } else {
        /* In contents list */

        if (ut_listdel (what, where, var_cont, what))
          return (1);
      }
    }
  }

  /* Put it where it's supposed to go */
  /* ignore return values and hope.   */

  (void) ut_listadd (who, hm, dstlist, what);
  (void) ut_set (who, what, typ_obj, var_loc, hm);

  /* Tell the thing */

  if (msg != (char *) 0)
    say (what, msg, (char *) 0);
  return (0);
}