dw_fluffos_v2/
dw_fluffos_v2/fluffos-2.9-ds2.05/
dw_fluffos_v2/fluffos-2.9-ds2.05/ChangeLog.old/
dw_fluffos_v2/fluffos-2.9-ds2.05/Win32/
dw_fluffos_v2/fluffos-2.9-ds2.05/compat/
dw_fluffos_v2/fluffos-2.9-ds2.05/compat/simuls/
dw_fluffos_v2/fluffos-2.9-ds2.05/include/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/clone/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/command/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/data/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/etc/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/include/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/inherit/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/inherit/master/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/log/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/compiler/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/efuns/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/operators/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/u/
dw_fluffos_v2/fluffos-2.9-ds2.05/tmp/
dw_fluffos_v2/fluffos-2.9-ds2.05/windows/
dw_fluffos_v2/lib/
dw_fluffos_v2/lib/binaries/cmds/
dw_fluffos_v2/lib/binaries/cmds/creator/
dw_fluffos_v2/lib/binaries/cmds/living/
dw_fluffos_v2/lib/binaries/cmds/player/
dw_fluffos_v2/lib/binaries/d/admin/obj/
dw_fluffos_v2/lib/binaries/d/liaison/
dw_fluffos_v2/lib/binaries/global/virtual/
dw_fluffos_v2/lib/binaries/global/virtual/setup_compiler/
dw_fluffos_v2/lib/binaries/obj/handlers/autodoc/
dw_fluffos_v2/lib/binaries/obj/handlers/terrain_things/
dw_fluffos_v2/lib/binaries/obj/misc/
dw_fluffos_v2/lib/binaries/obj/misc/buckets/
dw_fluffos_v2/lib/binaries/obj/monster/
dw_fluffos_v2/lib/binaries/obj/reactions/
dw_fluffos_v2/lib/binaries/obj/reagents/
dw_fluffos_v2/lib/binaries/secure/cmds/creator/
dw_fluffos_v2/lib/binaries/secure/master/
dw_fluffos_v2/lib/binaries/std/
dw_fluffos_v2/lib/binaries/std/dom/
dw_fluffos_v2/lib/binaries/std/effects/object/
dw_fluffos_v2/lib/binaries/std/guilds/
dw_fluffos_v2/lib/binaries/std/languages/
dw_fluffos_v2/lib/binaries/std/races/
dw_fluffos_v2/lib/binaries/std/room/
dw_fluffos_v2/lib/binaries/std/room/basic/
dw_fluffos_v2/lib/binaries/std/shops/
dw_fluffos_v2/lib/binaries/std/shops/inherit/
dw_fluffos_v2/lib/binaries/www/
dw_fluffos_v2/lib/cmds/guild-race/
dw_fluffos_v2/lib/cmds/guild-race/crafts/
dw_fluffos_v2/lib/cmds/guild-race/other/
dw_fluffos_v2/lib/cmds/playtester/
dw_fluffos_v2/lib/cmds/playtester/senior/
dw_fluffos_v2/lib/d/admin/
dw_fluffos_v2/lib/d/admin/log/
dw_fluffos_v2/lib/d/admin/mapper/31-10-01/mapmaker/event/
dw_fluffos_v2/lib/d/admin/meetings/
dw_fluffos_v2/lib/d/admin/obj/
dw_fluffos_v2/lib/d/admin/room/we_care/
dw_fluffos_v2/lib/d/admin/save/
dw_fluffos_v2/lib/d/dist/
dw_fluffos_v2/lib/d/dist/mtf/
dw_fluffos_v2/lib/d/dist/pumpkin/
dw_fluffos_v2/lib/d/dist/pumpkin/chars/
dw_fluffos_v2/lib/d/dist/pumpkin/desert/
dw_fluffos_v2/lib/d/dist/pumpkin/gumboot/
dw_fluffos_v2/lib/d/dist/pumpkin/hospital/
dw_fluffos_v2/lib/d/dist/pumpkin/inherit/
dw_fluffos_v2/lib/d/dist/pumpkin/map/
dw_fluffos_v2/lib/d/dist/pumpkin/plain/
dw_fluffos_v2/lib/d/dist/pumpkin/pumpkin/
dw_fluffos_v2/lib/d/dist/pumpkin/save/
dw_fluffos_v2/lib/d/dist/pumpkin/squash/
dw_fluffos_v2/lib/d/dist/pumpkin/terrain/
dw_fluffos_v2/lib/d/dist/pumpkin/woods/
dw_fluffos_v2/lib/d/dist/start/
dw_fluffos_v2/lib/d/learning/TinyTown/buildings/
dw_fluffos_v2/lib/d/learning/TinyTown/map/
dw_fluffos_v2/lib/d/learning/TinyTown/roads/
dw_fluffos_v2/lib/d/learning/add_command/
dw_fluffos_v2/lib/d/learning/arms_and_weps/
dw_fluffos_v2/lib/d/learning/chars/
dw_fluffos_v2/lib/d/learning/cutnpaste/
dw_fluffos_v2/lib/d/learning/examples/npcs/
dw_fluffos_v2/lib/d/learning/examples/player_houses/npcs/
dw_fluffos_v2/lib/d/learning/examples/terrain_map/basic/
dw_fluffos_v2/lib/d/learning/functions/
dw_fluffos_v2/lib/d/learning/handlers/
dw_fluffos_v2/lib/d/learning/help_topics/npcs/
dw_fluffos_v2/lib/d/learning/help_topics/objects/
dw_fluffos_v2/lib/d/learning/help_topics/rcs_demo/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/crowd/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/situations/
dw_fluffos_v2/lib/d/learning/items/
dw_fluffos_v2/lib/d/learning/save/
dw_fluffos_v2/lib/d/liaison/
dw_fluffos_v2/lib/d/liaison/NEWBIE/doc/
dw_fluffos_v2/lib/d/liaison/NEWBIE/save/oldlog/
dw_fluffos_v2/lib/db/
dw_fluffos_v2/lib/doc/
dw_fluffos_v2/lib/doc/creator/
dw_fluffos_v2/lib/doc/creator/autodoc/include/reaction/
dw_fluffos_v2/lib/doc/creator/autodoc/include/ritual_system/
dw_fluffos_v2/lib/doc/creator/autodoc/include/talker/
dw_fluffos_v2/lib/doc/creator/autodoc/include/terrain_map/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/baggage/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/clock/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/clothing/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/cont_save/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/corpse/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/money/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/monster/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/scabbard/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/service_provider/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/state_changer/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/wand/
dw_fluffos_v2/lib/doc/creator/autodoc/std/book_dir/
dw_fluffos_v2/lib/doc/creator/autodoc/std/key/
dw_fluffos_v2/lib/doc/creator/autodoc/std/learning/
dw_fluffos_v2/lib/doc/creator/autodoc/std/map/
dw_fluffos_v2/lib/doc/creator/autodoc/std/race/
dw_fluffos_v2/lib/doc/creator/autodoc/std/weapon_logic/
dw_fluffos_v2/lib/doc/creator/files/
dw_fluffos_v2/lib/doc/creator/policy/
dw_fluffos_v2/lib/doc/creator/room/
dw_fluffos_v2/lib/doc/effects/
dw_fluffos_v2/lib/doc/ideas/
dw_fluffos_v2/lib/doc/known_command/
dw_fluffos_v2/lib/doc/lpc/basic_manual/
dw_fluffos_v2/lib/doc/lpc/intermediate/
dw_fluffos_v2/lib/doc/new/add_command/
dw_fluffos_v2/lib/doc/new/handlers/
dw_fluffos_v2/lib/doc/new/living/
dw_fluffos_v2/lib/doc/new/living/race/
dw_fluffos_v2/lib/doc/new/living/spells/
dw_fluffos_v2/lib/doc/new/player/
dw_fluffos_v2/lib/doc/new/room/guild/
dw_fluffos_v2/lib/doc/new/room/outside/
dw_fluffos_v2/lib/doc/new/room/storeroom/
dw_fluffos_v2/lib/doc/object/
dw_fluffos_v2/lib/doc/playtesters/
dw_fluffos_v2/lib/doc/policy/
dw_fluffos_v2/lib/doc/weapons/
dw_fluffos_v2/lib/global/handlers/
dw_fluffos_v2/lib/global/virtual/setup_compiler/
dw_fluffos_v2/lib/include/
dw_fluffos_v2/lib/include/cmds/
dw_fluffos_v2/lib/include/effects/
dw_fluffos_v2/lib/include/npc/
dw_fluffos_v2/lib/include/shops/
dw_fluffos_v2/lib/net/daemon/chars/
dw_fluffos_v2/lib/net/inherit/
dw_fluffos_v2/lib/net/intermud3/
dw_fluffos_v2/lib/net/intermud3/services/
dw_fluffos_v2/lib/net/obj/
dw_fluffos_v2/lib/net/save/
dw_fluffos_v2/lib/net/smnmp/
dw_fluffos_v2/lib/net/snmp/
dw_fluffos_v2/lib/obj/amulets/
dw_fluffos_v2/lib/obj/b_day/
dw_fluffos_v2/lib/obj/examples/
dw_fluffos_v2/lib/obj/food/alcohol/
dw_fluffos_v2/lib/obj/food/chocolates/
dw_fluffos_v2/lib/obj/food/fruits/
dw_fluffos_v2/lib/obj/food/meat/
dw_fluffos_v2/lib/obj/food/nuts/
dw_fluffos_v2/lib/obj/food/seafood/
dw_fluffos_v2/lib/obj/food/vegetables/
dw_fluffos_v2/lib/obj/fungi/
dw_fluffos_v2/lib/obj/furnitures/artwork/
dw_fluffos_v2/lib/obj/furnitures/bathroom/
dw_fluffos_v2/lib/obj/furnitures/beds/
dw_fluffos_v2/lib/obj/furnitures/cabinets/
dw_fluffos_v2/lib/obj/furnitures/chairs/
dw_fluffos_v2/lib/obj/furnitures/chests/
dw_fluffos_v2/lib/obj/furnitures/clocks/
dw_fluffos_v2/lib/obj/furnitures/crockery/
dw_fluffos_v2/lib/obj/furnitures/cupboards/
dw_fluffos_v2/lib/obj/furnitures/cushions/
dw_fluffos_v2/lib/obj/furnitures/fake_plants/
dw_fluffos_v2/lib/obj/furnitures/lamps/
dw_fluffos_v2/lib/obj/furnitures/mirrors/
dw_fluffos_v2/lib/obj/furnitures/outdoor/
dw_fluffos_v2/lib/obj/furnitures/safes/
dw_fluffos_v2/lib/obj/furnitures/shelves/
dw_fluffos_v2/lib/obj/furnitures/sideboards/
dw_fluffos_v2/lib/obj/furnitures/sofas/
dw_fluffos_v2/lib/obj/furnitures/stoves/
dw_fluffos_v2/lib/obj/furnitures/tables/
dw_fluffos_v2/lib/obj/furnitures/wardrobes/
dw_fluffos_v2/lib/obj/handlers/
dw_fluffos_v2/lib/obj/handlers/autodoc/
dw_fluffos_v2/lib/obj/jewellery/anklets/
dw_fluffos_v2/lib/obj/jewellery/bracelets/
dw_fluffos_v2/lib/obj/jewellery/earrings/
dw_fluffos_v2/lib/obj/jewellery/misc/
dw_fluffos_v2/lib/obj/jewellery/necklaces/
dw_fluffos_v2/lib/obj/jewellery/rings/
dw_fluffos_v2/lib/obj/media/
dw_fluffos_v2/lib/obj/misc/buckets/
dw_fluffos_v2/lib/obj/misc/jars/
dw_fluffos_v2/lib/obj/misc/papers/
dw_fluffos_v2/lib/obj/misc/player_shop/
dw_fluffos_v2/lib/obj/misc/shops/
dw_fluffos_v2/lib/obj/misc/traps/
dw_fluffos_v2/lib/obj/monster/
dw_fluffos_v2/lib/obj/monster/godmother/
dw_fluffos_v2/lib/obj/monster/transport/
dw_fluffos_v2/lib/obj/plants/inherit/
dw_fluffos_v2/lib/obj/potions/
dw_fluffos_v2/lib/open/boards/
dw_fluffos_v2/lib/save/autodoc/
dw_fluffos_v2/lib/save/bank_accounts/
dw_fluffos_v2/lib/save/boards/frog/
dw_fluffos_v2/lib/save/books/bed_catalog/
dw_fluffos_v2/lib/save/creators/
dw_fluffos_v2/lib/save/mail/
dw_fluffos_v2/lib/save/mail/p/
dw_fluffos_v2/lib/save/soul/data/
dw_fluffos_v2/lib/save/tasks/
dw_fluffos_v2/lib/save/vaults/
dw_fluffos_v2/lib/secure/cmds/lord/
dw_fluffos_v2/lib/secure/config/
dw_fluffos_v2/lib/secure/items/
dw_fluffos_v2/lib/secure/player/
dw_fluffos_v2/lib/soul/
dw_fluffos_v2/lib/soul/i/
dw_fluffos_v2/lib/soul/j/
dw_fluffos_v2/lib/soul/k/
dw_fluffos_v2/lib/soul/o/
dw_fluffos_v2/lib/soul/q/
dw_fluffos_v2/lib/soul/to_approve/
dw_fluffos_v2/lib/soul/u/
dw_fluffos_v2/lib/soul/v/
dw_fluffos_v2/lib/soul/wish_list/
dw_fluffos_v2/lib/soul/y/
dw_fluffos_v2/lib/soul/z/
dw_fluffos_v2/lib/std/creator/
dw_fluffos_v2/lib/std/effects/
dw_fluffos_v2/lib/std/effects/attached/
dw_fluffos_v2/lib/std/effects/external/
dw_fluffos_v2/lib/std/effects/fighting/
dw_fluffos_v2/lib/std/effects/other/
dw_fluffos_v2/lib/std/environ/
dw_fluffos_v2/lib/std/guilds/
dw_fluffos_v2/lib/std/hospital/
dw_fluffos_v2/lib/std/house/
dw_fluffos_v2/lib/std/house/onebedhouse/
dw_fluffos_v2/lib/std/house/onebedhut/
dw_fluffos_v2/lib/std/house/tworoomflat/
dw_fluffos_v2/lib/std/languages/
dw_fluffos_v2/lib/std/liquids/
dw_fluffos_v2/lib/std/nationality/
dw_fluffos_v2/lib/std/nationality/accents/
dw_fluffos_v2/lib/std/nationality/accents/national/
dw_fluffos_v2/lib/std/nationality/accents/regional/
dw_fluffos_v2/lib/std/npc/goals/
dw_fluffos_v2/lib/std/npc/goals/basic/
dw_fluffos_v2/lib/std/npc/goals/misc/
dw_fluffos_v2/lib/std/npc/inherit/
dw_fluffos_v2/lib/std/npc/plans/
dw_fluffos_v2/lib/std/npc/plans/basic/
dw_fluffos_v2/lib/std/outsides/
dw_fluffos_v2/lib/std/races/shadows/
dw_fluffos_v2/lib/std/room/basic/topography/
dw_fluffos_v2/lib/std/room/controller/
dw_fluffos_v2/lib/std/room/controller/topography/
dw_fluffos_v2/lib/std/room/furniture/games/
dw_fluffos_v2/lib/std/room/furniture/inherit/
dw_fluffos_v2/lib/std/room/inherit/carriage/
dw_fluffos_v2/lib/std/room/inherit/topography/
dw_fluffos_v2/lib/std/room/punishments/
dw_fluffos_v2/lib/std/room/topography/area/
dw_fluffos_v2/lib/std/room/topography/iroom/
dw_fluffos_v2/lib/std/room/topography/milestone/
dw_fluffos_v2/lib/std/shadows/
dw_fluffos_v2/lib/std/shadows/attached/
dw_fluffos_v2/lib/std/shadows/curses/
dw_fluffos_v2/lib/std/shadows/disease/
dw_fluffos_v2/lib/std/shadows/fighting/
dw_fluffos_v2/lib/std/shadows/room/
dw_fluffos_v2/lib/std/shops/controllers/
dw_fluffos_v2/lib/std/shops/objs/
dw_fluffos_v2/lib/std/shops/player_shop/
dw_fluffos_v2/lib/std/shops/player_shop/office_code/
dw_fluffos_v2/lib/std/socket/
dw_fluffos_v2/lib/www/
dw_fluffos_v2/lib/www/external/autodoc/
dw_fluffos_v2/lib/www/external/java/telnet/Documentation/
dw_fluffos_v2/lib/www/external/java/telnet/Documentation/images/
dw_fluffos_v2/lib/www/external/java/telnet/examples/
dw_fluffos_v2/lib/www/external/java/telnet/tools/
dw_fluffos_v2/lib/www/pics/
dw_fluffos_v2/lib/www/secure/creator/
dw_fluffos_v2/lib/www/secure/editors/
dw_fluffos_v2/lib/www/secure/survey_results/
dw_fluffos_v2/win32/
/*  -*- LPC -*-  */
/*
 * $Locker:  $
 * $Id: interroom.c,v 1.2 2002/02/28 23:26:13 rhinehold Exp $
 *
 *
 */

