Lyonesse/bin/
Lyonesse/doc/eng/
Lyonesse/doc/ita/
Lyonesse/lib/
Lyonesse/lib/buildings/
Lyonesse/lib/clans/
Lyonesse/lib/data/
Lyonesse/lib/etc/
Lyonesse/lib/house/
Lyonesse/lib/misc/
Lyonesse/lib/plralias/A-E/
Lyonesse/lib/plralias/F-J/
Lyonesse/lib/plralias/K-O/
Lyonesse/lib/plralias/P-T/
Lyonesse/lib/plralias/U-Z/
Lyonesse/lib/plralias/ZZZ/
Lyonesse/lib/plrobjs/A-E/
Lyonesse/lib/plrobjs/F-J/
Lyonesse/lib/plrobjs/K-O/
Lyonesse/lib/plrobjs/P-T/
Lyonesse/lib/plrobjs/U-Z/
Lyonesse/lib/plrobjs/ZZZ/
Lyonesse/lib/plrsave/A-E/
Lyonesse/lib/plrsave/F-J/
Lyonesse/lib/plrsave/K-O/
Lyonesse/lib/plrsave/P-T/
Lyonesse/lib/plrsave/U-Z/
Lyonesse/lib/plrsave/ZZZ/
Lyonesse/lib/ships/
Lyonesse/lib/stables/
Lyonesse/lib/text/help/
Lyonesse/lib/world/
Lyonesse/lib/world/bld/
Lyonesse/lib/world/ship/
Lyonesse/lib/world/shp/
Lyonesse/lib/world/wls/
Lyonesse/lib/world/wls/Life/
Lyonesse/lib/world/wls/Map/
Lyonesse/log/
/**************************************************************************
 * #   #   #   ##   #  #  ###   ##   ##  ###       http://www.lyonesse.it *
 * #    # #   #  #  ## #  #    #    #    #                                *
 * #     #    #  #  # ##  ##    #    #   ##   ## ##  #  #  ##             *
 * #     #    #  #  # ##  #      #    #  #    # # #  #  #  # #            *
 * ###   #     ##   #  #  ###  ##   ##   ###  #   #  ####  ##    Ver. 1.0 *
 *                                                                        *
 * -Based on CircleMud & Smaug-     Copyright (c) 2001-2002 by Mithrandir *
 *                                                                        *
 * ********************************************************************** */
/* ************************************************************************
*   File: house.c                                       Part of CircleMUD *
*  Usage: Handling of player houses                                       *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */

#include "conf.h"
#include "sysdep.h"

#include "structs.h"
#include "comm.h"
#include "handler.h"
#include "db.h"
#include "interpreter.h"
#include "utils.h"
#include "house.h"
#include "constants.h"

/* external globals */
extern bool LoadingCharObj;

/* local globals */
HOUSE_CONTROL house_control[MAX_HOUSES];
int num_of_houses = 0;

/* local functions */
int House_get_filename(int vnum, char *filename);
int House_load(room_vnum vnum);
int House_save( OBJ_DATA *obj, FILE *fp, int location );
void House_restore_weight(OBJ_DATA *obj);
void House_delete_file(int vnum);
int find_house(room_vnum vnum);
void House_save_control(void);
void hcontrol_list_houses(CHAR_DATA *ch);
void hcontrol_build_house(CHAR_DATA *ch, char *arg);
void hcontrol_destroy_house(CHAR_DATA *ch, char *arg);
void hcontrol_pay_house(CHAR_DATA *ch, char *arg);
ACMD(do_hcontrol);
ACMD(do_house);

/* external functions */
OBJ_DATA *fread_one_obj( FILE *fp, int *location );
int fwrite_one_obj( OBJ_DATA *obj, FILE *fp, int mode, int location );


/* First, the basics: finding the filename; loading/saving objects */

/* Return a filename given a house vnum */
int House_get_filename(int vnum, char *filename)
{
	if (vnum == NOWHERE)
		return (0);
	
	sprintf(filename, LIB_HOUSE"%d.house", vnum);
	return (1);
}

