/* move.c */

#include "copyright.h"
#include <ctype.h>

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

void enter_room();

void moveto(what, where)
    dbref what;
    dbref where;
{
  enter_room(what, where);
}

void moveit(what, where)
    dbref what;
    dbref where;
{
  dbref loc, old;

  /* remove what from old loc */
  if ((loc = old = 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 */
      safe_tel(what, where);
      return;
      /*NOTREACHED*/
      break;
  }

  /* now put what in where */
  PUSH(what, db[where].contents);

  db[what].location = where;
  if (Hearer(what) && (where != NOTHING) && !Dark(where) && (old != where)) {
    did_it(what, old, "LEAVE", NULL, "OLEAVE", "has left.", "ALEAVE", old);
    if(Typeof(where) != TYPE_ROOM)
      did_it(what, where, NULL, NULL, "OXENTER", NULL, NULL, old);
    if(Typeof(old) != TYPE_ROOM)
      did_it(what, old, NULL, NULL, "OXLEAVE", NULL, NULL, where);
    did_it(what, where, "ENTER", NULL, "OENTER", "has arrived.", "AENTER",
           where);
    did_it(what, what, "MOVE", NULL, "OMOVE", NULL, "AMOVE", where);
  }
}

#define Dropper(thing) (Hearer(thing) && \
		       (db[db[thing].owner].flags & PLAYER_CONNECT))

void send_contents(loc, dest)
    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 (Dropper(first)) {
      db[first].location = loc;
      PUSH(first, db[loc].contents);
    } else
      enter_room(first, (db[first].flags & STICKY) ? HOME : dest);
    first = rest;
  }

  db[loc].contents = reverse(db[loc].contents);
}

void maybe_dropto(loc, dropto)
    dbref loc;
    dbref dropto;
{
  dbref thing;
  if (loc == dropto)
    return;			/* bizarre special case */
  if (Typeof(loc) != TYPE_ROOM)
    return;
  /* check for players */
  DOLIST(thing, db[loc].contents) {
    if (Dropper(thing))
      return;
  }

  /* no players, send everything to the dropto */
  send_contents(loc, dropto);
}

void enter_room(player, loc)
    dbref player;
    dbref loc;
{
  dbref old;
  dbref dropto;
  static int deep = 0;
  if (deep++ > 15) {
    deep--;
    return;
  }

  if(player < 0 || player >= db_top) {
    deep--;
    return;
  }
#ifdef notdef
  if (loc == NOTHING) {			/* death to -1 */
	  free_object(player);
	  deep--;
	  return;
  }

#endif
  /* check for room == HOME */
  if (loc == HOME)
    loc = db[player].exits;	/* home */

  if ((Typeof(player) != TYPE_PLAYER) && (Typeof(player) != TYPE_THING)) {
    fprintf(stderr, "ERROR: Non object moved!! %d\n", player);
    fflush(stderr);
    deep--;
    return;
  }
  if (Typeof(loc) == TYPE_EXIT) {
    fprintf(stderr, "ERROR: Attempt to move %d to exit %d\n", player, loc);
    deep--;
    return;
  }

  /* get old location */
  old = db[player].location;

  /* go there */
  moveit(player, loc);

  /* if old location has STICKY dropto, send stuff through it */

  if ((loc != old) && Dropper(player) &&
      (old != NOTHING) && (Typeof(old) == TYPE_ROOM) &&
      ((dropto = db[old].location) != NOTHING) &&
      (db[old].flags & STICKY))
    maybe_dropto(old, dropto);


  /* autolook */
  look_room(player, loc, 0);
  deep--;
}


/* teleports player to location while removing items they shouldnt take */
void safe_tel(player, dest)
    dbref player;
    dbref dest;
{
  dbref first;
  dbref rest;
  if (dest == HOME)
    dest = db[player].exits;
  if (db[db[player].location].owner == db[dest].owner) {
    enter_room(player, dest);
    return;
  }
  first = db[player].contents;
  db[player].contents = NOTHING;

  /* blast locations of everything in list */
  DOLIST(rest, first) {
    db[rest].location = NOTHING;
  }

  while (first != NOTHING) {
    rest = db[first].next;
    /* if thing is ok to take then move to player else send home */
    /* thing is not okay to move if it's KEY, or it's STICKY and its */
    /* home is not the player */
    if (controls(player, first) ||
	((Typeof(first) == TYPE_THING && !(db[first].flags & THING_KEY)) &&
	 (!(db[first].flags & STICKY) || (db[first].exits == player)))) {
      PUSH(first, db[player].contents);
      db[first].location = player;
    } else
      enter_room(first, HOME);
    first = rest;
  }
  db[player].contents = reverse(db[player].contents);
  enter_room(player, dest);
}