/*
** interroom.c - a handler to handle being "in between" the regular rooms
**     on a long road.
**
** The milestones are the "normal" rooms that people travel between when
** moving compass directions.  milestone_coords[] is a cache of the
** milestone coordinates.
**
** irooms[][] is an array of arrays of rooms; one array of rooms for
** each segment of the road (one less than the number of milestone_coords).
**
** bases[] is an array of base rooms to clone for the interrooms (one for
** each gap between the milestones).
*/

/**
 * The interroom handler is a scheme for handling multiple levels of
 * detail along piecewise linear paths (eg, roads).  The vertices of
 * the path (called "milestones") behave like the rooms along a
 * traditional road.  However, between each milestone are a number of
 * "in-between" rooms (called "interrooms" or "irooms").  Irooms are
 * generally cloned as needed, while milestones are generally unique
 * rooms.  The irooms between two milestones are reached via exits
 * with the same names as those connecting the milestones, but
 * prepended with a "motion verb" (eg, "walk", giving iroom exits of
 * "walk east", "walk west", etc.).<p>
 *
 * The interroom handler is a sort of "mini-handler".  It is meant to
 * be used as an inheritable, with each path having its own handler.
 * Although there is no limit to the number of milestones along a
 * given path, a few points should be kept in mind:
 * <ul>
 * <li> Each milestone can be specified only once in a given handler,
 * except the first and last, which may be identical (ie, you can have
 * a loop, but not a figure-eight).
 *
 * <li> The calculations are based on the bounding box of the path.  A
 * long relatively straight road (ie, north/south or east/west is
 * fairly efficient, but a mostly diagonal road is the least
 * efficient.  So a road that's straight on one half and diagonal on
 * the other half might be better broken into two paths.
 * </ul>
 * Each handler specifies the milestones, the rooms to clone for the
 * irooms (the irooms in each segment are clones of the same room),
 * the sizes of the irooms (again, the irooms in each segment are of the
 * same size), and the save file to use for caching information (like
 * room coordinates).
 *
 * In the documentation, directions along the path are sometimes referred
 * to as "to the left" or "to the right".  These mean "toward the lower
 * numberd milestone" or "toward the higher numbered milestone",
 * respectively, regardless of the compass direction of the path.
 *
 * @author Jeremy
 * @see iroom
 * @see milestone
 * @see topography
 */

