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/
/*
    TinyMUD-like teleportation. We hope. Permissions, somewhat complex,
are handled by building up some bit vectors of things that are true about the
present situation, and then using them to verify that the teleport is legal.

*/


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


/* Some flags that can be set in bit vectors. For checking perms */
#define TEL_WHAT_PLAY       0001
#define TEL_WHAT_OBJ        0002
#define TEL_WHAT_OWNED      0004
#define TEL_WHAT_LINKOK     0010
#define TEL_WHAT_ME     0020

#define TEL_TO_OWNED        0001
#define TEL_TO_LINKOK       0002
#define TEL_TO_ROOM     0004
#define TEL_TO_PLAY     0010
#define TEL_TO_HOME     0020
#define TEL_TO_ME       0040
#define TEL_TO_HERE     0100
#define TEL_TO_ACTOR        0200

#define TEL_FRM_OWNED       0001
#define TEL_FRM_HERE        0002
#define TEL_FRM_ME      0004
#define TEL_FRM_CONT        0010
#define TEL_FRM_PLY     0020
#define TEL_FRM_ROOM        0040
#define TEL_FRM_USED        0100

static int wiz;
static int whatflg;
static int fromflg;
static int toflg;

int telep_ok (char *who, char *to, char *from, char *what);


/* ARGSUSED */
int cmd_telep (int ac, char *av[], char *who, char *aswho)
{
  char what[MAXOID];
  char *here;
  char *from;
  char *to;
  char *listvar;
  char hm[MAXOID];

  wiz = ut_flagged (aswho, var_wiz);
  here = ut_loc (run_actor ());
  whatflg = fromflg = toflg = 0;

  /* Figure out what it is we're supposed to move */
  if (matchlocal (who, av[1], here, MTCH_MEOK | MTCH_UNIQ | MTCH_QUIET, what)
    && matchlocal (who, av[1], here, MTCH_FRST | MTCH_QUIET, what) &&
    matchlocal (who, av[1], here, MTCH_NONLOC, what)) {
    say (aswho, "I can't see any ", av[1], " here\n", (char *) 0);
    return (UERR_NOMATCH);
  }

  /* And where it's supposed to go */
  if (!strcmp ("home", av[2])) {
    if (!ut_home (what, hm)) {
      say (aswho, "I can't figure out how to home ", av[1], ".\n",
        (char *) 0);
      return (UERR_BADOID);
    }
    to = hm;
    toflg |= TEL_TO_HOME;
  } else if (!strcmp ("here", av[2]))
    to = here;
  else if (!strcmp ("me", av[2]))
    to = aswho;
  else if (!strcmp ("actor", av[2]))
    to = run_actor ();
  else
    to = av[2];

  /* Figure out where and what it is */
  from = ut_loc (what);
  if (!strcmp (here, from))
    fromflg |= TEL_FRM_HERE;

  if (!strcmp (aswho, from))
    fromflg |= TEL_FRM_ME;

  /* Figure out what sort of thing this is */
  if (!strcmp (what, aswho))
    whatflg |= TEL_WHAT_ME;

  if (ut_listchk (from, listvar = var_ply, what)) {
    whatflg |= TEL_WHAT_PLAY;
    fromflg |= TEL_FRM_PLY;
  } else if (ut_listchk (from, listvar = var_cont, what)) {
    whatflg |= TEL_WHAT_OBJ;
    fromflg |= TEL_FRM_CONT;
  } else {
    char *used = ut_getatt (from, 0, typ_obj, var_using, (char *) 0);

    if (used != (char *) 0 && !strcmp (what, used)) {
      whatflg |= TEL_WHAT_OBJ;
      fromflg |= TEL_FRM_USED;
    } else {
      say (aswho, av[1], " must be an exit or something.\n", (char *) 0);
      return (UERR_BADOID);
    }
  }

  /* Other misc stuff about the thing to 'port */
  if (ut_isobjown (aswho, what))
    whatflg |= TEL_WHAT_OWNED;

  if (!bool_locked (aswho, what, here, var_jump, 1)
    && !bool_locked (aswho, what, here, var_link, 1))
    whatflg |= TEL_WHAT_LINKOK;

  /* Now gather up remaining fact(s) about where this is coming from */
  if (ut_isobjown (aswho, from))
    fromflg |= TEL_FRM_OWNED;

  if (ut_flagged (from, var_isroom))
    fromflg |= TEL_FRM_ROOM;

  /* Finally gather data on where it's supposed to go */
  if (ut_isobjown (aswho, to))
    toflg |= TEL_TO_OWNED;

  if (!bool_locked (aswho, to, here, var_link, 1))
    toflg |= TEL_TO_LINKOK;

  if (ut_flagged (to, var_isroom))
    toflg |= TEL_TO_ROOM;

  if (ut_flagged (to, var_isplay))
    toflg |= TEL_TO_PLAY;

  if (!strcmp (aswho, to))
    toflg |= TEL_TO_ME;

  if (!strcmp (here, to))
    toflg |= TEL_TO_HERE;

  if (!strcmp (run_actor (), to))
    toflg |= TEL_TO_ACTOR;

  /* OK. Now we know everything, right? (heh. we have a headache. -mjr) */
  if (!telep_ok (aswho, to, from, what)) {
    say (aswho, "You can't do that!\n", (char *) 0);
    return (UERR_PERM);
  }

  /* Get it out of here */
  if (fromflg & TEL_FRM_USED) {
    if (ut_unset (aswho, from, var_using)) {
      say (aswho, "Teleport failed.\n", (char *) 0);
      return (UERR_FATAL);
    }
  } else {
    if (ut_listdel (aswho, from, listvar, what)) {
      say (aswho, "Teleport failed.\n", (char *) 0);
      return (UERR_FATAL);
    }
  }

  /* Droptos DO matter */
  if (whatflg & TEL_WHAT_OBJ) {
    char work[MAXOID];          /* Work buffer demanded by ut_dropto() */
    to = ut_dropto (what, to, work);
  }

  /* And in to there */
  if (ut_listadd (aswho, to, listvar, what)) {
    say (aswho, "Teleport failed.\n", (char *) 0);
    return (UERR_FATAL);
  }

  /* and change its location */
  if (ut_set (aswho, what, typ_obj, var_loc, to)) {
    say (aswho, "Teleport failed.\n", (char *) 0);
    return (UERR_FATAL);
  }

  /* In certain cases, deal with inventories. Send contents home, */
  /* leave used object alone. (On all non-wiz 'ports of players.  */
  if (!wiz && (whatflg & TEL_WHAT_PLAY)) {
    char *list;
    char nx[MAXOID];
    char ohom[MAXOID];

    list = ut_getatt (what, 0, typ_list, var_cont, (char *) 0);
    if (list != (char *) 0) {

      while ((list = lstnext (list, nx, sizeof (nx))) != (char *) 0) {
        if (!ut_home (nx, ohom))
          continue;

        /*
           Ignore return values.
           There's *nothing* we can do
         */
        (void) ut_listdel (aswho, what, var_cont, nx);
        (void) ut_listadd (aswho, ohom, var_cont, nx);
        (void) ut_set (aswho, nx, typ_obj, var_loc, ohom);
      }
    }
    list = ut_getatt (what, 0, typ_list, var_wearing, (char *) 0);
    if (list != (char *) 0) {

      while ((list = lstnext (list, nx, sizeof (nx))) != (char *) 0) {
        if (!ut_home (nx, ohom))
          continue;

        /*
           Ignore return values.
           There's *nothing* we can do
         */
        (void) ut_listdel (aswho, what, var_cont, nx);
        (void) ut_listadd (aswho, ohom, var_cont, nx);
        (void) ut_set (aswho, nx, typ_obj, var_loc, ohom);
      }
    }
    list = ut_getatt (what, 0, typ_obj, var_using, (char *) 0);
    if (list != (char *) 0 && ut_home (list, ohom) && strcmp (ohom, what)) {
      (void) ut_listadd (aswho, ohom, var_cont, list);
      (void) ut_set (aswho, list, typ_obj, var_loc, ohom);
      (void) ut_unset (aswho, what, var_using);
    }
#ifdef COMBAT
    list = ut_getatt (what, 0, typ_obj, var_weapon, (char *) 0);
    if (list != (char *) 0 && ut_home (list, ohom) && strcmp (ohom, what)) {
      (void) ut_listadd (aswho, ohom, var_cont, list);
      (void) ut_set (aswho, list, typ_obj, var_loc, ohom);
      (void) ut_unset (aswho, what, var_using);
    }
#endif
  }


  /* And now tell everyone what they need to hear */
  if ((whatflg & TEL_WHAT_PLAY) && (!ut_flagged (what, var_isdark))) {
    ut_roombcast (from, what, ut_name (what), " has left.\n", (char *) 0);
    ut_roombcast (to, what, ut_name (what), " has arrived.\n", (char *) 0);
    say (what, "You feel a wrenching sensation....\n", (char *) 0);
    lookat (what, to, LOOK_NAME | LOOK_PLAY | LOOK_CONT);
  }
  say (aswho, "Teleported.\n", (char *) 0);
  return (UERR_NONE);
}




