musicmud-2.1.6/data/
musicmud-2.1.6/data/help/
musicmud-2.1.6/data/policy/
musicmud-2.1.6/data/wild/
musicmud-2.1.6/data/world/
musicmud-2.1.6/doc/
musicmud-2.1.6/src/ident/
musicmud-2.1.6/src/lua/
musicmud-2.1.6/src/lua/include/
musicmud-2.1.6/src/lua/src/lib/
musicmud-2.1.6/src/lua/src/lua/
musicmud-2.1.6/src/lua/src/luac/
/* 
 * MusicMUD - Traverse an exit module
 * Copyright (C) 1998-2003 Abigail Brady
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 */


#include <ctype.h>

#include "events.h"
#include "musicmud.h"
#include "misc.h"
#include "util.h"
#include "verbs.h"
#include "trap.h"
#include "rooms.h"
#include "pflags.h"
#include "paths.h"
#include "shared.h"
#include "zoneload.h"
#include "move.h"
#include "pos.h"
#include "hooks.h"
#include "death.h"
#include "privs.h"
#include "units.h"
#include "levels.h"
#include "events.h"

#define MODULE "move"

static void deathroom(MudObject *who, MudObject *where) {
  if (!who->get_priv(PFL_IMMORTAL) && !is_mobile(who)) {
   
    if (where->get_flag(FL_DEATH)) {
      do_die(who, privs_of(who)*20, "deathroom");
      return;
    }
  }
}

static const char *motion_verb(MudObject *what) {
  if (what->get_flag(FL_CANTWALK)) return "ride";
  if (what->get_flag(FL_MOBILE)) return "gallop";
  if (what->get_flag(FL_FLYING)) return "fly";

  if (what->get("motionverb"))
    return what->get("motionverb");

  return "ride";
}


#define pl(a, b, c) (a->get_flag(FL_PLURAL)?(b):(c))