#include <map.h>

inherit "/std/room";

class IROOM_INFO {
  string base;
  int *size;
  mixed *bbox;  // Keeps track of bounding box of iroom segment
  int sgn_n;  // Keeps track of N/S direction (N = 1)
  int sgn_e;  // Keeps track of E/W direction (E = 1)
  string *exits; // In order of direction to milestone[i], milestone[i+1]
}

int closed;  // this path forms a closed loop
nosave int dbg_lvl = 0;
mixed *irooms;  // an array of arrays of objects
string *milestones;  // milestone room names
mixed *milestone_coords;  // cache of milestone coordinates
mapping milestone_idx = ([ ]);  // mapping of room name to index
class IROOM_INFO *iroom_info;
mixed *overall_bbox;
string save_file;
nosave string debugger = "jeremy";

mixed *bbox(int *coord_a, int *coord_b);
int in_bbox(int *coord_a, mixed bbox);

/**
 * This method sets the creator to whom debugging messages are sent.
 * @param d name of creator
 */
void set_debugger(string s) { debugger = s; }

/**
 * This method returns the name of the creator to whom debugging messages
 * are sent.
 * @return name of creator
 */
string query_debugger() { return debugger; }

/**
 * This method sets the level of debugging messages printed.
 * @param l debugging level (0 is off)
 */
