dw_fluffos_v1/
dw_fluffos_v1/fluffos-1.22c11/
dw_fluffos_v1/fluffos-1.22c11/ChangeLog.old/
dw_fluffos_v1/fluffos-1.22c11/Win32/
dw_fluffos_v1/fluffos-1.22c11/compat/
dw_fluffos_v1/fluffos-1.22c11/compat/simuls/
dw_fluffos_v1/fluffos-1.22c11/include/
dw_fluffos_v1/fluffos-1.22c11/mudlib/
dw_fluffos_v1/fluffos-1.22c11/testsuite/
dw_fluffos_v1/fluffos-1.22c11/testsuite/clone/
dw_fluffos_v1/fluffos-1.22c11/testsuite/command/
dw_fluffos_v1/fluffos-1.22c11/testsuite/data/
dw_fluffos_v1/fluffos-1.22c11/testsuite/etc/
dw_fluffos_v1/fluffos-1.22c11/testsuite/include/
dw_fluffos_v1/fluffos-1.22c11/testsuite/inherit/
dw_fluffos_v1/fluffos-1.22c11/testsuite/inherit/master/
dw_fluffos_v1/fluffos-1.22c11/testsuite/log/
dw_fluffos_v1/fluffos-1.22c11/testsuite/single/
dw_fluffos_v1/fluffos-1.22c11/testsuite/single/tests/compiler/
dw_fluffos_v1/fluffos-1.22c11/testsuite/single/tests/efuns/
dw_fluffos_v1/fluffos-1.22c11/testsuite/single/tests/operators/
dw_fluffos_v1/fluffos-1.22c11/testsuite/u/
dw_fluffos_v1/fluffos-1.22c11/tmp/
dw_fluffos_v1/lib/
dw_fluffos_v1/lib/binaries/cmds/
dw_fluffos_v1/lib/binaries/cmds/creator/
dw_fluffos_v1/lib/binaries/cmds/living/
dw_fluffos_v1/lib/binaries/cmds/player/
dw_fluffos_v1/lib/binaries/d/admin/obj/
dw_fluffos_v1/lib/binaries/d/liaison/
dw_fluffos_v1/lib/binaries/global/virtual/
dw_fluffos_v1/lib/binaries/global/virtual/setup_compiler/
dw_fluffos_v1/lib/binaries/obj/handlers/autodoc/
dw_fluffos_v1/lib/binaries/obj/handlers/terrain_things/
dw_fluffos_v1/lib/binaries/obj/misc/
dw_fluffos_v1/lib/binaries/obj/misc/buckets/
dw_fluffos_v1/lib/binaries/obj/monster/
dw_fluffos_v1/lib/binaries/obj/reactions/
dw_fluffos_v1/lib/binaries/obj/reagents/
dw_fluffos_v1/lib/binaries/secure/cmds/creator/
dw_fluffos_v1/lib/binaries/secure/master/
dw_fluffos_v1/lib/binaries/std/
dw_fluffos_v1/lib/binaries/std/dom/
dw_fluffos_v1/lib/binaries/std/effects/object/
dw_fluffos_v1/lib/binaries/std/guilds/
dw_fluffos_v1/lib/binaries/std/languages/
dw_fluffos_v1/lib/binaries/std/races/
dw_fluffos_v1/lib/binaries/std/room/
dw_fluffos_v1/lib/binaries/std/room/basic/
dw_fluffos_v1/lib/binaries/std/shops/
dw_fluffos_v1/lib/binaries/std/shops/inherit/
dw_fluffos_v1/lib/binaries/www/
dw_fluffos_v1/lib/cmds/guild-race/
dw_fluffos_v1/lib/cmds/guild-race/crafts/
dw_fluffos_v1/lib/cmds/guild-race/other/
dw_fluffos_v1/lib/cmds/playtester/
dw_fluffos_v1/lib/cmds/playtester/senior/
dw_fluffos_v1/lib/d/admin/
dw_fluffos_v1/lib/d/admin/log/
dw_fluffos_v1/lib/d/admin/mapper/31-10-01/mapmaker/event/
dw_fluffos_v1/lib/d/admin/meetings/
dw_fluffos_v1/lib/d/admin/obj/
dw_fluffos_v1/lib/d/admin/room/we_care/
dw_fluffos_v1/lib/d/admin/save/
dw_fluffos_v1/lib/d/dist/
dw_fluffos_v1/lib/d/dist/mtf/
dw_fluffos_v1/lib/d/dist/pumpkin/
dw_fluffos_v1/lib/d/dist/pumpkin/chars/
dw_fluffos_v1/lib/d/dist/pumpkin/desert/
dw_fluffos_v1/lib/d/dist/pumpkin/gumboot/
dw_fluffos_v1/lib/d/dist/pumpkin/hospital/
dw_fluffos_v1/lib/d/dist/pumpkin/inherit/
dw_fluffos_v1/lib/d/dist/pumpkin/map/
dw_fluffos_v1/lib/d/dist/pumpkin/plain/
dw_fluffos_v1/lib/d/dist/pumpkin/pumpkin/
dw_fluffos_v1/lib/d/dist/pumpkin/save/
dw_fluffos_v1/lib/d/dist/pumpkin/squash/
dw_fluffos_v1/lib/d/dist/pumpkin/terrain/
dw_fluffos_v1/lib/d/dist/pumpkin/woods/
dw_fluffos_v1/lib/d/dist/start/
dw_fluffos_v1/lib/d/learning/TinyTown/buildings/
dw_fluffos_v1/lib/d/learning/TinyTown/map/
dw_fluffos_v1/lib/d/learning/TinyTown/roads/
dw_fluffos_v1/lib/d/learning/add_command/
dw_fluffos_v1/lib/d/learning/arms_and_weps/
dw_fluffos_v1/lib/d/learning/chars/
dw_fluffos_v1/lib/d/learning/cutnpaste/
dw_fluffos_v1/lib/d/learning/examples/npcs/
dw_fluffos_v1/lib/d/learning/examples/player_houses/npcs/
dw_fluffos_v1/lib/d/learning/examples/terrain_map/basic/
dw_fluffos_v1/lib/d/learning/functions/
dw_fluffos_v1/lib/d/learning/handlers/
dw_fluffos_v1/lib/d/learning/help_topics/npcs/
dw_fluffos_v1/lib/d/learning/help_topics/objects/
dw_fluffos_v1/lib/d/learning/help_topics/rcs_demo/
dw_fluffos_v1/lib/d/learning/help_topics/rooms/
dw_fluffos_v1/lib/d/learning/help_topics/rooms/crowd/
dw_fluffos_v1/lib/d/learning/help_topics/rooms/situations/
dw_fluffos_v1/lib/d/learning/items/
dw_fluffos_v1/lib/d/learning/save/
dw_fluffos_v1/lib/d/liaison/
dw_fluffos_v1/lib/d/liaison/NEWBIE/doc/
dw_fluffos_v1/lib/d/liaison/NEWBIE/save/oldlog/
dw_fluffos_v1/lib/db/
dw_fluffos_v1/lib/doc/
dw_fluffos_v1/lib/doc/creator/
dw_fluffos_v1/lib/doc/creator/autodoc/include/reaction/
dw_fluffos_v1/lib/doc/creator/autodoc/include/ritual_system/
dw_fluffos_v1/lib/doc/creator/autodoc/include/talker/
dw_fluffos_v1/lib/doc/creator/autodoc/include/terrain_map/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/baggage/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/clock/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/clothing/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/cont_save/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/corpse/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/money/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/monster/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/scabbard/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/service_provider/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/state_changer/
dw_fluffos_v1/lib/doc/creator/autodoc/obj/wand/
dw_fluffos_v1/lib/doc/creator/autodoc/std/book_dir/
dw_fluffos_v1/lib/doc/creator/autodoc/std/key/
dw_fluffos_v1/lib/doc/creator/autodoc/std/learning/
dw_fluffos_v1/lib/doc/creator/autodoc/std/map/
dw_fluffos_v1/lib/doc/creator/autodoc/std/race/
dw_fluffos_v1/lib/doc/creator/autodoc/std/weapon_logic/
dw_fluffos_v1/lib/doc/creator/files/
dw_fluffos_v1/lib/doc/creator/policy/
dw_fluffos_v1/lib/doc/creator/room/
dw_fluffos_v1/lib/doc/effects/
dw_fluffos_v1/lib/doc/ideas/
dw_fluffos_v1/lib/doc/known_command/
dw_fluffos_v1/lib/doc/lpc/basic_manual/
dw_fluffos_v1/lib/doc/lpc/intermediate/
dw_fluffos_v1/lib/doc/new/add_command/
dw_fluffos_v1/lib/doc/new/handlers/
dw_fluffos_v1/lib/doc/new/living/
dw_fluffos_v1/lib/doc/new/living/race/
dw_fluffos_v1/lib/doc/new/living/spells/
dw_fluffos_v1/lib/doc/new/player/
dw_fluffos_v1/lib/doc/new/room/guild/
dw_fluffos_v1/lib/doc/new/room/outside/
dw_fluffos_v1/lib/doc/new/room/storeroom/
dw_fluffos_v1/lib/doc/object/
dw_fluffos_v1/lib/doc/playtesters/
dw_fluffos_v1/lib/doc/policy/
dw_fluffos_v1/lib/doc/weapons/
dw_fluffos_v1/lib/global/handlers/
dw_fluffos_v1/lib/global/virtual/setup_compiler/
dw_fluffos_v1/lib/include/
dw_fluffos_v1/lib/include/cmds/
dw_fluffos_v1/lib/include/effects/
dw_fluffos_v1/lib/include/npc/
dw_fluffos_v1/lib/include/shops/
dw_fluffos_v1/lib/net/daemon/chars/
dw_fluffos_v1/lib/net/inherit/
dw_fluffos_v1/lib/net/intermud3/
dw_fluffos_v1/lib/net/intermud3/services/
dw_fluffos_v1/lib/net/obj/
dw_fluffos_v1/lib/net/save/
dw_fluffos_v1/lib/net/smnmp/
dw_fluffos_v1/lib/net/snmp/
dw_fluffos_v1/lib/obj/amulets/
dw_fluffos_v1/lib/obj/b_day/
dw_fluffos_v1/lib/obj/examples/
dw_fluffos_v1/lib/obj/food/alcohol/
dw_fluffos_v1/lib/obj/food/chocolates/
dw_fluffos_v1/lib/obj/food/fruits/
dw_fluffos_v1/lib/obj/food/meat/
dw_fluffos_v1/lib/obj/food/nuts/
dw_fluffos_v1/lib/obj/food/seafood/
dw_fluffos_v1/lib/obj/food/vegetables/
dw_fluffos_v1/lib/obj/fungi/
dw_fluffos_v1/lib/obj/furnitures/artwork/
dw_fluffos_v1/lib/obj/furnitures/bathroom/
dw_fluffos_v1/lib/obj/furnitures/beds/
dw_fluffos_v1/lib/obj/furnitures/cabinets/
dw_fluffos_v1/lib/obj/furnitures/chairs/
dw_fluffos_v1/lib/obj/furnitures/chests/
dw_fluffos_v1/lib/obj/furnitures/clocks/
dw_fluffos_v1/lib/obj/furnitures/crockery/
dw_fluffos_v1/lib/obj/furnitures/cupboards/
dw_fluffos_v1/lib/obj/furnitures/cushions/
dw_fluffos_v1/lib/obj/furnitures/fake_plants/
dw_fluffos_v1/lib/obj/furnitures/lamps/
dw_fluffos_v1/lib/obj/furnitures/mirrors/
dw_fluffos_v1/lib/obj/furnitures/outdoor/
dw_fluffos_v1/lib/obj/furnitures/safes/
dw_fluffos_v1/lib/obj/furnitures/shelves/
dw_fluffos_v1/lib/obj/furnitures/sideboards/
dw_fluffos_v1/lib/obj/furnitures/sofas/
dw_fluffos_v1/lib/obj/furnitures/stoves/
dw_fluffos_v1/lib/obj/furnitures/tables/
dw_fluffos_v1/lib/obj/furnitures/wardrobes/
dw_fluffos_v1/lib/obj/handlers/
dw_fluffos_v1/lib/obj/handlers/autodoc/
dw_fluffos_v1/lib/obj/jewellery/anklets/
dw_fluffos_v1/lib/obj/jewellery/bracelets/
dw_fluffos_v1/lib/obj/jewellery/earrings/
dw_fluffos_v1/lib/obj/jewellery/misc/
dw_fluffos_v1/lib/obj/jewellery/necklaces/
dw_fluffos_v1/lib/obj/jewellery/rings/
dw_fluffos_v1/lib/obj/media/
dw_fluffos_v1/lib/obj/misc/buckets/
dw_fluffos_v1/lib/obj/misc/jars/
dw_fluffos_v1/lib/obj/misc/papers/
dw_fluffos_v1/lib/obj/misc/player_shop/
dw_fluffos_v1/lib/obj/misc/shops/
dw_fluffos_v1/lib/obj/misc/traps/
dw_fluffos_v1/lib/obj/monster/
dw_fluffos_v1/lib/obj/monster/godmother/
dw_fluffos_v1/lib/obj/monster/transport/
dw_fluffos_v1/lib/obj/plants/inherit/
dw_fluffos_v1/lib/obj/potions/
dw_fluffos_v1/lib/open/boards/
dw_fluffos_v1/lib/save/autodoc/
dw_fluffos_v1/lib/save/bank_accounts/
dw_fluffos_v1/lib/save/boards/frog/
dw_fluffos_v1/lib/save/books/bed_catalog/
dw_fluffos_v1/lib/save/creators/
dw_fluffos_v1/lib/save/mail/
dw_fluffos_v1/lib/save/mail/p/
dw_fluffos_v1/lib/save/newsrc/b/
dw_fluffos_v1/lib/save/newsrc/c/
dw_fluffos_v1/lib/save/newsrc/d/
dw_fluffos_v1/lib/save/newsrc/f/
dw_fluffos_v1/lib/save/newsrc/p/
dw_fluffos_v1/lib/save/newsrc/s/
dw_fluffos_v1/lib/save/newsrc/w/
dw_fluffos_v1/lib/save/players/c/
dw_fluffos_v1/lib/save/players/d/
dw_fluffos_v1/lib/save/players/g/
dw_fluffos_v1/lib/save/players/p/
dw_fluffos_v1/lib/save/players/s/
dw_fluffos_v1/lib/save/soul/data/
dw_fluffos_v1/lib/save/tasks/
dw_fluffos_v1/lib/save/vaults/
dw_fluffos_v1/lib/secure/cmds/lord/
dw_fluffos_v1/lib/secure/config/
dw_fluffos_v1/lib/secure/items/
dw_fluffos_v1/lib/secure/player/
dw_fluffos_v1/lib/soul/
dw_fluffos_v1/lib/soul/i/
dw_fluffos_v1/lib/soul/j/
dw_fluffos_v1/lib/soul/k/
dw_fluffos_v1/lib/soul/o/
dw_fluffos_v1/lib/soul/q/
dw_fluffos_v1/lib/soul/to_approve/
dw_fluffos_v1/lib/soul/u/
dw_fluffos_v1/lib/soul/v/
dw_fluffos_v1/lib/soul/wish_list/
dw_fluffos_v1/lib/soul/y/
dw_fluffos_v1/lib/soul/z/
dw_fluffos_v1/lib/std/creator/
dw_fluffos_v1/lib/std/effects/
dw_fluffos_v1/lib/std/effects/attached/
dw_fluffos_v1/lib/std/effects/external/
dw_fluffos_v1/lib/std/effects/fighting/
dw_fluffos_v1/lib/std/effects/other/
dw_fluffos_v1/lib/std/environ/
dw_fluffos_v1/lib/std/guilds/
dw_fluffos_v1/lib/std/hospital/
dw_fluffos_v1/lib/std/house/
dw_fluffos_v1/lib/std/house/onebedhouse/
dw_fluffos_v1/lib/std/house/onebedhut/
dw_fluffos_v1/lib/std/house/tworoomflat/
dw_fluffos_v1/lib/std/languages/
dw_fluffos_v1/lib/std/liquids/
dw_fluffos_v1/lib/std/nationality/
dw_fluffos_v1/lib/std/nationality/accents/
dw_fluffos_v1/lib/std/nationality/accents/national/
dw_fluffos_v1/lib/std/nationality/accents/regional/
dw_fluffos_v1/lib/std/npc/goals/
dw_fluffos_v1/lib/std/npc/goals/basic/
dw_fluffos_v1/lib/std/npc/goals/misc/
dw_fluffos_v1/lib/std/npc/inherit/
dw_fluffos_v1/lib/std/npc/plans/
dw_fluffos_v1/lib/std/npc/plans/basic/
dw_fluffos_v1/lib/std/outsides/
dw_fluffos_v1/lib/std/races/shadows/
dw_fluffos_v1/lib/std/room/basic/topography/
dw_fluffos_v1/lib/std/room/controller/
dw_fluffos_v1/lib/std/room/controller/topography/
dw_fluffos_v1/lib/std/room/furniture/games/
dw_fluffos_v1/lib/std/room/furniture/inherit/
dw_fluffos_v1/lib/std/room/inherit/carriage/
dw_fluffos_v1/lib/std/room/inherit/topography/
dw_fluffos_v1/lib/std/room/punishments/
dw_fluffos_v1/lib/std/room/topography/area/
dw_fluffos_v1/lib/std/room/topography/iroom/
dw_fluffos_v1/lib/std/room/topography/milestone/
dw_fluffos_v1/lib/std/shadows/
dw_fluffos_v1/lib/std/shadows/attached/
dw_fluffos_v1/lib/std/shadows/curses/
dw_fluffos_v1/lib/std/shadows/disease/
dw_fluffos_v1/lib/std/shadows/fighting/
dw_fluffos_v1/lib/std/shadows/room/
dw_fluffos_v1/lib/std/shops/controllers/
dw_fluffos_v1/lib/std/shops/objs/
dw_fluffos_v1/lib/std/shops/player_shop/
dw_fluffos_v1/lib/std/shops/player_shop/office_code/
dw_fluffos_v1/lib/std/socket/
dw_fluffos_v1/lib/www/
dw_fluffos_v1/lib/www/external/autodoc/
dw_fluffos_v1/lib/www/external/java/telnet/Documentation/
dw_fluffos_v1/lib/www/external/java/telnet/Documentation/images/
dw_fluffos_v1/lib/www/external/java/telnet/examples/
dw_fluffos_v1/lib/www/external/java/telnet/tools/
dw_fluffos_v1/lib/www/pics/
dw_fluffos_v1/lib/www/secure/creator/
dw_fluffos_v1/lib/www/secure/editors/
dw_fluffos_v1/lib/www/secure/survey_results/
/*  -*- LPC -*-  */
/*
 * $Locker:  $
 * $Id: handler_base.c,v 1.116 2003/07/14 19:44:09 pinkfish Exp $
 *
*/
/**
* @main
* Base inherit for terrain map handler.
* <p>
* When creating a new terrain map, inherit from this file to make your area
* handler.
* <p>
* When inheriting from this file, it is essential that the functions
* query_map_file() and setup_handler() be overriden to return the full
* pathname of the area map, and to set up type mappings.
* <p>
* To inherit from this file, use the following lines:
* <ul>
* <li>#include <terrain_map.h>
* <li>
* <li>inherit TERRAIN_MAP_HANDLER_BASE
* </ul>
*
* @index terrain_map_handler
* @see terrain_map_inside
* @see terrain_map_outside
* @see terrain_map_intro
* @see terrain_map_example
* @author Dek
* @started Thurs April 12 2001 Too Damn Early BST
**/

