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

inherit "/std/room/inherit/placement";
inherit "/std/room/inherit/room_save";

#define STANDARD_EXITS ({ "north", "northeast", "east", "southeast", \
                          "south", "southwest", "west", "northwest"})

#undef PLAYTESTING
#define DEBUG

private nosave int _setup_callout;

//
// The predefines for the functions.
//
varargs string* query_direc(object thing);
int query_relative(string direc);
varargs mixed query_door_control(string direc);
varargs mixed* query_dest_other(string direc);
int modify_exit(string dir, mixed* arr);
varargs int add_item(mixed shorts, mixed desc, int no_plural);
string the_short();
string* query_exits();
int query_exit(string direc);
void setup_doors();
void update_doors();
private void update_short_cache(string direc);
int test_occupier(string name);
string show_surface(string direc);
int allow_list();
int do_deny(string player, string location);
int do_allow(object player, string location);
int add_allowed(string pname);
int remove_allowed(string pname);
string query_owner();

/**
 * This class is used to store information on the decoration of this room (for
 * walls, floor etc.)
 */
class decoration {
  string short;
  string covering;
}

/**
 * This class stores information that we need to save about exits.  For example
 * if the player has changed the lock or added a trap.
 */
class exit_data {
  string long;
  string key;
  int difficulty;
  int closed;
  int locked;
  int autolock;
  int transparent;
  mixed trap_data;
}

/**
 * This class is used to cache short names for exits so we don't go loading
 * rooms all over the place.
 */
class exit_shorts {
  string one_short;
  string a_short;
}

private class decoration *decorations;
private string base_long;
private mapping exits;
private mapping exit_shorts;
private nosave mapping coverings; // here coz of sort_array
private nosave string owner;
private string *allowed = ({ });
private string *tell_denied = ({ });
private nosave string decoration_desc = "";
private nosave string quit_location;
private nosave int done_setup;
private nosave int setup_counter;

// used to track which surfaces this room actually has. decorations would
// include any it used to have too.
private nosave string *surfaces; 

#ifdef DEBUG
void debug_log(string fmt, mixed args ...) {
    debug_printf(fmt + "\n", args ...);
  if(base_name(this_object()) == "/d/cwc/Bes_Pelargic/Pong_Ping/roads/phoenix_avenue/housing/05entrycorridor") {
    log_file("HOUSING_DEBUG", "%s (%s): " + fmt + "\n", 
       ctime(time())[4..18], base_name(this_object()), args ...);
  }
}
#endif

/** @ignore yes */
void create() {
  room_save::create();
  exits = ([ ]);
  exit_shorts = ([ ]);
  decorations = ({ });
  tell_denied = ({ });
  this_object()->add_item("left", (: show_surface("left") :));
  this_object()->add_item("right", (: show_surface("right") :));
  this_object()->add_item("forward", (: show_surface("forward") :));
  this_object()->add_item("backward", (: show_surface("backward") :));
  this_object()->add_item("up", (: show_surface("up") :));
  this_object()->add_item("down", (: show_surface("down") :));

  /*
   * We try setup_doors() twice. The first time, if it succeeds is
   * good because it doesn't leave an opportunity for someone to get into
   * a room before the door locks. However sometimes this is too early
   * and no exits have been setup yet so the second one catches that case.
   */
  done_setup = 0;
  setup_doors(); 
  call_out("setup_doors", 0);
  //  call_out("setup_doors", 1);
} /* create() */

/** @ignore yes
 * Called by the add_item to describe a wall, floor or ceiling.
 */
