1stMud/CVS/
1stMud/area/CVS/
1stMud/backup/CVS/
1stMud/bin/
1stMud/bin/CVS/
1stMud/bin/extras/
1stMud/bin/extras/CVS/
1stMud/data/CVS/
1stMud/data/i3/CVS/
1stMud/doc/1stMud/
1stMud/doc/1stMud/CVS/
1stMud/doc/CVS/
1stMud/doc/Diku/
1stMud/doc/Diku/CVS/
1stMud/doc/MPDocs/CVS/
1stMud/doc/Merc/CVS/
1stMud/doc/Rom/
1stMud/doc/Rom/CVS/
1stMud/log/CVS/
1stMud/notes/
1stMud/notes/CVS/
1stMud/player/CVS/
1stMud/player/backup/CVS/
1stMud/player/deleted/CVS/
1stMud/src/CVS/
1stMud/src/config/CVS/
1stMud/src/h/CVS/
1stMud/src/o/CVS/
1stMud/win/CVS/
/**************************************************************************
*  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
*  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
*                                                                         *
*  Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael         *
*  Chastain, Michael Quan, and Mitchell Tse.                              *
*                                                                         *
*  In order to use any part of this Merc Diku Mud, you must comply with   *
*  both the original Diku license in 'license.doc' as well the Merc       *
*  license in 'license.txt'.  In particular, you may not remove either of *
*  these copyright notices.                                               *
*                                                                         *
*  Much time and thought has gone into this software and you are          *
*  benefiting.  We hope that you share your changes too.  What goes       *
*  around, comes around.                                                  *
***************************************************************************
*       ROM 2.4 is copyright 1993-1998 Russ Taylor                        *
*       ROM has been brought to you by the ROM consortium                 *
*           Russ Taylor (rtaylor@hypercube.org)                           *
*           Gabrielle Taylor (gtaylor@hypercube.org)                      *
*           Brian Moore (zump@rom.org)                                    *
*       By using this code, you have agreed to follow the terms of the    *
*       ROM license, in the file Rom24/doc/rom.license                    *
***************************************************************************
*          1stMud ROM Derivative (c) 2001-2004 by Markanth                *
*            http://www.firstmud.com/  <markanth@firstmud.com>            *
*         By using this code you have agreed to follow the term of        *
*             the 1stMud license in ../doc/1stMud/LICENSE                 *
***************************************************************************/

#include "merc.h"
#include "interp.h"
#include "recycle.h"
#include "olc.h"
#include "tables.h"
#include "vnums.h"

Declare_Do_F (home_buy);
Declare_Do_F (home_add);
Declare_Do_F (home_furnish);
Declare_Do_F (home_unfurnish);
Declare_Do_F (home_sell);
Declare_Do_F (home_describe);
Declare_Do_F (home_name);
Declare_Do_F (home_evict);
Declare_Do_F (home_invite);
Declare_Do_F (home_accept);
Declare_Do_F (home_deny);
Declare_Do_F (do_get_key);
Declare_Do_F (home_help);
Declare_Do_F (home_link);
Declare_Do_F (home_unlink);
Declare_Do_F (do_gohome);
Declare_Do_F (home_door);
Declare_Do_F (home_recall);

Proto (void unlink_reset, (RoomIndex *, ResetData *));

#define HOUSE_PRICE 5000000
#define ROOM_PRICE  3005000

struct home_type
{
  vnum_t vnum;
  money_t cost;
  int quest;
  int tp;
  char *name;
  char *list;
  int type;
  flag_t room_flags;
};

const struct home_type home_table[] = {
  {
   0, 125000, 10, 0, "healrate", "Up a rooms Heal Rate by 50", ROOM_VNUM,
   0},
  {0, 125000, 10, 0, "manarate", "Up a rooms Mana Rate by 50", ROOM_VNUM,
   0},
  {0, 125000, 10, 0, "saferoom", "Make a safe room", ROOM_VNUM, ROOM_SAFE},

  {
   0, 0, 0, 0, NULL, NULL, 0}
};

bool
is_home_owner (CharData * ch, RoomIndex * pHome)
{
  int i;

  if (pHome == NULL)
    return false;

  if (!ch || IsNPC (ch))
    return false;

  for (i = 0; i < HAS_HOME (ch); i++)
    if (ch->pcdata->home[i] == pHome->vnum)
      return true;

  return false;
}

int
tally_resets (RoomIndex * pRoom, money_t cost, int trivia, int quest)
{
  ResetData *pReset;
  int rcount = 0, i;

  if (pRoom == NULL)
    return 0;

  for (i = 0; home_table[i].name != NULL; i++)
    {
      if (home_table[i].type == VNUM_NONE || home_table[i].vnum <= 0)
	continue;
      for (pReset = pRoom->reset_first; pReset != NULL; pReset = pReset->next)
	{
	  if (pReset->arg1 == home_table[i].vnum)
	    {
	      cost += home_table[i].cost / 3;
	      trivia += home_table[i].tp / 3;
	      quest += home_table[i].quest / 3;
	      rcount++;
	    }
	}
    }
  return rcount;
}

bool
get_key (CharData * ch)
{
  ObjIndex *keyindex = NULL;
  ObjData *key = NULL;

  if (!ch || IsNPC (ch) || ch->pcdata->home_key <= 0)
    return false;

  if ((keyindex = get_obj_index (ch->pcdata->home_key)) == NULL)
    {
      chprintln (ch,
		 "Could not find your key, please have an immortal fix this.");
      return false;
    }

  if (get_obj_here (ch, NULL, keyindex->name) != NULL)
    {
      chprintln (ch, "You already have a key to your home.");
      return true;
    }

  if ((key = create_object (keyindex, 1)) == NULL)
    return false;

  obj_to_char (key, ch);
  chprintln (ch, "A house key appears in your inventory.");

  return true;
}

Do_Fun (do_get_key)
{
  get_key (ch);
}