#include <weather.h>
#include <terrain_map.h>
#include <player.h>

#define   AREA_OBSTACLE    0
#define   AREA_ROOM        1
#define   AREA_ROAD        2
#define   AREA_WORLD       3
#define   RANDOM_HANDLER   "/obj/handlers/random_num"

inherit "/std/room/inherit/terrain_map/distance_str";

#define X_DIFF 1
#define Y_DIFF 0
#define XY_DIFF 2

class char_map_data {
   string char;
   string colour;
   string path;
   string room_desc;
   mixed items;
   mixed item_desc;
   mixed chats;
   int type;
   int height;
   mapping height_rooms;
   int whole_sentance;
   string adjacent_desc;
   // How many rooms to jump with each movement.
   int room_jump;
   // If this is 1 then we treat is as a road and try to follow twists.
   // If it is 0 then we go in the direction as far as we can.
   int room_jump_matching;
}

class feature_data {
   int range;
   string* descs;
   string* night_descs;
   mixed f_items;
   mixed f_item_desc;
   mixed f_item_night_desc;
   int whole_sentance;
   mixed chats;
   mixed night_chats;
   object feature_ob;
}

class sign_data {
   string long;
   string read_mess;
   string short;
   string name;
   string language;
}

class real_coords {
   int x1;
   int y1;
   int x2;
   int y2;
   int xdiff;
   int ydiff;
   int xydiff; // Diff in a diagonal.
   int zdiff;
   int in_world_map;
}


private mixed     *_room_map = 0;
private mixed     *_area_map = 0;
private mixed     *_zone_map = 0;
private int       _visible_distance;
private int       _width = 0;
private int       _height = 0;
private int       _normal_skip_size = 1;
private int       _random_desc_limit = 2;
private int       _random_chat_limit = 4;
private int       _newline_mode = 1;
private mapping   _room_zones = ([ ]);
private mapping   _feature_rooms = ([ ]);
private mapping   _char_map = ([ ]);
private mapping   _zone_types = ([ ]);
private mapping   _features = ([ ]);
private mapping   _signposts = ([ ]);
private mapping   _standard_exits = ([ ]);
private mapping   _adjacent_terrains = ([ ]);
private mapping   _random_descs = ([ ]);
private mapping   _random_chats = ([ ]);
private class real_coords _real_coords;
private mapping _key = ([ ]);
private mapping   _direcs = ([ "north":     ({ 0, 1, "south", Y_DIFF, 0 }),
                       "northeast": ({ 1, 1, "southwest", XY_DIFF, ({ "obvious", 0 }) }),
                       "east":      ({ 1, 0, "west", X_DIFF, 0 }),
                       "southeast": ({ 1, -1, "northwest", XY_DIFF, ({ "obvious", 0 }) }),
                       "south":     ({ 0, -1, "north", Y_DIFF, 0 }),
                       "southwest": ({ -1, -1, "northeast", XY_DIFF, ({ "obvious", 0 }) }),
                       "west":      ({ -1, 0, "east", X_DIFF, 0 }),
                       "northwest": ({ -1, 1, "southeast", XY_DIFF, ({ "obvious", 0 }) }) ]);
// This is the order to do the directions in.  Make sure we do the
// main cardinal ones first.
private string* _direc_order = ({
                        "north", "east", "south", "west",
                        "northeast", "southeast", "southwest", "northwest" });

private mixed    *_feature_direcs = ({ ({ "southwest", "south", "southeast" }),
                               ({ "west", "Oops", "east" }),
                               ({ "northwest", "north", "northeast" }) });

void load_area_map();
void load_zone_map();
class char_map_data query_char_map(int x, int y);
void setup_area_map();
void setup_room(object room, string base_path);
string find_adjacent_terrain(int x, int y);

// A load of accessor functions to make debugging a lot easier.
mixed   *query_room_map() { return _room_map; }
mixed   *query_area_map() { return _area_map; }
mixed   *query_zone_map() { return _zone_map; }
mapping query_zone_types() { return _zone_types; }
mapping query_feature_rooms() { return _feature_rooms; }
mapping query_features() { return _features; }
mapping query_room_zones() { return _room_zones; }
mapping query_char_maps() { return _char_map; }
mapping query_signposts() { return _signposts; }
mapping query_random_descs() { return _random_descs; }
mapping query_random_chats() { return _random_chats; }
mapping query_standard_exits() { return _standard_exits; }
mapping query_adjacent_terrains() { return _adjacent_terrains; }
class real_coords query_debug_real_coord() { return _real_coords; }
mixed* query_real_coords() { return ({ ({ _real_coords->x1, _real_coords->y1 }), ({ _real_coords->x2, _real_coords->y2 }) }); }
int     *query_dimensions() { return ({ _width, _height }); }
void    setup_handler() { }
string  query_map_file() { return ""; } // OVERRIDE to return the map file path.

// No, this makes us have a 700M driver and end up swapping like crazy!
//int     clean_up() { return 0;  }

/**
 * This method sets the real coordinates for the room without informing
 * the map handler.
 * @param x1 the start x location
 * @param y1 the start y location
 * @param x1 the end x location
 * @param y1 the end y location
 */
void set_real_coordinates_no_inform(int x1, int y1, int x2, int y2) {
   int tmp;

   if (x1 > x2) {
      tmp = x1;
      x1 = x2;
      x2 = tmp;
   }
   if (y1 > y2) {
      tmp = y1;
      y1 = y2;
      y2 = tmp;
   }
   _real_coords = new(class real_coords, x1:x1, y1:y1, x2:x2, y2:y2);
   _real_coords->xdiff = (x2 - x1) / _width;
   _real_coords->ydiff = (y2 - y1) / _height;
   // All z differentials in 15 foot lengths
   _real_coords->zdiff = 15;
   _real_coords->xydiff = to_int(sqrt(to_float(_real_coords->xdiff) * to_float(_real_coords->xdiff)+
                               to_float(_real_coords->ydiff) * to_float(_real_coords->ydiff)));
}

/**
 * @param x1 the start x location
 * @param y1 the start y location
 * @param x1 the end x location
 * @param y1 the end y location
 */
void set_real_coordinates(int x1, int y1, int x2, int y2) {
   set_real_coordinates_no_inform(x1, y1, x2, y2);
   _real_coords->in_world_map = TERRAIN_MAP_WORLD_MAP->add_terrain(base_name(this_object()),
                                      _real_coords->x1, _real_coords->y1,
                                      _real_coords->x2, _real_coords->y2);
}

/**
 * This method sets the real offsets for movement in the rooms.  This
 * is needed in rooms that do not have real co-ordinates set but still
 * require movement to generate the correct messages.
 * @param xoff how far you move in the x direction
 * @param yoff how far you move in the y direction
 * @param zoff how far you move in the z direction
 */
void set_real_offsets(int xoff, int yoff, int zoff) {
   _real_coords = new(class real_coords);
   _real_coords->xdiff = xoff;
   _real_coords->ydiff = yoff;
   _real_coords->xydiff = to_int(sqrt(to_float(_real_coords->xdiff) * to_float(_real_coords->xdiff)+
                               to_float(_real_coords->ydiff) * to_float(_real_coords->ydiff)));
}

/**
 * This method returns the room coordinates from the real coordinates.
 * @param x the x coordinate
 * @param y the y coordinate
 */
class coord query_terrain_from_real_coord(int x, int y, int z) {
   return new(class coord,
       x: (x - _real_coords->x1)  / _real_coords->xdiff,
       y: (y - _real_coords->y1) / _real_coords->ydiff,
       z: z / _real_coords->zdiff
        );
}

/**
 * This method returns the real coordinates from the terrain coordinates.
 * For simplicity we always assume the terrain sits at the water level.
 * @param x the x coordinate
 * @param y the y coordinate
 */
class coord query_real_from_terrain_coord(int x, int y, int z) {
   if (!_real_coords) {
      return new(class coord, x : 0, y : 0);
   }
   return new(class coord,
      x: _real_coords->x1 + x * _real_coords->xdiff,
      y: _real_coords->y1 + y * _real_coords->ydiff,
      z: z * _real_coords->zdiff
     );
}

/**
* @ignore
*/
int query_newline_mode() { return _newline_mode; }


/**
* This method sets the 'newline' mode for room description extras.
* If set to 1, a newline will be inserted after all feature and
* neighbouring room items. 1 is the default.
* @param mode the new mode.
*/
void set_newline_mode(int mode)

{
  _newline_mode = mode;
}

/**
 * This method sets the distance you can see in this terrain based on the
 * map.  The distance is used to determine nice little map things to show
 * the players.
 * @param distance the distance you can see
 */
void set_visible_distance(int distance) {
   _visible_distance = distance;
}

/**
 * This method returns the distance that you can see in this terrain.
 * @return the visible distance in the terrain
 */
int query_visible_distance() {
   return _visible_distance;
}

/**
* @ignore
*/
void create()

{
   setup_area_map();
   setup_handler();
}


// Loads in the maps, and if the array of rooms hasn't been allocated, do so.
/**
* @ignore
*/
void setup_area_map()

{
   int   index;

   load_area_map();
   load_zone_map();

   if (!arrayp(_room_map)) {
      _room_map = ({ });
      for (index = 0; index < _width; index++)
         _room_map += ({ allocate(_width) });
   }
}


// Install a room in the room array, and set up the descs and
// exits if need be.
/**
 * Not needed.
* @ignore
void install_room(object room)

{
   int   *room_coords = room->query_terrain_coords();
   int   x = room_coords[0];
   int   y = room_coords[1];

   if (!_room_map[y][x]) {
      _room_map[y][x] = room;
   }

   if (!room->query_installed()) {
      setup_room(room, "No base path");
   }
}
*/

/**
 * This method returns the region used when calculating the extents of the
 * feature.
 * @param name the name of the feature to find the region of
 * @return the feature region
 */
object query_feature_ob(string name) {
   return _features[name]->feature_ob;
}


// Calculate whether any of the features are visible from this room.
// If so, put in the relevant descs and add_items.
/**
* @ignore
* @return the blocking data
*/
mapping calc_features(object room)
{
   class feature_data    feature;
   string     title;
   int        *room_coords = room->query_terrain_coords();
   int        x = room_coords[0];
   int        y = room_coords[1];
   int        z = room_coords[2];
   mapping    data;
   string direc;
   int range;
   mapping block_feature;
   mixed* features;
   mixed* tmp;

   block_feature = ([ ]);
   features = ({ });
   foreach(title, feature in _features) {
      if (!room->can_view_feature(title)) {
         continue;
      }

      if (!feature || !feature->feature_ob) {
         debug_printf("Unable to find feature object for %O\n", title);
         continue;
      }

      data = feature->feature_ob->query_feature_desc_from(x, y, z);
      if (!data || !sizeof(data)) {
         continue;
      }

      if (feature->feature_ob->query_blocking()) {
         foreach (direc, range in data) {
            if (!block_feature[direc]) {
               if (block_feature[direc] > range) {
                  block_feature[direc] = range;
               } else {
                  map_delete(data, direc);
               }
            }
         }
      }

      if (sizeof(data)) {
         features += ({ ({ title, feature, data }) });
      }
   }
   foreach (tmp in features) {
      data = tmp[2];
      // Check and see if we are blocked.
      if (sizeof(block_feature)) {
         foreach (direc, range in data) {
            if (block_feature[direc] && block_feature[direc] < range) {
               map_delete(data, direc);
            }
         }
      }
      if (sizeof(data)) {
         feature = tmp[1];
         title = tmp[0];
         room->add_feature(title, data, feature->f_items, feature->whole_sentance);
         if (!_feature_rooms[title]) {
            _feature_rooms[title] = ({ });
         } else {
            _feature_rooms[title] = filter(_feature_rooms[title], (: $1 && objectp($1) :));
         }

         _feature_rooms[title] |= ({ room });
      }
   }
}


// See if this room has a signpost. If so, make it.
/**
* @ignore
*/
void add_signposts(object room)

{
   class sign_data sign;
   int     *coords = room->query_terrain_coords();
   int     x = coords[0];
   int     y = coords[1];
   string  title = sprintf("%d:%d", x, y);
   object  sign_ob;

   //tell_creator("dek", "Looking for a sign: %s.\n", title);

   sign = _signposts[title];
   if (!sign) {
      return;
   }

   sign_ob = room->add_sign(sign->long, sign->read_mess,
                  sign->short, sign->name,
                  sign->language);
   sign_ob->move(room);
}