string show_surface(string direc) {
  class decoration tmp;
  int found;
  string lstr, dstr;
  mixed tmp2;

  switch(direc) {
  case "left":
  case "right":
  case "forward":
  case "backward":
    // Find relative directions.
    tmp2 = this_player()->query_facing();
    direc = (string)this_player()->find_abs(direc, tmp2[0]);
    break;
  case "up":
    direc = "ceiling";
    break;
  case "down":
    direc = "floor";
    break;
  }
  
  if(!decorations) {
    decorations = ({ });
  } else {
    // Check it doesn't already exist.
    foreach(tmp in decorations) {
      // remove any old surfaces that got saved somehow.
      if(member_array(tmp->short, surfaces) == -1)
        decorations -= ({ tmp });
      
      if(tmp->short == direc || tmp->short == direc + " wall") {
        found = 1;
        break;
      }
    }
  }

  if(found) {
    direc = replace(direc, ({ " wall", "" }));
    if(member_array(direc, query_direc()) != -1) {
      lstr = "$C$";
      update_short_cache(direc);
      if(exit_shorts[direc] && exit_shorts[direc]->one_short) {
        if(query_door_control(direc))
          lstr += query_door_control(direc)->one_short() + " leads";
        else
          lstr = "an exit leads";
        lstr += " to " + exit_shorts[direc]->one_short;
      } else {
        if(query_door_control(direc))
          lstr += "is the " + query_door_control(direc)->one_short();
        else
          lstr += "is an exit";
      }
      lstr += " and the ";
    } else
      lstr = "The ";

    dstr = desc_surface(direc);
    if(dstr != "")
      dstr = "  " + dstr;
    
    return lstr + tmp->short + " is " + tmp->covering + "." + dstr;
  }
  
  return "";
} /* show_surface() */

/** @ignore yes
 * Called by the add_item to describe a wall, floor or ceiling.
 */
string show_corner(string direc) {
  string tmp;

  tmp = desc_surface(direc);
  if(tmp == "") {
    tmp = "The " + direc + " corner is empty.";
  }
  return tmp;
} /* show_corner() */

/** @ignore yes
 * This function handles producing the long text for the exits.
 */
string exit_long() {
  string *direcs, lstr;
  int i, j;
  object door;
  
  direcs = query_direc();

  if(!sizeof(direcs))
    return "";

  lstr = "  $C$";
  for(i=0; i<sizeof(direcs); i++) {
    update_short_cache(direcs[i]);

    if(query_relative(direcs[i])) {
      j = member_array(this_player()->find_rel(direcs[i]), REL_DIRS);
      lstr += REL_DIRS[j+2];
      if(exit_shorts[direcs[i]]) {
        if(query_door_control(direcs[i]))
          lstr += " " + query_door_control(direcs[i])->a_short() + " leads "
            "to " + exit_shorts[direcs[i]]->a_short;
        else
          lstr += " an exit leads to " + exit_shorts[direcs[i]]->a_short;
      } else {
        if(query_door_control(direcs[i]))
          lstr += " is " + query_door_control(direcs[i])->a_short();
        else
          lstr += " is an exit";
      }
    } else {
      door = query_door_control(direcs[i]);
      if(door) {
        if(member_array("window", door->query_name()) != -1)
          lstr += door->a_short() + " looks out";
        else
          lstr += door->a_short() + " leads " + direcs[i];
      } else
        lstr += "an exit leads " + direcs[i];
    }

    if(i < sizeof(direcs)-2)
      lstr += ", ";
    if(i == sizeof(direcs)-2)
      lstr += " and ";
    if(i == sizeof(direcs)-1)
      lstr += ".";
  }

  return lstr;
} /* exit_long() */

/**
 * @ignore yes
 * This function handles producing the long text for the decorations.
 */
string decoration_long() {
  class decoration tmp;
  string lstr, wstr, tmpstr, *tmparray, *names;
  int i;

#ifdef 0
  // Can't use this coz tables etc. have variable shorts and we can't tell
  // when they change.
  if(decoration_desc != "")
    return decoration_desc;
#endif
  
  lstr = "";
  if(sizeof(decorations)) {
    coverings = ([ ]);
    names = ({ "walls" });
    foreach ( tmp in decorations ) {
      if(!coverings[tmp->covering])
        coverings[tmp->covering] = ({ tmp->short });
      else
        coverings[tmp->covering] += ({ tmp->short });
      if(classp(tmp) && strsrch(tmp->short, "wall") == -1)
        names += ({ tmp->short });
    }

    i = 0;
    tmparray = sort_array(keys(coverings),
                        (: sizeof(coverings[$1]) - sizeof(coverings[$2]) :));

    // Too many surfaces so lets just describe them as multicoloured.
    if(sizeof(tmparray) > 3) {
      lstr += "  The " + query_multiple_short(names) + " are multicoloured.  ";
    } else if(sizeof(tmparray) == 1) {
      lstr += "  The surfaces are all " + decorations[0]->covering + ".  ";
    } else {
      foreach(tmpstr in tmparray) {
        if(i == 0)
          wstr = "  The ";
        else
          wstr = "the ";
        
        if(i == sizeof(tmparray)-1 && sizeof(coverings[tmpstr]) > 1)
          wstr += "other surfaces";
        else
          wstr += query_multiple_short(coverings[tmpstr], "", 1);
        
        if(sizeof(coverings[tmpstr]) > 1)
          wstr += " are ";
        else
          wstr += " is ";
        wstr += tmpstr;
        
        if(i < sizeof(tmparray) -2)
          wstr += ", ";
        else if(i == sizeof(tmparray) -2)
          wstr += " and ";
        else if(i == sizeof(tmparray) -1)
          wstr += ".  ";
        i++;
        lstr += wstr;
      }
    }
  }
  decoration_desc = lstr;
  return decoration_desc;
}