/* It is here that we code up the teleport rules */
int telep_ok (char *who, char *to, char *from, char *what)
{
  /* Wizards can send stuff anywhere thats reasonable */
  if (wiz) {
    return (((whatflg & TEL_WHAT_PLAY) &&
        (toflg & TEL_TO_ROOM)) ||
      ((whatflg & TEL_WHAT_OBJ) && (toflg & (TEL_TO_ROOM | TEL_TO_PLAY))));
  }
  /* Only another wiz can do this and we eliminated that possibility */
  if (ut_flagged (what, var_wiz))
    return (0);

  /* Find out about who is doing this, and check the rules */
  if (ut_flagged (who, var_isplay)) {
    if (whatflg & TEL_WHAT_PLAY) {
      if (whatflg & (TEL_WHAT_ME | TEL_WHAT_OWNED | TEL_WHAT_LINKOK)) {
        return (((toflg & TEL_TO_ROOM) &&
            (toflg & (TEL_TO_OWNED | TEL_TO_LINKOK))) ||
          (toflg & TEL_TO_HOME));
      }
      return ((fromflg & TEL_FRM_HERE) &&
        (fromflg & TEL_FRM_OWNED) && (toflg & TEL_TO_HOME));
    }

    if (whatflg & TEL_WHAT_OBJ) {
      if (whatflg & (TEL_WHAT_OWNED | TEL_WHAT_LINKOK)) {
        return ((toflg & (TEL_TO_ME | TEL_TO_ACTOR)) ||
          ((toflg & (TEL_TO_ROOM | TEL_TO_PLAY)) &&
            (toflg & (TEL_TO_OWNED | TEL_TO_LINKOK))) ||
          (toflg & TEL_TO_HOME));
      }

      if (((fromflg & TEL_FRM_ROOM) &&
          (fromflg & TEL_FRM_OWNED)) || (fromflg & TEL_FRM_ME))
        return (toflg & TEL_TO_HOME);
    }
    return (0);
  }

  if (ut_flagged (who, var_isroom)) {
    if ((whatflg & TEL_WHAT_OBJ) &&
      (whatflg & (TEL_WHAT_OWNED | TEL_WHAT_LINKOK))) {
      return ((toflg & (TEL_TO_ME | TEL_TO_ACTOR)) ||
        ((toflg & (TEL_TO_ROOM | TEL_TO_PLAY)) &&
          (toflg & (TEL_TO_OWNED | TEL_TO_LINKOK))) || (toflg & TEL_TO_HOME));
    }
    if ((whatflg & TEL_WHAT_PLAY) && (fromflg & TEL_FRM_ME)) {
      return ((toflg & TEL_TO_HOME) ||
        ((whatflg & (TEL_WHAT_OWNED | TEL_WHAT_LINKOK)) &&
          (toflg & (TEL_TO_OWNED | TEL_TO_LINKOK)))
        );
    }
    return (0);
  }

  /* Must be an exit or a thing */
  if (lstlook (ut_getatt (who, 0, typ_list, var_loc, var_xit, (char *) 0),
      who)) {
    if ((whatflg & TEL_WHAT_OBJ) &&
      (whatflg & (TEL_WHAT_OWNED | TEL_WHAT_LINKOK))) {
      return (((toflg & (TEL_TO_ROOM | TEL_TO_PLAY)) &&
          (toflg & (TEL_TO_OWNED | TEL_TO_LINKOK))) || (toflg & TEL_TO_HOME));
    }
    return (0);
  }

  /* Must be a thing. We hope so, anyway. */
  if ((whatflg & TEL_WHAT_PLAY) && !strcmp (run_actor (), what)) {
    return (((toflg & TEL_TO_ROOM) &&
        (toflg & (TEL_TO_OWNED | TEL_TO_LINKOK))) || (toflg & TEL_TO_HOME));
  }

  if ((whatflg & TEL_WHAT_OBJ) &&
    (whatflg & (TEL_WHAT_ME | TEL_WHAT_OWNED | TEL_WHAT_LINKOK))) {
    return ((toflg & TEL_TO_ACTOR) ||
      ((toflg & (TEL_TO_ROOM | TEL_TO_PLAY)) &&
        (toflg & (TEL_TO_OWNED | TEL_TO_LINKOK))) || (toflg & TEL_TO_HOME));
  }
  return (0);
}