Do_Fun (home_help)
{
  if (ch->pcdata->home_invite != 0)
    {
      cmd_syntax (ch, NULL, n_fun,
		  "accept    - accept an invitation to someone's home",
		  "deny      - turn down an invitation to someone's home",
		  NULL);
    }
  if (!HAS_HOME (ch))
    cmd_syntax (ch, NULL, n_fun,
		"buy       - purchase a home in <direction> (must be in the right area)",
		NULL);
  else
    {
      cmd_syntax (ch, NULL, n_fun,
		  "sell      - sells your home completly for 1/3 the price",
		  "add       - adds another room in <direction>, must be in your home",
		  "describe  - describes a room using the string editor",
		  "name      - name or rename's a room",
		  "furnish   - add objects or mobiles to a room in your home",
		  "unfurnish - sells objects or mobiles placed in your home",
		  "key       - gets a replacement key for your home",
		  "door      - sets a door on a room",
		  "recall    - sets your recall room for 'gohome'",
		  "evict     - kick's someone out of your home",
		  "invite    - invite someone to your home",
		  "link      - link a room to a specified area",
		  "unlink    - Unlink an exit", NULL);
    }
}

Do_Fun (do_home)
{
  if (!ch)
    return;

  if (IsNPC (ch))
    {
      chprintln (ch, "Mobiles don't have homes =).");
      return;
    }

  vinterpret (ch, n_fun, argument, "buy", home_buy, "add", home_add,
	      "sell", home_sell, "describe", home_describe, "name",
	      home_name, "furnish", home_furnish, "unfurnish",
	      home_unfurnish, "key", do_get_key, "recall", do_gohome,
	      "evict", home_evict, "link", home_link, "unlink",
	      home_unlink, "list", home_help, "invite", home_invite,
	      "accept", home_accept, "deny", home_deny, NULL, home_help);
}

Do_Fun (home_buy)
{
  RoomIndex *pRoom = NULL;
  ObjIndex *pObj;
  AreaData *pArea;
  ExitData *pExit, *toExit;
  int door, rev, iHash;
  vnum_t i;

  if (!ch || IsNPC (ch))
    return;

  pArea = ch->in_room->area;

  if (!pArea || !IsSet (pArea->area_flags, AREA_PLAYER_HOMES))
    {
      chprintln (ch, "Player Homes are not allowed in this area.");
      return;
    }

  if (!check_worth (ch, HOUSE_PRICE, VALUE_GOLD))
    {
      chprintlnf (ch, "{wIt costs %d gold to buy a home.", HOUSE_PRICE);
      return;
    }

  if (ch->pcdata->trivia < 40)
    {
      chprintln (ch, "It costs 40 trivia points to buy a house.");
      return;
    }

  if (HAS_HOME (ch))
    {
      chprintlnf (ch,
		  "{wYou already own a home, use '%s add' or '%s furnish'.",
		  cmd_name (do_home), cmd_name (do_home));
      return;
    }

  if (!IsSet (ch->in_room->room_flags, HOME_ENTRANCE))
    {
      chprintln (ch, "You must be at an entrance to a future home.");
      return;
    }

  if ((door = get_direction (argument)) == -1 || door > 3)
    {
      chprintln (ch,
		 "Which direction from here do you want to start your home?");
      return;
    }

  if (ch->in_room->exit[door])
    {
      chprintln (ch, "A room already exits in that location.");
      return;
    }

  for (i = pArea->min_vnum; i < pArea->max_vnum; i++)
    {
      if ((get_obj_index (i)) == NULL)
	{
	  pObj = new_obj_index ();
	  pObj->vnum = i;
	  pObj->area = pArea;
	  replace_strf (&pObj->name, "key %s home", ch->name);
	  replace_strf (&pObj->short_descr, "%s's key",
			capitalize (ch->name));
	  replace_strf (&pObj->description,
			"The key to %s's home is here.",
			capitalize (ch->name));
	  pObj->item_type = ITEM_KEY;
	  SetBit (pObj->wear_flags, ITEM_TAKE);
	  SetBit (pObj->wear_flags, ITEM_HOLD);
	  pObj->condition = -1;
	  iHash = i % MAX_KEY_HASH;
	  LinkSingle (pObj, obj_index_hash[iHash], next);
	  ch->pcdata->home_key = pObj->vnum;
	  break;
	}
    }

  for (i = pArea->min_vnum; i < pArea->max_vnum; i++)
    {
      if ((get_room_index (i)) == NULL)
	{

	  pRoom = new_room_index ();
	  pRoom->area = pArea;
	  pRoom->vnum = i;
	  pRoom->sector_type = SECT_INSIDE;
	  replace_strf (&pRoom->name, "%s\'s Home", capitalize (ch->name));
	  replace_str (&pRoom->description,
		       "This is your room description. You can edit this with HOME DESCRIBE."
		       NEWLINE);
	  replace_str (&pRoom->owner, ch->name);
	  SetBit (pRoom->room_flags, ROOM_INDOORS | ROOM_SAVE_OBJS);
	  iHash = i % MAX_KEY_HASH;
	  LinkSingle (pRoom, room_index_hash[iHash], next);


	  pExit = new_exit ();
	  pExit->u1.to_room = pRoom;
	  pExit->orig_door = door;
	  SetBit (pExit->rs_flags,
		  EX_ISDOOR | EX_CLOSED | EX_NOPASS | EX_PICKPROOF |
		  EX_DOORBELL);
	  pExit->exit_info = pExit->rs_flags;
	  replace_str (&pExit->keyword, "door");
	  replace_strf (&pExit->description,
			"You see the door to %s's home.",
			capitalize (ch->name));
	  ch->in_room->exit[door] = pExit;

	  rev = rev_dir[door];

	  toExit = new_exit ();
	  toExit->u1.to_room = ch->in_room;
	  toExit->orig_door = rev;
	  toExit->rs_flags = pExit->rs_flags;
	  RemBit (toExit->rs_flags, EX_DOORBELL);
	  toExit->exit_info = toExit->rs_flags;
	  replace_str (&toExit->keyword, "door");
	  replace_strf (&toExit->description,
			"You see the door to %s's home.",
			capitalize (ch->name));
	  pRoom->exit[rev] = toExit;


	  if (get_key (ch) == true)
	    {
	      SetBit (pExit->rs_flags, EX_LOCKED);
	      pExit->exit_info = pExit->rs_flags;
	      pExit->key = ch->pcdata->home_key;
	    }
	  SetBit (pArea->area_flags, AREA_CHANGED);
	  break;
	}
    }

  if (!pRoom)
    {
      chprintln (ch,
		 "A new home could not be created for you. please contact an immortal.");
      return;
    }

  ch->pcdata->home[HAS_HOME (ch)] = pRoom->vnum;
  HAS_HOME (ch) += 1;

  deduct_cost (ch, HOUSE_PRICE, VALUE_GOLD);

  ch->pcdata->trivia -= 40;

  chprintln (ch, "Congratulations, you are now a proud home owner.");
  save_char_obj (ch);
  return;
}