/** @ignore yes
 * Internal function to update the dynamic long description of the room.
 */
string query_long() {
  string lstr, tmp;
  
  if(base_long)
    lstr = "This is " + base_long + ".";
  else
    lstr = "";
  tmp = decoration_long(); 
  if(tmp != "")
    lstr += tmp;
  else
    lstr += "  ";
  
  return (lstr + furniture_long() + exit_long() + "\n");
}

/** @ignore yes
 * Just makes sure room exits are relative inside houses and that doors
 * are closed.
 */
int setup_exit(string direc, int relative) {
  if(member_array(direc, STANDARD_EXITS) != -1 && relative)
    modify_exit(direc, ({ "relative", 1 }));

  modify_exit(direc, ({ "closed", 1 }));

  return 1;
}

/**
 * Set the fixed part of the long description.
 * @param desc The description. (eg. a small and cosy front room)
 */
void set_base_desc(string desc) {
  base_long = desc;
}

/**
 * Query the fixed part of the description.
 * @return the description
 *
 * @see set_base_desc
 */
string query_base_desc() {
  return base_long;
}

/**
 * Set the room into which people should be placed if they quit in this room
 * but are not allowed to control this room.
 *
 * @param filename the filename of a room.
 */
void set_quit_handler(string filename) {
  quit_location = filename;
}

/**
 * This method returns the room that people will be placed in if they logout
 * while in this room and not allowed to control this room.
 * @see set_quit_handler
 * @return string the filename of the room.
 */
string query_quit_handler() {
  if(this_player() && !test_occupier(this_player()->query_name()) &&
     quit_location)
    return quit_location;
  
  return base_name(this_object());
}

/**
 * Add a surface (wall, floor etc.) and give the basic covering the surface
 * has prior to decoration.
 *
 * @param short The short name of the surface (eg. north wall)
 * @param covering The text of the covering (eg. finished pine board)
 */
void add_surface(string short, string covering ){
  class decoration tmp;
  string dir;

  if(!surfaces)
    surfaces = ({ });
  
  surfaces += ({ short });
  
  if(!decorations)
    decorations = ({ });
  else {
    // Check it doesn't already exist.
    foreach(tmp in decorations)
      if(tmp->short == short) {
        add_item(tmp->short, (: show_surface($(tmp->short)) :));
        short = replace(short, ({" wall", ""}));
        foreach(dir in keys(ABS_FACING)) {
          if(ABS_FACING[short] &&
             ABS_FACING[dir] == (ABS_FACING[short] % 8 ) + 1) {
            add_item(dir+" corner", (: show_corner($(dir)) :));
            break;
          }
        }
        return;
      }
  }
  
  tmp = new(class decoration );
  tmp->short = short;
  tmp->covering = covering;
  decorations += ({ tmp });
  add_item(tmp->short, (: show_surface($(tmp->short)) :));
  short = replace(short, ({" wall", ""}));
  foreach(dir in keys(ABS_FACING))
    if(ABS_FACING[short] &&
       ABS_FACING[dir] == (ABS_FACING[short] + 1) % 8) {
      add_item(dir+" corner", (: show_corner($(dir)) :));
      break;
    }
  
  decoration_desc = "";
}

/** @ignore yes
 * return a list of surfaces
 */
string *query_surfaces() {
  string *stmp;
  class decoration tmp;

  stmp = ({ });
  foreach(tmp in decorations) {
    stmp += ({ replace(tmp->short, ({" wall", ""})) });
  }
  return stmp;
}

/**
 * Modify a surface (wall, floor etc.) and give it a new covering.
 *
 * @param short The short of the surface.
 * @param covering The new covering decsription.
 * @return 1 for success, 0 for failure
 *
 * @see add_surface
 */