int can_move(player, direction)
    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(player, direction, type)
    dbref player;
    const char *direction;
    int type;   /* type 0 is normal, type 1 is global */
{
  dbref exit;
  dbref loc;
  if (!string_compare(direction, "home")) {
    /* send him home */
    /* but steal all his possessions */
    if ((loc = db[player].location) != NOTHING && !Dark(player) && !Dark(loc)){
      /* tell everybody else */
      notify_except(db[loc].contents, player,
		    tprintf("%s goes home.", db[player].name));
    }
    /* 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...");
    safe_tel(player, HOME);
  } else {
    /* find the exit */
#ifdef DO_GLOBALS
    if (type == 1)
      init_match_global_check_keys(player, direction, TYPE_EXIT);
    else
#endif
    init_match_check_keys(player, direction, TYPE_EXIT);
    match_exit();
    switch (exit = match_result()) {
      case NOTHING:
	/* try to force the object */
	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 */
	/* check to see if we got through */
	if (could_doit(player, exit)) {
	  did_it(player, exit, "SUCCESS", NULL, "OSUCCESS", NULL, "ASUCCESS",
		 NOTHING);
	  did_it(player, exit, "DROP", NULL, "ODROP", NULL, "ADROP",
		 db[exit].location);
	  switch (Typeof(db[exit].location)) {
	    case TYPE_ROOM:
	      enter_room(player, db[exit].location);
	      break;
	    case TYPE_PLAYER:
	    case TYPE_THING:
	      {
		if (db[db[exit].location].flags & GOING) {
		  notify(player, "You can't go that way.");
		  return;
		}
		if (db[db[exit].location].location == NOTHING)
		  return;
		safe_tel(player, db[exit].location);
	      }
	      break;
	    case TYPE_EXIT:
	      notify(player, "This feature coming soon.");
	      break;
	  }
	} else
	  did_it(player, exit, "FAILURE", "You can't go that way.", "OFAILURE",
		 NULL, "AFAILURE", NOTHING);
	break;
    }
  }
}

void do_get(player, what)
    dbref player;
    const char *what;
{
  dbref loc = db[player].location;
  dbref thing;

  if ((Typeof(loc) != TYPE_ROOM) && !(db[loc].flags & ENTER_OK) &&
      !controls(player, loc)) {
    notify(player, "Permission denied.");
    return;
  }
  if(((thing = is_possess(player, what)) != NOTHING) &&
     (thing != AMBIGUOUS)) {
   if(could_doit(player,thing) && (db[db[thing].location].flags & ENTER_OK)) {
     notify(db[thing].location,
            tprintf("%s was taken from you.",db[thing].name));
     moveto(thing,player);
     notify(thing, "Taken.");
     did_it(player, thing, "SUCCESS", "Taken.", "OSUCCESS", NULL, "ASUCCESS",
	    NOTHING);
   } else
     did_it(player, thing, "FAILURE", "You can't take that from there.",
	    "OFAILURE", NULL, "AFAILURE", NOTHING);
  } else {
    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_PLAYER:
        case TYPE_THING:
	  if (thing == player) {
	    notify(player, "You cannot get yourself!");
	    return;
	  }
	  if (could_doit(player, thing)) {
	    moveto(thing, player);
	    notify(thing, "Taken.");
	    did_it(player, thing, "SUCCESS", "Taken.", "OSUCCESS", NULL,
		   "ASUCCESS", NOTHING);
	  } else
	    did_it(player, thing, "FAILURE", "You can't pick that up.",
		   "OFAILURE", NULL, "AFAILURE", NOTHING);
	  break;
        case TYPE_EXIT:
	  notify(player, "You can't pick up exits.");
	  return;
        default:
	  notify(player, "You can't take that!");
	  break;
      }
    }
  }
}