int do_traverse(MudObject *player, MudObject *ext, const char *mvrb, MudObject *whofollow) {

  if (whofollow == player)
    {
      /* don't follow self. */
      return 0;
    }

  if (dotrap(E_BEFORETRAVERSE, player, ext)) return 0;
  /* ::: before_traverse o1== exit; before everything, including door openness checking. return 1 to abort. */


    if (!player->get_flag(FL_FLYING)) {
      if (streq(body(player), "tree")) {
	player->printf("As a tree, you are firmly rooted to the spot.\n");
	return true;
      }
      if (streq(body(player), "plant")) {
	player->printf("You can't locomote.\n");
	return true;
      }
    }

    if (player->get_flag(FL_SITTING)) {
      player->printf("You clamber to your feet.\n");
      player->oprintf(cansee, "%#M %[clambers/clamber] to %s feet.\n", player, his_or_her(player));
      setpos(player, P_STANDING);
    }

    MudObject *old_loc = player->owner;
    MudObject *door = ext->get_object("door");
    if (door && state(door)) {
	if (door->get_flag(FL_SECRET))
	    player->printf("You can't go ^d%s^n.\n", ext->get("short"));
	else {
	  const char *doorwhy = door->get("doorwhy");
	  if (!doorwhy) 
	    doorwhy = pl(door, "aren't open", "isn't open");
	  if (whofollow) {
	    player->printf("You can't follow %M as %#Y %s.\n", whofollow, door, doorwhy);
	  } else {
	    player->printf("%#Y %s.\n", door, doorwhy);
	  }
	}
	return 0;
    }
    
    int climbing = 0;
    
    MudObject *dest = ext->get_object("link");
    if (!dest) {
      dest = ext->get_object("climbto");
      climbing = 1;
    }

    MudObject *m = mount(player);
    NewWorld others;
    
    if (m) {
      MudObject *o;
      int i;
      foreach(old_loc->children, o, i) {
	if (mount(o)==m && o != player)
	  others.add(o);
      }
    }
    
    if (!dest) {
      player->printf("You can't go ^d%s^n.\n", ext->get("short"));
      log(PFL_SEEINFO, 0, "bug", "%s found a bad exit %s", player->id, ext->id);
      return 0;
    }

    
    if (player->owner)
      player->owner->ref();

    
    if (dest) {
      dest->ref();
      if (dest->get_flag(FL_WILDERNESS) && dest->nuke_me) {
	MudObject *o; int i;
	foreach(dest->children, o, i) {
	  if (o->get_flag(FL_TRANSIENT)) {
	    o->nuke_me = 0;
	  }
	}
	dest->nuke_me = 0;
      }
    }

    if (is_in(dest, player) || dest==player) {
        player->printf("PARADOX ALERT!.\n");
	return 0;
    }

    if (mud->get_int("tournament", 0) && player->get("tourn") && dest->get_flag(FL_SHIP)) {
      player->printf("You may not leave the station when taking part in a tournament.\n");
      return 0;
    }

    if (mud->get_int("tournament", 0) && player->get("tourn") && dest->get_flag(FL_SANCTUARY)) {
      player->printf("You may not enter sanctuary areas when taking part in a tournament.\n");
      return 0;
    }

    if (mud->get_int("tfrom", 0) && player->get("tourn")) {
      player->printf("Not until the tournament has formally started.\n");
      return 0;
    }
    
    if (is_player(player) && player->get_priv(PFL_REMORT) && !can_affect(player, dest->get("zone"))) {
      if (whofollow) {
	player->printf("You can't follow %M as you can't go to that zone (%s) without remorting.\n", whofollow, 
		       dest->get("zone"));
	return 0;
      }


	player->printf("You can't go to that zone (%s) without remorting.\n", dest->get("zone"));
	return 0;
    }

    if (!old_loc->get_flag(FL_NOGRAVITY)) {
      long long left = mass_capacity_left_in_grams(player);
      if (left < 0) {
	player->printf("You are carrying too much to move.\n");
	return 0;
      }
    }


    if (MudObject *y=get_flagged_object(player, FL_TIEDTOROOM)) {
      if (whofollow) {
	player->printf("You can't follow %M whilst carrying %Y.\n", whofollow, y);
      } else {
	player->printf("You can't move from here whilst carrying %Y.\n", y);
      }
      return 0;
    }

    if (!player->get_flag(FL_FLYING)) {
      
      if (old_loc->get_flag(FL_BROKEN) && player->get("$camefrom") && 
	  !streq(player->get("$camefrom"), ext->get("short"))) {
	player->printf("You cannot go ^d%s^n as the bridge is broken.\n", 
		       ext->get("short"));
	return 0;
      }
    
    // NOLEGS 
    if (!canwalk(player) && dryland(dest)) {
      if (whofollow) {
	player->printf("You can't follow %M onto land.\n", whofollow);
      } else {
	player->printf("You can't travel on land.\n");
      }
      return 0;
    }
    

    if (dryland(dest)) {
      if (m && ((m->get_flag(FL_BOAT) && !m->get_flag(FL_LANDVEHICLE))
	     || (m->get_flag(FL_CANTWALK)))) {
	if (whofollow) {
	  player->printf("You can't follow %M ashore on %Y.\n", whofollow, m);
	} else {
	  player->printf("You can't take %Y ashore.\n", m);
	}
	return 0;
      }
    }
   
    // NOGILLS
    if (inwater(dest)) {
      if (m && !m->get_flag(FL_BOAT) && !canswim(m)) {
	if (whofollow) {
	  player->printf("You can't take %Y to sea to follow %M.\n", m, whofollow);
	} else {
	  player->printf("You can't take %Y to sea.\n", m);
	}
	return 0;
      }

      if (!canswim(player)) {
	if (!m) {
	  if (whofollow) {
	    player->printf("You can't follow %M as you can't swim.\n");
	  } else {
	    player->printf("You can't swim.\n");
	  }
	  return 0;
	}
      }
    }
    }
    

    if (m) {
      if (dotrap(E_BEFOREMOVED, player, m, dest))
	/* ::: before_moved o1==object carried/mounted, o2==new room; return 1 to abort */
	return 0;
    }
    {
      MudObject *o;
      int i;
      foreach(player->children, o, i)
	if (o != m)
	  if (dotrap(E_BEFOREMOVED, player, o, dest))
	    return 0;
    }

    if (m && climbing) {
      player->printf("You can't climb on %Y.\n", m);
      return 0;
    }

    if (m && !dest->get_flag(FL_OUTDOORS) && m->get_flag(FL_MOBILE)) {
      if (whofollow) {
	player->printf("You try to follow %M %s, but %s %[refuses/refuse] to go that way.\n", whofollow, ext->get("short"), he_or_she(m));
      } else {
	player->printf("%#M %[refuses/refuse] to go that way.\n", m);
      }
      return 0;
    }
    

    if (m && streq(ext->get("short"), "up") && m->get_flag(FL_BARUP)) {
      player->printf("You can't go up on %Y!\n", m);
      return 0;
    }
    

    // RESERVED ROOMS :
    int min_level = dest->get_int("min_level");
    if (privs_of(player) < min_level) {
      
      if (ext->get_flag(FL_SECRET)) {
	player->printf("You can't go ^d%s^n.\n", ext->get("short"));
      } else {
	if (whofollow) {
	  player->printf("You can't follow %M that way : %s\n", whofollow, dest->get("min_why"));
	} else {
	  player->printf("You can't go that way : %s\n", dest->get("min_why"));
	}
      }
      return 0;
    }
    

    if (is_player(player) && dest->get("nogo")) {
      if (whofollow) {
	player->printf("You try to follow %M.\n", whofollow);
      }
      player->printf("%#s %s.\n", dest->get("nogo"), ext->get("short"));
      return 0;
    }
    

    // OBJECT NEEDED ROOMS : (wizards ignore)
    if (!player->get_priv(PFL_GOTO))
	{
	  MudObject *needed = dest->get_object("obj_need");
	  if (needed && needed->owner != player) {
	    if (whofollow) {
	      player->printf("You try to follow %M.\n", whofollow);
	    }
	    player->printf("%#s\n", dest->get("obj_whynot"));
	    return 0;
	  }
	}
    

    // MOBILES WITH "BAR" BEHAVIOR : (wizards ignore)
    if (!player->get_priv(PFL_NOHASSLE))
	if (!trigger_mobiles(player->owner, player, ext)) {
	    return 0;
	}
    

    // ONEPRSON/PRIVATE ROOMS
    if (!spare_people_capacity(dest)) {
      if (whofollow) {
	player->printf("You try to follow %M but don't fit.\n", whofollow);
      } else {
	player->printf("That location is full.\n");
      }
	return 0;
    }
    

    // AIRLESS ROOMS
    if (dest->get_flag(FL_AIRLESS) && !old_loc->get_flag(FL_AIRLESS)) {
	if (!wf_wears_with_flag(player, FL_SPACESUIT)) {
	  if (whofollow) {
	    player->printf("You can't follow %M into space without wearing a spacesuit.\n", whofollow);
	  } else {
	    player->printf("It's not a good idea to go there without wearing a spacesuit.\n");
	  }
	    return 0;
	}
    }
    

    if (dest->get_flag(FL_DEEPSPACE)) {
       player->printf("You cannot go into deep space.\n");
       return 0;
    }
    

    // ROOMS WITH MISSIONS
    if (!is_mobile(player)) {
      MudObject *nq = ext->get_object("quest");
      if (nq && nq->owner == player) nq = 0;
      
      if (!nq) {
	nq = dest->get_object("quest");
	if (nq && nq->owner == player) nq = 0;
      }

      if (nq && old_loc->get_object("quest") != nq) {
	int ok = 0;
	MudObject *os = old_loc->get_object("ship");
	MudObject *ds = dest->get_object("ship");

	if (os == dest ||
	    ds == old_loc ||
	    (os == ds && ds))
	  {
	    ok = 1;
	  }

	if (!ok) {	  
	  if (whofollow) {
	    player->printf("%#M %[has/have] gone into a mission-restricted area which you are not allowed into.\n", whofollow);
	  } else {
	    player->printf("You have no business there.\n");
	  }
	}
	return 0;
      }
    }
    


    const char *mverb_s = "walks";
    const char *mverb_p = "walk";

    const char *averb_s = "arrives";
    const char *averb_p = "arrive";
    
    string sbuf;

    if (player->get("motionverb")) {
      mverb_p = player->get("motionverb");
      sbuf = mverb_p;
      sbuf += "s";
      mverb_s = sbuf.c_str();
    }

    if (player->owner->get_flag(FL_NOGRAVITY)) {
      mverb_s = "goes";
      mverb_p = "go";
    }

    if (!player->get_flag(FL_FLYING)) {
      if (!m) {
	if (dryland(dest) && player->get_flag(FL_SWIMMING)) {
	  player->set_flag(FL_SWIMMING, 0);
	  player->printf("You get up out of the water.\n");
	  player->oprintf(cansee, "%#M %[gets/get] up out of the water.\n", player);
	}
	if (inwater(dest) && !player->get_flag(FL_SWIMMING) && !player->get_flag(FL_SHIP)) {
	  player->set_flag(FL_SWIMMING, 1);
	  player->printf("You start swimming.\n");
	  player->oprintf(cansee, "%#M %[starts/start] swimming.\n", player);
	}
      }      
    }

    if (player->get_flag(FL_SHIP)) {
      if (player->get_flag(FL_LANDVEHICLE)) {
	mverb_s = "drives";
	mverb_p = "drive";
	averb_s = "drives in";
	averb_p = "drive in";
      } else {
	mverb_s = "sails";
	mverb_p = "sail";
	averb_s = "sails in";
	averb_p = "sail in";
      }
    } else if (player->get_flag(FL_SWIMMING)) {
      mverb_s = "swims";
      mverb_p = "swim";
      averb_s = "swims in";
      averb_p = "swim in";
    } else if (player->get_flag(FL_NOLEGS)) {
      mverb_s = "slithers";
      mverb_p = "slither";
    } else if (player->get_flag(FL_FLYING)) {
      mverb_s = "flies";
      mverb_p = "fly";
    }

    string mvs;
    if (mvrb) {
      mverb_p = mvrb;
      mvs = mvrb;
      mvs += "s";
      mverb_s = mvs.c_str();
    }

    if (player->get("_fighting")) {
       player->printf ("Not while fighting.\n");
       return 0;
    }

    {
      MudObject *o;
      int i;
      foreach(old_loc->children, o, i) {
	if (o != player && !o->get_flag(FL_EXIT)) {
	  if (dotrap(E_BEFOREDEPART, player, o, dest)) return 0;
	  /* ::: before_depart o1==every object in from room, o2==destination; before we are about to move. return 1 to abort. */
	}
      }
    }
    
    if (dotrap(E_BEFOREGO, player, player, dest)) return 0;
    /* ::: before_leave o1== old room, o2== new room; before we are about to move. return 1 to abort. */

    if (dotrap(E_BEFORELEAVE, player, old_loc, dest)) return 0;
    /* ::: before_leave o1== old room, o2== new room; before we are about to move. return 1 to abort. */
    
    if (dotrap(E_BEFOREENTRY, player, dest, old_loc)) return 0;
    /* ::: before_entry o1== new room, o2== old room; before we are about to move. return 1 to abort. */
    
    player->lprintf(notinroom(dest) && secret(player) && cansee, "%#M %[leaves/leave] %M.\n", player, old_loc);

    {
      MudObject *obj;
      int i;
      foreach(old_loc->children, obj, i) if (obj != player && visible_to(obj, player) && cansee(obj)) {

	if (others.get(obj->id))
	  continue;

	if (climbing) {
	  obj->printf("%#M %[climbs/climb] %s %M.\n", player, ext->get("dir"), ext);
	  continue;
	}

	if (whofollow) {
	  obj->printf("%#M %[follows/follow] %M %s.\n", player, whofollow, ext->get("short"));
	} else {
	  if (m) {
	    if (others)
		obj->printf("%#M %s%[s/] %s %P on with %W.\n", player, motion_verb(m), m, 
			    ext->get("short"), others);
	    else
	      obj->printf("%#M %s%[s/] %s on %P.\n", player, motion_verb(m), ext->get("short"), m);
	  } else {
	    obj->printf("%#M %s %s.\n", player, player->get_flag(FL_PLURAL)?mverb_p:mverb_s, 
			ext->get("short"));
	  }
	}
      }
    }
    
    if (!player->get_flag(FL_SWIMMING) && (is_player(player)||is_mobile(player))) {
      old_loc->set("$lastperson", player->id);
      old_loc->set("$lastdir", ext->id);
      old_loc->set("$lasttime", now);
    }

    {
      MudObject *tr;
      int i;
      foreach(&player->tracers, tr, i) {
	tr->printf("[trace : ^Z%M %[goes/go] %s from %s %s]\n", 
		   player, ext->get("short"), player->owner->id, 
		   format_roomname(dest, player, "", "", "to", 1).c_str(),
		   dest->id);
      }
    }

    set_owner(player, dest);

    {
      MudObject *o;
      int i;
      foreach((&others), o, i)
	set_owner(o, dest);
    }

    player->unset("$camefrom");
    player->unset("$camefromloc");

    {
      MudObject *o;
      int i;
      foreach(old_loc->children, o, i) {
	rectify_state(o);
      }
    }

    newplace(player, old_loc);

    string to = format_roomname(dest, player, "^{D\3RName\4", "^}\3/RName\4", "to", IS_COMMANDER(player));

    int oddlook = 0;

    if (climbing) {
      player->printf("You climb %s %M %s.\n", ext->get("dir"), ext, to);
      oddlook = 1;
    } else {
      if (whofollow) {
	
	player->printf("You follow %M %s %s.\n", whofollow, ext->get("short"), to);
	oddlook = 1;
      } else {
	
	if (m) {
	  player->printf("You %s %s on %Y %s.\n", motion_verb(m), ext->get("short"), m, to);
	  oddlook = 1;
	  if (m->owner == old_loc) {
	    set_owner(m, dest);
	  }
	} else {
	  player->printf("You %s %s %s.\n", mverb_p, ext->get("short"), to);
	  
	  if (player->get_flag(FL_SHIP)) {
	    shipmsg(player, "%#P %s %s %s.\n", player, pl(player,mverb_p,mverb_s), 
		    ext->get("short"), to);
	  }
	  
	  oddlook = 1;
	}      
      }
    }
    
    {
      MudObject *o;
      int i;
      foreach(&others, o, i) {
	o->printf("%#M %s%[s/] %P %s with you %s.\n", player, motion_verb(m), m, ext->get("short"), 
		  to);
      }
    }
    
    const char *r = exit_rev(ext->get("short"));

    int i;
    MudObject *obj;
    foreach(dest->children, obj, i) if (obj != player && visible_to(obj, player) && cansee(obj)) {

      if (others.get(obj->id))
	continue;
      
      if (!whofollow) {

	if (climbing) {
	  obj->printf("%#M %[climbs/climb] %s %M to here.\n", player, ext->get("dir"), ext);
	  continue;
	}
	
	if (r) {
	  if (m) {
	    obj->printf("%#M %s%[s/] in from %s on %P.\n", player, motion_verb(m), r, m);
	  } else {
	      obj->printf("%#M %s from %s.\n", player, pl(player, averb_p, averb_s), r);
	  }
	} else {
	  if (m) {
	    obj->printf("%#M %s%[s/] here on %P.\n", player, motion_verb(m), m);
	  } else {
	    obj->printf("%#M %s.\n", player, pl(player, averb_p, averb_s));
	  }
	}
	
      } else {
	
	if (r) {
	  if (obj == whofollow) {
	    obj->printf("%#M %[follows/follow] you here from %s.\n", player, r);
	  } else {
	    obj->printf("%#M %[follows/follow] %M here from %s.\n", player, whofollow, r);
	  }
	} else {
          obj->printf("%#M %s.\n", player, pl(player, averb_p, averb_s));
        }
	
      }
      
    }
    
    player->set("$camefrom", other_dir(ext->get("short")));
    player->set("$camefromloc", old_loc->id);

    player->lprintf(notinroom(old_loc) && secret(player) && cansee, "%#M %[enters/enter] %M.\n", player, dest);

    if (is_player(player) || player->snoopers.getsize())
      if (oddlook) {
  	player->interpret("look -");
      } else {
  	player->interpret("look");
      }

    {
      MudObject *o;
      int i;
      foreach (&others, o, i) {
	o->interpret("look -");
      }
    }

    // TRIGGER DEATHROOMS AND SPECIALS
    deathroom(player, player->owner);

    {
      MudObject *o;
      int i;
      foreach(&others, o, i) {
	// TRIGGER DEATHROOMS AND SPECIALS
	deathroom(o, o->owner);
      }
    }
    
    dotrap(E_AFTERLEAVE, player, old_loc, dest);
    /* ::: after_leave o1== old room, o2== new room; just after 'look'. */
    
    dotrap(E_AFTERENTRY, player, dest, old_loc);
    /* ::: after_entry o1== new room, o2== old room; just after 'look'. */
    
    dotrap(E_AFTERGO, player, player, dest);
    /* ::: after_go o1== thing moving, o2== new room; just after 'look'. */

    if (m)
      dotrap(E_AFTERMOVED, player, m, dest);
	/* ::: after_moved o1==object carried/mounted, o2==new room; return 1 to abort */

    {
      MudObject *o;
      int i;
      foreach(player->children, o, i)
	if (o != m)
	  dotrap(E_AFTERMOVED, player, o, dest);
    }

    {
      MudObject *o;
      int i;
      foreach(dest->children, o, i)
	{
	  if (o->get_flag(FL_ENTERFILEPLAN))
	    {
	      o->set_flag(FL_ENTERFILEPLAN, 0);
	      o->interpret("file_plan");
	      o->set("!$1", o->id);
	    }

	  if (o != player && !o->get_flag(FL_EXIT)) {
	    dotrap(E_ONARRIVAL, player, o, old_loc);	
	    /* ::: arrival o1==everything in destination, o2==old room; just after 'look'. */

	    if (player->owner != dest)
	      break;
	  }
	}
    }
    
    NewWorld made;

    if (player->owner == dest && dest != old_loc) {
	World<MudObject> fol = player->followers;
	if (fol.getsize()) {
	    MudObject *d;
	    int i;

	    NewWorld notmade;

	    foreach((&fol), d, i) 
	      if (d != player && d->owner == old_loc) {
		if (!d->get_object("_fighting") && !d->get_flag(FL_SITTING) && !d->get_flag(FL_SLEEPING)) {
		  if (!traverse(d, ext, mvrb, whofollow?whofollow:player)) {
		    notmade.add(*d);
		  } else {
		    made.add(*d);
		  }
		}
	      }
	    
	    if (notmade.getsize()) {
	      fol.add(*player);
	      foreach((&fol), d, i) if (d->owner == dest && cansee(d)) {
		d->printf("%#W become separated from the group.\n", 
			  &notmade,
			  plural(notmade)?"have":"has");
	      }
	    }
	}
    }
    

    if (!whofollow) {
      if (player->owner == dest) 
	dotrap(E_AFTERPARTYENTRY, player, dest, old_loc);
      /* ::: after_party_entry o1==new room, o2==old room; after all party has arrived */

      if (player->owner == dest) 
	dotrap(E_AFTERPARTYLEAVE, player, old_loc, dest);
      /* ::: after_party_leave o1==old room, o2==new room; after all party has arrived */

      MudObject *o;
      int i;
      foreach(dest->children, o, i) if (o != player && !o->get_flag(FL_EXIT) &&
					!made.get(o->id)) {
	if (player->owner == dest) 
	dotrap(E_ONPARTYARRIVAL, player, o, old_loc);
	/* ::: party_arrival o1==every object in dest, o2==old room; after all party has arrived */
      }
    }
    

    if (player->get_flag(FL_PITIT) && find_pit(player->owner)) {
      player->interpret("drop all");
    }

    return 1;
}

#define CLEANUP DO_TRAVERSE = 0;

#include "verbmodule.h"

void startup() {
  DO_TRAVERSE = do_traverse;
}