#define MAX_BAG_ROWS	5

/* Load all objects for a house */
int House_load(room_vnum vnum)
{
	FILE *fp;
	ROOM_DATA *pRoom = NULL;
	OBJ_DATA *obj, *obj2, *cont_row[MAX_BAG_ROWS];
	char fname[128];
	char letter;
	char *word;
	int num_objs = 0, j;
	int location;
	
	/* Empty all of the container lists (you never know ...) */
	for (j = 0; j < MAX_BAG_ROWS; j++)
		cont_row[j] = NULL;

	if (!(pRoom = get_room(vnum)))
		return (0);
	if (!House_get_filename(vnum, fname))
		return (0);
	if (!(fp = fopen(fname, "r")))	/* no file found */
		return (0);
	if (feof(fp))
		return (0);
	
	LoadingCharObj = TRUE;

	for ( ; ; )
	{
		letter = fread_letter( fp );
		if ( letter == '*' )
		{
			fread_to_eol( fp );
			continue;
		}
		
		if ( feof( fp ) )
			break;

		if ( letter != '#' )
		{
			log( "SYSERR: LoadRooms: # not found." );
			break;
		}
		
		word = fread_word( fp );

		if ( !strcmp( word, "OBJECT" ) )	// Objects
		{
			if ( ( obj = fread_one_obj( fp, &location ) ) == NULL )
				continue;
			
			num_objs += obj->count;

			obj_to_room( obj, pRoom );

			for (j = MAX_BAG_ROWS - 1; j > -location; j--)
			{
				if (cont_row[j])
				{
					for (; cont_row[j]; cont_row[j] = obj2)
					{
						obj2 = cont_row[j]->next_content;
						obj_to_room( cont_row[j], pRoom );
					}
					cont_row[j] = NULL;
				}
			}
			if (j == -location && cont_row[j])
			{
				if (GET_OBJ_TYPE(obj) == ITEM_CONTAINER ||
				    GET_OBJ_TYPE(obj) == ITEM_MISSILE_CONT)
				{
					obj_from_room(obj);
					obj->first_content	= NULL;
					obj->last_content	= NULL;
					for (; cont_row[j]; cont_row[j] = obj2)
					{
						obj2 = cont_row[j]->next_content;
						obj_to_obj(cont_row[j], obj);
					}
					obj_to_room( obj, pRoom );
				}
				else
				{
					for (; cont_row[j]; cont_row[j] = obj2)
					{
						obj2 = cont_row[j]->next_content;
						obj_to_room( cont_row[j], pRoom );
					}
					cont_row[j] = NULL;
				}
			}
			if (location < 0 && location >= -MAX_BAG_ROWS)
			{
				obj_from_room(obj);
				if ((obj2 = cont_row[-location - 1]) != NULL)
				{
					while (obj2->next_content)
						obj2 = obj2->next_content;
					obj2->next_content = obj;
				}
				else
					cont_row[-location - 1] = obj;
			}
		}
		else if ( !strcmp( word, "END"    ) )	// Done
			break;
	}

	LoadingCharObj = FALSE;

	fclose( fp );
	return (1);
}

/* Save all objects for a house (recursive; initial call must be followed
   by a call to House_restore_weight)  Assumes file is open already. */
int House_save( OBJ_DATA *obj, FILE *fp, int location )
{
	OBJ_DATA *tmp;
	int result;
	
	if (obj)
	{
		House_save( obj->prev_content, fp, MIN(0, location) - 1 );
		House_save( obj->last_content, fp, location );

		result = fwrite_one_obj(obj, fp, 0, 0);

		if (!result)
			return (0);
		
		for (tmp = obj->in_obj; tmp; tmp = tmp->in_obj)
			GET_OBJ_WEIGHT(tmp) -= GET_OBJ_WEIGHT(obj);
	}
	return (1);
}