int decorate_surface(string short, string covering ){
  class decoration tmp;

  foreach(tmp in decorations)
    if(tmp->short == short) {
      tmp->covering = covering;
      event(this_object(), "save");
      decoration_desc = "";
      return 1;
    }
  
  return 0;
}


/** @ignore yes */
void query_decorations() {
  printf("%O\n", decorations);
}

/** @ignore yes
 * Determine which items in this room should be saved.
 */
int test_save(object thing) {
  return thing->query_furniture() || thing->query_missing_item_receipt();
}

/** @ignore yes
 * Call the test_remove functions in both placement & room_save
 */
int test_remove(object thing, int flag, mixed dest) {
  if(!placement::test_remove(thing, flag, dest))
    return 0;
  return room_save::test_remove(thing, flag, dest);
}

/** @ignore yes
 * Stops positioned objects from showing.  If it has a CURRENT_ROOM_VERB
 * property it's obviously a placed object and shouldn't been seen.
 */
object *my_process_non_living(object *int_non_livings, int start) {
  object *not_positioned;

  not_positioned = filter(int_non_livings,
                          (: !$1->query_property("current room position") &&
                           !$1->query_property("current room hidden") :) );
  return not_positioned;
} /* process_non_living() */

/** @ignore yes */
void init() {
  placement::init();

  // If this player is an occupier (and really an occupier not just an
  // authorised player) then give them the ability to allow/deny people
  // control in this room
  if(!allowed) {
    allowed = ({ });
  }
  
  add_command("allow", "list", (: allow_list() :));
  if(test_occupier(this_player()->query_name()) ||
     this_player()->query_creator()) {

    if(!HOUSING->query_house(base_name(this_object()))) {
      add_command("allow", "<indirect:player:here>",
                  (: do_allow($1[0], "room") :));
      add_command("deny", "<word'player'>",
                  (: do_deny($4[0], "room") :));
    } else {
      add_command("allow", "<indirect:player:here> <word'house|room'>",
                  (: do_allow($1[0], $4[1]) :));
      add_command("deny", "<word'player'> <word'house|room'>",
                  (: do_deny($4[0], $4[1]) :));
    }
  }
}

/**
 * This method is used to setup things when someone enters the room.
 * @param ob the object that entered the room
 */
protected void event_enter_checks(object ob) {
  if(!tell_denied) {
    tell_denied = ({ });
  }
  
  if(ob && interactive(ob) &&
     member_array(ob->query_name(), tell_denied) != -1) {
    write("%^YELLOW%^%^BOLD%^You are no longer allowed to control this "
          "room.%^RESET%^\n");
    tell_denied -= ({ ob->query_name() });
  }
}

/** @ignore yes */
void event_enter(object ob, string message, object from) {
#ifdef PLAYTESTING
  if(interactive(ob) &&
     (!ob->query_creator() &&
     !"/obj/handlers/playtesters"->query_playtester(ob->query_name())) &&
     ob->query_name() != "gumboot") {
    tell_object(ob, "Sorry, this area is only open to playtesters.\n");
    ob->move(from);
  }
#endif
  event_enter_checks(ob);
} /* event_enter() */

/** @ignore yes
 * List who can control this room.
 */
int allow_list() {
  string *tmp;

  if(owner && owner != "For Sale" && owner != "Under Offer") {
    tmp = ({ owner }) + allowed;
  } else {
    tmp = allowed;
  }

  if (!test_occupier(this_player()->query_name()) &&
      !this_player()->query_creator()) {
    add_failed_mess("You are not allowed to control this room.\n", ({ }));
    return 0;
  }

  switch(sizeof(tmp)) {
  case 0:
    add_succeeded_mess(({"Noone is allowed to control this room.\n",
                           "$N messes with something in " + the_short() +
                           ".\n"}));
    break;
  case 1:
    add_succeeded_mess(({query_multiple_short(tmp) +
                           " is allowed to control this room.\n",
                           "$N messes with something in " + the_short() +
                           ".\n"}));
    break;
  default:
    add_succeeded_mess(({query_multiple_short(tmp) +
                           " are allowed to control this room.\n",
                           "$N messes with something in " + the_short() +
                           ".\n"}));
  }
  return 1;
}