void set_debug_level(int l) { dbg_lvl = l; }

/**
 * This method returns the current debugging level.
 * @return current debug level (0 is off)
 */
int query_debug_level() { return dbg_lvl; }

/**
 * @ignore
 * For debugging only
 */
mixed query(string s) { return fetch_variable(s); }

/**
 * @ignore
 * Integer absolute value function.
 */
int abs(int i) {
  return (i<0)?-i:i;
}

/**
 * @ignore
 * Float absolute value function.
 */
float fabs(float i) {
  return (i<0)?-i:i;
}

/**
 * This method sets the path of the file to use for saving data that
 * shouldn't have to be recalculated very often.
 * @param s pathname of save file
 */
void set_save_file(string s) { save_file = s; }

/**
 * This method returns the path of the file used for saving.
 * return pathname of save file
 */
string query_save_file() { return save_file; }

/**
 * This method is used to set the pathnames of the milestones.  Each
 * milestone should inherit one of the milestone inheritables.
 * @param array of filenames of the milestones for this path.
 * @see milestone
 */
void set_milestones(string *s) {
  int i;

  milestones = copy(s);
  if (milestones[0] == milestones[<1]) {
    // This is a closed loop
    closed = 1;
  }
  for (i = 0; i < sizeof(milestones) - closed; i++) {
    milestone_idx[s[i]] = i;
  }
  milestone_coords = allocate(sizeof(milestones));
  irooms = allocate(sizeof(milestones)-1);
  iroom_info = allocate(sizeof(milestones)-1);
  for (i = 0; i < sizeof(iroom_info); i++) {
    iroom_info[i] = new(class IROOM_INFO);
    iroom_info[i]->exits = allocate(2);
  }
  overall_bbox = 0;
} /* set_milestones() */

/**
 * This method returns the array of milestone filenames.
 * @return an array of milestone filenames
 * @see set_milestones()
 */
string *query_milestones() { return milestones; }

/**
 * This method returns the filename of the given milestone.
 * @param i index number of milestone
 * @returns filename of milestone i
 * @see set_milestones()
 */
string query_milestone(int i) { return milestones[i]; }

/**
 * @ignore
 * This method is used internally to set (or update) the coordinates
 * of a milestone.  It also updates related info, such as the bounding
 * boxes.
 */
void set_milestone_coords(int i, int *c) {

  if (!arrayp(c) || (sizeof(c) != 3)) {
    return;
  }
  if (!arrayp(milestone_coords[i]) || (milestone_coords[i][0] != c[0])
  || (milestone_coords[i][1] != c[1]) || (milestone_coords[i][2] != c[2])) {
    // Update iroom_info on either side of milestone
    milestone_coords[i] = copy(c);
    if ((i > 0) && arrayp(milestone_coords[i-1])) {
      iroom_info[i-1]->bbox = bbox(milestone_coords[i-1], milestone_coords[i]);
      if (milestone_coords[i-1][0] < milestone_coords[i][0]) {
        iroom_info[i-1]->sgn_n = 1;
      } else if (milestone_coords[i-1][0] > milestone_coords[i][0]) {
        iroom_info[i-1]->sgn_n = -1;
      } else {
        iroom_info[i-1]->sgn_n = 0;
      }
      if (milestone_coords[i-1][1] < milestone_coords[i][1]) {
        iroom_info[i-1]->sgn_e = 1;
      } else if (milestone_coords[i-1][1] > milestone_coords[i][1]) {
        iroom_info[i-1]->sgn_e = -1;
      } else {
        iroom_info[i-1]->sgn_e = 0;
      }
      // Update overall bounding box (do I need to add in room size?)
      if (!arrayp(overall_bbox)) {
        overall_bbox = copy(iroom_info[i-1]->bbox);
      } else {
        if (iroom_info[i-1]->bbox[0][0] < overall_bbox[0][0]) {
          overall_bbox[0][0] = iroom_info[i-1]->bbox[0][0];
        }
        if (iroom_info[i-1]->bbox[0][1] < overall_bbox[0][1]) {
          overall_bbox[0][1] = iroom_info[i-1]->bbox[0][1];
        }
        if (iroom_info[i-1]->bbox[1][0] > overall_bbox[1][0]) {
          overall_bbox[1][0] = iroom_info[i-1]->bbox[1][0];
        }
        if (iroom_info[i-1]->bbox[1][1] > overall_bbox[1][1]) {
          overall_bbox[1][1] = iroom_info[i-1]->bbox[1][1];
        }
      }
    }
    if ((i < sizeof(iroom_info)) && arrayp(milestone_coords[i+1])) {
      iroom_info[i]->bbox = bbox(milestone_coords[i], milestone_coords[i+1]);
      if (milestone_coords[i][0] < milestone_coords[i+1][0]) {
        iroom_info[i]->sgn_n = 1;
      } else if (milestone_coords[i][0] > milestone_coords[i+1][0]) {
        iroom_info[i]->sgn_n = -1;
      } else {
        iroom_info[i]->sgn_n = 0;
      }
      if (milestone_coords[i][1] < milestone_coords[i+1][1]) {
        iroom_info[i]->sgn_e = 1;
      } else if (milestone_coords[i][1] > milestone_coords[i+1][1]) {
        iroom_info[i]->sgn_e = -1;
      } else {
        iroom_info[i]->sgn_e = 0;
      }
      // Update overall bounding box (do I need to add in room size?)
      if (!arrayp(overall_bbox)) {
        overall_bbox = copy(iroom_info[i]->bbox);
      } else {
        if (iroom_info[i]->bbox[0][0] < overall_bbox[0][0]) {
          overall_bbox[0][0] = iroom_info[i]->bbox[0][0];
        }
        if (iroom_info[i]->bbox[0][1] < overall_bbox[0][1]) {
          overall_bbox[0][1] = iroom_info[i]->bbox[0][1];
        }
        if (iroom_info[i]->bbox[1][0] > overall_bbox[1][0]) {
          overall_bbox[1][0] = iroom_info[i]->bbox[1][0];
        }
        if (iroom_info[i]->bbox[1][1] > overall_bbox[1][1]) {
          overall_bbox[1][1] = iroom_info[i]->bbox[1][1];
        }
      }
    }
    //printf("Setting room %d to %O\n", i, c);
  }
} /* set_milestone_coords() */

/**
 * @ignore
 * This method returns the array of coordinates for the milestones.
 */
mixed query_milestone_coords() { return milestone_coords; }

/**
 * This method is used to set the filenames of the rooms to clone for
 * the irooms.  Each segment of the path (ie, between two milestones)
 * can use a different base room.  Each base room should inherit one
 * of the iroom inheritables.
 * @param s array of filenames of irooms
 * @see iroom
 */