// See what zones (if any) this room should belong to, and add them.
/**
* @ignore
*/
void setup_zones(object room)

{
   string  *zones;
   int     *coords = room->query_terrain_coords();
   int     x = coords[0];
   int     y = coords[1];
   string  type;
   string  zone;

   if (!sizeof(_zone_map)) {
      return;
   }

   type = _zone_map[y][x..x];
   zones = _zone_types[type];
   if (!zones) {
      return;
   }

   foreach (zone in zones) {
      room->add_zone(zone);
   }
}


void calc_random_descs(object room, string key)

{
   int      limit = _random_desc_limit;
   int      *indices = ({ });
   int      *coords = room->query_terrain_coords();
   int      seed = (coords[0] * 2) + coords[1];
   mapping  desc;
   mixed    *descs = _random_descs[key];
   int      index;

   if (limit > sizeof(descs)) {
      limit = sizeof(descs);
   }

   if (!limit) {
      return;
   }

   while (sizeof(indices) < limit) {
      index = RANDOM_HANDLER->random(sizeof(descs), seed++);
      if (member_array(index, indices) >= 0) {
         continue;
      }

      indices += ({ index });
   }
   for (index = 0; index < limit; index++) {
      desc = descs[indices[index]];
      room->add_random_desc(desc["desc"]);
      room->add_item(desc["items"], desc["item_desc"]);
   }
}


void calc_random_chats(object room, string key)

{
}

/**
 * This method finds the co-ordinates to use for getting to the
 * next room.  THis handles messing about to make bridges and things
 * @param x the x coordinate
 * @param y the y coordinate
 * @param z the z coordinate
 * @param direc the direction it is from
 * @param key the key is it from
 * @return the new mangled co-ordinates
 */
string query_dest_coordinate_string(int x, int y, int z, string direc, string key) {
   class char_map_data  room_setup;

   room_setup = query_char_map(x, y);

   if (room_setup->type == AREA_WORLD) {
      return room_setup->path->query_dest_coordinate_string(x, y, z, direc, key);
   }
   return 0;
}

/**
 * This method returns the ground room for a specific co-ordinate.
 * @param x the x location
 * @param y the y location
 * @return the terrain co-ordinates of the ground room
 */
int* query_ground_room_co_ords(int x, int y) {
   class char_map_data  room_setup;
   
   room_setup = query_char_map(x, y);
   return ({ x, y, room_setup->height });
}

/**
 * This method returns the location of the ground room.  It is basically
 * just the co-ordates connected onto the terrain_handler definition.
 * @param x the x location
 * @param y the y location
 * @return the string name of the ground room
 */
string query_ground_room(int x, int y) {
   int* co_ords;

   co_ords = query_ground_room_co_ords(x, y);
   return base_name(this_object()) + ":" + co_ords[0] + ":" + co_ords[1] + ":" + co_ords[2];
}

/**
 * This method returns the basic room as the specified location.  A climb
 * room type means a room that is up or down on the z axis from where we
 * are, but we are allowed to climb to.
 * @param x the x location
 * @param y the y location
 * @param z the z location
 * @param map_room check down through the air for the room for the map
 * @param base the updated base (for climbing rooms)
 * @return the basic room for the location
 */
string query_climb_base_room(int x, int y, int z, int map_room, string ref base) {
   class char_map_data  room_setup;
   string *tmp;
   
   room_setup = query_char_map(x, y);

   if (!room_setup) {
      return 0;
   }
   if (room_setup->type == AREA_WORLD) {
     // Pass off the creation to the room file.
     if(file_size(room_setup->path + ".c") == -1) {
       tmp = explode(room_setup->path, "_edge");
       if(sizeof(tmp) && file_size(tmp[0] + ".c") > 0)
         room_setup->path = tmp[0];
     }
     
     return room_setup->path->find_base_terrain_room(x, y, z, room_setup->path, map_room, ref base);
   } else if (room_setup->height_rooms) {
      // Find the new room.
      if (z > room_setup->height) {
         if (map_room == TERRAIN_MAP_ROOM_MAP) {
            return room_setup->path;
         }
         if (map_room != TERRAIN_MAP_ROOM_CLIMB) {
            // air room.
            return TERRAIN_MAP_DEFAULT_AIR_ROOM;
         }
         return 0;
      }
      if (z == room_setup->height) {
         return room_setup->path;
      }
      if (room_setup->height_rooms[z]) {
         return room_setup->height_rooms[z];
      }
      if (room_setup->height_rooms["minimum"] > z) {
         return 0;
      }
      return room_setup->height_rooms["default"];
   } else if (z == room_setup->height) {
      return room_setup->path;
   } else if (map_room && z > room_setup->height) {
      return room_setup->path;
   } else if (z > room_setup->height) {
      if (map_room != TERRAIN_MAP_ROOM_CLIMB) {
         return TERRAIN_MAP_DEFAULT_AIR_ROOM;
      }
   }
   return 0;
}

/**
 * This returns the destination room in the given directoin, initially
 * it tries for a normal exit type.  If that fails, it tries for a climbing
 * exit type.  A climbing exit type is one that moves up or down from the
 * current z height.
 * @param x the x co-ordinate
 * @param y the y co-ordinate
 * @param z the z co-ordinate
 * @param base the base path of the parent terrain handler
 */
string query_base_room(int x, int y, int z, int map_room) {
   string dest;

   dest = "bing";
   return query_climb_base_room(x, y, z, map_room, ref dest);
}

/**
 * The main nasty func for setting up a room. It adds all the necessary exits,
 * and adds to the room's extra_look & add_items for anything outside the room
 * which should be visible. It's fairly nasty. But hey - it works.
 */
void setup_room(object room, string base_path)
{
   string main_base;
   string base;
   int      *room_coords = room->query_terrain_coords();
   mixed    *coords;
   int      x;
   int      y;
   string   direc;
   class char_map_data  room_setup;
   string*  dirs;
   mapping  adjacent_descs = ([ ]);
   mapping  adjacent_sent = ([ ]);
   mapping  distant;
   string   adjacent;
   string   key;
   string   room_long = "";
   string   key_here;
   mixed    *exit_mods;
   object   new_room;
   string   outside_types = "";
   mixed    *exits;
   mapping  exit;
   string   *sentances;
   mixed   tmp;
   int z;
   int road_jump;
   class coord fluff;
   string* miss_road;
   mapping ignore_exits;
   string  walk_pre;
   string  walk_full;
   string  journey_pre;
   string  new_base_path;

   if (!arrayp(room_coords)) {
      debug_printf("Unable to find room coordinates from %O\n", file_name(room));
      return ;
   }
   room_setup = query_char_map(room_coords[0], room_coords[1]);
   key_here = room_setup->char;

   if (room_setup->room_desc) {
      adjacent_descs[room_setup->room_desc] = ({ "here" });
      adjacent_sent[room_setup->char] = 1;
      if( room_setup->items ) {
          if( stringp( room_setup->items ) &&
              stringp( room_setup->item_desc ) ) {
              room->add_item( room_setup->items, room_setup->item_desc );
          } else if( arrayp( room_setup->items ) &&
              arrayp( room_setup->item_desc ) && sizeof( room_setup->items ) ) {
              if( sizeof( room_setup->items ) !=
                  sizeof( room_setup->item_desc ) ) {
                  debug_printf( "Error, unequal arrays.  Item not set." );
              } else {
                  int i;

                  for( i = 0; i < sizeof( room_setup->items ); i++ ) {
                      room->add_item( room_setup->items[i],
                          room_setup->item_desc[i] );
                  }
              }
          }
      }

   }

   if (room_setup->room_jump_matching) {
      road_jump = room_setup->room_jump;
   }

   walk_pre = room->query_terrain_map_walk_exit();
   journey_pre = room->query_terrain_map_journey_exit();

   exits = _standard_exits[sprintf("%d:%d:%d", room_coords[0], room_coords[1], room_coords[2])];
   ignore_exits = ([ ]);
   if (exits) {
      foreach(exit in exits) {
         room->add_exit(exit["direc"], exit["dest"], exit["type"]);
         if (exit["modifiers"]) {
            room->modify_exit(exit["direc"], exit["modifiers"]);
         }
         ignore_exits[exit["direc"]] = 1;
      }
   }

   //
   // This is the start of the pre-caclulated exits for the terrain.
   // All the cardinal directions are checked for their ability to be
   // gone up and down.
   // Most checks are done on the base object (if possible) to avoid
   // loading everything in sight.
   //

   // Check the up and down exits first.
   main_base = base_name(this_object());
   if (room_setup->height_rooms ||
       room_setup->type == AREA_WORLD) {
      int i;

      x = room_coords[0];
      y = room_coords[1];
      // Ok, zip through this mapping and see if we match anything.
      // This finds if there is a room above or below us at all.
      for (i = 0; i < 2; i++) {
         z = room_coords[2];
         if (!i) {
            direc = "up";
            tmp = "down";
            z++;
         } else {
            direc = "down";
            tmp = "up";
            z--;
         }
         base = query_base_room(x, y, z, TERRAIN_MAP_ROOM_EXACT);
         if (base) {
            // There is a room!  Check and see if we can get in it or not.
            new_room = load_object(base);
            new_base_path = base;
            if (!new_room) {
               continue;
            }
            if (new_room->can_enter_from(key_here, direc, base_path) &&
                room->can_exit_to(key_here, direc, new_base_path)) {
               room->add_exit(direc, main_base + ":" + x + ":" + y + ":" + z, "path");
               exit_mods = room->get_to_same(tmp, direc, key, new_base_path);
               if (exit_mods) {
                   room->modify_exit(direc, exit_mods);
               }
            }
         }
      }
   }

   //
   // This is to check for crossing over roads/rivers on a diagonal.
   //
   miss_road = allocate(3 * 3);

   // Check the other exits.
   foreach(direc in _direc_order) {
      coords = _direcs[direc];
      x = coords[0] + room_coords[0];
      y = coords[1] + room_coords[1];
      z = room_coords[2];
      if ((x < 0) || (x >= _width) || (y < 0) || (y >= _height)) {
         // Check and see if we have an adjacent terrain or not
         base = find_adjacent_terrain(x, y);
         if (!base) {
            continue;
         }
         fluff = query_real_from_terrain_coord(x, y, z);
         fluff = base->query_terrain_from_real_coord(fluff->x, fluff->y, fluff->z);
         room_setup = base->query_char_map(fluff->x, fluff->y);
         if (room_setup && room_setup->type != AREA_OBSTACLE) {
            tmp = base->query_base_room(fluff->x, fluff->y, z, TERRAIN_MAP_ROOM_EXACT);
            if (tmp) {
               new_base_path = tmp;
               new_room = load_object(tmp);
               base += ":" + fluff->x + ":" + fluff->y + ":" + z;
            } else {
               new_room = 0;
               new_base_path = 0;
            }
         }
      } else {
         room_setup = query_char_map(x, y);
         if (room_setup && room_setup->type != AREA_OBSTACLE) {
            tmp = query_base_room(x, y, z, TERRAIN_MAP_ROOM_EXACT);
            new_base_path = tmp;
            if (tmp) {
               new_room = load_object(tmp);
               base = main_base + ":" + x + ":" + y + ":" + z;
            } else {
               new_room = 0;
            }
         }
      }
      if (!room_setup || !new_room) {
         continue;
      }

      key = room_setup->char;
      // Make sure it is cardinal.  Check to see if we can leap this
      // location or not.
      // We use the file name of the new room to check for the diagonal
      // exit policy problems.  This should solve the problems of moving
      // across maps.
      if (!coords[0] || !coords[1]) {
         miss_road[(coords[0] + 1) * 3 + coords[1] + 1] = new_base_path;
      } else {
         // Only do this if the values are not 0 too.
         if ((miss_road[(coords[0] + 1) * 3 + 1] != new_base_path ||
              miss_road[coords[1] + 4] != new_base_path) &&
             miss_road[coords[1] + 4] &&
             miss_road[(coords[0] + 1) * 3 + 1] &&
             !miss_road[coords[1] + 4]->can_move_between(key, direc,
                                    miss_road[(coords[0] + 1) * 3 + 1],
                                    miss_road[coords[1] + 4], new_base_path)) {
            // Don't allow this to happen.
            continue;
         }
      }

      switch(room_setup->type) {
         case AREA_OBSTACLE   :
            break;
         case AREA_ROAD       :
         case AREA_ROOM       :
         case AREA_WORLD      :
           if (ignore_exits[direc]) {
              break;
           }
           if (new_room->can_enter_from(key_here, direc, base_path) &&
                room->can_exit_to(key, direc, new_base_path)) {

              // Now check and see if we need to modify the exit to this
              // room at all.
              tmp = new_room->query_enter_exit_direction(key, key_here, direc, room, base_path);
              if (tmp) {
                 room->add_exit(tmp, base, "road");
                 walk_full = tmp;
              } else {
                 // If they are not doing anything, setup all the default
                 // hop stuff.
                 room->add_exit(direc, base, "road");
                 room->add_exit(walk_pre + direc, base, "road");
                 room->add_default_exit(direc, base);
                 walk_full = walk_pre + direc;
              }
              if (coords[4]) {
                 room->modify_exit(walk_full, coords[4]);
                 room->modify_exit(direc, coords[4]);
              }
              if (key_here == key ||
                  (road_jump &&
                   room_setup->room_jump_matching &&
                   room_setup->room_jump == road_jump)) {
                 exit_mods = room->get_to_same(_direcs[direc][2], direc, key, new_base_path);
                 if (exit_mods) {
                    room->modify_exit(direc, exit_mods);
                    room->modify_exit(walk_full, exit_mods);
                 }
                 if (room_setup->room_jump > 1) {
                    //room->modify_exit(direc, ({ "obvious", 0 }));
                    room->modify_exit(walk_full, ({ "obvious", 0 }));
                    room->add_variable_exit(journey_pre + direc);
                    room->add_exit(journey_pre + direc, base, "road");
                    exit_mods = room->get_to_same(walk_full, journey_pre + direc, key, new_base_path);
                    if (coords[4]) {
                       room->modify_exit(journey_pre + direc, coords[4]);
                    }
                    if (exit_mods) {
                        room->modify_exit(journey_pre + direc, exit_mods);
                    }
                 }
              } else {
                 exit_mods = room->get_to_other(_direcs[direc][2], direc, key, new_base_path);
                 if (exit_mods) {
                     room->modify_exit(walk_full, exit_mods);
                 }
                 exit_mods = new_room->
                             get_from_other(_direcs[direc][2], direc, key_here, base_path);
                 if (exit_mods) {
                     room->modify_exit(walk_full, exit_mods);
                 }
              }
              room->modify_exit(direc, ({ "obvious", 0 }));
           }
           break;
      }
      if ((key_here != key) && (member_array(key[0], outside_types) < 0)) {
         outside_types += key;
      }
      adjacent = room_setup->adjacent_desc;
      if (adjacent && strlen(adjacent) > 1) {
         if (room->can_view_adjacent_desc(room_setup->char,
                                          key_here,
                                          room_setup->path,
                                          z,
                                          direc)) {
            if (!room_setup->whole_sentance) {
               adjacent_sent[adjacent] = 1;
            }
            if (!adjacent_descs[adjacent]) {
               adjacent_descs[adjacent] = ({ });
               if( room_setup->items ) {
                   if (!adjacent_sent[room_setup->char]) {
                      adjacent_sent[room_setup->char] = 1;
                      if( stringp( room_setup->items ) &&
                          stringp( room_setup->item_desc ) ) {
                          room->add_item( room_setup->items,
                              room_setup->item_desc );
                      } else if( arrayp( room_setup->items ) &&
                          arrayp( room_setup->item_desc ) &&
                          sizeof( room_setup->items ) ) {
                          if( sizeof( room_setup->items ) !=
                              sizeof( room_setup->item_desc ) ) {
                              debug_printf( "Error, unequal arrays.  Item not "
                                  "set." );
                          } else {
                              int i;
   
                              for( i = 0; i < sizeof( room_setup->items ); i++) {
                                  room->add_item( room_setup->items[i],
                                      room_setup->item_desc[i] );
                              }
                          }
                      }
                   }
               }
            }
            adjacent_descs[adjacent] += ({ direc });
         }
      }
   }

   sentances = ({ });
   foreach(adjacent, dirs in adjacent_descs) {
      tmp = replace_string(adjacent,
                "$D", query_multiple_short(dirs, "the", 1));
      if (adjacent_sent[adjacent]) {
         if (strlen(tmp) > 1) {
            sentances += ({ tmp });
         }
      } else if (tmp != "") {
         if (query_newline_mode()) {
            room_long += tmp + "\n";
         } else {
            room_long += "  " + tmp;
         }
      }
   }

   if (sizeof(sentances)) {
      if (!query_newline_mode()) {
         room_long += "  ";
      }
      room_long += capitalize(query_multiple_short(sentances, "the", 1)) + ".";
      if (query_newline_mode()) {
         room_long += "\n";
      }
   }

   room->set_outside_types(outside_types);
   add_signposts(room);
   setup_zones(room);

   // Do the features.
   exit = calc_features(room);
   if (_real_coords && (_real_coords->x1 || _real_coords->x2)) {
      fluff = query_real_from_terrain_coord(room_coords[0], room_coords[1], 0);
      foreach (string title in TERRAIN_MAP_WORLD_MAP->query_features_at(fluff->x, fluff->y)) {
         if (!room->can_view_feature(title)) {
            continue;
         }
         distant = title->query_feature_desc_from(fluff->x, fluff->y, fluff->z);
         if (sizeof(distant)) {
            // Check and see if the distant features are blocked.
            if (sizeof(exit)) {
               foreach (direc, int range in distant) {
                  if (exit[direc] && exit[direc] < range) {
                     map_delete(distant, direc);
                  }
               }
            }
            if (sizeof(distant)) {
               room->add_distant_feature(title, distant);
            }
         }
      }
   }


   calc_random_descs(room, key_here);
   calc_random_chats(room, key_here);

   room->set_extra_long(room_long);

   room->set_installed(1);
   room->calc_exits();
}