Do_Fun (home_add)
{
  RoomIndex *pRoom = NULL;
  AreaData *pArea;
  ExitData *pExit;
  int door, rev, iHash;
  vnum_t i;

  if (!ch || IsNPC (ch))
    return;

  pArea = ch->in_room->area;

  if (!pArea || !IsSet (pArea->area_flags, AREA_PLAYER_HOMES))
    {
      chprintln (ch, "Player Homes are not allowed in this area.");
      return;
    }

  if (!HAS_HOME (ch))
    {
      chprintlnf (ch, "Use '%s buy' to purchase a home first.",
		  cmd_name (do_home));
      return;
    }

  if (!check_worth (ch, ROOM_PRICE, VALUE_GOLD))
    {
      chprintln (ch, "{wYou don't have enough to buy a another room.");
      return;
    }

  if (ch->pcdata->quest.points < 200)
    {
      chprintln (ch, "{wYou need 200 questpoints to buy another room.");
      return;
    }

  if (HAS_HOME (ch) == PC_HOME_COUNT)
    {
      chprintln (ch, "{wYou have the max allowable rooms in your house.");
      return;
    }

  if (!is_home_owner (ch, ch->in_room))
    {
      chprintln (ch, "You must add only to your home!");
      return;
    }

  if ((door = get_direction (argument)) == -1 || door > 3)
    {
      chprintln (ch, "Which direction from here do you want the new room?");
      return;
    }

  if (ch->in_room->exit[door])
    {
      chprintln (ch, "A room already exits in that location.");
      return;
    }

  for (i = pArea->min_vnum; i < pArea->max_vnum; i++)
    {
      if ((get_room_index (i)) == NULL)
	{

	  pRoom = new_room_index ();
	  pRoom->area = pArea;
	  pRoom->vnum = i;
	  pRoom->sector_type = SECT_INSIDE;
	  replace_strf (&pRoom->name, "%s\'s %d Room",
			capitalize (ch->name), HAS_HOME (ch) + 1);
	  replace_str (&pRoom->description,
		       "This is your room description. You can edit this with HOME DESCRIBE."
		       NEWLINE);
	  replace_str (&pRoom->owner, ch->name);
	  SetBit (pRoom->room_flags, ROOM_INDOORS | ROOM_SAVE_OBJS);
	  iHash = i % MAX_KEY_HASH;
	  LinkSingle (pRoom, room_index_hash[iHash], next);

	  ch->in_room->exit[door] = new_exit ();
	  ch->in_room->exit[door]->u1.to_room = pRoom;
	  ch->in_room->exit[door]->orig_door = door;
	  rev = rev_dir[door];
	  pExit = new_exit ();
	  pExit->u1.to_room = ch->in_room;
	  pExit->orig_door = rev;
	  pRoom->exit[rev] = pExit;
	  break;
	}
    }

  if (!pRoom)
    {
      chprintln (ch,
		 "A new room could not be created for you. please contact an immortal.");
      return;
    }

  ch->pcdata->home[HAS_HOME (ch)] = pRoom->vnum;
  HAS_HOME (ch) += 1;

  deduct_cost (ch, ROOM_PRICE, VALUE_GOLD);
  ch->pcdata->quest.points -= 200;

  chprintln
    (ch, "The mayors hired contractors come and do additions to your home.");
  chprintlnf (ch, "Your home now contains %d rooms.", HAS_HOME (ch));
  save_char_obj (ch);
  SetBit (pArea->area_flags, AREA_CHANGED);
  return;
}

Do_Fun (home_sell)
{
  RoomIndex *pRoom = NULL;
  int home, i = 0, rooms = 0;
  money_t cost = 0;
  int quest = 0;
  int tp = 0;
  char buf[MSL];

  if (!ch || IsNPC (ch))
    return;

  if (!(rooms = HAS_HOME (ch)))
    {
      chprintln (ch, "You don't have a home yet.");
      return;
    }

  if (NullStr (argument))
    {
      chprintln (ch,
		 "This command allows you to sell your home.  The Mayors");
      chprintln (ch,
		 "contractors  will completly demolish your home when it is sold,");
      chprintln (ch,
		 "and you will be compensated half the cost of the home,");
      chprintln (ch, "including any items you have purchased.");
      chprintln (ch,
		 "{ROnce your home is sold, it cannot be brought back!!!{x");
      cmd_syntax (ch, NULL, n_fun, "confirm", NULL);
      return;
    }
  else if (!str_cmp (argument, "confirm"))
    {
      bool found = false;

      sprintf (buf, "%ld", HOME_KEY (ch));
      oedit_delete (NULL, NULL, buf);
      for (home = 0; home < PC_HOME_COUNT; home++)
	{
	  if (!found && ch->pcdata->home[home] == HOME_ROOM (ch))
	    found = true;
	  if ((pRoom = get_room_index (ch->pcdata->home[home])) != NULL)
	    {
	      SetBit (pRoom->area->area_flags, AREA_CHANGED);
	      i += tally_resets (pRoom, cost, tp, quest);
	      sprintf (buf, "%ld", ch->pcdata->home[home]);
	      if (redit_delete (NULL, NULL, buf))
		cost += ROOM_PRICE / 3;
	    }
	}
      if (!found && HOME_ROOM (ch)
	  && (pRoom = get_room_index (HOME_ROOM (ch))) != NULL)
	{
	  SetBit (pRoom->area->area_flags, AREA_CHANGED);
	  i += tally_resets (pRoom, cost, tp, quest);
	  sprintf (buf, "%ld", HOME_ROOM (ch));
	  if (redit_delete (NULL, NULL, buf))
	    cost += ROOM_PRICE / 3;
	}
      add_cost (ch, cost, VALUE_GOLD);
      ch->pcdata->trivia += tp;
      ch->pcdata->quest.points += quest;
      chprintlnf
	(ch,
	 "Your home {R(%d rooms) {G(%d furnishings){x has been sold and you are now {Y%ld{x gold coins richer!",
	 rooms, i, cost);
      memset (ch->pcdata->home, 0, MAX_HOME_VNUMS);
      save_char_obj (ch);
      return;
    }
  else
    {
      home_sell (n_fun, ch, "");
      return;
    }
}