/** @ignore yes
 *
 * Allows a player to do things in the room as though they were the
 * owner/occupier.
 * This function is called by the 'allow' add_command.
 */
/* NOTE: This uses object player and requires the owner and the player to be
 * logged in  and present to be allowed. This is a deliberate security
 * feature to prevent people allowing their alts and so using houses to
 * multiplay!
 */
int do_allow(object player, string location) {
  string pname;
  string *rooms;
  string room;
  int failed;

  if((query_owner() != this_player()->query_name()) &&
     !this_player()->query_creator()) {
    add_failed_mess("You are not allowed to control this room.\n", ({ }));
    return 0;
  }

  if(environment(player) != this_object()) {
    return add_failed_mess("$I is not here.\n", ({ player }));
  }

  pname = lower_case(player->query_name());
  if(test_occupier(pname)) {
    add_failed_mess("$I is already able to control this room.\n");
    return 0;
  }

  if(location == "house") {
    rooms = HOUSING->query_rooms(base_name(this_object()));
    failed = 1;
    foreach(room in rooms) {
      failed &= room->add_allowed(pname);
    }
  } else {
    failed = add_allowed(pname);
  }

  if (failed) {
     add_failed_mess("Failed to add " + pname + " as being able to control "
                     "the " + location + ".\n");
     return 0;
  }

  tell_object(player, "You are now permitted to control this " + location +
              ".\n");
  add_succeeded_mess(({ pname + " is allowed to control this " + location +
                          ".\n", ""}));
  return 1;
} /* do_allow() */

/** @ignore yes
 *
 * Takes a player off the allowed list.
 * This function is called by the 'deny' add_command.
 */
int do_deny(string player, string location) {
  object ob;
  string *rooms, room;
  
  if(member_array(player, allowed) == -1) {
    add_failed_mess(player + " is not on the allow list.\n");
    return 0;
  }

  if (!test_occupier(this_player()->query_name()) &&
      !this_player()->query_creator()) {
    add_failed_mess("You are not allowed to control this room.\n");
    return 0;
  }

  if(!test_occupier(player)) {
    add_failed_mess(player + " is already not allowed to control this room.\n");
    return 0;
  }

  if(location == "house") {
    rooms = HOUSING->query_rooms(base_name(this_object()));
    foreach(room in rooms)
      room->remove_allowed(player);
  } else {
    remove_allowed(player);
  }

  ob = find_player(player);
  if(ob && environment(ob) == this_object()) {
    tell_object(ob, "%^YELLOW%^%^BOLD%^You are no longer allowed to control " +
                the_short() + ".\n%^RESET%^");
  } else {
    tell_denied += ({ player });
  }

  add_succeeded_mess(({player + " is no longer allowed to control this " +
                     location + ".\n", "" }));
  return 1;
} /* do_deny() */

/** @ignore yes
 *
 * Update our cache of short exit names.
 */
private void update_short_cache(string direc) {
  string ostr, astr;

  if(!exit_shorts)
    exit_shorts = ([ ]);
  
  if(find_object(query_dest_other(direc)[0])) {
    ostr = query_dest_other(direc)[0]->one_short();
    if(this_player())
      ostr = this_player()->convert_message(ostr);
    
    astr = query_dest_other(direc)[0]->a_short();
    if(this_player())
      astr = this_player()->convert_message(astr);
    
    if(!exit_shorts[direc] ||
       ostr != exit_shorts[direc]->one_short ||
       astr != exit_shorts[direc]->a_short) {
      exit_shorts[direc] = new(class exit_shorts,
                               one_short : ostr,
                               a_short : astr);
    }
  }
}

/** @ignore yes
 * Update the door info when the room is dested, prior to saving.
 */
void update_doors() {
  string exit;
  object door_ob;

  if(!done_setup)
    setup_doors();

  if(!exits)
    exits = ([ ]);
  
  foreach(exit in query_exits()) {
    door_ob = query_door_control(exit);
    if(door_ob) {
      if(door_ob->query_key() == "generic_key") {
#ifdef DEBUG
        if(exits[exit])
          debug_log("update_doors", "Door %s has generic_key, exit data "
                    "has %s", exit, exits[exit]->key);
        else
          debug_log("update_doors", "Door %s has generic_key, no existing "
                    "exit data.", exit);
#endif
      }
      
      exits[exit] = new(class exit_data,
                        long : door_ob->query_long(),
                        key : door_ob->query_key(),
                        difficulty: door_ob->query_difficulty(),
                        closed : door_ob->query_closed(),
                        locked : door_ob->query_locked(),
                        autolock : door_ob->query_autolock(),
                        transparent : door_ob->query_transparent(),
                        trap_data : door_ob->query_trap_data());

#ifdef DEBUG2
      debug_log("update_doors: Door %s: key: %s, difficulty: %d, closed: %d, "
                "locked: %d, owner: %s", exit, exits[exit]->key,
                exits[exit]->difficulty, exits[exit]->closed,
                exits[exit]->locked, owner);
#endif
    }
  }
}