/**
 * THis method finds a terrain adjacent to us from this location
 * in the specific direction.
 * @param x the x location
 * @param y the y location
 */
string find_adjacent_terrain(int x, int y)
{
   string index;
   class coord real;

   if (!_real_coords || !_real_coords->in_world_map) {
      return 0;
   }

   index = x + ":" + y;
   if (!undefinedp(_adjacent_terrains[index])) {
      return _adjacent_terrains[index];
   }

   real = query_real_from_terrain_coord(x, y, 0);
//printf("%O, %O) %O\n", x, y, real);
   // Ok, now see whayt we can find on the world map handler.
   _adjacent_terrains[index] = TERRAIN_MAP_WORLD_MAP->find_terrain_at(real->x, real->y);
   return _adjacent_terrains[index];
}

/**
 * This is specifically for debug.  It finds al the adjacent terrains.
 */
void find_all_adjacent_terrains() {
   find_adjacent_terrain(-1,-1);
   find_adjacent_terrain(-1,0);
   find_adjacent_terrain(-1,40);
   find_adjacent_terrain(0,40);
   find_adjacent_terrain(40,40);
   find_adjacent_terrain(40,0);
   find_adjacent_terrain(40,-1);
   find_adjacent_terrain(0,-1);
}

string* debug_adjacent_terrains() {
   return ({ find_adjacent_terrain(-1, 0),
             find_adjacent_terrain(0, -1),
             find_adjacent_terrain(0, _height),
             find_adjacent_terrain(_width, 0) });
}

/**
 * This method find the specific squence of rooms in the given direction
 * with the given room jump.  We do not worry about contents here.  Only if
 * it is a special interest marker or if there are two different ways from
 * the location of the same type.  Items in the map are set as being
 * special interest to stop a jump.
 * @param x the x co-ordinate to start from
 * @param y the y co-ordinate to start from
 * @return the list of rooms
 */
mixed* query_rooms_to(int x, int y, int z, string direc, int len,
                       string main_key, int jump_len, int road_type ) {
   class char_map_data  room_setup;
   class coord real;
   string new_direc;
   string found_direc;
   string terr;
   string bit;
   int num;
   int found;
   int old_x;
   int old_y;
   int new_x;
   int new_y;
   mixed* ret;
   mixed* data;

   ret = ({ ({ }), ({ }) });
   do {
      data = _direcs[direc];
      if (!data) {
         debug_printf("Bad direction %O\n", direc);
         continue;
      }
      old_x = x;
      old_y = y;
      x += data[0];
      y += data[1];
      if ((x < 0) || (x >= _width) || (y < 0) || (y >= _height)) {
         terr = find_adjacent_terrain(x, y);
         if (!terr) {
            return ret;
         }
         real = query_real_from_terrain_coord(x, y, 0);
         data = terr->query_more_terrains_rooms_from(real->x, real->y, real->z, direc,
                                 len - num, jump_len,
                                 main_key, road_type);
         if (sizeof(data)) {
            ret[0] += data[0];
            ret[1] += data[1];
         }
         return ret;
      }
      ret[0] += ({ direc });
      bit = sprintf("%d:%d:%d", x, y, z);
      ret[1] += ({ sprintf("%s:%s", base_name(this_object()), bit) });
      // Always stop if there is an exit forced in the room.
      if (_standard_exits[bit]) {
         break;
      }

      if (road_type) {
         // Search around for the next location.
         found = 0;
         foreach (new_direc, data in _direcs) {
            new_x = x + data[0];
            new_y = y + data[1];
            if (new_x != old_x || new_y != old_y) {
               if ((new_x < 0) || (new_x >= _width) || (new_y < 0) || (new_y >= _height)) {
                  terr = find_adjacent_terrain(new_x, new_y);
                  if (terr) {
                     real = query_real_from_terrain_coord(new_x, new_y, z);
                     real = terr->query_terrain_from_real_coord(real->x, real->y, z);
                     room_setup = terr->query_char_map(real->x, real->y);
                  } else {
                     room_setup = 0;
                  }
               } else {
                  room_setup = query_char_map(new_x, new_y);
               }
               if (room_setup && room_setup->room_jump == jump_len) {
//printf("Found! %O %O %O\n", new_direc, room_setup->room_jump, jump_len);
                  found_direc = new_direc;
                  found++;
               }
            }
         }
//printf("(%O, %O) %O %O %O\n", x, y, found, found_direc, jump_len);
         if (found == 1) {
            direc = found_direc;
         } else {
            // dead end or junction.
            break;
         }
      } else {
         room_setup = query_char_map(x + data[0], y + data[1]);
         if (!room_setup || room_setup->char != main_key) {
            break;
         }
      }
   } while (num++ < len);

   return ret;
} /* query_rooms_to() */

/**
 * This method is used by connecting terrains to find the next room in
 * sequence.
 * @param x the real x coordinate
 * @param y the real y coordinate
 * @param moves_left the number of moves left to pop along
 * @param jump the jump length we are looking for
 * @param main_key the key, this is only used for non-road jumps
 * @param road_jump is this a road jump
 */
mixed* query_more_terrains_rooms_from(int x, int y, int z, string direc,
                                       int moves_left, int jump,
                                       string main_key, int road_jump) {
   class coord bing;
   class char_map_data  room_setup;
   int found;
   string found_direc;
   string new_direc;
   mixed* data;
   int new_x;
   int new_y;

//tell_creator("pinkfish", "%O: %O %O %O %O %O\n", base_name(this_object()), x, y, direc, road_jump, main_key);
   bing = query_terrain_from_real_coord(x, y, 0);
   room_setup = query_char_map(bing->x, bing->y);
   if (road_jump) {
      // Is the jump value set correctly here?
      if (room_setup->room_jump != jump) {
         data = _direcs[direc];
         bing->x -= data[0];
         bing->y -= data[1];
         // Try and find it from here.  We move away from the edge of the
         // terrain and search from there (the room we came from).
         foreach (new_direc, data in _direcs) {
            new_x = bing->x + data[0];
            new_y = bing->y + data[1];
            room_setup = query_char_map(new_x, new_y);
            if (room_setup && room_setup->room_jump == jump) {
               found = 1;
               found_direc = new_direc;
               break;
            }
         }
         if (!found) {
            return ({ });
         }
         direc = found_direc;
      } else {
         data = _direcs[direc];
         bing->x -= data[0];
         bing->y -= data[1];
      }
      return query_rooms_to(bing->x, bing->y, bing->z, direc, moves_left,
                            main_key, jump, road_jump);
   } else {
      // Find where we are.
      if (room_setup->char == main_key) {
         data = _direcs[direc];
         bing->x -= data[0];
         bing->y -= data[1];
         return query_rooms_to(bing->x, bing->y, bing->z, direc, moves_left,
                            main_key, jump, road_jump);
      }
      return ({ });
   }
}

/**
 * This method finds the next room in the path.  It returns the next
 * room as the first element in the array and the list of the directions
 * to get there as the second element.
 * @param x the start x position
 * @param y the start y position
 * @param z the start z position
 * @param direc the direction to go from here
 * @return ({ the end location, directions... })
 */
string* find_next_room_from(int x, int y, int z, string direc) {
   class char_map_data  room_setup;
   string test;
   object ob;
   object* obs;
   mixed* stuff;
   int pos;

   //direc = direc[strlen(SKIP_EXIT_PRE)..];

   room_setup = query_char_map(x, y);
   stuff = query_rooms_to(x, y, z, direc, room_setup->room_jump,
                          room_setup->char, room_setup->room_jump,
                          room_setup->room_jump_matching);
   foreach (test in stuff[0]) {
      ob = find_object(test);
      if (ob) {
         obs = filter(all_inventory(ob), (: living($1) :));
         if (sizeof(obs) > 0) {
            return ({ test }) + stuff[0..pos];
         }
      }
      pos++;
   }
   if (sizeof(stuff[1])) {
      return ({ stuff[1][<1] }) + stuff[0];
   }
   return 0;
} /* find_next_room_from() */

// Called by the room-inherit when a room has a zone added. Lets us maintain
// a list of what rooms are in what zones, so we can do snazzy things with them.
/**
* @ignore
*/
void add_room_to_zone(object room, string zone)

{
   object *zones = _room_zones[zone];
   if (!zones)
      zones = ({ room });
   else if (member_array(room, zones) == -1)
      zones += ({ room });

   _room_zones[zone] = zones - ({ 0 });
}


/**
* This method queries the path of a room. If it takes the form:
* '/d/full/path/name.c:x:y' where x and y are integers, it clones
* a new room at the specified coordinates, sets it up fully, and returns it.
* If the room path is of the standard form, it simply returns the loaded
* room.
* @param room_path the full pathname of the room to be cloned.
* @return the cloned/loaded room, or 0 if it could not be cloned/loaded
* @see load_room_at
* @see query_room_at
*/
mixed query_room(int x, int y, int z)

{
   class char_map_data room_map = query_char_map(x, y);
   string base_path;
   object room;
   class coord real;

   base_path = query_base_room(x, y, z, TERRAIN_MAP_ROOM_EXACT);
   if (!base_path) {
      return 0;
   }

   //
   // Not in the driver mapping, so we must need to create it.
   // We assume the external stuff is doing the correct name stuff.
   //
   room = clone_object(base_path);
   if (!room) {
      debug_printf("Unable to create room from %O at %O %O %O\n", base_path, x, y, z);
   } else {
      room->create_terrain_room();
      room->set_terrain_coords(x, y, z);
      real = query_real_from_terrain_coord(x, y, z);
      room->set_co_ord(({ real->x, real->y, real->z }));
      if (classp(_real_coords)) {
         room->set_room_size(({ _real_coords->xdiff, _real_coords->ydiff, _real_coords->zdiff }));
      }
      room->set_terrain_handler(base_name(this_object()));
      room->terrain_setup();
      room->add_property("base path", base_path);
      _room_map[y][x] = room;
      setup_room(room, base_path);
      if (room_map->type == AREA_WORLD && base_path != room_map->path) {
         // Use this to do any useful special stuff (like river flows).
         room_map->path->setup_in_terrain(room, x, y, z);
      }
   }

   return room;
}


/**
* This method is used to find a room at the specified coordinates,
* if one has already been cloned.
* If a room has been cloned at those coordinates, it is returned,
* @param x the x coordinate of the room (internal coordinate system)
* @param y the y coordinate of the room (internal coordinate system)
* @return the room at those coordinates, or 0 if none has been cloned
* @see load_room_at
* @see query_room
* @see query_rooms_in_block
* @see query_rooms_in_range
*/
object query_room_at(int x, int y, int z)

{
   if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height))
      return 0;

   return find_object(sprintf("%s:%d:%d:%d", base_name(this_object()), x, y, z));
}


/**
* This method is used to find all the rooms currently cloned within a
* specified rectangular area.
* @param x the x coordinate of rectangle's bottom left corner
* @param y the y coordinate of rectangle's bottom left corner
* @param width the width of the rectangle
* @param height the height of the rectangle
* @return an array containing any rooms currently cloned within the
* specified rectangle
* @see query_room_at
* @see query_rooms_in_range
* @see query_rooms_in_zone
*/
object *query_rooms_in_block(int x, int y, int z, int width, int height)

{
   int      h, v;
   mixed    *result = ({ });
   object   room;

   for (h = x; h < (x + width); h++) {
      for (v = y; v < (y + height); v++) {
         room = query_room_at(h, v, z);
         if (room)
            result = result + ({ room });
      }
   }

   return result;
}


/**
* This method is used to find all the rooms currently cloned within a
* specified distance of the specified coordinates.
* @param x the x coordinate of the center point
* @param y the y coordinate of the center point
* @param max the maximum distance of rooms to be returned
* @param min the minimum distance of rooms to be returned. Defaults to 0
* @return an array containing any rooms currently cloned within the
* specified range
* @see query_room_at
* @see query_rooms_in_block
* @see query_rooms_in_zone
*/
varargs object *query_rooms_in_range(int x, int y, int z, int max, int min)

{
   int      h, v;
   mixed    *result = ({ });
   object   room;
   int    dist;

   for (h = x - max; h <= (x + max); h++) {
      for (v = y - max; v <= (y + max); v++) {
        dist = to_int(sqrt(pow(x - h, 2) + pow(y - v, 2)) + 0.4999);
     //   tell_creator("dek", "Coords: (%d, %d), Range: %d, Dist: %d\n", h, v, max, dist);
        if ((dist > max) || (dist < min))
           continue;
         room = query_room_at(h, v, z);
         if (room)
            result = result + ({ room });
      }
   }

   return result;
}


/**
* This method is used to find a room at the specified coordinates,
* or to clone and setup a new one if none exists so far.
* @param x the x coordinate of the room (internal coordinate system)
* @param y the y coordinate of the room (internal coordinate system)
* @return the room at those coordinates, or 0 if none can be cloned
* @see load_room_at
* @see query_room
*/
object load_room_at(int x, int y, int z)

{
   class char_map_data room_map = query_char_map(x, y);

   if (!room_map) {
      debug_printf("Unable to load room at %d %d\n", x, y);
      return 0;
   }

   if (room_map->type == AREA_OBSTACLE) {
      return 0;
   }

   return load_object(sprintf("%s:%d:%d:%d", base_name(this_object()), x, y, z));
}


// Given coordinates, get the mapping which controls what goes in the room.
/**
* @ignore
*/
class char_map_data query_char_map(int x, int y)
{
   string   type;
   class char_map_data  room_map;

   if ((x < 0) || (x >= _width) || (y < 0) || (y >= _height)) {
      return 0;
   }

   if (x >= sizeof(_area_map[y])) {
      debug_printf("The x value is out of range: %d > %d", x, sizeof(_area_map[y]));
      return 0;
   }