Do_Fun (home_door)
{
  int door;
  char arg1[MIL], arg2[MIL];
  ExitData *pexit;

  if (!ch || IsNPC (ch))
    return;

  if (!HAS_HOME (ch))
    {
      chprintln (ch, "You have to BUY a home before you can describe it.");
      return;
    }
  if (!is_home_owner (ch, ch->in_room))
    {
      chprintln (ch, "But you do not own this room!");
      return;
    }

  argument = first_arg (argument, arg1, false);
  argument = first_arg (argument, arg2, false);

  if (NullStr (arg1))
    {
      cmd_syntax (ch, NULL, n_fun,
		  "<exit> <flag>          - flag an exit.",
		  "<exit> list            - list exit flags.",
		  "<exit> key <object>    - make a key for exit.",
		  "<exit> name <string>   - name an exit.",
		  "<exit> desc <string>   - describe an exit.", NULL);
      return;
    }

  if ((door = get_direction (arg1)) != -1
      && (pexit = ch->in_room->exit[door]) != NULL)
    {
      flag_t flag;

      if (!str_prefix (arg2, "list"))
	{
	  show_flags (ch, exit_flags);
	  return;
	}
      else if ((flag = flag_value (exit_flags, arg2)) != NO_FLAG)
	{
	  ExitData *toExit;

	  ToggleBit (pexit->rs_flags, flag);

	  if ((IsSet (pexit->rs_flags, EX_PICKPROOF)
	       || IsSet (pexit->rs_flags, EX_EASY)
	       || IsSet (pexit->rs_flags, EX_HARD)
	       || IsSet (pexit->rs_flags, EX_INFURIATING))
	      && !IsSet (pexit->rs_flags, EX_LOCKED))
	    {
	      ToggleBit (pexit->rs_flags, EX_LOCKED);
	    }

	  if ((IsSet (pexit->rs_flags, EX_LOCKED)
	       || IsSet (pexit->rs_flags, EX_NOPASS))
	      && !IsSet (pexit->rs_flags, EX_CLOSED))
	    {
	      ToggleBit (pexit->rs_flags, EX_CLOSED);
	    }
	  if (IsSet (pexit->rs_flags, EX_CLOSED)
	      && !IsSet (pexit->rs_flags, EX_ISDOOR))
	    {
	      ToggleBit (pexit->rs_flags, EX_ISDOOR);
	    }
	  pexit->exit_info = pexit->rs_flags;

	  if (pexit->u1.to_room
	      && (toExit = pexit->u1.to_room->exit[rev_dir[door]]))
	    {

	      toExit->rs_flags = pexit->rs_flags;

	      if ((IsSet (toExit->rs_flags, EX_PICKPROOF)
		   || IsSet (toExit->rs_flags, EX_EASY)
		   || IsSet (toExit->rs_flags, EX_HARD)
		   || IsSet (toExit->rs_flags, EX_INFURIATING))
		  && !IsSet (toExit->rs_flags, EX_LOCKED))
		{
		  ToggleBit (toExit->rs_flags, EX_LOCKED);
		}
	      if ((IsSet (toExit->rs_flags, EX_LOCKED)
		   || IsSet (toExit->rs_flags, EX_NOPASS))
		  && !IsSet (toExit->rs_flags, EX_CLOSED))
		{
		  ToggleBit (toExit->rs_flags, EX_CLOSED);
		}

	      if (IsSet (toExit->rs_flags, EX_CLOSED)
		  && !IsSet (toExit->rs_flags, EX_ISDOOR))
		{
		  ToggleBit (toExit->rs_flags, EX_ISDOOR);
		}
	      toExit->exit_info = toExit->rs_flags;
	    }
	  act ("Exit is now $t.", ch, arg2, pexit, TO_CHAR);
	  SetBit (ch->in_room->area->area_flags, AREA_CHANGED);
	  return;
	}
      else if (!str_prefix (arg2, "key"))
	{
	  ObjData *key;

	  if ((key = get_obj_here (ch, NULL, argument)) == NULL
	      || key->item_type != ITEM_KEY)
	    {
	      chprintln (ch, "No such key!");
	      return;
	    }

	  pexit->key = key->pIndexData->vnum;
	  SetBit (ch->in_room->area->area_flags, AREA_CHANGED);
	  act ("Exit now uses $p as a key.", ch, key, pexit->keyword,
	       TO_CHAR);
	  return;
	}
      else if (!str_prefix (arg2, "name"))
	{
	  replace_str (&pexit->keyword, argument);
	  if (NullStr (argument))
	    chprintln (ch, "Exit name removed.");
	  else
	    act ("Exit is now named $T.", ch, dir_name[door], argument,
		 TO_CHAR);
	  SetBit (ch->in_room->area->area_flags, AREA_CHANGED);
	  return;
	}
      else if (!str_prefix (arg2, "describe"))
	{
	  replace_str (&pexit->description, argument);
	  if (NullStr (argument))
	    chprintln (ch, "Exit description removed.");
	  else
	    act ("Exit is now described as $T.", ch, dir_name[door],
		 argument, TO_CHAR);
	  SetBit (ch->in_room->area->area_flags, AREA_CHANGED);
	  return;
	}
    }
  home_door (n_fun, ch, "");
}