/* restore weight of containers after House_save has changed them for saving */
void House_restore_weight(OBJ_DATA *obj)
{
	if (obj)
	{
		House_restore_weight(obj->first_content);
		House_restore_weight(obj->next_content);
		if (obj->in_obj)
			GET_OBJ_WEIGHT(obj->in_obj) += GET_OBJ_WEIGHT(obj);
	}
}


/* Save all objects in a house */
void House_crashsave(room_vnum vnum)
{
	FILE *fp;
	ROOM_DATA *pRoom;
	char buf[MAX_STRING_LENGTH];
	
	if (!(pRoom = get_room(vnum)))
		return;
	if (!House_get_filename(vnum, buf))
		return;
	if (!(fp = fopen(buf, "w")))
	{
		perror("SYSERR: Error saving house file");
		return;
	}

	if (!House_save(pRoom->last_content, fp, 0))
	{
		fclose(fp);
		return;
	}

	fclose(fp);

	House_restore_weight(pRoom->first_content);
	REMOVE_BIT(ROOM_FLAGS(pRoom), ROOM_HOUSE_CRASH);
}


/* Delete a house save file */
void House_delete_file(int vnum)
{
	char fname[MAX_INPUT_LENGTH];
	FILE *fl;
	
	if (!House_get_filename(vnum, fname))
		return;
	if (!(fl = fopen(fname, "r")))
	{
		if (errno != ENOENT)
			log("SYSERR: Error deleting house file #%d. (1): %s", vnum, strerror(errno));
		return;
	}

	fclose(fl);

	if (remove(fname) < 0)
		log("SYSERR: Error deleting house file #%d. (2): %s", vnum, strerror(errno));
}

/******************************************************************
 *  Functions for house administration (creation, deletion, etc.  *
 *****************************************************************/

int find_house(room_vnum vnum)
{
	int i;
	
	for (i = 0; i < num_of_houses; i++)
		if (house_control[i].vnum == vnum)
			return (i);
		
	return (NOWHERE);
}

/* Save the house control information */
void House_save_control(void)
{
	FILE *fl;
	
	if (!(fl = fopen(HCONTROL_FILE, "wb")))
	{
		perror("SYSERR: Unable to open house control file.");
		return;
	}

	/* write all the house control recs in one fell swoop.  Pretty nifty, eh? */
	fwrite(house_control, sizeof(HOUSE_CONTROL), num_of_houses, fl);
	
	fclose(fl);
}


/* call from boot_db - will load control recs, load objs, set atrium bits */
/* should do sanity checks on vnums & remove invalid records */
void House_boot(void)
{
	HOUSE_CONTROL temp_house;
	ROOM_DATA *real_house, *real_atrium;
	FILE *fl;
	EXIT_DATA *pexit;
	
	memset((char *)house_control,0,sizeof(HOUSE_CONTROL)*MAX_HOUSES);
	
	if (!(fl = fopen(HCONTROL_FILE, "rb")))
	{
		if (errno == ENOENT)
			log("   House control file '%s' does not exist.", HCONTROL_FILE);
		else
			perror("SYSERR: " HCONTROL_FILE);
		return;
	}

	while (!feof(fl) && num_of_houses < MAX_HOUSES)
	{
		fread(&temp_house, sizeof(HOUSE_CONTROL), 1, fl);
		
		if (feof(fl))
			break;
		
		if (get_name_by_id(temp_house.owner) == NULL)
			continue;			/* owner no longer exists -- skip */
		
		if (!(real_house = get_room(temp_house.vnum)))
			continue;			/* this vnum doesn't exist -- skip */
		
		if (find_house(temp_house.vnum) != NOWHERE)
			continue;			/* this vnum is already a house -- skip */
		
		if (!(real_atrium = get_room(temp_house.atrium)))
			continue;			/* house doesn't have an atrium -- skip */
		
		if (temp_house.exit_num < 0 || temp_house.exit_num >= NUM_OF_DIRS)
			continue;			/* invalid exit num -- skip */
		
		if ( !( pexit = get_exit( real_house, temp_house.exit_num ) ) ||
			pexit->to_room->number != real_atrium->number )
			continue;			/* exit num mismatch -- skip */
		
		house_control[num_of_houses++] = temp_house;
		
		SET_BIT(ROOM_FLAGS(real_house), ROOM_HOUSE | ROOM_PRIVATE);
		SET_BIT(ROOM_FLAGS(real_atrium), ROOM_ATRIUM);
		House_load(temp_house.vnum);
	}
	
	fclose(fl);
	House_save_control();
}