   type = _area_map[y][x..x];

   room_map = copy(_char_map[type]);

   if (!room_map) {
      debug_printf("There is no char type for '%s'", type);
      return 0;
   }

   room_map->char = type;
   return room_map;
}

/**
 * This method returns a grid of the specified size around the center
 * point to give creators an overview of where they are currently.
 * @param x the x co-ordinate
 * @param y the y co-ordinate
 * @param width the width of the box
 */
string query_debug_map(int x, int y, int width, int showx, int showy) {
   int i;
   int j;
   int sx;
   int sy;
   int ny;
   string ret;
   string base;
   string new_base;
   string colour;

   ret = "";
   sx = x - width / 2;
   sy = y + width / 2;
   for (j = 0; j < width; j++) {
      if (sy - j >= 0 && sy - j < _height) {
         for (i = 0; i < width; i++) {
            if (sx + i >= 0 && sx + i < _width) {
               if (sx +i == showx && sy - j == showy) {
                  ret += "%^YELLOW%^" + _area_map[sy - j][sx + i..sx + i] +
                         "%^RESET%^";
                  colour = 0;
               } else {
                  if (colour) {
                     ret += "%^RESET%^";
                     colour = 0;
                  }
                  ret += _area_map[sy - j][sx + i..sx + i];
               }
            } else {
               // Need to get the character from a nearby terrain instead.
               if (sx + i >= _width) {
                  base = find_adjacent_terrain(_width + 1, sy - j);
               } else {
                  base = find_adjacent_terrain(-1, sy - j);
               }
//printf("Base %O (%d,%d)\n", base, sx + i, sy - j);
               if (base) {
                  //ret += "-";
                  if (colour != "%^CYAN%^") {
                     ret += "%^BOLD%^%^CYAN%^";
                     colour = "%^CYAN%^";
                  }
                  if (sx + i >= _width) {
                     //ret += "-" + (sx + i - _width) + "-";
                     // Assume same size.
                     ret += base->query_debug_map_char(sx + i - _width, sy - j);
                  } else {
                     //ret += "-" + (sx + i + _width) + "-";
                     ret += base->query_debug_map_char(sx + i + _width, sy - j);
                  }
               } else {
                  ret += " ";
               }
            }
         }
         if (colour) {
            ret += "%^RESET%^";
            colour = 0;
         }
         ret += "\n";
      } else {
         // Need to get the character from a nearby terrain instead.
         base = find_adjacent_terrain(sx + i, sy - j);
         if (base) {
            if (sy - j < 0) {
               ny = sy - j + _height;
            } else {
               ny = sy - j - _height;
            }
            for (i = 0; i < width; i++) {
               if (sx + i >= 0 && sx + i < _width) {
                  if (colour != "%^MAGENTA%^") {
                     ret += "%^BOLD%^%^MAGENTA%^";
                     colour = "%^MAGENTA%^";
                  }
                  ret += base->query_debug_map_char(sx + i, ny);
               } else {
                  new_base = find_adjacent_terrain(sx + i, sy - j);
                  if (new_base) {
                     if (colour != "%^RED%^") {
                        ret += "%^BOLD%^%^RED%^";
                        colour = "%^RED%^";
                     }
                     if (sx + i >= _width) {
                        // Assume same size.
                        ret += new_base->query_debug_map_char(sx + i - _width, ny);
                     } else {
                        ret += new_base->query_debug_map_char(sx + i + _width, ny);
                     }
                  } else {
                     ret += " ";
                  }
               }
            }
            ret += "%^RESET%^\n";
            colour = 0;
         }
      }
   }
   return ret;
}

/**
 * This method returns the specific debug map character at the
 * given location.
 * @param x the x location
 * @param y the y location
 * @return the map character
 */
string query_debug_map_char(int x, int y) {
   return _area_map[y][x..x];
}

/**
 * This method returns a grid of the specified size around the center
 * point to give creators an overview of where they are currently.
 * @param x the x co-ordinate
 * @param y the y co-ordinate
 * @param width the width of the box
 */
string query_debug_map_feature(string name) {
   int x;
   int y;
   class feature_data feature;
   mixed data;
   string ret;
   int colour;
   int found;
   //string direc;
   //int distance;

   feature = _features[name];
   ret = "";
   colour = 0;
   for (y = _height - 1; y >= 0; y--) {
      for (x = 0; x < _width; x++) {
         data = feature->feature_ob->query_feature_desc_from(x, y, 0, 1);
         found = sizeof(data);
         if (found) {
            if (!colour) {
               ret += "%^YELLOW%^";
               colour = 1;
            }
         } else {
            if (colour) {
               ret += "%^RESET%^";
               colour = 0;
            }
         }
         ret += _area_map[y][x..x];
      }
      ret += "%^RESET%^\n";
      colour = 0;
   }
   return ret;
}

/**
 * This method returns a grid of the specified size around the center
 * point to give creators an overview of where they are currently.
 * @param x the x co-ordinate
 * @param y the y co-ordinate
 * @param width the width of the box
 */
string query_debug_map_feature_distant(string feature) {
   int x;
   int y;
   mixed data;
   string ret;
   int colour;
   class coord fluff;

   ret = "";
   colour = 0;
   for (y = _height - 1; y >= 0; y--) {
      for (x = 0; x < _width; x++) {
         fluff = query_real_from_terrain_coord(x, y, 0);
         data = feature->query_feature_desc_from(fluff->x, fluff->y, fluff->z);
         if (data && sizeof(data)) {
            if (!colour) {
               ret += "%^YELLOW%^";
               colour = 1;
            }
         } else {
            if (colour) {
               ret += "%^RESET%^";
               colour = 0;
            }
         }
         ret += _area_map[y][x..x];
      }
      ret += "%^RESET%^\n";
      colour = 0;
   }
   return ret;
}

/**
 * This adds the new string to the key.
 */
private void add_key(mapping key, string char, string str) {
  if(strsrch(str, "intersection") != -1)
    return;
  
  if(!key[char])
    key[char] = str;
  else if(strsrch(key[char], str) == -1 ) {
    key[char] += ", " + str;
  }
}

/**
 * This updates the map and makes the data.
 */
private int update_map(mapping data, mixed* map, mapping key, mixed *colours,
                       int x, int y, int z, int offx, int offy) {
   class char_map_data info;
   string char, colour, tmp;
   string room;
   int block;
   object ob;
   
   if (x < 0) {
      if (y < 0) {
         data["-y-x"] = 1;
      } else if (y >= _height) {
         data["+y-x"] = 1;
      } else {
         data["-x"] = 1;
      }
      return 0;
   } else if (x >= _width) {
      if (y < 0) {
         data["-y+x"] = 1;
      } else if (y >= _height) {
         data["+y+x"] = 1;
      } else {
         data["+x"] = 1;
      }
      return 0;
   } else if (y < 0) {
      data["-y"] = 1;
      return 0;
   } else if (y >= _height) {
      data["+y"] = 1;
      return 0;
   }

   info = query_char_map(x, y);
   if (!info) {
      return 1;
   }
   if (info->type == AREA_OBSTACLE) {
      map[y - offy][x - offx] = info->char;
      colours[y - offy][x - offx] = info->colour;
      if(!info->colour) {
        colours[y - offy][x - offx] = "%^BLUE%^";
      }
      if (info->room_desc == "Obstacle" && stringp(info->items)) {
        add_key(key, info->char, info->items);
      } else if (info->room_desc != "") {
        add_key(key, info->char, info->room_desc);
      } else {
         map[y - offy][x - offx] = " ";
      }
      data["block" + info->char] = 1;
      return 1;
   } else {
      char = data[info->char];
      colour = data[info->colour];
      if (!char || !colour) {
         room = query_base_room(x, y, z, TERRAIN_MAP_ROOM_MAP);
         if (room) {
            ob = load_object(room);
            if (ob) {
               char = ob->query_terrain_map_character();
               colour = ob->query_terrain_map_colour();
               block = ob->query_terrain_map_block();

               if(char && ob) {
                  tmp = ob->query_key_desc();
                  if(!tmp) {
                     tmp = ob->query_short();
                  }
                  if(!tmp) {
                     debug_printf("Room %s has no short.", base_name(ob));
                  } else {
                     add_key(key, char, tmp);
                  }
               }

               if (block || info->height > z) {
                  data["block" + info->char] = 1;
               }
            } else {
               debug_printf("Unable to find %O\n", room);
            }
         }
         if (!char) {
            char = info->char;
         }
         if(!colour) {
            colour = info->colour;
         }

         data[info->char] = char;
      }
      map[y - offy][x - offx] = char;
      colours[y - offy][x - offx] = colour;
   }
   if(char && !key[char] && !data["nokey"+info->char]) {
      data["nokey" + info->char] = 1;
      debug_printf("No key for %s", char);
   }
   return data["block" + info->char];
}

/**
 * This method fills in the line with stuff if it is needed.
 * @param map the map to fill in
 * @param sx the startx
 * @param sy the starty
 * @param width the width of the map
 */
private string line_in_map(mapping data, mixed* map, mapping key,
                           mixed *colours, int offx, int offy,
                           int sx, int sy, int ex, int ey, int z) {
   int x;
   int y;
   int dx;
   int dy;
   int incx;
   int incy;
   int balance;

   if (ex >= sx) {
      dx = ex - sx;
      incx = 1;
   } else {
      dx = sx - ex;
      incx = -1;
   }

   if (ey >= sy) {
      dy = ey - sy;
      incy = 1;
   } else {
      dy = sy - ey;
      incy = -1;
   }

   x = sx;
   y = sy;
   if (dx >= dy) {
      dy <<= 1;
      balance = dy - dx;
      dx <<= 1;
      while (x != ex) {
         if (update_map(data, map, key, colours, x, y, z, offx, offy)) {
            break;
         }
         if (balance >= 0) {
            y += incy;
            balance -= dx;
         }
         balance += dy;
         x += incx;
      }
   } else {
      dx <<= 1;
      balance = dx - dy;
      dy <<= 1;
      while (y != ey) {
         if (update_map(data, map, key, colours, x, y, z, offx, offy)) {
            break;
         }
         if (balance >= 0) {
            x += incx;
            balance -= dy;
         }
         balance += dx;
         y += incy;
      }
   }
   // Last pixel...
   update_map(data, map, key, colours, x, y, z, offx, offy);
}

/**
 * Updates the blocking structures.
 */
private void update_blocking(mapping data,
                             mixed* map, mixed* colours, int x, int y,
                             int distance) {
   int dx;
   int dy;
   int adx;
   int ady;
   int dist;
   int nx;
   int ny;
   int tx;
   int ty;
   int incx;
   int incy;
   string* template;
   string str;

   dx = (x - distance);
   dy = (y - distance);

   //str = sprintf("%d", x % 10);
   str = " ";
   if (dx < 0) {
      adx = - dx;
      incx = -1;
   } else {
      adx = dx;
      incx = 1;
   }
   if (dy < 0) {
      ady = - dy;
      incy = -1;
   } else {
      ady = dy;
      incy = 1;
   }

   if (incx != incy) {
      incx = -incx;
      incy = - incy;
   }

   if (adx > ady) {
      template = TERRAIN_MAP_WORLD_MAP->query_blocking_template(ady, adx, 0);
   } else {
      template = TERRAIN_MAP_WORLD_MAP->query_blocking_template(adx, ady, 0);
   }
   // Error.
   if (!template) {
      debug_printf("Error with the template");
      return ;
   }
   dist = distance + 1;
   for (tx = 0; tx < dist; tx++) {
      for (ty = 0; ty < dist; ty++) {
         if (template[tx][ty] == '*') {
            // Zap the structures.
            if (adx > ady) {
               nx = distance + tx * incx;
               ny = distance + ty * incy;
            } else {
               nx = distance + ty * incx;
               ny = distance + tx * incy;
            }
            if (nx < sizeof(map) && ny < sizeof(map[nx]) &&
                nx >= 0 && ny >= 0) {
               map[nx][ny] = str;
               colours[nx][ny] = 0;
            }
            if (adx == ady) {
               nx = distance + tx * incx;
               ny = distance + ty * incy;
               map[nx][ny] = str;
               colours[nx][ny] = 0;
            } else if (adx == 0) {
               nx = distance + ty * incx;
               ny = distance - tx * incy;
               map[nx][ny] = str;
            } else if (ady == 0) {
               nx = distance - tx * incx;
               ny = distance + ty * incy;
               map[nx][ny] = str;
               colours[nx][ny] = 0;
            }
         }
      }
   }
}

/**
 * This method does the updating of the map based on the template.
 * Pretty easy really, check each location in the map to see if it
 * exists and what we should do with it.
 */
mapping query_player_map_internal_template(mixed* map, mapping key,
                                  mixed *colours, int x_c, int y_c, int z_c,
                                  int distance) {
   int offx;
   int offy;
   int x;
   int y;
   mapping data;


   data = ([ ]);
   offx = x_c - distance;
   offy = y_c - distance;
   // Go from the top corner to the bottom corner.
   for (x = 0; x < sizeof(map); x++) {
      for (y = 0; y < sizeof(map[x]); y++) {
         if (map[y][x] == 1) {
            // Update it.
            if (update_map(data, map, key, colours, x + offx, y + offy, z_c, offx, offy)) {
               // Need to update the blocking.
               // Check for diagonals and blocking.
               if (x > 0) {
                  if (y > 0 && data["block" + map[y-1][x-1]]) {
                     update_blocking(data, map, colours, x, y, distance);
                     update_blocking(data, map, colours, x, y-1, distance);
                     //map[y-1][x] = "#";
                  } else if (y < sizeof(map) - 1 && data["block" + map[y+1][x-1]]) {
                     update_blocking(data, map, colours, x, y, distance);
                     update_blocking(data, map, colours, x, y + 1, distance);
                     //map[y+1][x] = "#";
                  } else if (y > 0 && data["block" + map[y-1][x]]) {
                     update_blocking(data, map, colours, x, y, distance);
                     update_blocking(data, map, colours, x - 1, y - 1, distance);
                  } else {
                     update_blocking(data, map, colours, x, y, distance);
                  }
               } else {
                  update_blocking(data, map, colours, x, y, distance);
               }
            }
         }
      }
   }
   return data;
}

/**
 * This method generates a player map using templates instead of using
 * lines from the center point.  This is more efficent than a
 * line drawing method.  THe max_size is used to
 * make sure the size of the visible area is squashed, if we want a
 * smaller partial map for other displays.  The max size must be
 * an odd number.
 */
