text: #include "copyright.h"
  #include "os.h"
  #include "db.h"
  #include "config.h"
  #include "interface.h"
  #include "match.h"
  #include "externs.h"
  
  void moveto (dbref what, dbref where)
  {
    dbref loc;
  
    /* remove what from old loc */
    if ((loc = db[what].location) != NOTHING) {
      db[loc].contents = remove_first (db[loc].contents, what);
    }
  
    /* test for special cases */
    switch (where) {
    case NOTHING:
      db[what].location = NOTHING;
      return;                     /* NOTHING doesn't have contents */
    case HOME:
      where = db[what].exits;     /* home */
      break;
    }
  
    /* now put what in where */
    PUSH (what, db[where].contents);
  
    db[what].location = where;
  }
  
  static void send_contents (dbref loc, dbref dest)
  {
    dbref first;
    dbref rest;
  
    first = db[loc].contents;
    db[loc].contents = NOTHING;
  
    /* blast locations of everything in list */
    DOLIST (rest, first) {
      db[rest].location = NOTHING;
    }
  
    while (first != NOTHING) {
      rest = db[first].next;
      if (Typeof (first) != TYPE_THING) {
        moveto (first, loc);
      } else {
        moveto (first, (db[first].flags & STICKY) ? HOME : dest);
      }
      first = rest;
    }
  
    db[loc].contents = reverse (db[loc].contents);
  }
  
  void maybe_dropto (dbref loc, dbref dropto)
  {
    dbref thing;
  
    if (loc == dropto)
      return;                     /* bizarre special case */
  
    /* check for players */
    DOLIST (thing, db[loc].contents) {
      if (Typeof (thing) == TYPE_PLAYER)
        return;
    }
  
    /* no players, send everything to the dropto */
    send_contents (loc, dropto);
  }
  
  void enter_room (dbref player, dbref loc)
  {
    dbref old;
    dbref dropto;
    char buf[BUFFER_LEN];
  
    /* check for room == HOME */
    if (loc == HOME)
      loc = db[player].exits;     /* home */
  
    /* get old location */
    old = db[player].location;
  
    /* check for self-loop */
    /* self-loops don't do move or other player notification */
    /* but you still get autolook and penny check */
    if (loc != old) {
  
  #ifdef TIMESTAMPS
      db[loc].usecnt++;
      db[loc].lastused = time (0);
  #endif /* TIMESTAMPS */
  
  
      if (old != NOTHING) {
        /* notify others unless DARK */
        if (!Dark (old) && !Dark (player)) {
          sprintf (buf, "%s has left.", db[player].name);
          notify_except (db[old].contents, player, buf);
        }
      }
  
      /* go there */
      moveto (player, loc);
  
      /* if old location has STICKY dropto, send stuff through it */
      if (old != NOTHING
        && (dropto = db[old].location) != NOTHING && (db[old].flags & STICKY)) {
        maybe_dropto (old, dropto);
      }
  
      /* tell other folks in new location if not DARK */
      if (!Dark (loc) && !Dark (player)) {
        sprintf (buf, "%s has arrived.", db[player].name);
        notify_except (db[loc].contents, player, buf);
      }
    }
  
    /* autolook */
    look_room (player, loc);
  
    /* check for pennies */
    if (!controls (player, loc)
      && db[player].pennies <= MAX_PENNIES && RAND () % PENNY_RATE == 0) {
      notify (player, "You found a penny!");
      db[player].pennies++;
    }
  }
  
  void send_home (dbref thing)
  {
    switch (Typeof (thing)) {
    case TYPE_PLAYER:
      /* send his possessions home first! */
      /* that way he sees them when he arrives */
      send_contents (thing, HOME);
      enter_room (thing, db[thing].exits);        /* home */
      break;
    case TYPE_THING:
      moveto (thing, db[thing].exits);    /* home */
      break;
    default:
      /* no effect */
      break;
    }
  }
  
  int can_move (dbref player, const char *direction)
  {
    if (!string_compare (direction, "home"))
      return 1;
  
    /* otherwise match on exits */
    init_match (player, direction, TYPE_EXIT);
    match_exit ();
    return (last_match_result () != NOTHING);
  }
  
  void do_move (dbref player, const char *direction)
  {
    dbref exit;
    dbref loc;
    char buf[BUFFER_LEN];
  
    if (!string_compare (direction, "home")) {
      /* send him home */
      /* but steal all his possessions */
      if ((loc = db[player].location) != NOTHING) {
        /* tell everybody else */
        sprintf (buf, "%s goes home.", db[player].name);
        notify_except (db[loc].contents, player, buf);
      }
      /* give the player the messages */
      notify (player, "There's no place like home...");
      notify (player, "There's no place like home...");
      notify (player, "There's no place like home...");
      notify (player, "You wake up back home, without your possessions.");
      send_home (player);
    } else {
      /* find the exit */
      init_match_check_keys (player, direction, TYPE_EXIT);
      match_exit ();
      switch (exit = match_result ()) {
      case NOTHING:
        notify (player, "You can't go that way.");
        break;
      case AMBIGUOUS:
        notify (player, "I don't know which way you mean!");
        break;
      default:
        /* we got one */
  #ifdef TIMESTAMPS
        db[exit].usecnt++;
        db[exit].lastused = time (0);
  #endif /* TIMESTAMPS */
  
        /* check to see if we got through */
        if (can_doit (player, exit, "You can't go that way.")) {
          enter_room (player, db[exit].location);
        }
        break;
      }
    }
  }
  
  void do_get (dbref player, const char *what)
  {
    dbref loc;
    dbref thing;
  
    init_match_check_keys (player, what, TYPE_THING);
    match_neighbor ();
    match_exit ();
    if (Wizard (player))
      match_absolute ();          /* the wizard has long fingers */
  
    if ((thing = noisy_match_result ()) != NOTHING) {
      if (db[thing].location == player) {
        notify (player, "You already have that!");
        return;
      }
      switch (Typeof (thing)) {
      case TYPE_THING:
  #ifdef TIMESTAMPS
        db[thing].usecnt++;
        db[thing].lastused = time (0);
  #endif /* TIMESTAMPS */
        if (can_doit (player, thing, "You can't pick that up.")) {
          moveto (thing, player);
          notify (player, "Taken.");
        }
        break;
      case TYPE_EXIT:
        if (!controls (player, thing)) {
          notify (player, "You can't pick that up.");
        } else if (db[thing].location != NOTHING) {
          notify (player, "You can't pick up a linked exit.");
  #ifdef RESTRICTED_BUILDING
        } else if (!Builder (player)) {
          notify (player, "Only authorized builders may pick up exits.");
  #endif /* RESTRICTED_BUILDING */
        } else {
          /* take it out of location */
          if ((loc = getloc (player)) == NOTHING)
            return;
          if (!member (thing, db[loc].exits)) {
            notify (player, "You can't pick up an exit from another room.");
            return;
          }
          db[loc].exits = remove_first (db[loc].exits, thing);
          PUSH (thing, db[player].contents);
          db[thing].location = player;
          notify (player, "Exit taken.");
        }
        break;
      default:
        notify (player, "You can't take that!");
        break;
      }
    }
  }
  
  void do_drop (dbref player, const char *name)
  {
    dbref loc;
    dbref thing;
    char buf[BUFFER_LEN];
    int reward;
  
    if ((loc = getloc (player)) == NOTHING)
      return;
  
    init_match (player, name, TYPE_THING);
    match_possession ();
  
    switch (thing = match_result ()) {
    case NOTHING:
      notify (player, "You don't have that!");
      break;
    case AMBIGUOUS:
      notify (player, "I don't know which you mean!");
      break;
    default:
      if (db[thing].location != player &&
        !(Typeof (thing) == TYPE_EXIT) && db[thing].location == NOTHING) {
        /* Should not ever happen. */
        notify (player, "You can't drop that.");
      } else if (Typeof (thing) == TYPE_EXIT) {
        /* special behavior for exits */
        if (!controls (player, loc)) {
          notify (player, "You can't put an exit down here.");
          return;
        }
        /* else we can put it down */
        moveto (thing, NOTHING);  /* take it out of the pack */
        PUSH (thing, db[loc].exits);
        notify (player, "Exit dropped.");
      } else if (db[loc].flags & TEMPLE) {
        /* sacrifice time */
        send_home (thing);
        sprintf (buf, "%s is consumed in a burst of flame!", db[thing].name);
        notify (player, buf);
  #ifndef TINKER
        sprintf (buf, "%s sacrifices %s.", db[player].name, db[thing].name);
  #else   /* TINKER */
        sprintf (buf, "%s donates %s.", db[player].name, db[thing].name);
  #endif  /* TINKER */
        notify_except (db[loc].contents, player, buf);
  
        /* check for reward */
        if (!controls (player, thing)) {
          reward = db[thing].pennies;
          if (reward < 1 || db[player].pennies > MAX_PENNIES) {
            reward = 1;
          } else if (reward > MAX_OBJECT_ENDOWMENT) {
            reward = MAX_OBJECT_ENDOWMENT;
          }
  
          db[player].pennies += reward;
          sprintf (buf,
            "You have received %d %s for your donation.",
            reward, reward == 1 ? "penny" : "pennies");
          notify (player, buf);
        }
      } else if (db[thing].flags & STICKY) {
        send_home (thing);
        notify (player, "Dropped.");
      } else if (db[loc].location != NOTHING && !(db[loc].flags & STICKY)) {
        /* location has immediate dropto */
        moveto (thing, db[loc].location);
        notify (player, "Dropped.");
      } else if ((db[thing].flags & DARK)
        && !can_link_to (player, Typeof (thing), loc)) {
        notify (player, "You cannot drop that DARK object here.");
      } else {
        moveto (thing, loc);
        notify (player, "Dropped.");
        sprintf (buf, "%s dropped %s.", db[player].name, db[thing].name);
        notify_except (db[loc].contents, player, buf);
      }
      break;
    }
  }
  