Do_Fun (home_describe)
{
  RoomIndex *loc;
  char buf[MSL];
  int len;
  bool found = false;

  loc = ch->in_room;

  if (!ch || IsNPC (ch))
    return;

  if (!HAS_HOME (ch))
    {
      chprintln (ch, "You have to BUY a home before you can describe it.");
      return;
    }
  if (!is_home_owner (ch, loc))
    {
      chprintln (ch, "But you do not own this room!");
      return;
    }
  if (NullStr (argument))
    {
      chprintln (ch, "This command allows you to describe your home.");
      chprintln (ch, "You should not describe items that are in the room,");
      chprintln (ch,
		 "rather allowing the furnishing of the home to do that.");
      chprintln (ch,
		 "If you currently own this room, you will be placed into.");
      chprintln (ch,
		 "the room editor. Be warned that while in the room editor,");
      chprintln (ch,
		 "you are only allowed to type the description. If you are");
      chprintln (ch,
		 "unsure or hesitant about this, please note the Immortals,");
      chprintln (ch, "or better, discuss the how-to's with a Builder.");
      chprintlnf (ch,
		  "If Using the string editor bothers you type /q on a new line"
		  NEWLINE "and use the syntax: %s +/- <string>{x", n_fun);
      string_append (ch, &loc->description);
      SetBit (loc->area->area_flags, AREA_CHANGED);
      return;
    }
  else
    {
      buf[0] = '\0';

      if (argument[0] == '-')
	{
	  if (NullStr (loc->description))
	    {
	      chprintln (ch, "No lines left to remove.");
	      return;
	    }

	  strcpy (buf, loc->description);

	  for (len = strlen (buf); len > 0; len--)
	    {
	      if (buf[len] == '\r')
		{
		  if (!found)
		    {

		      if (len > 0)
			len--;
		      found = true;
		    }
		  else
		    {

		      buf[len + 1] = '\0';
		      replace_str (&loc->description, buf);
		      chprintln (ch, "OK.");
		      return;
		    }
		}
	    }
	  buf[0] = '\0';
	  replace_str (&loc->description, buf);
	  chprintln (ch, "Description cleared.");
	  return;
	}
      if (argument[0] == '+')
	{
	  if (loc->description != NULL)
	    strcat (buf, loc->description);
	  argument++;
	  while (isspace (*argument))
	    argument++;
	}

      if (strlen (buf) + strlen (argument) >= MSL - 2)
	{
	  chprintln (ch, "Description too long.");
	  return;
	}

      strcat (buf, argument);
      strcat (buf, NEWLINE);
      replace_str (&loc->description, buf);
      SetBit (loc->area->area_flags, AREA_CHANGED);
    }
  return;
}

Do_Fun (home_name)
{
  RoomIndex *loc;

  loc = ch->in_room;

  if (!ch || IsNPC (ch))
    return;

  if (!HAS_HOME (ch))
    {
      chprintln (ch, "You have to BUY a home before you can rename it.");
      return;
    }
  if (!is_home_owner (ch, loc))
    {
      chprintln (ch, "But you do not own this room!");
      return;
    }

  if (NullStr (argument))
    {
      chprintln (ch, "Change the this room title to what?");
      return;
    }

  if (cstrlen (argument) > 25)
    {
      chprintln (ch, "This is the name, not the description.");
      return;
    }

  replace_str (&loc->name, argument);
  SetBit (loc->area->area_flags, AREA_CHANGED);
  return;
}

Lookup_Fun (furnish_lookup)
{
  int i;

  for (i = 0; home_table[i].name != NULL; i++)
    {
      if (is_name (name, home_table[i].name))
	return i;
    }
  return -1;
}