string query_player_map_template(int x_c, int y_c, int z_c, int visibility,
             int max_size) {
   string ret, item, base, current, *keys;
   mixed *map, *colours;
   mapping data, key;
   int i, j;
   int distance;
   //class char_map_data tmp;

   distance = _visible_distance * visibility / 100;
   if (!distance) {
      distance = 1;
   }
   data = ([ ]);
   key = ([ ]);
   map = ({ });
   colours = ({ });
   // Returns a template for the given visiblity distance.
   map = TERRAIN_MAP_WORLD_MAP->query_map_template(distance);
   colours = TERRAIN_MAP_WORLD_MAP->query_map_template(distance);

   // Slice the map down to size.
   if (max_size && (max_size % 2) != 1) {
      max_size--;
   }
   if (max_size && sizeof(map) > max_size) {
      j = (max_size - 1) / 2;
      map = map[distance - j..distance + j];
      colours = colours[distance - j..distance + j];
      for (i = 0; i < sizeof(map); i++) {
         map[i] = map[i][distance - j..distance + j];
         colours[i] = colours[i][distance - j..distance + j];
      }
      // Need to mess with x_c and y_c
      x_c += (distance - j);
      y_c += (distance - j);
   }

   data = query_player_map_internal_template(map, key, colours, x_c, y_c, z_c,
                                    distance);
   if (data["-y"]) {
     base = find_adjacent_terrain(x_c, -1);
     if (base) {
         if (!load_object(base)) {
            debug_printf("Unable to load %O\n", base);
         } else {
            base->query_player_map_internal_template(map, key, colours, x_c,
                                            y_c + _height, z_c, distance);
         }
      }
   }
   if (data["+y"]) {
      base = find_adjacent_terrain(x_c, _height + 1);
      if (base) {
         if (!load_object(base)) {
            debug_printf("Unable to load %O\n", base);
         } else {
            base->query_player_map_internal_template(map, key, colours, x_c,
                                            y_c - _height, z_c, distance);
         }
      }
   }
   if (data["-x"]) {
      base = find_adjacent_terrain(-1, y_c);
      if (base) {
         if (!load_object(base)) {
            debug_printf("Unable to load %O\n", base);
         } else {
            base->query_player_map_internal_template(map, key, colours, x_c + _width,
                                            y_c, z_c, distance);
         }
      }
   }
   if (data["+x"]) {
      base = find_adjacent_terrain(_width + 1, y_c);
      if (base) {
         if (!load_object(base)) {
            debug_printf("Unable to load %O\n", base);
         } else {
            base->query_player_map_internal_template(map, key, colours, x_c - _width,
                                            y_c, z_c, distance);
         }
      }
   }
   if (data["-y+x"]) {
      base = find_adjacent_terrain(_width + 1, -1);
      if (base) {
         if (!load_object(base)) {
            debug_printf("Unable to load %O\n", base);
         } else {
            base->query_player_map_internal_template(map, key, colours, x_c - _width,
                                            y_c + _height, z_c,
                                            distance);
         }
      }
   }
   if (data["+y+x"]) {
      base = find_adjacent_terrain(_width + 1, _height + 1);
      if (base) {
         if (!load_object(base)) {
            debug_printf("Unable to load %O\n", base);
         } else {
            base->query_player_map_internal_template(map, key, colours, x_c - _width,
                                            y_c - _height, z_c,
                                            distance);
         }
      }
   }
   if (data["+y-x"]) {
      base = find_adjacent_terrain(-1, _height + 1);
      if (base) {
         if (!load_object(base)) {
            debug_printf("Unable to load %O\n", base);
         } else {
            base->query_player_map_internal_template(map, key, colours, x_c + _width,
                                            y_c - _height, z_c,
                                            distance);
         }
      }
   }
   if (data["-y-x"]) {
      base = find_adjacent_terrain(-1, -1);
      if (base) {
         if (!load_object(base)) {
            debug_printf("Unable to load %O\n", base);
         } else {
            base->query_player_map_internal_template(map, key, colours, x_c + _width,
                                            y_c + _height, z_c,
                                            distance);
         }
      }
   }
   
   map[sizeof(map) / 2][sizeof(map) / 2] = "@";

   if(!this_player()->query_property(PLAYER_PLAIN_MAPS)) {
     colours[sizeof(map) / 2][sizeof(map) / 2] = "%^YELLOW%^";
     for(i=0; i<sizeof(map); i++) {
       current = "";
       for(j=0; j<sizeof(map); j++) {
         if(map[i][j] == "\r" || !stringp(map[i][j])) {
           map[i][j] = " ";
         }
         
         if (!current || current == " ") {
           current = "";
         }
         if(!colours[i][j] || colours[i][j] == " ") {
           colours[i][j] = "";
         }
         
         if(colours[i][j] != current) {
           if (colours[i][j] == "" || !stringp(colours[i][j])) {
             if(current != "" && current != " ") {
               map[i][j] = "%^RESET%^" + map[i][j];
               current = "";
             }
           } else if (colours[i][j] != "\r") {
             map[i][j] = colours[i][j] + map[i][j];
             current = colours[i][j];
           }
         }
         //map[i][j] = "[" + map[i][j] + "]";
         if(i == x_c && j == y_c && current != "") {
           map[i][j] += current;
         }
       }
       if(current) {
         map[i][j-1] += "%^RESET%^";
       }
     }
   }

   //ret = replace(ret, "%^", "");
   if(_key && !max_size) {
     keys = ({ });
     foreach(item in keys(key)) {
       keys += map(explode(sprintf("%s %-=40s", item, key[item]), "\n"),
                   (: "     " + $1 :));
     }
   }
   
   ret = "";
   for(i = sizeof(map) - 1; i >= 0; i--) {
     ret += implode(map(map[i], (: stringp($1) ? $1 : " " :)), "");
     if(!max_size && sizeof(map)-2-i>= 0 && sizeof(map)-2-i < sizeof(keys)) {
       ret += keys[sizeof(map)-2-i];
     }
     ret += "\n";
   }
   
   return ret;
}

/**
 * This updates the player map based on the input.
 * @ignore yes
 */
mapping query_player_map_internal(mixed* map, mapping key, mixed *colours,
                                  int x_c, int y_c, int z_c, int distance) {
   int x, y, e, u, v;
   int endx, endy, offx, offy;
   mapping data;

   data = ([ ]);

   offx = x_c - distance;
   offy = y_c - distance;
   x = 0;
   y = distance;
   u = 1;
   v = 2 * distance - 1;
   e = 0;

   while (x < y) {
      // point 1.
      endx = x_c + x;
      endy = y_c + y;
      line_in_map(data, map, key, colours, offx, offy, x_c, y_c, endx, endy, z_c);
      // point 2.
      endx = x_c + y;
      endy = y_c - x;
      line_in_map(data, map, key, colours, offx, offy, x_c, y_c, endx, endy, z_c);
      // point 3.
      endx = x_c - x;
      endy = y_c - y;
      line_in_map(data, map, key, colours, offx, offy, x_c, y_c, endx, endy, z_c);
      // point 4.
      endx = x_c - y;
      endy = y_c + x;
      line_in_map(data, map, key, colours, offx, offy, x_c, y_c, endx, endy, z_c);
      x++;
      e += u;
      u += 2;
      if (v < 2 * e) {
         y--;
         e -= v;
         v -= 2;
      }
      if (x > y) {
         break;
      }
      // point 5.
      endx = x_c + y;
      endy = y_c + x;
      line_in_map(data, map, key, colours, offx, offy, x_c, y_c, endx, endy, z_c);
      // point 6.
      endx = x_c + x;
      endy = y_c - y;
      line_in_map(data, map, key, colours, offx, offy, x_c, y_c, endx, endy, z_c);
      // point 7.
      endx = x_c - y;
      endy = y_c - x;
      line_in_map(data, map, key, colours, offx, offy, x_c, y_c, endx, endy, z_c);
      // point 8.
      endx = x_c - x;
      endy = y_c + y;
      line_in_map(data, map, key, colours, offx, offy, x_c, y_c, endx, endy, z_c);
   }

   // The algorithm for a circle misses a few so we'll fill them in here.
   // The "gap" rooms always have filled-in locations on either side so
   // test for that condition and fill-in as necessary.
   for(y=0; y<sizeof(map); y++)
     for(x=1; x<sizeof(map[y])-1; x++)
       if(map[y][x] == "\r" && map[y][x-1] != "\r" && map[y][x+1] != "\r") {
         update_map(data, map, key, colours, x + offx, y + offy, z_c, offx,
                    offy);
       }

   return data;
}

/**
 * This method returns the player line of sight view of the current terrain.
 * It depends on height and other wonderful things.
 * @param x_c the x center point
 * @param y_c the y center point
 * @param z_c the z center point
 * @param visibility the visibility percentage currently
 */
string query_player_map(int x_c, int y_c, int z_c, int visibility) {
   string ret, item, base, current, *keys;
   mixed *map, *colours;
   mapping data, key, colour_key;
   int i, j;
   int distance;
   //class char_map_data tmp;

   distance = _visible_distance * visibility / 100;
   if (!distance) {
      distance = 1;
   }
   data = ([ ]);
   key = ([ ]);
   map = ({ });
   colours = ({ });
   for (i = 0; i < distance * 2 + 1; i++) {
      map += ({ allocate(distance * 2 + 1, "\r") });
      colours += ({ allocate(distance * 2 + 1, "\r") });
      
   }

   data = query_player_map_internal(map, key, colours, x_c, y_c, z_c,
                                    distance);
   if (data["-y"]) {
     base = find_adjacent_terrain(x_c, -1);
     if (base) {
         if (!load_object(base)) {
            debug_printf("Unable to load %O\n", base);
         } else {
            base->query_player_map_internal(map, key, colours, x_c,
                                            y_c + _height, z_c, distance);
         }
      }
   }
   if (data["+y"]) {
      base = find_adjacent_terrain(x_c, _height + 1);
      if (base) {
         if (!load_object(base)) {
            debug_printf("Unable to load %O\n", base);
         } else {
            base->query_player_map_internal(map, key, colours, x_c,
                                            y_c - _height, z_c, distance);
         }
      }
   }
   if (data["-x"]) {
      base = find_adjacent_terrain(-1, y_c);
      if (base) {
         if (!load_object(base)) {
            debug_printf("Unable to load %O\n", base);
         } else {
            base->query_player_map_internal(map, key, colours, x_c + _width,
                                            y_c, z_c, distance);
         }
      }
   }
   if (data["+x"]) {
      base = find_adjacent_terrain(_width + 1, y_c);
      if (base) {
         if (!load_object(base)) {
            debug_printf("Unable to load %O\n", base);
         } else {
            base->query_player_map_internal(map, key, colours, x_c - _width,
                                            y_c, z_c, distance);
         }
      }
   }
   if (data["-y+x"]) {
      base = find_adjacent_terrain(_width + 1, -1);
      if (base) {
         if (!load_object(base)) {
            debug_printf("Unable to load %O\n", base);
         } else {
            base->query_player_map_internal(map, key, colours, x_c - _width,
                                            y_c + _height, z_c,
                                            distance);
         }
      }
   }
   if (data["+y+x"]) {
      base = find_adjacent_terrain(_width + 1, _height + 1);
      if (base) {
         if (!load_object(base)) {
            debug_printf("Unable to load %O\n", base);
         } else {
            base->query_player_map_internal(map, key, colours, x_c - _width,
                                            y_c - _height, z_c,
                                            distance);
         }
      }
   }
   if (data["+y-x"]) {
      base = find_adjacent_terrain(-1, _height + 1);
      if (base) {
         if (!load_object(base)) {
            debug_printf("Unable to load %O\n", base);
         } else {
            base->query_player_map_internal(map, key, colours, x_c + _width,
                                            y_c - _height, z_c,
                                            distance);
         }
      }
   }
   if (data["-y-x"]) {
      base = find_adjacent_terrain(-1, -1);
      if (base) {
         if (!load_object(base)) {
            debug_printf("Unable to load %O\n", base);
         } else {
            base->query_player_map_internal(map, key, colours, x_c + _width,
                                            y_c + _height, z_c,
                                            distance);
         }
      }
   }
   
   y_c -= y_c - distance;
   x_c -= x_c - distance;

   if(!this_player()->query_property(PLAYER_PLAIN_MAPS)) {
     colour_key = ([ ]);
     map[y_c][x_c] = "%^YELLOW%^@%^RESET%^";

     // This is going to go through the 2d map array and put in colour and
     // reset codes as necessary.
     for(i=0; i<sizeof(map); i++) {
       current = "";
       for(j=0; j<sizeof(map); j++) {
         if(map[i][j] == "\r")
           map[i][j] = " ";
         
         if(!current || current == " ") current = "";
         if(!colours[i][j] || colours[i][j] == " ") colours[i][j] = "";

         // Make a colour coded map.
         if(key[map[i][j]])
           colour_key[colours[i][j] + map[i][j] + "%^RESET%^"] =
             key[map[i][j]]; 

         if(colours[i][j] != current) {
           if(colours[i][j] == "") {
             if(current != "" && current != " ") {
               map[i][j] = "%^RESET%^" + map[i][j];
               current = "";
             }
           } else if(colours[i][j] != "\r") {
             map[i][j] = colours[i][j] + map[i][j];
             current = colours[i][j];
           }
         }
         
         //map[i][j] = "[" + map[i][j] + "]";
         if(i == x_c && j == y_c && current != "")
           map[i][j] += current;
       }
       if(current)
         map[i][j-1] += "%^RESET%^";
     }
   } else {
     map[y_c][x_c] = "@";
     colour_key = key;
   }
   
   if(_key) {
     keys = ({ });
     foreach(item in keys(colour_key)) {
       keys += map(explode(sprintf("%s %-=40s", item, colour_key[item]), "\n"),
                           (: "     " + $1 :));
     }
   }

   ret = "";
   for(i = sizeof(map) - 1; i >= 0; i--) {
     ret += implode(map[i], "");
     if(sizeof(map)-2-i>= 0 && sizeof(map)-2-i < sizeof(keys))
       ret += keys[sizeof(map)-2-i];
     ret += "\n";
   }

   // This is normally done in the colour section.
   if(this_player()->query_property(PLAYER_PLAIN_MAPS))
     ret = replace_string(ret, "\r", " ");
   
   return ret;
}

/**
 * This is used by the terrain system to work out what is beside
 * us.
 * @param x the x position
 * @param y the y position
 */
class char_map_data query_char_map_real_coord(int x, int y)
{
   class coord our;

   our = query_terrain_from_real_coord(x, y, 0);
   return query_char_map(our->x, our->y);
}

/**
* This method is used to find all rooms currently loaded within the
* specified room zone.
* @param zone the name of the room zone
* @return an array containing all the currently cloned rooms within this zone
* @see query_rooms_in_block
* @see query_rooms_in_range
*/
object *query_rooms_in_zone(string zone)

{
   if (_room_zones[zone]) {
      _room_zones[zone] -= ({ 0 });
      return _room_zones[zone];
   }
   return ({ });
}


/**
* This method displays a message in all the currently loaded rooms
* within the specified zone(s).
* @param zones a zone name as a string, or an array of zone names
* @param message the message to be displayed
* @param mess_class the message type. Normally this need not be specified
* @see query_rooms_in_zone
* @see tell_feature
*/
varargs void tell_zones(mixed zones, string message, mixed mess_class)

{
   object  *rooms = ({ });
   string  zone;

   if (!mess_class)
      mess_class = "zone_shout";

   if (stringp(zones))
      zones = ({ zones });

   foreach (zone in zones)
      rooms += query_rooms_in_zone(zone);

   if (sizeof(rooms))
      message(mess_class, message, rooms);
}


/**
* This message displays messages in all rooms within range of a feature.
* The message can be either a string, which goes to all the rooms, or an
* array of strings, in which case they get spread across the distances (in
* which case you should supply the same number of array elements as in the
* descriptions in the original add_feature. Any occurences of $D in the
* message(s) will be replaced in each room with the direction that the
* feature lies in.
* @param title the name of the feature, as used in add_feature
* @param message the message to be displayed
* @see tell_rooms_in_zone
* @see add_feature
* @see modify_feature
* @see set_feature_chats
*/
varargs void tell_feature(string title, mixed message)

{
   int     index;
   string  mess_class = sprintf("feature:%s", title);

   if (!_feature_rooms[title]) {
      return;
   }

   _feature_rooms[title] -= ({ 0 });
   if (!sizeof(_feature_rooms[title])) {
      return;
   }

   if (stringp(message)) {
      message(mess_class, message, _feature_rooms[title]);
   }

   if (arrayp(message)) {
      for (index = 0; index < sizeof(message); index++) {
         message(sprintf("%s:%d", mess_class, index), message[index],
         _feature_rooms[title]);
      }
   }
}


// Called by rooms to get the feature description that applies to them.
// It'll do direction substitution if it's supplied with a direc to subs.
/**
* @ignore
*/
varargs string* query_distant_feature_desc(string title, mapping direcs,
                                           int visibility) {
   int       night = (WEATHER->query_tod() == "night");
   string str;

   str = title->calc_feature_desc(direcs, night, visibility);
   if (str) {
      return ({ str });
   }
   return ({ });
}