/** @ignore yes
 * Initialise the doors when the room is loaded.
 */
void setup_doors() {
  string exit;
  object door;
  string other, other_id;
  
#ifdef DEBUG2
  debug_log("Setup doors called.");
#endif

  if(!this_object()->query_exits()) {
    this_object()->calc_exits();
  }

  if(!exits || !sizeof(exits)) {
    if(!this_object()->query_exits()) {
#ifdef DEBUG2
      debug_log("Leaving setup_doors, No exits to setup.");
#endif
    } else if(query_inventory_loaded()) {
#ifdef DEBUG
      debug_log("Leaving setup_doors, exits not setup %O, autoloading: %d "
                "inventory loaded: %d.",
                exits, query_auto_loading(), query_inventory_loaded());
#endif
    }
    return;
  }

  foreach(exit in keys(exits)) {
    door = query_door_control(exit);
    if(!door) {
      if(!query_exit(exit)) {
        map_delete(exits, exit);
      }
      continue;
    }

    if(!owner)
      catch(owner = HOUSING->query_owner(base_name(this_object())));

    //if(!done_setup) {
#ifdef DEBUG2
      if(exit == "out")
        debug_log("Setting up %s: key: %s, difficulty: %d, closed: %d, "
                  "locked: %d, owner: %s", exit, exits[exit]->key,
                  exits[exit]->difficulty, exits[exit]->closed,
                  exits[exit]->locked, owner);
#endif
      modify_exit(exit, ({ "key", exits[exit]->key,
                             "difficulty", exits[exit]->difficulty,
                             "closed", exits[exit]->closed,
                             "locked", exits[exit]->locked,
                             "autolock", exits[exit]->autolock,
                             "lock owner", owner,
                             }));
    
      if(exits[exit]->trap_data && arrayp(exits[exit]->trap_data)) {
        door->setup_trap(exits[exit]->trap_data[0],
                         exits[exit]->trap_data[1],
                         exits[exit]->trap_data[2],
                         exits[exit]->trap_data[3],
                         exits[exit]->trap_data[4]);
        if(exits[exit]->trap_data[5])
          door->set_trap_armed(1);
      }
      //}
    
    // This prevents rooms causing other rooms to load. Hopefully it's ok to
    // have it commented out.
    other = door->query_dest();
    other_id = door->query_other_id();

    // This prevents the room causing other playerhousing rooms to be loaded.
    // The check for "out" just makes certain we do the front door.
#ifdef NO_SLOW_LOAD
    // I've disabled this for the moment to see if having the cascade of
    // room loads will be ok with the ramdisk and whether it'll fix any
    // door issues.
    if(!find_object(other) && exit != "out")
      continue;
#endif
    if(!other_id) {
      door->force_other();
      other_id = door->query_other_id();
    }

    // If we have no other_id we need to try again later.
    if(!other_id) {
#ifdef DEBUG2
      debug_log("No other_id, adding call_out.");
#endif
      if(!_setup_callout) {
        _setup_callout = call_out("setup_doors", 0);
        return;
      }
    }
  
    other->modify_exit(other_id, ({ "key", exits[exit]->key,
                                      "difficulty", exits[exit]->difficulty,
                                      "closed", exits[exit]->closed,
                                      "locked", exits[exit]->locked,
                                      "autolock", exits[exit]->autolock,
                                      "lock owner", owner,
                                      }));
    if(exits[exit]->trap_data && arrayp(exits[exit]->trap_data)) {
      door = other->query_door_control(other_id);
      if(door && objectp(door)) {
        door->setup_trap(exits[exit]->trap_data[0],
                         exits[exit]->trap_data[1],
                         exits[exit]->trap_data[2],
                         exits[exit]->trap_data[3],
                         exits[exit]->trap_data[4]);
        if(exits[exit]->trap_data[5])
          door->set_trap_armed();
      }
    }
  }
  done_setup = 1;
}