Do_Fun (home_furnish)
{
  ResetData *loc_reset, *pReset;
  ObjData *furn = NULL;
  ObjIndex *pObj = NULL;
  CharData *moby = NULL;
  CharIndex *pMob = NULL;
  int i;

  if (!ch || IsNPC (ch))
    return;

  if (!HAS_HOME (ch))
    {
      chprintln (ch, "You have to BUY a home before you can furnish it.");
      return;
    }

  if (!is_home_owner (ch, ch->in_room))
    {
      chprintln (ch, "But you do not own this room!");
      return;
    }

  if (NullStr (argument) || !str_cmp (argument, "list"))
    {
      chprintln (ch, "This command allows you to furnish your home.");
      chprintln (ch,
		 "You must be standing in your home. You cannot have more");
      chprintln (ch, "than five items and five mobiles in your home.");
      chprintln (ch,
		 "Other things like heal/mana rate have a limit instead.");
      cmd_syntax (ch, NULL, n_fun, "<option>", NULL);
      for (i = 0; home_table[i].name != NULL; i++)
	{
	  switch (home_table[i].type)
	    {
	    default:
	    case VNUM_NONE:
	    case ROOM_VNUM:
	      break;
	    case MOB_VNUM:
	      pMob = get_char_index (home_table[i].vnum);
	      break;
	    case OBJ_VNUM:
	      pObj = get_obj_index (home_table[i].vnum);
	      break;
	    }
	  chprintlnf (ch, "\t%-15s - %ld gold, %dqp, %dtp (%s)",
		      pMob ? pMob->short_descr : pObj ? pObj->
		      short_descr : home_table[i].list, home_table[i].cost,
		      home_table[i].quest, home_table[i].tp,
		      home_table[i].name);
	  pMob = NULL;
	  pObj = NULL;
	}
      return;
    }
  else
    {
      if ((i = furnish_lookup (argument)) == -1)
	{
	  chprintln (ch, "Furnish what?");
	}

      if (!check_worth (ch, home_table[i].cost, VALUE_GOLD))
	{
	  chprintlnf
	    (ch, "You don't have enough gold for to buy a %s.",
	     home_table[i].name);
	  return;
	}
      if (ch->pcdata->quest.points < home_table[i].quest)
	{
	  chprintlnf (ch, "You need %s to buy a %s.",
		      intstr (home_table[i].quest, "questpoint"),
		      home_table[i].name);
	  return;
	}
      if (ch->pcdata->trivia < home_table[i].tp)
	{
	  chprintlnf (ch, "You need %s to buy a %s.",
		      intstr (home_table[i].quest, "trivia point"),
		      home_table[i].name);
	  return;
	}
      if (home_table[i].type == VNUM_NONE)
	{
	  if (!str_cmp (home_table[i].name, "healrate"))
	    {
	      if (ch->in_room->heal_rate >= 200)
		{
		  chprintln (ch, "This rooms heal rate can't go any higher.");
		  return;
		}
	      ch->in_room->heal_rate = Min (ch->in_room->heal_rate + 50, 200);
	    }
	  if (!str_cmp (home_table[i].name, "manarate"))
	    {
	      if (ch->in_room->mana_rate >= 200)
		{
		  chprintln (ch, "This rooms mana rate can't go any higher.");
		  return;
		}
	      ch->in_room->mana_rate = Min (ch->in_room->mana_rate + 50, 200);
	    }
	}
      else if (home_table[i].type == OBJ_VNUM)
	{
	  for (furn = ch->in_room->content_first; furn;
	       furn = furn->next_content)
	    {
	      if (furn->pIndexData->vnum == home_table[i].vnum)
		{
		  chprintln
		    (ch, "You already have one of those in this room.");
		  return;
		}
	    }
	  if ((pObj = get_obj_index (home_table[i].vnum)) != NULL)
	    {
	      furn = create_object (pObj, pObj->level);
	      obj_to_room (furn, ch->in_room);
	    }
	  else
	    chprintln
	      (ch, "Cannot find that object, please contact an immortal.");
	}
      else if (home_table[i].type == MOB_VNUM)
	{
	  for (pReset = ch->in_room->reset_first; pReset != NULL;
	       pReset = pReset->next)
	    {
	      if (pReset->command == 'M'
		  && pReset->arg1 == home_table[i].vnum)
		{
		  chprintln
		    (ch, "You already have one of those in this room.");
		  return;
		}
	    }

	  if ((pMob = get_char_index (home_table[i].vnum)) != NULL)
	    {
	      moby = create_mobile (pMob);
	      loc_reset = new_reset ();
	      loc_reset->command = 'M';
	      loc_reset->arg1 = pMob->vnum;
	      loc_reset->arg2 =
		ch->in_room->area->max_vnum - ch->in_room->area->min_vnum;
	      loc_reset->arg3 = ch->in_room->vnum;
	      loc_reset->arg4 = 1;
	      add_reset (ch->in_room, loc_reset, 0);
	      char_to_room (moby, ch->in_room);
	    }
	  else
	    chprintln
	      (ch, "Cannot find that mobile, please contact an immortal.");
	}
      else
	{
	  chprintln
	    (ch,
	     "Unable to complete your request.  Please inform an Immortal.");
	  return;
	}

      if (home_table[i].room_flags > 0)
	SetBit (ch->in_room->room_flags, home_table[i].room_flags);

      deduct_cost (ch, home_table[i].cost, VALUE_GOLD);

      ch->pcdata->quest.points -= home_table[i].quest;
      ch->pcdata->trivia -= home_table[i].tp;

      chprintlnf
	(ch,
	 "You have been deducted %ld gold, %dqp and %dtp for your purchase.",
	 home_table[i].cost, home_table[i].quest, home_table[i].tp);
      SetBit (ch->in_room->area->area_flags, AREA_CHANGED);
      return;
    }
}

Do_Fun (home_unfurnish)
{
  ResetData *pReset = NULL, *pResNext;
  ObjData *obj, *obj_next;
  CharData *mob, *mob_next;
  int i = 0;
  bool found = false;

  if (!ch || IsNPC (ch))
    return;

  if (!HAS_HOME (ch))
    {
      chprintln (ch, "You have to BUY a home before you can furnish it.");
      return;
    }

  if (!ch->in_room || !is_home_owner (ch, ch->in_room))
    {
      chprintln (ch, "But you do not own this room!");
      return;
    }

  if (NullStr (argument))
    {
      chprintln (ch, "Unfurnish which object?.");
      return;
    }
  else
    {
      for (i = 0; home_table[i].name != NULL; i++)
	{
	  if (home_table[i].type == VNUM_NONE || home_table[i].vnum <= 0)
	    continue;

	  if (home_table[i].type == MOB_VNUM)
	    {
	      for (pReset = ch->in_room->reset_first; pReset;
		   pReset = pResNext)
		{
		  pResNext = pReset->next;

		  if (pReset->command == 'M'
		      && pReset->arg1 == home_table[i].vnum)
		    {
		      unlink_reset (ch->in_room, pReset);
		      free_reset (pReset);
		      if (home_table[i].room_flags > 0)
			RemBit (ch->in_room->room_flags,
				home_table[i].room_flags);
		      SetBit (ch->in_room->area->area_flags, AREA_CHANGED);
		      found = true;
		      for (mob = ch->in_room->person_first; mob != NULL;
			   mob = mob_next)
			{
			  mob_next = mob->next_in_room;

			  if (IsNPC (mob)
			      && mob->pIndexData->vnum == home_table[i].vnum)
			    extract_char (mob, true);
			}
		    }
		}
	    }
	  else if (home_table[i].type == OBJ_VNUM)
	    {
	      for (obj = ch->in_room->content_first; obj != NULL;
		   obj = obj_next)
		{
		  obj_next = obj->next_content;

		  if (obj->pIndexData->vnum == home_table[i].vnum)
		    {
		      extract_obj (obj);
		      found = true;
		      if (home_table[i].room_flags > 0)
			RemBit (ch->in_room->room_flags,
				home_table[i].room_flags);
		    }
		}
	    }

	  add_cost (ch, home_table[i].cost / 3, VALUE_GOLD);
	  ch->pcdata->quest.points += home_table[i].quest / 3;
	  ch->pcdata->trivia += home_table[i].tp / 3;
	  chprintlnf
	    (ch,
	     "You sell %s back to the mayor for %ld gold, %dqp and %dtp.",
	     home_table[i].list, home_table[i].cost / 3,
	     home_table[i].quest / 3, home_table[i].tp / 3);
	}
      if (!found)
	chprintln (ch, "Unable to find your request.");
      return;
    }
}