// Called by rooms to get the item desc of a feature.
// It'll do direction substitution if it's supplied with a direc to subs.
/**
* @ignore
*/
varargs string* query_feature_desc(string title, mapping direcs,
                                   int visibility) {
   class feature_data   feature;
   string*   item_desc;
   string*   dirs;
   int       night = (WEATHER->query_tod() == "night");
   string    direc;
   int       range;
   mapping   bits;
   int       index;
   string    str;
   int       new_range;

   if (!feature) {
      feature = _features[title];
   }

   if (!feature) {
      return ({ });
   }

   if( visibility == 0 ) {
      return ({ });
   }

   if (!sizeof(feature->descs)) {
      str = feature->feature_ob->calc_feature_desc(direcs, night,
                                                       visibility);
      if (str) {
         return ({ str });
      }
      return ({ });
   }

   bits = ([ ]);
   foreach (direc, range in direcs) {
      if (night) {
         item_desc = feature->night_descs;
      } else {
         item_desc = feature->descs;
      }
      if (!item_desc) {
         debug_printf("Bad night description for %O", title);
         continue;
      }
      new_range = range * 100 / visibility;
      // Make sure the modified range is less the visibility
      if (feature->range > new_range && sizeof(item_desc)) {
         index = sizeof(item_desc) * new_range / feature->range;
         str = item_desc[index];
         if (str && str != "") {
            if (!bits[str]) {
               bits[str] = ({ });
            }
            bits[str] += ({ direc });
         }
      }
   }

   item_desc = ({ });
   foreach (str, dirs in bits) {
      item_desc += ({ replace_string(str, "$D", query_multiple_short(dirs)) });
   }

   return item_desc;
}

/**
 * This method returns the item description.
 * @param title the title of the feature
 * @return the feature item description
 */
varargs string query_feature_item_desc(string title) {
   class feature_data   feature;
   mixed   item_desc;
   int       night = (WEATHER->query_tod() == "night");

   if (!feature) {
      feature = _features[title];
   }

   if (!feature) {
      return "";
   }

   if (night) {
      item_desc = feature->f_item_night_desc;
   } else {
      item_desc = feature->f_item_desc;
   }

   if (!pointerp(item_desc)) {
      return item_desc;
   }
   return item_desc[0];
}


// Called by rooms when chatting, to get feature chats.
/**
* @ignore
*/
varargs string get_a_feature_chat(string title, int range, string direc)

{
   class feature_data   feature;
   mixed     chat;
   int       night = (WEATHER->query_tod() == "night");

   if (!feature) {
      feature = _features[title];
   }

   if (!feature) {
      return "";
   }

   if (night) {
      chat = feature->night_chats;
   } else {
      chat = feature->chats;
   }

   if (!chat) {
      return "";
   }

   if (arrayp(chat)) {
      chat = chat[random(sizeof(chat))];
   }

   if (arrayp(chat)) { // ranged chat
      if ((range < 0) || (range >= sizeof(chat))) {
         return "";
      }
      chat = chat[range];
   }
   if (!stringp(chat)) {// Something's wrong.
      return "";
   }

   if (direc) {
      chat = replace(chat, ({ "$D", direc }));
   }

   return chat;
}




// Called by rooms when chatting, to get outside chats.
/**
* @ignore
*/
string get_an_outside_chat(string types)

{
   class char_map_data   item;
   string    *chats = ({ });
   int       index;
   mixed     *item_chats;

   for (index = 0; index < sizeof(types); index++) {
      item = _char_map[types[index..index]];
      if (!item) {
         continue;
      }

      item_chats = item->chats;
      if (!item_chats) {
         continue;
      }

      if (stringp(item_chats))
         chats += ({ item_chats });
      else if (arrayp(item_chats))
         chats += item_chats;
   }
   if (!sizeof(chats))
      return "";

   return chats[random(sizeof(chats))];
}


/**
* This method is used to add an 'obstacle' to an area map. An obstacle is
* effectively an area on the map which cannot be entered, and can be seen
* from adjacent locations. Any occurences of $D in the description will
* be replaced with the direction(s) in which the obstacle can be seen.
* An add_item is automatically performed to allow for consistency.
* @param type the ASCII type used on the map to denote the obstacle
* @param description the description of the obstacle, as seen from
* neighbouring rooms
* @param items a string, or array of strings giving the corresponding
* add_item name(s) (identical to the 'shorts' parameter in add_item)
* @param item_desc a string, or array of strings giving the corresponding
* @param items add_item name(s) (identical to the 'shorts' parameter in add_item)
* @param item_desc a string, or array of strings giving the corresponding item desc
* @param room_desc allows a description for the obstacle in the key
* @example
*   add_obstacle("*", "Thick green bushes block your way to the $D.",
                      ({ "bush", "bushes" }), "Thick and green.", "Thick bushes");
* @see add_room_type
* @see add_road_type
* @see set_external_chats
* @see add_item
*/
varargs void add_obstacle_type(string type, string description, mixed items, mixed item_desc, string room_desc)

{
   if (!type) {
      throw("Bad type");
   }
   if (!items) {
      items = ({ });
      item_desc = ({ });
   }
   if (!room_desc) {
      room_desc = "Obstacle";
   }
   _char_map[type] = new(class char_map_data,
                         type: AREA_OBSTACLE, adjacent_desc:description,
                         room_desc : room_desc,
                         items:items, item_desc:item_desc);
}


/**
* This method is used to add a standard skip room type to the terrain map.
* Mapped to an ASCII character as used in the ASCII room map.  A skipped
* room is one in which every nth room in the terrain will be skipped
* unless it has something interesting in it.  The height is used to
* do specific sorts of checks, like if a specific direction is passable or not.
* @param type the ASCII type used on the map to denote the room
* @param base_path the full pathname of the source file for the room
* @param size the number of rooms in each clump to skip over
* @param road_skip if this is 1 then the skip type is a 'road skip', this
* will assume the path is one wide and try to follow around curves
* @param height the height of the room
* @param height_rooms is a mapping containing what rooms to use at different height levels
* @example
*    add_room_type(".", ROOMS + "field");
* @see add_room_type
* @see add_road_type
* @see add_obstacle_type
*/
void add_special_type(string type, string base_path, int size, int road_skip, int height,
                      mapping height_rooms)

{
   _char_map[type] = new(class char_map_data,
                         type: AREA_ROOM, path:base_path,
                         room_jump : size,
                         room_jump_matching : road_skip,
                         height : height,
                         height_rooms : height_rooms);
}

/**
* This method is used to add a standard room type to the terrain map,
* mapped to an ASCII character as used in the ASCII room map.
* @param type the ASCII type used on the map to denote the room
* @param base_path the full pathname of the source file for the room
* @example
*    add_room_type(".", ROOMS + "field");
* @see add_room_type
* @see add_road_type
* @see add_obstacle_type
*/
void add_room_type(string type, string base_path)

{
   _char_map[type] = new(class char_map_data,
                         type: AREA_ROOM, path:base_path);
}


/**
* This method is used to add a standard road type to the terrain map,
* mapped to an ASCII character as used in the ASCII room map.
* A road is a terrain map room which can be seen from adjacent locations.
* The handler uses 'room_desc' to add an entry to the room description,
* describing where the road goes. 'description' is added to the
* descriptions of adjacent locations, to enable the road to be seen.
* In 'room_desc' and 'description', $D will be expanded to the relevant
* directions.
* @param type the ASCII type used on the map to denote the room
* @param base_path the full pathname of the source file for the room
* @param room_desc the 'continuation' description, which added to the room description to show where the road leads
* @param description the 'adjacent' description, which is seen from adjacent locations.
* @param items add_item name(s) (identical to the 'shorts' parameter in add_item)
* @param item_desc a string, or array of strings giving the corresponding item desc
* @example
*    add_road_type("+", ROOMS + "path", "The dusty path leads $D.",
*                  "There is a dusty path to the $D.",
*                  "path", "Hey! Look! A path!");
* @see add_room_type
* @see add_road_type
* @see set_external_chats
* @see add_item
*/
varargs void add_road_type(string type, string base_path, string room_desc,
              string description, mixed items, mixed item_desc)

{
   _char_map[type] = new(class char_map_data,
                        type: AREA_ROAD, adjacent_desc:description,
                        items:items, item_desc:item_desc,
                        room_desc:room_desc, path:base_path);
}

/**
* This method is used to add a world map based file into the terrain.
* It handles all the options like a special road type, but queries all the
* details off the base room instead.
* @param type the ASCII type used on the map to denote the room
* @param base_path the full pathname of the source file for the room
* @example
*    add_world_room_type("+", ROOMS + "path", "The dusty path leads $D.",
*                  "There is a dusty path to the $D.",
*                  "path", "Hey! Look! A path!");
* @see add_room_type
* @see add_road_type
* @see set_external_chats
* @see add_item
*/
varargs void add_world_room_type(string type, string base_path) {
   mapping bits;
   string *tmp;

   // If the world type doesn't exist see if we can find a more
   // generic version instead.
   if(file_size(base_path + ".c") == -1) {
     tmp = explode(base_path, "_edge");
     if(sizeof(tmp) && file_size(tmp[0] + ".c") > 0) {
       debug_printf("%s doesn't exist, using %s",
                    base_path, tmp[0]);
       base_path = tmp[0];
     }
   }     
   
   if (file_size(base_path + ".c") > 0) {
      bits = base_path->query_adjacent_items();
      if (!mapp(bits)) {
         debug_printf("Incorrect room type %O", type);
      } else {
         _char_map[type] = new(class char_map_data,
                        type: AREA_WORLD,
                        path: base_path,
                        adjacent_desc:base_path->query_adjacent_description(),
                        items:keys(bits),
                        item_desc:values(bits),
                        //room_desc:room_desc,
                         room_jump : base_path->query_room_jump_size(),
                         height : base_path->query_default_height(),
                         room_jump_matching : base_path->query_follow_road());
      }
   } else {
      debug_printf("Unable to add %O since %O doesn't exist.", type, base_path);
   }
}

/**
* This method is used to add a standard road type to the terrain map,
* mapped to an ASCII character as used in the ASCII room map.
* A road is a terrain map room which can be seen from adjacent locations.
* The handler uses 'room_desc' to add an entry to the room description,
* describing where the road goes. 'description' is added to the
* descriptions of adjacent locations, to enable the road to be seen.
* In 'room_desc' and 'description', $D will be expanded to the relevant
* directions.
* A skipped
* room is one in which every nth room in the terrain will be skipped
* unless it has something interesting in it.
* @param type the ASCII type used on the map to denote the room
* @param base_path the full pathname of the source file for the room
* @param size the number of rooms in each clump to skip over
* @param road_skip if this is 1 then the skip type is a 'road skip', this
* will assume the path is one wide and try to follow around curves
* @param height the height of the room
* @param room_desc the 'continuation' description, which added to the room description to show where the road leads
* @param description the 'adjacent' description, which is seen from adjacent locations.
* @param items add_item name(s) (identical to the 'shorts' parameter in add_item)
* @param item_desc a string, or array of strings giving the corresponding item desc
* @param height_rooms is a mapping containing what rooms to use at different height levels
* @example
*    add_road_type("+", ROOMS + "path", "The dusty path leads $D.",
*                  "There is a dusty path to the $D.",
*                  "path", "Hey! Look! A path!");
* @see add_room_type
* @see add_road_type
* @see set_external_chats
* @see add_item
*/
varargs void add_road_special_type(string type, string base_path,
              int size, int road_skip, int height,
              string room_desc,
              string description, mixed items, mixed item_desc,
              mapping height_rooms)

{
   _char_map[type] = new(class char_map_data,
                        type: AREA_ROAD, adjacent_desc:description,
                        items:items, item_desc:item_desc,
                        room_desc:room_desc, path:base_path,
                         room_jump : size,
                         height : height,
                         height_rooms : height_rooms,
                         room_jump_matching : road_skip);
}


/**
* This method is used to add a standard exit to a specified location
* in a terrain map. The chief use is to insert exits to standard rooms
* or other terrain maps.
* @param x the x coordinate of the room (internal coordinate system)
* @param y the y coordinate of the room (internal coordinate system)
* @param direc the direction of the exit (as in add_exit)
* @param dest the path of the exit's destination (as in add_exit)
* @param type the type of exit (as in add_exit)
* @param modifiers an optional array of exit modifiers (as in modify_exit)
* @example
*    add_exit_at(8, 0, "south", PEARWOOD + "stream_entrance", "road",
*                       ({ "move mess", "You wade along the stream bed.\n",
*                          "enter mess", "$N wades in from the north.",
*                          "exit mess", "$N wades off to the $T." }));
*
* @see add_room_type
* @see add_road_type
* @see add_obstacle_type
* @see add_exit
* @see modify_exit
*/
varargs void add_exit_at(int x, int y, string direc, string dest,
                         string type, mixed modifiers)

{
   string   title = sprintf("%d:%d:%d", x, y, 0);

   if (!dest)
      return;
   if (!type)
      type = "road";

   if (!_standard_exits[title])
      _standard_exits[title] = ({ });

   _standard_exits[title] += ({ ([ "direc":direc, "dest":dest,
                                   "type":type, "modifiers":modifiers ]) });
}

/**
* This method is used to add a standard exit to a specified location
* in a terrain map. The chief use is to insert exits to standard rooms
* or other terrain maps.
* @param x the x coordinate of the room (internal coordinate system)
* @param y the y coordinate of the room (internal coordinate system)
* @param direc the direction of the exit (as in add_exit)
* @param dest the path of the exit's destination (as in add_exit)
* @param type the type of exit (as in add_exit)
* @param modifiers an optional array of exit modifiers (as in modify_exit)
* @example
*    add_exit_at(8, 0, "south", PEARWOOD + "stream_entrance", "road",
*                       ({ "move mess", "You wade along the stream bed.\n",
*                          "enter mess", "$N wades in from the north.",
*                          "exit mess", "$N wades off to the $T." }));
*
* @see add_room_type
* @see add_road_type
* @see add_obstacle_type
* @see add_exit
* @see modify_exit
*/
varargs void add_z_exit_at(int x, int y, int z, string direc, string dest,
                         string type, mixed modifiers)

{
   string   title = sprintf("%d:%d:%d", x, y, z);

   if (!dest)
      return;
   if (!type)
      type = "road";

   if (!_standard_exits[title])
      _standard_exits[title] = ({ });

   _standard_exits[title] += ({ ([ "direc":direc, "dest":dest,
                                   "type":type, "modifiers":modifiers ]) });
}


/**
* This method is used to limit the number of random descs that are
* picked out for each room.
* @param new_limit the new desc limit
* @see set_random_chat_limit
* @see add_random_desc
* @see add_random_chat
*/
void set_random_desc_limit(int new_limit)

{
   _random_desc_limit = new_limit;
}


/**
* This method is used to limit the number of random chats that are
* picked out for each room.
* @param new_limit the new chat limit
* @see set_random_desc_limit
* @see add_random_desc
* @see add_random_chat
*/
void set_random_chat_limit(int new_limit)

{
   _random_chat_limit = new_limit;
}


/**
* This method is to add to the list of random descriptions that can be added to
* specific room types. A specific random sequence is used so that the randoms
* won't vary over time.
* @param types the string of ASCII character type(s) that this desc could apply to
* @param items add_item name(s) (identical to the 'shorts' parameter in add_item)
* @param item_desc a string, or array of strings giving the corresponding item desc
* @see set_random_desc_limit
* @see set_random_chat_limit
* @see add_random_chat
* @see add_item
*/
void add_random_desc(string types, string description, mixed items, mixed item_desc)

{
   int   index;

   for (index = 0; index < sizeof(types); index++) {
      if (!_random_descs[types[index..index]])
         _random_descs[types[index..index]] = ({ });

      _random_descs[types[index..index]] += ({ ([ "desc": description,
                               "items":items, "item_desc":item_desc ]) });

   }

}


/**
* This method is to add to the list of random chats that can be added to
* specific room types. A specific random sequence is used so that the randoms
* won't vary over time.
* @param types the string of ASCII character type(s) that this desc could apply to
* @param chats an array of chats to add. Usual add_chat format.
* @see set_random_desc_limit
* @see set_random_chat_limit
* @see add_random_chat
*/
void add_random_chat(string types, string *chats)

