1stMud4.5.3/
1stMud4.5.3/backup/
1stMud4.5.3/bin/
1stMud4.5.3/bin/extras/
1stMud4.5.3/data/i3/
1stMud4.5.3/doc/1stMud/
1stMud4.5.3/doc/Diku/
1stMud4.5.3/doc/MPDocs/
1stMud4.5.3/doc/Rom/
1stMud4.5.3/notes/
/**************************************************************************
*  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;
			}
			TouchArea(pArea);
			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);
	TouchArea(pArea);
	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)
			{
				TouchArea(pRoom->area);
				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)
		{
			TouchArea(pRoom->area);
			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);
			TouchArea(ch->in_room->area);
			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;
			TouchArea(ch->in_room->area);
			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);
			TouchArea(ch->in_room->area);
			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);
			TouchArea(ch->in_room->area);
			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);
		TouchArea(loc->area);
		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);
		TouchArea(loc->area);
	}
	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);
	TouchArea(loc->area);
	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);
		TouchArea(ch->in_room->area);
		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);
						TouchArea(ch->in_room->area);
						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;

	TouchArea(pRoom->area);
	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;
	TouchArea(pRoom->area);
	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)
			TouchArea(pArea);
	}
}

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;
}