/* "House Control" functions */

const char *HCONTROL_FORMAT =
"Usage: hcontrol build <house vnum> <exit direction> <player name>\r\n"
"       hcontrol destroy <house vnum>\r\n"
"       hcontrol pay <house vnum>\r\n"
"       hcontrol show\r\n";

void hcontrol_list_houses(CHAR_DATA *ch)
{
	int i;
	char *timestr, *temp;
	char built_on[128], last_pay[128], own_name[128];
	
	if (!num_of_houses)
	{
		send_to_char("No houses have been defined.\r\n", ch);
		return;
	}
	strcpy(buf, "Address  Atrium  Build Date  Guests  Owner        Last Paymt\r\n");
	strcat(buf, "-------  ------  ----------  ------  ------------ ----------\r\n");
	send_to_char(buf, ch);
	
	for (i = 0; i < num_of_houses; i++)
	{
		/* Avoid seeing <UNDEF> entries from self-deleted people. -gg 6/21/98 */
		if ((temp = get_name_by_id(house_control[i].owner)) == NULL)
			continue;
		
		if (house_control[i].built_on)
		{
			timestr = asctime(localtime(&(house_control[i].built_on)));
			*(timestr + 10) = '\0';
			strcpy(built_on, timestr);
		}
		else
			strcpy(built_on, "Unknown");
		
		if (house_control[i].last_payment)
		{
			timestr = asctime(localtime(&(house_control[i].last_payment)));
			*(timestr + 10) = '\0';
			strcpy(last_pay, timestr);
		}
		else
			strcpy(last_pay, "None");
		
		/* Now we need a copy of the owner's name to capitalize. -gg 6/21/98 */
		strcpy(own_name, temp);
		
		sprintf(buf, "%7d %7d  %-10s    %2d    %-12s %s\r\n",
			house_control[i].vnum, house_control[i].atrium, built_on,
			house_control[i].num_of_guests, CAP(own_name), last_pay);
		
		send_to_char(buf, ch);
		House_list_guests(ch, i, TRUE);
	}
}



void hcontrol_build_house(CHAR_DATA *ch, char *arg)
{
	HOUSE_CONTROL temp_house;
	ROOM_DATA *real_house, *real_atrium;
	EXIT_DATA *pexit;
	char arg1[MAX_INPUT_LENGTH];
	sh_int exit_num;
	room_vnum virt_house, virt_atrium;
	long owner;
	
	if (num_of_houses >= MAX_HOUSES)
	{
		send_to_char("Max houses already defined.\r\n", ch);
		return;
	}
	
	/* first arg: house's vnum */
	arg = one_argument(arg, arg1);
	if (!*arg1)
	{
		send_to_char(HCONTROL_FORMAT, ch);
		return;
	}
	virt_house = atoi(arg1);
	if (!(real_house = get_room(virt_house)))
	{
		send_to_char("No such room exists.\r\n", ch);
		return;
	}
	if ((find_house(virt_house)) != NOWHERE)
	{
		send_to_char("House already exists.\r\n", ch);
		return;
	}
	
	/* second arg: direction of house's exit */
	arg = one_argument(arg, arg1);
	if (!*arg1)
	{
		send_to_char(HCONTROL_FORMAT, ch);
		return;
	}
	if ((exit_num = search_block(arg1, dirs, FALSE)) < 0)
	{
		sprintf(buf, "'%s' is not a valid direction.\r\n", arg1);
		send_to_char(buf, ch);
		return;
	}
	if ( ( pexit = get_exit(real_house, exit_num) ) == NULL)
	{
		sprintf(buf, "There is no exit %s from room %d.\r\n", dirs[exit_num],
			virt_house);
		send_to_char(buf, ch);
		return;
	}
	
	real_atrium = pexit->to_room;
	virt_atrium = real_atrium->number;
	
	if ( pexit->rexit->to_room->number != real_house->number )
	{
		send_to_char("A house's exit must be a two-way door.\r\n", ch);
		return;
	}
	
	/* third arg: player's name */
	one_argument(arg, arg1);
	if (!*arg1)
	{
		send_to_char(HCONTROL_FORMAT, ch);
		return;
	}
	if ((owner = get_id_by_name(arg1)) < 0)
	{
		sprintf(buf, "Unknown player '%s'.\r\n", arg1);
		send_to_char(buf, ch);
		return;
	}
	
	temp_house.mode			= HOUSE_PRIVATE;
	temp_house.vnum			= virt_house;
	temp_house.atrium		= virt_atrium;
	temp_house.exit_num		= exit_num;
	temp_house.built_on		= time(0);
	temp_house.last_payment		= 0;
	temp_house.owner		= owner;
	temp_house.num_of_guests	= 0;
	
	house_control[num_of_houses++]	= temp_house;
	
	SET_BIT(ROOM_FLAGS(real_house), ROOM_HOUSE | ROOM_PRIVATE);
	SET_BIT(ROOM_FLAGS(real_atrium), ROOM_ATRIUM);
	House_crashsave(virt_house);
	
	send_to_char("House built.  Mazel tov!\r\n", ch);
	House_save_control();
}