{
   int   index;

   for (index = 0; index < sizeof(types); index++) {
      if (!_random_chats[types[index..index]])
         _random_chats[types[index..index]] = ({ });
      _random_chats[types[index..index]] += chats;
   }

}


/**
* This method is used to setup 'external' chats for a road or obstacle,
* which will be added to the room chats of adjacent locations. You
* should supply the ASCII character corresponding to the road/obstacle
* type, and an array of chat strings (in the same format as supplied to
* room_chat)
* @param type the ASCII type used on the map to denote the room
* @param base_path the full pathname of the source file for the room
*    set_external_chats("=",
*                 ({ "The stream bubbles merrily on its way.",
*                    "You hear a faint splashing sound from the stream." }));
* @see add_road_type
* @see add_obstacle_type
* @see set_feature_chats
* @see room_chat
*/
void set_external_chats(string type, mixed chats)

{
   if (!_char_map[type]) {
      return;
   }

   ((class char_map_data)_char_map[type])->chats = chats;
}


/**
* This method is called to add a new 'feature' to a terrain map.
* Features have no real existance, but add atmosphere to a whole area,
* by being visible across a large number of locations.
* You define location, size, item desc, and add an array of 'longs'
* sorted by distance, plus the usual item descriptions. Any occurrences
* of $D in the longs array will be replaced by the direction in which
* the feature lies.
* @param title the name of the feature
* @param x the x coordinate of the room (internal coordinate system)
* @param y the y coordinate of the room (internal coordinate system)
* @param width the width of the feature
* @param height the height of the feature
* @param range the distance (number of rooms) it can be seen for
* @param descs array of descriptions to be shown at different ranges.
* These are divided equally along the total range
* @param items add_item name(s) (identical to the 'shorts' parameter in add_item)
* @param item_desc a string, or array of strings giving the corresponding
* @param night_descs the night description array
* @param night_items the descriptions for the items at night
* @example
*   add_feature("northmountain", 3, 0, 5, 1, 7,
*            ({ "A huge snowy mountain towers over you to the $D.",
*               "A huge snowy mountain looms to the $D.",
*               "A fair way off to the $D, you can see a huge snowy mountain.",
*               "A long way $Dwards, you glimpse the snowy peak of a "
*               "mountain." }),
*               "mountain", "From here, it looks quite astoundingly huge.",
*            ({ "A huge black bulk obscures the sky to the $D.",
*               "A huge black bulk covers the horizon to the $D.",
*               "A black lump obscures the horizon to the $D",
*               "" }),
*               "The dark black silloette looks like a mountain.");
* @see add_road_type
* @see add_obstacle_type
* @see set_feature_chats
* @see modify_feature
* @see add_item
*/
void add_feature(string title, int x, int y, int width, int height,
                 int range, string *descs, mixed items, mixed item_desc,
                 string* night_descs,
                 mixed night_items)

{
   object region;
   object feature;

   region = clone_object(TERRAIN_MAP_REGION_RECTANGULAR);
   region->add_feature_position(x, y, width, height);
   feature = clone_object(TERRAIN_MAP_FEATURE_BASE);
   feature->set_region(region);
   feature->set_max_day_range(range);
   feature->set_max_night_range(range);
   _features[title] = new(class feature_data,
                         range:range, descs:descs,
                         f_items:items, f_item_desc:item_desc,
                         f_item_night_desc : night_items,
                         whole_sentance:1,
                         feature_ob : feature,
                         night_descs : night_descs);
}

/**
* This method is called to add a new 'feature' to a terrain map.
* Features have no real existance, but add atmosphere to a whole area,
* by being visible across a large number of locations.  This method is
* different to the normal add_feature in that the system will try
* and construct a sentance from the features instead of once sentance
* per feature.
* You define location, size, item desc, and add an array of 'longs'
* sorted by distance, plus the usual item descriptions. Any occurrences
* of $D in the longs array will be replaced by the direction in which
* the feature lies.
* @param title the name of the feature
* @param x the x coordinate of the room (internal coordinate system)
* @param y the y coordinate of the room (internal coordinate system)
* @param width the width of the feature
* @param height the height of the feature
* @param range the distance (number of rooms) it can be seen for
* @param descs array of descriptions to be shown at different ranges.
* @param items add_item name(s) (identical to the 'shorts' parameter in add_item)
* @param item_desc a string, or array of strings giving the corresponding
* These are divided equally along the total range
* @example
*   add_feature_sentance("northmountain", 3, 0, 5, 1, 7,
*            ({ "a huge snowy mountain towers over you to the $D",
*               "a huge snowy mountain looms to the $D",
*               "a fair way off to the $D, you can see a huge snowy mountain",
*               "a long way $Dwards, you glimpse the snowy peak of a "
*               "mountain" }),
*               "mountain", "From here, it looks quite astoundingly huge." );
* @see add_road_type
* @see add_obstacle_type
* @see set_feature_chats
* @see modify_feature
* @see add_item
*/
void add_feature_sentance(string title, int x, int y, int width, int height,
                 int range, string *descs, mixed items, mixed item_desc,
                 string* night_descs, mixed night_items)

{
   object region;
   object feature;

   region = clone_object(TERRAIN_MAP_REGION_RECTANGULAR);
   region->add_feature_position(x, y, width, height);
   feature = clone_object(TERRAIN_MAP_FEATURE_BASE);
   feature->set_region(region);
   feature->set_max_day_range(range);
   feature->set_max_night_range(range);
   _features[title] = new(class feature_data,
                         feature_ob : feature,
                         range:range, descs:descs,
                         f_items:items, f_item_desc:item_desc,
                         f_item_night_desc : night_items,
                         whole_sentance:0,
                         night_descs : night_descs);
}


/**
* This method is called to add a new 'feature' to a terrain map.
* Features have no real existance, but add atmosphere to a whole area,
* by being visible across a large number of locations.
* You define location, size, item desc, and add an array of 'longs'
* sorted by distance, plus the usual item descriptions. Any occurrences
* of $D in the longs array will be replaced by the direction in which
* the feature lies.
* @param title the name of the feature
* @param region the region object to use in calculating the feature
* @param range the distance (number of rooms) it can be seen for
* @param descs array of descriptions to be shown at different ranges.
* These are divided equally along the total range
* @param items add_item name(s) (identical to the 'shorts' parameter in add_item)
* @param item_desc a string, or array of strings giving the corresponding
* @param whole_sentance use a whole sentance for the descriptions or wrap
* them up into fragments
* @example
*   region = clone_object(TERRAIN_MAP_REGION_POLYGON);
*   region->add_vertex(12, 13);
*   region->add_vertex(14,15);
*   region->add_vertex(17,18);
*   add_feature_region("northmountain", region, 7,
*            ({ "A huge snowy mountain towers over you to the $D.",
*               "A huge snowy mountain looms to the $D.",
*               "A fair way off to the $D, you can see a huge snowy mountain.",
*               "A long way $Dwards, you glimpse the snowy peak of a "
*               "mountain." }),
*               "mountain", "From here, it looks quite astoundingly huge.",
*            ({ }), 0 );
* @see add_road_type
* @see add_obstacle_type
* @see set_feature_chats
* @see modify_feature
* @see add_item
*/
void add_feature_region(string title, object region,
                 int range, string *descs, mixed items, mixed item_desc,
                 string* night_descs,
                 mixed night_items,
                 int whole_sentance)

{
   object feature;

   feature = clone_object(TERRAIN_MAP_FEATURE_BASE);
   feature->set_region(region);
   feature->set_max_day_range(range);
   feature->set_max_night_range(range);
   _features[title] = new(class feature_data,
                         range:range, descs:descs,
                         f_items:items, f_item_desc:item_desc,
                         whole_sentance:whole_sentance,
                         f_item_night_desc : night_items,
                         feature_ob : feature,
                         night_descs : night_descs);
}

/**
* This method is called to add a new 'feature' to a terrain map.
* Features have no real existance, but add atmosphere to a whole area,
* by being visible across a large number of locations.
* You define location, size, item desc, and add an array of 'longs'
* sorted by distance, plus the usual item descriptions. Any occurrences
* of $D in the longs array will be replaced by the direction in which
* the feature lies.
* @param title the name of the feature
* @param region the region object to use in calculating the feature
* @param range the distance (number of rooms) it can be seen for
* @param descs array of descriptions to be shown at different ranges.
* These are divided equally along the total range
* @param items add_item name(s) (identical to the 'shorts' parameter in add_item)
* @param item_desc a string, or array of strings giving the corresponding
* @param whole_sentance use a whole sentance for the descriptions or wrap
* them up into fragments
* @example
*   region = clone_object(TERRAIN_MAP_REGION_POLYGON);
*   region->add_vertex(12, 13);
*   region->add_vertex(14,15);
*   region->add_vertex(17,18);
*   add_feature_region("northmountain", region, 7,
*            ({ "A huge snowy mountain towers over you to the $D.",
*               "A huge snowy mountain looms to the $D.",
*               "A fair way off to the $D, you can see a huge snowy mountain.",
*               "A long way $Dwards, you glimpse the snowy peak of a "
*               "mountain." }),
*               "mountain", "From here, it looks quite astoundingly huge.",
*            ({ }), 0 );
* @see add_road_type
* @see add_obstacle_type
* @see set_feature_chats
* @see modify_feature
* @see add_item
*/
void add_feature_ob(string title, object feature) {
   if (_real_coords && _real_coords->xdiff) {
      feature->set_distance_multiplier(_real_coords->xdiff);
   }
   _features[title] = new(class feature_data,
                          f_items : feature->query_items(),
                          f_item_desc : feature->query_day_items(),
                          f_item_night_desc : feature->query_night_items(),
                          feature_ob : feature);
}

/**
* This method is used to modify the descriptions and/or item descs of
* an established feature. Changes will be instantly seen in all the
* rooms the feature is visible from.
* @param title the name of the feature
* @param descs array of descriptions to be shown at different ranges.
* add_item name(s) (identical to the 'shorts' parameter in add_item)
* @param item_desc a string, or array of strings giving the corresponding
* These are divided equally along the total range
* @see set_feature_chats
* @see add_feature
* @see add_item
*/
varargs void modify_feature(string title, string *descs, mixed item_desc)

{
   if (!_features[title]) {
      return;
   }

   if (descs) {
      _features[title]->descs = descs;
   }
   if (item_desc) {
      _features[title]->f_item_desc = item_desc;
   }
}


/**
* This method is used to add room chats to locations that can view a
* particular feature. $D substitution is carried out on the chats.
* @param title the name of the feature
* @param chats an array of strings (room chats related to the feature)
* @see add_feature
* @see modify_feature
* @see room_chat
*/
void set_feature_chats(string title, mixed *chats, mixed* night_chats)

{
   if (!_features[title]) {
      return;
   }

   ((class feature_data)_features[title])->chats = chats;
   ((class feature_data)_features[title])->night_chats = night_chats;
}


/**
* This method is used to remove a named feature from the terrain map
* @param title the name of the feature
* @see add_feature
*/
void remove_feature(string title)

{
   object room;

   if (!_features[title]) {
      return;
   }

   map_delete(_features, title);
   if (!_feature_rooms[title]) {
      return;
   }

   _feature_rooms[title] -= ({ 0 });
   foreach(room in _feature_rooms[title]) {
      room->remove_feature(title);
   }

   map_delete(_feature_rooms, title);
}


/**
* This method is used to add a signpost into a specified location in a
* terrain_map. It is identical to the /std/room/ add_sign, except for
the x and y coordinates which specify where the sign should go.
* @param title the name of the feature
* @param x the x coordinate of the center point
* @param y the y coordinate of the center point
* @example
*   add_sign(4, 3, "A wooden signpost.\n",
*                  "\n-----------------\n"
*                    "| Ladder: East  |\n"
*                    "| Bridge: South |\n"
*                    "-----------------",
*                  "signpost", ({ "signpost", "sign" }), "common");
*
* @see add_sign
*/
void add_sign(int x, int y, string sign_long, mixed sign_read_mess,
              string sign_short, mixed sign_name, string sign_language)

{
   string   title = sprintf("%d:%d", x, y);

   _signposts[title] = new(class sign_data,
                         long:sign_long, read_mess:sign_read_mess,
                         short:sign_short, name:sign_name,
                         language:sign_language);
}


/**
* This method is used to map ASCII characters used in the zone map onto
* one or more actual room zones.
* @param type the ASCII character used in the zone map
* @param zones a zone name or an array of zone names
* @example
*   add_zones("*", ({ "zone2", "zone1" }));
*/
void add_zones(string type, string *zone_names)

{
   _zone_types[type] = zone_names;
}


// Dests all the rooms, and reloads all the maps.
/**
* @ignore
*/
void reset_handler()

{
   mixed *rooms;

   if (arrayp(_room_map)) {
      foreach(rooms in _room_map) {
         rooms->dest_me();
      }
   }
   _room_map = 0;
   _area_map = 0;
   setup_area_map();
}


// Load up the zone map. Based on the map_file name.
/**
* @ignore
*/
void load_zone_map()

{
   string   map_path = query_map_file() + ".zones";
   int      index = 1;
   string   line;
   int      width;
   int      height;

   _zone_map = ({ });
   while (line = read_file(map_path, index++, 1)) {
      if (width < sizeof(line))
         width = sizeof(line);
      _zone_map += ({ line });
   }
   height = index - 1;

   if ((width != _width) || (height != _height))
      _zone_map = ({ });
}


// Load up the area map. Based on the map_file name.
/**
* @ignore
*/
void load_area_map()

{
   string   map_path = query_map_file() + ".map";
   int      index = 1;
   int      height;
   string   line;

   _area_map = ({ });
   while (line = read_file(map_path, index++, 1)) {
      if (sizeof(line)) {
         line = line[0..<2];
         if (_width < sizeof(line)) {
            _width = sizeof(line);
         }
         //if (sizeof(line) < _width)
            //continue;
         _area_map = ({ line }) + _area_map;
         height++;
      }
   }
   _height = height;
   if (!height) {
      printf("Unable to open %O\n", map_path);
   }
}

/**
* @ignore
* Handles the cloning of a room when it is specified as:
* path/name:x:y
*/
object create_virtual_object(string x_str, string y_str, string z_str)

{
   int x = to_int(x_str);
   int y = to_int(y_str);
   int z = to_int(z_str);

   if (!stringp(x_str) || !stringp(y_str) || !stringp(z_str)) {
      return 0;
   }

   return query_room(x, y, z);
}

/**
 * This method finds the room at the specific real coordinate.
 * @param x the x location
 * @param y the y location
 * @return the room, or 0 on failure
 */
string find_room_at_real_coord(int x, int y, int z) {
   class char_map_data room_map;
   class coord our;

   our = query_terrain_from_real_coord(x, y, z);
   if (!our) {
      return 0;
   }
   room_map = query_char_map(our->x, our->y);
   if (!room_map) {
      debug_printf("Unable to load room at %d %d (%d %d)\n", our->x, our->y, x, y);
      return 0;
   }

   if (room_map->type == AREA_OBSTACLE) {
      return 0;
   }

   return sprintf("%s:%d:%d:%d", base_name(this_object()), our->x, our->y, our->z);
} /* find_room_at_real_coord() */

/**
 * This method returns the distance that is moved if you go in the
 * specified direction.
 * @return the distance
 */
int query_direction_distance(string dir) {
  switch (_direcs[dir][3]) {
   case X_DIFF :
      return _real_coords->xdiff;
   case Y_DIFF :
      return _real_coords->ydiff;
   case XY_DIFF :
      return _real_coords->xydiff;
   }
   return 0;
}

/** @ignore yes */
void dest_me() {
   class feature_data womble;
   string name;

   foreach (name, womble in _features) {
      if (womble->feature_ob) {
         womble->feature_ob->dest_me();
      }
   }

   destruct(this_object());
}