/**
 * This method returns the current owner of the house.
 */
string query_owner() {
  if(owner)
    return owner;
  return HOUSING->query_owner(base_name(this_object()));
}

/**
 * This method locally sets the owner of the house.
 */
string set_owner(string who) { owner = who; }

/**
 * This function adds a name to the allow list of this room.
 *
 * @param pname The name of a player.
 * @return 1 for success, 0 for failure
 */
int add_allowed(string pname) {
  if(test_occupier(pname))
    return 0;

  allowed += ({ pname });
  event(this_object(), "save");

  return 0;
}

/**
 * This function removes a name from the allow list of this room.
 *
 * @param pname The name of a player.
 * @return 1 for success, 0 for failure
 */
int remove_allowed(string pname) {
  if(!test_occupier(pname))
    return 0;

  allowed -= ({ pname });
  event(this_object(), "save");

  return 1;
}

/**
 * This function determines if a given individual can be considered to be the
 * occupier of this house. Normally this means the person currently renting or
 * owning this house.
 *
 * For shared ownership houses simply mask this function and do whatever you
 * have to. :)
 *
 * @param name The person to be tested
 * @return 1 if they are the occupier, 0 if not.
 */
int test_occupier(string name) {
  if(!owner)
    catch(owner = HOUSING->query_owner(base_name(this_object())));

  // If there is no owner then anyone in the house is considered the occupier.
  if(!owner || lower_case(owner) == "for sale" ||
     lower_case(owner) == "under offer")
    return 1;
  
  if(PLAYER_HANDLER->test_creator(name))
    return 1;

  if(!allowed)
    allowed = ({ });

  return (owner == name || (member_array(name, allowed) != -1));
}

/**
 * This function informs the house that its owner has changed, it does not
 * actually change the owner since that is done in the handler.
 *
 * @param old_owner The name of the old owner
 * @param new_owner The name of the new owner
 * @return 1 for success, 0 for failure.
 */
int ownership_change(string old_owner, string new_owner) {
  if(old_owner != new_owner) {
    owner = new_owner;
    allowed = ({ });
    return 1;
  }
  return 0;
}

/**
 * This method returns the current list of people allowed to modify this
 * room.
 * @return the allowed people to change this room
 */
string *query_allowed() {
   if (allowed) {
     return allowed;
   }
   return ({ });
} /* query_allowed() */

/**
 * This method tells us if the person is allowed to use this room.
 * @param name the name of the person
 */
int is_allowed(string name) {
  // The owner, those on the allow list and creators are allowed.
  if((query_owner() && (lower_case(name) == lower_case(query_owner()))) ||
     (member_array(lower_case(name), query_allowed()) != -1) ||
     PLAYER_HANDLER->test_creator(name)) {
    return 1;
  }
   
   return 0;
}

/** @ignore yes */
void dest_me() {
#ifdef NOT_USED
  if (base_name(this_object()) + ".c" != __FILE__)
    update_doors();
#endif
  ::dest_me();
}

/** @ignore yes */
void event_open(object door, object opener) {
#ifdef DEBUG2
  debug_log("door opened by %s [%d]", opener->query_name(), done_setup);
#endif
  if(done_setup) {
    ::event_open(door, opener);
    update_doors();
  }
}

/** @ignore yes */
void event_close(object door, object closer) {
#ifdef DEBUG2  
    debug_log("door closed by %s [%d]", closer->query_name(), done_setup);
#endif
  if(done_setup) {
    ::event_close(door, closer);
    update_doors();
  }
}

/** @ignore yes */
void event_unlock(object door, object unlocker) {
#ifdef DEBUG2
  debug_log("door unlocked by %s [%d]", 
            unlocker?unlocker->query_name():"noone", done_setup);
#endif
  if(done_setup) {
    ::event_unlock(door, unlocker);
    update_doors();
  }
}

/** @ignore yes */
void event_lock(object door, object locker) {
#ifdef DEBUG2
  debug_log("door locked by %s [%d]", locker?locker->query_name():"noone",
            done_setup);
#endif
  if(done_setup) {
    ::event_lock(door, locker);
    update_doors();
  }
}