void hcontrol_destroy_house(CHAR_DATA *ch, char *arg)
{
	ROOM_DATA *real_atrium, *real_house;
	int i, j;
	
	if (!*arg)
	{
		send_to_char(HCONTROL_FORMAT, ch);
		return;
	}
	if ((i = find_house(atoi(arg))) == NOWHERE)
	{
		send_to_char("Unknown house.\r\n", ch);
		return;
	}
	if (!(real_atrium = get_room(house_control[i].atrium)))
		log("SYSERR: House %d had invalid atrium %d!", atoi(arg),
			house_control[i].atrium);
	else
		REMOVE_BIT(ROOM_FLAGS(real_atrium), ROOM_ATRIUM);
	
	if (!(real_house = get_room(house_control[i].vnum)))
		log("SYSERR: House %d had invalid vnum %d!", atoi(arg), house_control[i].vnum);
	else
		REMOVE_BIT(ROOM_FLAGS(real_house), ROOM_HOUSE | ROOM_PRIVATE | ROOM_HOUSE_CRASH);
	
	House_delete_file(house_control[i].vnum);
	
	for (j = i; j < num_of_houses - 1; j++)
		house_control[j] = house_control[j + 1];
	
	num_of_houses--;
	
	send_to_char("House deleted.\r\n", ch);
	House_save_control();
	
	/*
	 * Now, reset the ROOM_ATRIUM flag on all existing houses' atriums,
	 * just in case the house we just deleted shared an atrium with another
	 * house.  --JE 9/19/94
	 */
	for (i = 0; i < num_of_houses; i++)
		if (!(real_atrium = get_room(house_control[i].atrium)))
			SET_BIT(ROOM_FLAGS(real_atrium), ROOM_ATRIUM);
}


void hcontrol_pay_house(CHAR_DATA *ch, char *arg)
{
	int i;
	
	if (!*arg)
		send_to_char(HCONTROL_FORMAT, ch);
	else if ((i = find_house(atoi(arg))) == NOWHERE)
		send_to_char("Unknown house.\r\n", ch);
	else
	{
		sprintf(buf, "Payment for house %s collected by %s.", arg, GET_NAME(ch));
		mudlog(buf, NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE);
		
		house_control[i].last_payment = time(0);
		House_save_control();
		send_to_char("Payment recorded.\r\n", ch);
	}
}