void set_bases(string *s) {
  int i;

  if (sizeof(s) != sizeof(iroom_info)) {
    error("Wrong number of bases (" + sizeof(s) + ") for given milestones (" +
          sizeof(milestones) + ").\n");
    return;
  }
  for (i = 0; i < sizeof(s); i++) {
    iroom_info[i]->base = s[i];
  }
} /* set_bases() */

/**

 * This method is used to set the sizes of the irooms.  Each segment
 * of the path (ie, between two milestones) can use a different size.
 * However, the distance between milestones must be an integer
 * multiple of the size (in each dimension).  The number of irooms in
 * each segment is calculated automatically.
 * @param s array of sizes of irooms (n x 1 or n x 3)
 * @see iroom
 */
void set_sizes(mixed *s) {
  int i;

  if (sizeof(s) != sizeof(iroom_info)) {
    error("Wrong number of sizes (" + sizeof(s) + ") for given milestones (" +
          sizeof(milestones) + ").\n");
    return;
  }
  for (i = 0; i < sizeof(s); i++) {
    iroom_info[i]->size = allocate(3);
    if (intp(s[i])) {
      iroom_info[i]->size[0] = s[i];
      iroom_info[i]->size[1] = s[i];
      iroom_info[i]->size[2] = s[i];
    } else if (arrayp(s[i])) {
      iroom_info[i]->size[0] = s[i][0];
      iroom_info[i]->size[1] = s[i][1];
      iroom_info[i]->size[2] = s[i][2];
    } else {
      error("Illegal room size given (" + i + ").\n");
      continue;
    }
  }
} /* set_sizes() */

/**
 * @ignore
 */
class IROOM_INFO *query_iroom_info() { return iroom_info; }

/**
 * This method determines if iroom j (between milestones i & i+1) is
 * just before a milestone when going in direction dir.  If dir == 0,
 * the direction is towards milestone i (ie, to the "left"); if
 * dir == 1, the direction is towards milestone i+1 (ie, to the "right").
 * @param i milestone "to the left" of the iroom
 * @param j iroom index
 * @param dir direction
 * @return true if the iroom is just before a milestone in direction dir
 * @see query_irooms_inv()
 */
int query_endpoint(int i, int j, int dir) {
  // See query_irooms_inv() for comments on dir
  if ((dir && (j == sizeof(irooms[i])-1)) || (!dir && (j == 0))) {
    return 1;
  }
  return 0;
}

/**
 * This method returns all of the objects in the rooms between this
 * room and the next milestone (non-inclusive).  This is used when the
 * player uses the exit directly to the next milestone, to inform the
 * player of what [s]he passed along the way.<p>
 * 'dir' is a bit of a kludge.  If it is 1, the movement is to milestone
 * i+1; if it is 0, the movement is to milestone i, <i>unless</i> j == -1,
 * in which case the movement is to milestone i-1 (this latter case is
 * only used when the current room is a milestone).
 * @param i milestone "to the left" of the current iroom
 * @param j iroom index (or -1 if a milestone)
 * @param dir direction
 * @return an array of the objects between this room and the next milestone
 */
object *query_irooms_inv(int i, int j, int dir) {
  // dir==1 -> moving towards milestone i+1
  // dir==0 ->
  //     if j == -1 -> moving towards milestone i-1 (only used by milestones)
  //     else       -> moving towards milestone i
  int k;
  object *invs;

  invs = ({ });
  if (dir) {
    if (!arrayp(irooms[i])) {
      return invs;
    }
    for (k = j+1; k < sizeof(irooms[i]); k++) {
      //tell_creator("jeremy", "Checking irooms[%d][%d]\n", i, k);
      if (objectp(irooms[i][k])) {
        invs += all_inventory(irooms[i][k]);
      }
    }
  } else {
    if (j == -1) {
      if (closed) {
        // Wraparound for closed path
        i = sizeof(milestones) - 2;
      } else {
        i--;
      }
      if (!arrayp(irooms[i])) {
        return invs;
      } else {
        j = sizeof(irooms[i]);
      }
    } else if (!arrayp(irooms[i])) {
      return invs;
    }
    for (k = j-1; k >= 0; k--) {
      //tell_creator("jeremy", "Checking irooms[%d][%d]\n", i, k);
      if (objectp(irooms[i][k])) {
        invs += all_inventory(irooms[i][k]);
      }
    }
  }
  return invs;
} /* query_irooms_inv() */

/**
 * This method returns the coordinates of a rectangle that bounds the
 * given points.  The parameters are each arrays of three integers.  The
 * returned value is a 2-element array, each element being an array of
 * three integers.  The first element is the minimum values (ie, the
 * lower-southwestern corner) and the second is the maximum values (the
 * upper-northeastern corner).
 * @param coord_a the coordinates of one point
 * @param coord_b the coordinates of the other point
 * @return the bounding box
 */
mixed *bbox(int *coord_a, int *coord_b) {
  // Finds rectangle that bounds the given points.  Returns a 2-element
  // array of coordinates (min, max);
  int c;
  mixed *bbox;

  bbox = allocate(2);
  bbox[0] = allocate(3);
  bbox[1] = allocate(3);
  for (c = 0; c < 3; c++) {
    if (coord_a[c] < coord_b[c]) {
      bbox[0][c] = coord_a[c];
      bbox[1][c] = coord_b[c];
    } else {
      bbox[0][c] = coord_b[c];
      bbox[1][c] = coord_a[c];
    }
  }
  return bbox;
} /* bbox() */

/**
 * This method returns 1 if the given coordinates lie within the given
 * bounding box.  coord_a is an array of three integers (north,
 * east, up).  Currently, the "up" value is not checked.  bbox is a
 * two-element array of coordinates.
 * @param coord_a the coordinates of the point to check
 * @param bbox the bounding box to check the point against
 * @return true if the point lies within the bounding box
 */
int in_bbox(int *coord_a, mixed bbox) {
  // Note: this does not check the height (3rd coordinate)
  if ((coord_a[0] < bbox[0][0]) || (coord_a[0] > bbox[1][0])
  || (coord_a[1] < bbox[0][1]) || (coord_a[1] > bbox[1][1]))
    return 0;
  return 1;
} /* in_bbox() */

/**
 * This method calculates the number of irooms between milestone i and
 * milestone i+1.
 * @param i "left-hand" milestone
 * @return the number of irooms between the milestones
 */
int number_of_irooms(int i) {
  int n;
  mixed bbox;
  class IROOM_INFO ir;

  ir = iroom_info[i];
  bbox = ir->bbox;
  if (ir->sgn_n == 0) {
    n = (bbox[1][1] - bbox[0][1])/(ir->size[1]*2);
  } else if (ir->sgn_e == 0) {
    n = (bbox[1][0] - bbox[0][0])/(ir->size[0]*2);
  } else {
    // Diagonal
    n = (bbox[1][1] - bbox[0][1])/(ir->size[1]*2);
    if (n != (bbox[1][0] - bbox[0][0])/(ir->size[0]*2)) {
      error("Interroom sizes don't match diagonal.\n");
      return 0;
    }
  }
  return n-1;
} /* number_of_irooms() */