void do_drop(player, name)
    dbref player;
    const char *name;
{
  dbref loc;
  dbref thing;
  int reward;
  char tbuf1[BUFFER_LEN];

  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!");
      return;
    case AMBIGUOUS:
      notify(player, "I don't know which you mean!");
      return;
    default:
      if (db[thing].location != player) {
	/* Shouldn't ever happen. */
	notify(player, "You can't drop that.");
      } else if (Typeof(thing) == TYPE_EXIT) {
	notify(player, "Sorry you can't drop exits.");
	return;
      } else if (db[loc].flags & ROOM_TEMPLE) {
	/* sacrifice time */
	if (Typeof(thing) == TYPE_PLAYER) {
	  notify(player, "Hey! No blood sacrifices please!.");
	  return;
	}
	if (db[thing].flags & THING_SAFE) {
	  notify(player, 
		 "That object is marked SAFE and cannot be sacrificed.");
	  return;
	}
	notify(thing, "You have been sacrificed.");
	moveto(thing, NOTHING);
	/* Patched to destroy sacrificed objects */
	notify(player, tprintf("%s is consumed in a burst of flame!",
			       db[thing].name));
#ifdef FULL_INVIS
	if(!Dark(player)) {
	  notify_except(db[loc].contents, player,
			tprintf("%s sacrifices %s.", db[player].name,
				db[thing].name));
	} else {
	  notify_except(db[loc].contents, player,
			tprintf("Someone sacrifices %s.", db[thing].name));
	}
#else
	notify_except(db[loc].contents, player,
		      tprintf("%s sacrifices %s.", db[player].name,
			       db[thing].name));
#endif
	/* check for reward */
	if (!controls(player, thing) || (Typeof(player) != TYPE_PLAYER)) {
	  reward = Pennies(thing);
	  if (reward < 1 || (Pennies(db[player].owner) > MAX_PENNIES)) {
	    reward = 1;
	  } else if (reward > MAX_OBJECT_ENDOWMENT) {
	    reward = MAX_OBJECT_ENDOWMENT;
	  }
	  giveto(db[player].owner, reward);
	  notify(player, tprintf("You have received %d %s for your sacrifice.",
			         reward, reward == 1 ? MONEY : MONIES));
          db[thing].flags |= THING_DEST_OK;
	  do_destroy(player,tprintf("#%d",thing), 1);
	}
      } else if (db[thing].flags & STICKY) {
	notify(thing, "Dropped.");
	safe_tel(thing, HOME);
      } else if (db[loc].location != NOTHING &&
		 (Typeof(loc) == TYPE_ROOM) && !(db[loc].flags & STICKY)) {
	/* location has immediate dropto */
	notify(thing, "Dropped.");
	moveto(thing, db[loc].location);
      } else {
	notify(thing, "Dropped.");
	moveto(thing, loc);
      }
      break;
  }
  sprintf(tbuf1, "dropped %s.", db[thing].name);
  did_it(player, thing, "DROP", "Dropped.", "ODROP", tbuf1, "ADROP", NOTHING);
}


void do_enter(player, what)
    dbref player;
    const char *what;
{
  dbref thing;
  init_match_check_keys(player, what, TYPE_THING);
  match_neighbor();
  match_exit();
  match_absolute();		/* necessary for enter aliases to work */

  if ((thing = noisy_match_result()) == NOTHING) {
    /* notify(player,"I don't see that here.");   */
    return;
  }
  switch (Typeof(thing)) {
    case TYPE_ROOM:
    case TYPE_EXIT:
      notify(player, "Permission denied.");
      break;
    default:
      /* the object must pass the lock. Also, the thing being entered */
      /* has to be controlled, or must be enter_ok */
      if (!(((db[thing].flags & ENTER_OK) || controls(player, thing)) &&
	  (eval_boolexp(player, db[thing].enterkey, thing, 0, ENTERLOCK)))) {
	did_it(player, thing, "EFAIL", "Permission denied.", "OEFAIL",
	       NULL, "AEFAIL", NOTHING);
	return;
      }
      if(thing == player) {
	notify(player, "Sorry, you must remain beside yourself!");
	return;
      }
      safe_tel(player, thing);
      break;
  }
}

void do_leave(player)
    dbref player;
{
  if (Typeof(db[player].location) == TYPE_ROOM) {
    notify(player, "You can't leave");
    return;
  }
  enter_room(player, db[db[player].location].location);
}

#ifdef DO_GLOBALS
int global_exit (player, direction)
  dbref player;
  const char *direction;
{
  init_match_global(player, direction, TYPE_EXIT);
  match_exit();
  return (last_match_result() != NOTHING);
}
#endif