Do_Fun (do_gohome)
{
  RoomIndex *location = NULL;
  int i;

  if (!ch)
    return;

  if (IsNPC (ch))
    {
      chprintln (ch, "Only players can go home.");
      return;
    }

  if (!HOME_ROOM (ch) || (location = get_room_index (HOME_ROOM (ch))) == NULL)
    {
      for (i = 0; i < HAS_HOME (ch); i++)
	if ((location = get_room_index (ch->pcdata->home[i])) != NULL)
	  break;

      if (location == NULL)
	{
	  chprintln (ch, "You don't have a home *pout*.");
	  return;
	}
    }

  perform_recall (ch, location, "go home");
  return;
}

Do_Fun (home_evict)
{
  CharData *victim;

  if (!HAS_HOME (ch))
    {
      chprintln (ch, "You don't have a home to kick them out of.");
      return;
    }

  if (NullStr (argument))
    {
      cmd_syntax (ch, NULL, n_fun, "evict <char>", NULL);
      return;
    }

  if ((victim = get_char_world (ch, argument)) == NULL)
    {
      chprintln (ch, "They aren't here.");
      return;
    }

  if (IsNPC (victim))
    {
      chprintln (ch, "No such player.");
      return;
    }

  if (victim == ch)
    {
      chprintln (ch, "What would be the point of that?");
      return;
    }

  if (ch->fighting != NULL)
    {
      chprintln (ch, "Your a little busy right now.");
      return;
    }

  if (!is_home_owner (ch, victim->in_room))
    {
      chprintln (ch, "They aren't in your home.");
      return;
    }

  if (victim->fighting != NULL)
    stop_fighting (victim, true);

  char_from_room (victim);
  char_to_room (victim, get_room_index (ROOM_VNUM_TEMPLE));
  act ("$n has someone escort you out of $s home.", ch, NULL, victim,
       TO_VICT);
  do_function (victim, &do_look, "auto");
  act ("You are no longer welcome in $n's home.", ch, NULL, victim, TO_VICT);
  act ("$N is no longer welcome in your home.", ch, NULL, victim, TO_CHAR);
  return;
}

Do_Fun (home_link)
{
  int door;
  AreaData *pArea;
  RoomIndex *pRoom, *toRoom = NULL;
  char arg[MIL];
  vnum_t vnum;

  if (!HAS_HOME (ch))
    {
      chprintlnf (ch, "Use '%s buy' to purchase a home first.",
		  cmd_name (do_home));
      return;
    }

  if (!check_worth (ch, ROOM_PRICE, VALUE_GOLD))
    {
      chprintln (ch, "{wYou don't have enough to buy a another room.");
      return;
    }

  if (ch->pcdata->quest.points < 125)
    {
      chprintln (ch, "It costs 125 quest points to add a link to this room.");
      return;
    }

  if (!is_home_owner (ch, ch->in_room))
    {
      chprintln (ch, "You must add only to your home!");
      return;
    }

  argument = one_argument (argument, arg);

  if ((door = get_direction (arg)) == -1)
    {
      chprintln (ch, "Which direction from here do you want the new exit?");
      return;
    }

  pRoom = ch->in_room;

  if (pRoom->exit[door])
    {
      chprintln (ch, "An exit already exits in that location.");
      return;
    }

  if (NullStr (argument))
    {
      chprintln (ch, "Invalid area to link to.");
      return;
    }

  for (pArea = area_first; pArea != NULL; pArea = pArea->next)
    {
      if (!str_prefix (argument, pArea->name))
	break;
    }

  if (pArea == NULL)
    {
      chprintln (ch, "Invalid area to link to.");
      return;
    }

  for (vnum = pArea->min_vnum; vnum < pArea->max_vnum; vnum++)
    {
      if ((toRoom = get_room_index (vnum)) != NULL)
	break;
    }

  if (toRoom == NULL)
    {
      chprintln (ch, "Unable to link to that area.");
      return;
    }

  pRoom->exit[door] = new_exit ();

  pRoom->exit[door]->u1.to_room = toRoom;

  pRoom->exit[door]->orig_door = door;

  SetBit (pRoom->area->area_flags, AREA_CHANGED);
  deduct_cost (ch, ROOM_PRICE, VALUE_GOLD);
  ch->pcdata->quest.points -= 125;
  chprintlnf (ch, "Exit to %s added.", pArea->name);
}

Do_Fun (home_unlink)
{
  char arg[MIL];

  RoomIndex *pRoom, *pToRoom;
  int rev, door;

  if (!HAS_HOME (ch))
    {
      chprintln (ch, "You don't own a home.");
      return;
    }

  if (!is_home_owner (ch, ch->in_room))
    {
      chprintln (ch, "You don't own this room!");
      return;
    }

  argument = one_argument (argument, arg);

  if ((door = get_direction (arg)) == -1)
    {
      chprintln (ch,
		 "Which direction from here do you want to delete the exit?");
      return;
    }

  pRoom = ch->in_room;

  if (!pRoom->exit[door])
    {
      chprintln (ch, "There is no exit in that direction.");
      return;
    }

  rev = rev_dir[door];
  pToRoom = pRoom->exit[door]->u1.to_room;

  if (pToRoom->area == pRoom->area)
    {
      chprintln (ch, "You can't unlink that exit.");
      return;
    }
  free_exit (pRoom->exit[door]);
  pRoom->exit[door] = NULL;

  add_cost (ch, ROOM_PRICE / 3, VALUE_GOLD);
  ch->pcdata->quest.points += 125 / 3;
  SetBit (pRoom->area->area_flags, AREA_CHANGED);
  chprintln (ch, "Exit unlinked.");
  return;
}