/**
 * This method clones an iroom at the requested location, and sets up
 * the pertinent details.
 * @param i milestone "to the left" of the iroom
 * @param idx index of this iroom within its segment
 * @param icoord the coordinates of the new iroom
 * @return the cloned iroom
 */
object clone_iroom(int i, int idx, int *icoord) {
  object iroom;

  iroom = clone_object(iroom_info[i]->base);
  // Set up new room
  iroom->set_room_size(iroom_info[i]->size);
  iroom->set_co_ord(icoord);
  iroom->set_milestone_index( ({ i, idx }) );
  iroom->add_exit(iroom_info[i]->exits[0], milestones[i], "road");
  iroom->add_exit(iroom_info[i]->exits[1], milestones[i+1], "road");
  iroom->add_milestone_dir(iroom_info[i]->exits[0]);
  iroom->add_milestone_dir(iroom_info[i]->exits[1]);
  // This adds the topo exits that were set up with add_topo_exits();
  // this way, everything else is all set up.
  iroom->add_topo_exits_int();
  return iroom;
} /* clone_iroom() */

/**
 * This method is called to set up a milestone.  It sets up the coordinates
 * and some of the exit information.
 * @param ms milestone to be set up
 */
void setup_milestone(object ms) {
  int *coord, i, j, idir;
  string *dir;

  if (undefinedp(i = milestone_idx[file_name(ms)])) {
    error("This room (" + file_name(ms) + ") is not a milestone.\n");
    return;
  }
  ms->set_milestone_index(file_name(this_object()), i);
  coord = ms->query_co_ord();
  if (arrayp(coord)) {
    set_milestone_coords(i, coord);
  } else {
    if (arrayp(milestone_coords[i])) {
      coord = milestone_coords[i];
    } else {
      coord = MAP->static_query_co_ord(file_name(ms));
      set_milestone_coords(i, coord);
    }
    ms->set_co_ord(coord);
  }
  if (closed && (i == 0)) {
    milestone_coords[<1] = milestone_coords[0];
  }
  dir = ms->query_dest_dir();
  if ((i > 0) || closed) {
    // Left-hand room exit
    if (closed && i == 0) {
      // Wraparound for closed path
      j = sizeof(milestones) - 2;
    } else {
      j = i - 1;
    }
    if ((idir = member_array(milestones[j], dir)) < 0) {
      // This is no longer an error, but we still need an entry.
      //error("No exit to " + milestones[j] + " from " + milestones[i] +
      //      ".\n");
      ms->add_milestone_dir(file_name(this_object()), 0);
    } else {
      iroom_info[j]->exits[0] = dir[idir-1];
      ms->add_milestone_dir(file_name(this_object()), dir[idir-1]);
    }
  } else {
    // Need a dummy entry
    ms->add_milestone_dir(file_name(this_object()), 0);
  }
  if (i < sizeof(irooms)) {
    // Right-hand room exit
    j = i + 1;
    if ((idir = member_array(milestones[j], dir)) < 0) {
      // This is no longer an error, but we still need an entry.
      //error("No exit to " + milestones[j] + " from " + milestones[i] +
      //      ".\n");
      ms->add_milestone_dir(file_name(this_object()), 0);
    } else {
      iroom_info[i]->exits[1] = dir[idir-1];
      ms->add_milestone_dir(file_name(this_object()), dir[idir-1]);
    }
  }
} /* setup_milestone() */

/**
 * This method finds the room along the path that contains the given point.
 * Obviously, things will go wrong if the rooms somehow overlap.
 * @param coord_a the coordinates of the point
 * @param hint_idx indicates that only this index should be checked
 * (optional)
 * @return the room that contains coord_a, or 0 if no matches
 */
varargs object find_room_at_coord(int *coord_a, int hint_idx) {
  int i, idx, in_bbox;
  int start_i, stop_i;
  int *icoord = ({ 0, 0, 0 });
  mixed bbox;
  float slope;
  class IROOM_INFO ir;

  if (dbg_lvl > 0) {
    tell_creator(debugger, "%s::find_room_at_coord()\n"
                 "  coord_a: %O\n"
                 "  hint_idx: %s\n",
                 file_name(this_object()), coord_a,
                 undefinedp(hint_idx)?"NULL":hint_idx+"");
  }
  // The only way I can think to do this is check every pair of
  // adjacent milestones; I originally planned a binary-search type of
  // check, but I think I'd still need to scan all the milestones to find
  // the bounding boxes.
  // First check that we're in the neighborhood
  if (!in_bbox(coord_a, overall_bbox)) {
    return 0;
  }
  in_bbox = 0;
  // See if the choice has been narrowed down for us
  if (undefinedp(hint_idx)) {
    start_i = 0;
    stop_i = sizeof(iroom_info);
  } else {
    start_i = hint_idx;
    stop_i = hint_idx + 1;
  }
  for (i = start_i; i < stop_i; i++) {
    ir = iroom_info[i];
    bbox = ir->bbox;
    if (dbg_lvl > 1) {
      tell_creator(debugger, "%s::find_room_at_coord()\n"
                   "  Checking bbox(%d,%d): %O\n",
                   file_name(this_object()), i, i+1, bbox);
    }
    if (in_bbox(coord_a, bbox)) {
      if (dbg_lvl > 1) {
        tell_creator(debugger, "%s::find_room_at_coord()\n"
                     "  In bbox(%d,%d)...\n",
                     file_name(this_object()), i, i+1);
      }
      // Check that the coordinates lie on the line
      if (ir->sgn_e == 0) {
        // N/S (vertical) line
        if (abs(coord_a[1]-milestone_coords[i][1]) < ir->size[1]) {
          if (dbg_lvl > 2) {
            tell_creator(debugger, "%s::find_room_at_coord()\n"
                         "  On vline between (%d,%d)...\n",
                         file_name(this_object()), i, i+1);
          }
          break;
        }
      } else {
        slope = to_float(milestone_coords[i][0] - milestone_coords[i+1][0])
          / to_float(milestone_coords[i][1] - milestone_coords[i+1][1]);
        if (abs(slope * (coord_a[1] - milestone_coords[i+1][1]) +
            milestone_coords[i+1][0] - coord_a[0]) < ir->size[0]) {
          if (dbg_lvl > 2) {
            tell_creator(debugger, "%s::find_room_at_coord()\n"
                         "  On line between (%d,%d)...\n",
                         file_name(this_object()), i, i+1);
          }
          break;
        }
      }
    }
  }
  if (i == stop_i) {
    // Couldn't find one
    if (dbg_lvl > 2) {
      tell_creator(debugger, "%s::find_room_at_coord()\n"
                   "  Couldn't find a room...\n",
                   file_name(this_object()));
    }
    return 0;
  }
  // We now know what rooms the iroom is between...
  if (ir->sgn_n == 0) {
    // East/west road
    idx = abs(coord_a[1] - milestone_coords[i][1]
              + ir->size[1]*ir->sgn_e)/(ir->size[1]*2);
    icoord[0] = milestone_coords[i][0];
    icoord[1] = milestone_coords[i][1] + idx*ir->sgn_e*ir->size[1]*2;
  } else {
    idx = abs(coord_a[0] - milestone_coords[i][0]
              + ir->size[0]*ir->sgn_n)/(ir->size[0]*2);
    icoord[0] = milestone_coords[i][0] + idx*ir->sgn_n*ir->size[0]*2;
    if (ir->sgn_e == 0) {
      icoord[1] = milestone_coords[i][1];
    } else {
      icoord[1] = milestone_coords[i][1] + idx*ir->sgn_e*ir->size[1]*2;
    }
  }
  if (dbg_lvl > 2) {
    tell_creator(debugger, "%s::find_room_at_coord()\n"
                 "  idx: %O\n",
                 file_name(this_object()), idx);
  }
  if (idx == 0) {
    // We hit right on a milestone
    return load_object(milestones[i]);
  }
  if (!arrayp(irooms[i])) {
    irooms[i] = allocate(number_of_irooms(i));
  }
  if (idx == sizeof(irooms[i])+1) {
    // We hit on the other end
    return load_object(milestones[i+1]);
  }
  // Decrement idx to index irooms[i] (since idx==0 is the milestone)
  idx--;
  if (!objectp(irooms[i][idx])) {
    icoord[2] = (milestone_coords[i+1][2] - milestone_coords[i][2])
      * idx / sizeof(irooms[i]) + milestone_coords[i][2];
    // Clone new iroom
    irooms[i][idx] = clone_iroom(i, idx, icoord);
  }
  return irooms[i][idx];
} /* find_room_at_coord() */