/* The hcontrol command itself, used by imms to create/destroy houses */
ACMD(do_hcontrol)
{
	char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH];
	
	half_chop(argument, arg1, arg2);
	
	if (is_abbrev(arg1, "build"))
		hcontrol_build_house(ch, arg2);
	else if (is_abbrev(arg1, "destroy"))
		hcontrol_destroy_house(ch, arg2);
	else if (is_abbrev(arg1, "pay"))
		hcontrol_pay_house(ch, arg2);
	else if (is_abbrev(arg1, "show"))
		hcontrol_list_houses(ch);
	else
		send_to_char(HCONTROL_FORMAT, ch);
}


/* The house command, used by mortal house owners to assign guests */
ACMD(do_house)
{
	int i, j, id;
	
	one_argument(argument, arg);
	
	if (!ROOM_FLAGGED(IN_ROOM(ch), ROOM_HOUSE))
		send_to_char("You must be in your house to set guests.\r\n", ch);
	else if ((i = find_house(ch->in_room->number)) == NOWHERE)
		send_to_char("Um.. this house seems to be screwed up.\r\n", ch);
	else if (GET_IDNUM(ch) != house_control[i].owner)
		send_to_char("Only the primary owner can set guests.\r\n", ch);
	else if (!*arg)
		House_list_guests(ch, i, FALSE);
	else if ((id = get_id_by_name(arg)) < 0)
		send_to_char("No such player.\r\n", ch);
	else if (id == GET_IDNUM(ch))
		send_to_char("It's your house!\r\n", ch);
	else
	{
		for (j = 0; j < house_control[i].num_of_guests; j++)
		{
			if (house_control[i].guests[j] == id)
			{
				for (; j < house_control[i].num_of_guests; j++)
					house_control[i].guests[j] = house_control[i].guests[j + 1];
				house_control[i].num_of_guests--;
				House_save_control();
				send_to_char("Guest deleted.\r\n", ch);
				return;
			}
		}

		if (house_control[i].num_of_guests == MAX_GUESTS)
		{
			send_to_char("You have too many guests.\r\n", ch);
			return;
		}

		j = house_control[i].num_of_guests++;

		house_control[i].guests[j] = id;
		House_save_control();
		send_to_char("Guest added.\r\n", ch);
	}
}



/* Misc. administrative functions */


/* crash-save all the houses */
void House_save_all(void)
{
	ROOM_DATA *real_house;
	int i;
	
	for (i = 0; i < num_of_houses; i++)
		if ( (real_house = get_room(house_control[i].vnum)) )
			if (ROOM_FLAGGED(real_house, ROOM_HOUSE_CRASH))
				House_crashsave(house_control[i].vnum);
}


/* note: arg passed must be house vnum, so there. */
int House_can_enter(CHAR_DATA *ch, room_vnum house)
{
	int i, j;
	
	if (GET_LEVEL(ch) >= LVL_GRGOD || (i = find_house(house)) == NOWHERE)
		return (1);
	
	switch (house_control[i].mode)
	{
	case HOUSE_PRIVATE:
		if (GET_IDNUM(ch) == house_control[i].owner)
			return (1);
		for (j = 0; j < house_control[i].num_of_guests; j++)
			if (GET_IDNUM(ch) == house_control[i].guests[j])
				return (1);
	}
	
	return (0);
}

void House_list_guests(CHAR_DATA *ch, int i, int quiet)
{
	int j;
	char *temp;
	char buf[MAX_STRING_LENGTH], buf2[MAX_NAME_LENGTH + 2];
	
	if (house_control[i].num_of_guests == 0)
	{
		if (!quiet)
			send_to_char("  Guests: None\r\n", ch);
		return;
	}
	
	strcpy(buf, "  Guests: ");
	
	/* Avoid <UNDEF>. -gg 6/21/98 */
	for (j = 0; j < house_control[i].num_of_guests; j++)
	{
		if ((temp = get_name_by_id(house_control[i].guests[j])) == NULL)
			continue;
		sprintf(buf2, "%s ", temp);
		strcat(buf, CAP(buf2));
	}
	
	strcat(buf, "\r\n");
	send_to_char(buf, ch);
}