Do_Fun (home_invite)
{
  CharData *vch;

  if (NullStr (argument))
    {
      cmd_syntax (ch, NULL, n_fun, "invite <person>", NULL);
      return;
    }

  if ((vch = get_char_world (ch, argument)) == NULL)
    {
      chprintln (ch, "They aren't online.");
      return;
    }

  if (IsNPC (vch))
    {
      chprintln (ch, "You can't invite them.");
      return;
    }

  if (vch == ch)
    {
      chprintln (ch, "You can't invite yourself.");
      return;
    }
  if (vch->pcdata->home_invite != 0)
    {
      chprintln (ch, "They have already been invited somewhere.");
      return;
    }
  if (vch->fighting != NULL)
    {
      chprintln (ch, "They're a little busy right now.");
      return;
    }

  vch->pcdata->home_invite = ch->id;

  act
    ("$n has invited you to $s home. Type '$t accept' to accept the invitation"
     NEWLINE
     "and be transfered to $s home, or type '$t deny' to cancel the invitation.",
     ch, cmd_name (do_home), vch, TO_VICT);

  act ("Invitation sent to $N.", ch, NULL, vch, TO_CHAR);
}

Do_Fun (home_accept)
{
  CharData *owner;
  RoomIndex *pRoom;
  int i;

  if (ch->pcdata->home_invite == 0)
    {
      chprintln (ch, "You haven't been invited anywhere.");
      return;
    }

  if (ch->fighting != NULL || InWar (ch))
    {
      chprintln (ch, "You're a little busy right now.");
      return;
    }

  if ((owner = get_char_vnum (ch->pcdata->home_invite)) == NULL)
    {
      chprintln (ch,
		 "Hmm, seems the person who invited you isn't around anymore.");
      return;
    }

  pRoom = NULL;

  if ((pRoom = get_room_index (HOME_ROOM (owner))) == NULL)
    {
      for (i = 0; i < HAS_HOME (ch); i++)
	if ((pRoom = get_room_index (owner->pcdata->home[i])) != NULL)
	  break;
    }

  if (!pRoom)
    {
      chprintln (ch, "BUG: inviter doesn't own a home!");
      return;
    }

  char_from_room (ch);
  char_to_room (ch, pRoom);
  act ("$n arrives.", ch, NULL, NULL, TO_ROOM);
  do_function (ch, &do_look, "auto");
  ch->pcdata->home_invite = 0;
  return;
}

Do_Fun (home_deny)
{
  CharData *owner;

  if (ch->pcdata->home_invite == 0)
    {
      chprintln (ch, "You haven't been invited anywhere.");
      return;
    }

  owner = get_char_vnum (ch->pcdata->home_invite);

  if (owner)
    {
      act ("You turn down $N's invitation.", ch, NULL, owner, TO_CHAR);
      act ("$n turns down your invitation.", ch, NULL, owner, TO_VICT);
    }
  else
    chprintln (ch, "You turn down the invitation.");

  ch->pcdata->home_invite = 0;
}


Do_Fun (home_recall)
{
  if (!HAS_HOME (ch))
    {
      chprintln (ch, "You don't own a home.");
      return;
    }

  if (!is_home_owner (ch, ch->in_room))
    {
      chprintln (ch, "This isn't your home!");
      return;
    }

  ch->pcdata->home_room = ch->in_room->vnum;
  chprintlnf (ch, "You can use '%s' to come back here.",
	      cmd_name (do_gohome));
}

void
delete_home (CharData * ch)
{
  int i;
  char buf[MSL];

  if (HAS_HOME (ch))
    {
      RoomIndex *pRoom;
      AreaData *pArea = NULL;

      for (i = 0; i < PC_HOME_COUNT; i++)
	{
	  if ((pRoom = get_room_index (ch->pcdata->home[i])) != NULL)
	    {
	      pArea = pRoom->area;
	      sprintf (buf, "%ld", ch->pcdata->home[i]);
	      redit_delete (NULL, NULL, buf);
	    }
	}
      sprintf (buf, "%ld", HOME_KEY (ch));
      oedit_delete (NULL, NULL, buf);
      if (pArea)
	SetBit (pArea->area_flags, AREA_CHANGED);
    }
}


void
write_room_objs (RoomIndex * pRoom, FileData * fp)
{
  if (!pRoom || !pRoom->content_last || !fp)
    return;

  write_obj (NULL, pRoom->content_last, fp, 0, 0, SAVE_ROOM);
}

void
save_room_objs (void)
{
  FileData *fp;
  RoomIndex *pRoom;
  int iHash;

  if ((fp = f_open (ROOM_OBJS_FILE, "w")) != NULL)
    {
      for (iHash = 0; iHash < MAX_KEY_HASH; iHash++)
	{
	  for (pRoom = room_index_hash[iHash]; pRoom; pRoom = pRoom->next)
	    {
	      if (!IsSet (pRoom->room_flags, ROOM_SAVE_OBJS)
		  && (!IsSet (pRoom->area->area_flags, AREA_PLAYER_HOMES)
		      || NullStr (pRoom->owner)))
		continue;

	      write_room_objs (pRoom, fp);
	    }
	}
      f_printf (fp, "#" END_MARK LF);
      f_close (fp);
    }
}

void
load_room_objs (void)
{
  FileData *fp;

  log_string ("Loading saved objects...");

  if ((fp = f_open (ROOM_OBJS_FILE, "r")) == NULL)
    {
      bug (ROOM_OBJS_FILE " not found");
    }
  else
    {
      for (;;)
	{
	  char letter;
	  char *word;

	  letter = read_letter (fp);
	  if (letter == '*')
	    {
	      read_to_eol (fp);
	      continue;
	    }

	  if (letter != '#')
	    {
	      bug ("# not found.");
	      break;
	    }

	  word = read_word (fp);
	  if (!str_cmp (word, get_obj_save_header (SAVE_ROOM)))
	    read_obj (NULL, fp, SAVE_ROOM);
	  else if (!str_cmp (word, "END"))
	    break;
	  else
	    {
	      bug ("bad section.");
	      break;
	    }
	}
      f_close (fp);
    }

  return;
}