/**
 * This method returns the room at the given index, or 0 if there is
 * no such room.  Generally, idx is the index of the iroom within its
 * segment; the following are for special cases:<ul>
 * <li> -1 means milestone i
 * <li> sizeof(irooms[i]) means milestone i+1
 * <li> -2 means the last iroom of this segment
 * <ul>
 * @param i milestone "to the left" of the room (but see above)
 * @param idx index of iroom (but see above)
 * @return the room at the given index
 */
object find_room_at_index(int i, int idx) {
  // A couple of kludges:
  //    idx == -1 means milestone i
  //    idx == sizeof(irooms[i]) means milestone i+1
  //    idx == -2 means the last iroom of this segment
  int *icoord = ({ 0, 0, 0 });
  class IROOM_INFO ir;

  if (dbg_lvl > 0) {
    tell_creator(debugger, "%s::find_room_at_index()\n"
                 "  i: %O, idx: %O\n",
                 file_name(this_object()), i, idx);
  }
  if (closed && (i == -1)) {
    // Assume we wrapped around on a closed path
    i = sizeof(milestones) - 2;
  }
  if ((i < 0) || (i >= sizeof(irooms))) {
    return 0;
  }
  if (idx == -1) {
    return load_object(milestones[i]);
  }
  if (!arrayp(irooms[i])) {
    irooms[i] = allocate(number_of_irooms(i));
  }
  if (idx == sizeof(irooms[i])) {
    return load_object(milestones[i+1]);
  }
  if (idx == -2) {
    idx = sizeof(irooms[i]) - 1;
  }
  if ((idx < 0) || (idx >= sizeof(irooms[i]))) {
    return 0;
  }
  if (!objectp(irooms[i][idx])) {
    ir = iroom_info[i];
    if (ir->sgn_n == 0) {
      // East/west road
      icoord[0] = milestone_coords[i][0];
      icoord[1] = milestone_coords[i][1] + (idx+1)*ir->sgn_e*ir->size[1]*2;
    } else {
      icoord[0] = milestone_coords[i][0] + (idx+1)*ir->sgn_n*ir->size[0]*2;
      if (ir->sgn_e == 0) {
        icoord[1] = milestone_coords[i][1];
      } else {
        icoord[1] = milestone_coords[i][1] + (idx+1)*ir->sgn_e*ir->size[1]*2;
      }
    }
    icoord[2] = (milestone_coords[i+1][2] - milestone_coords[i][2])
      * idx / sizeof(irooms[i]) + milestone_coords[i][2];
    // Clone new iroom
    irooms[i][idx] = clone_iroom(i, idx, icoord);
  }
  return irooms[i][idx];
} /* find_room_at_index() */

/**
 * This method determines which side(s) of bbox the given point is on,
 * setting a bit in the return value as follows:<ul>
 * <li> bit 0: south
 * <li> bit 1: north
 * <li> bit 2: west
 * <li> bit 3: east
 * <\ul>
 * This effectively divides the plane into nine regions.  If two points
 * have the same region number, then their line can't cross bbox.  An
 * easy way to check this is to bitwise-and ("&") the query_region()
 * for two points: if the result is non-zero, then the line between
 * the points can't cross bbox (note however that a result of zero doesn't
 * guarantee that they do cross bbox).
 * @param coord the coordinates of the point
 * @param bbox the bounding box
 * @return an int indicating on which side of bbox the point lies
 */
int query_region(int *coord, mixed bbox) {
  // This checks which side(s) of the box the point is on, setting
  // a different bit for each side; the idea is that if two points are
  // on the same side, they don't cross the box.
  int ret;

  if (coord[0] < bbox[0][0]) {
    ret += 1;
  } else if (coord[0] > bbox[1][0]) {
    ret += 2;
  }
  if (coord[1] < bbox[0][1]) {
    ret += 4;
  } else if (coord[1] > bbox[1][1]) {
    ret += 8;
  }
  return ret;
} /* query_region() */

/**
 * This method returns the room on the path that lies along the line
 * between coord_a and coord_b.  Note that if the line crosses more than
 * one room, the one closest to coord_a is returned.
 * @param coord_a the coordinates of the first point
 * @param coord_b the coordinates of the second poing
 * @return the room between the two points, or 0 if no such room exists
 */
object find_room_at_crossing(int *coord_a, int *coord_b) {
  int i, a, b, c, d, dist2, ret_dist2;
  float e, f, det;
  int *isect;
  object ret;
  mixed bbox, road_a, road_b;

  if (dbg_lvl > 0) {
    tell_creator(debugger, "%s::find_room_at_crossing()\n"
                 "  coord_a: %O\n"
                 "  coord_b: %O\n",
                 file_name(this_object()), coord_a, coord_b);
  }
  // First check that we're in the neighborhood
  if (query_region(coord_a, overall_bbox)
      & query_region(coord_b, overall_bbox)) {
    return 0;
  }
  for (i = 0; i < sizeof(iroom_info); i++) {
    // Check if the line crosses the bounding box
    bbox = iroom_info[i]->bbox;
    if (query_region(coord_a, iroom_info[i]->bbox)
        & query_region(coord_b, iroom_info[i]->bbox)) {
      //tell_creator("jeremy", "Boxes don't overlap (%d).\n", i);
      continue;
    }
    // Find intersection of lines.
    // I really wanted to avoid floats, but the sizes of some of the
    // coordinates made it a necessity when multiplying them together.
    road_a = allocate(3);
    road_b = allocate(3);
    if (iroom_info[i]->sgn_n > 0) {
      road_a[0] = bbox[0][0];
      road_b[0] = bbox[1][0];
    } else {
      road_a[0] = bbox[1][0];
      road_b[0] = bbox[0][0];
    }
    if (iroom_info[i]->sgn_e > 0) {
      road_a[1] = bbox[0][1];
      road_b[1] = bbox[1][1];
    } else {
      road_a[1] = bbox[1][1];
      road_b[1] = bbox[0][1];
    }
    a = coord_b[0] - coord_a[0];
    b = coord_a[1] - coord_b[1];
    c = road_b[0] - road_a[0];
    d = road_a[1] - road_b[1];
    e = to_float(a)*coord_a[1] + to_float(b)*coord_a[0];
    f = to_float(c)*road_a[1] + to_float(d)*road_a[0];
    det = a*d - b*c;
    // The compiler apparently doesn't recognize scientific notation
    if (fabs(det) < 0.0000000000001) {
      //tell_creator("jeremy", "Determinant is 0.\n");
      continue;
    }
    isect = allocate(3);
    isect[0] = to_int(floor((a*f - e*c)/det + 0.5));
    isect[1] = to_int(floor((e*d - b*f)/det + 0.5));
    if (!in_bbox(isect, bbox) || !in_bbox(isect, bbox(coord_a, coord_b))) {
      // They intersect, but not between the endpoints
      continue;
    }
    dist2 = (isect[0]-coord_a[0])*(isect[0]-coord_a[0]) +
      (isect[1]-coord_a[1])*(isect[1]-coord_a[1]);
    if (objectp(ret) && (dist2 >= ret_dist2)) {
      // This one is further away
      continue;
    }
    ret = find_room_at_coord(isect, i);
    if (dbg_lvl > 0) {
      tell_creator(debugger, "%s::find_room_at_crossing()\n"
                   "  find_room_at_coord() returned %O\n",
                   file_name(this_object()), ret);
    }
    ret_dist2 = dist2;
    // I don't think there's any way this should happen, but I'll check anyway
    if (!objectp(ret)) {
      error("Couldn't find a room where there should be one!\n");
    }
  }
  return ret;
} /* find_room_at_crossing() */

/**
 * @ignore
 */
void create() {
  string std_euid;

  std_euid = "/secure/master"->creator_file(file_name(this_object()));
  seteuid(std_euid);
  do_setup++;
  ::create();
  do_setup--;

  if ( !do_setup ) {
    this_object()->setup();
    this_object()->reset();
  }
  // Keeps us from being unloaded (one of the drawbacks of making the
  // handler a room; I wonder if that was a smart move...).
  set_keep_room_loaded(1);
} /* create() */

/**
 * @ignore
 */
void dest_me() {

  if (stringp(save_file)) {
    unguarded( (: save_object, save_file :) );
  }
  ::dest_me();
} /* dest_me() */

// These are testing convenience functions
/**
 * @ignore
 */
int goto_room_at_coord(int *coord) {
  return this_player()->move_with_look(find_room_at_coord(coord));
}

/**
 * @ignore
 */
int goto_room_at_index(int i, int j) {
  return this_player()->move_with_look(find_room_at_index(i, j));
}

/**
 * @ignore
 */
int goto_room_at_crossing(int *a, int *b) {
  return this_player()->move_with_look(find_room_at_crossing(a, b));
}

/**
 * @ignore
 * This function isn't quite for public use yet.
 */
void recalc_milestones(int idx, int callouts, object tp) {
  // This should initially be called with both parameters 0 (or unspecified);
  // It callouts itself until it gets through all the milestones.
  int *c;
  string ms;

  if (callouts > 10) {
    error(sprintf("Too many callouts in %s:recalc_iroom_info(%d, %d)\n",
          file_name(this_object()), idx, callouts));
    return;
  }
  if (!idx && !callouts && !tp) {
    // Reinitialize everything
    rm(save_file);
    this_object()->setup();
    idx = 0;
    tp = this_player();
  }
  ms = milestones[idx];
  if (!arrayp(c = ms->query_co_ord())) {
    // The call_out is necessary to give time for the coords to be set
    call_out("recalc_milestones", 0, idx, callouts+1, tp);
    tell_object(tp, sprintf("Waiting on %s (%d, %d)...\n",
                          ms, idx, callouts));
    return;
  }
  ms->setup_milestone(ms);
  tell_object(tp, sprintf("Loaded room %s at (%d, %d, %d)...\n",
                          ms, c[0], c[1], c[2]));
  idx++;
  if (idx < sizeof(milestones)) {
    // This could be a direct call, but hey, this won't be run that
    // often, and it makes the code more uniform.
    call_out("recalc_milestones", 0, idx, 0, tp);
    return;
  }
  tell_object(tp, "IRoom info successfully recalculated.\n");
} /* recalc_milestones() */

/**
 * @ignore
 */
void clear_map_handler() {
  // This is necessary if the rooms move to new coordinates, since
  // the map handler doesn't seem to update itself very often.
  // For right now, this has to be called manually.
  int i;
  string ms, directory, troom;

  for (i = 0; i < sizeof(milestones); i++) {
    ms = milestones[i];
    // These are needed for the map handler
    directory = implode( explode( ms, "/" )[ 0..<2 ], "/" );
    troom = explode( ms, "/" )[ <1 ];
    if ( troom[ <2.. ] == ".c" ) {
      troom = troom[ 0..<3];
    }
    MAP->del(directory, troom);
    printf("Deleting %s in %s from map handler.\n", directory, troom);
  }
} /* clear_map_handler() */

/**
 * @ignore
 */
int sanity_checks() {
  // This does a few sanity checks, like making sure all milestones
  // have coordinates and sizes, the interroom areas have bounding
  // boxes, etc.  It returns 1 on success.
  int i, success = 1;

  for (i = 0; i < sizeof(milestone_coords); i++) {
    if (sizeof(milestone_coords[i]) != 3) {
      printf("Milestone %d (%s) doesn't have proper coordinates: %O\n",
             i, milestones[i], milestone_coords[i]);
      success = 0;
    }
  }
  for (i = 0; i < sizeof(iroom_info); i++) {
    if (sizeof(iroom_info[i]->size) != 3) {
      printf("Interroom segment %d has improper size: %O\n",
             i, iroom_info[i]->size);
      success = 0;
    }
    if ((sizeof(iroom_info[i]->bbox) != 2) ||
        (sizeof(iroom_info[i]->bbox[0]) != 3) ||
        (sizeof(iroom_info[i]->bbox[1]) != 3)) {
      printf("Interroom segment %d has improper bounding box: %O\n",
             i, iroom_info[i]->bbox);
      success = 0;
    }
  }
  return success;
}