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: buildings.c                                                      *
 *                                                                        *
 * Buildings code                                                         *
 *                                                                        *
 **************************************************************************/

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

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

#if defined(KEY)
#undef KEY
#endif

#define KEY( literal, field, value )					\
				if ( !strcmp( word, literal ) )			\
				{										\
				    field  = value;						\
				    fMatch = TRUE;						\
				    break;								\
				}


/* external functions */
SPECIAL(tradingpost);
ROOM_DATA *create_wild_room(COORD_DATA *coord, bool Static);
OBJ_DATA *fread_one_obj(FILE *fp, int *location);
TRADING_POST *find_trading_post(int vnum);
bitvector_t asciiflag_conv(char *flag);
int		save_objs(OBJ_DATA *obj, FILE *fp, int mode, int location);
int		sprintascii(char *out, bitvector_t bits);
void	strip_cr(char *buffer);
void	setup_trigger( ROOM_DATA *pRoom, FILE *fl );

/* globals */
BUILDING_TYPE	*building_template[NUM_BUILDING_TYPE];	/* array of building templates	*/
BUILDING_DATA	*building_list[BLD_HASH];				/* global hash table		*/
BLD_WORKS		*first_build_work	= NULL;
BLD_WORKS		*last_build_work	= NULL;

int				num_of_bld_templ	= 0;
int				top_of_buildings	= 0;
int				last_works_num		= 0;

bool			building_loaded		= FALSE;

/* local functions */
void SaveBuildings(void);

/* ================================================================= */

/* ***************************************************************** */
/* Code for Display Informations of a building                       */    
/* ***************************************************************** */

/* display the condition of a building */
void show_building_cond(CHAR_DATA *ch, BUILDING_DATA *bld)
{
	int stat, cond = percentage(bld->curr_val.health, bld->max_val.health);

	const char *building_health_descr[] = 
	{
		"is in perfect conditions",				// 0
		"looks normal",							// 1
		"is slightly damaged",					// 2
		"looks damaged",						// 3
		"is heavily damaged",					// 4
		"needs immediate repairings",			// 5
		"is almost ruined",						// 6
		"seems that could collapse at any time"	// 7
	};

	if		(cond < 5)	stat = 7;
	else if (cond < 10)	stat = 6;
	else if (cond < 20)	stat = 5;
	else if (cond < 35)	stat = 4;
	else if (cond < 50)	stat = 3;
	else if (cond < 70)	stat = 2;
	else if (cond < 85)	stat = 1;
	else				stat = 0;

	ch_printf(ch, "%s %s (%d / %d).\r\n",
		bld->description,
		building_health_descr[stat], bld->curr_val.health, bld->max_val.health);
}

/* show a list of buildings in the room */
void list_building_to_char(BUILDING_DATA *bldlist, CHAR_DATA *ch)
{
	BUILDING_DATA *bld, *blist[20];
	int num = 0, counter, locate_list[20];
	bool found;

	if (!ch)	return;

	for (bld = bldlist; bld; bld = bld->next_in_room)
	{
		if (!bld->type || !bld->type->name || !bld->description)
			continue;

		if (num < 20)
		{
			found = FALSE;
			for (counter = 0; counter < num && !found; counter++)
			{
				if (bld->type->vnum == blist[counter]->type->vnum &&
				    BUILDING_FLAGS(bld) == BUILDING_FLAGS(blist[counter]))
				{
					locate_list[counter]++;
					found = TRUE;
				}
			}
			if (!found)
			{
				blist[num] = bld;
				locate_list[num] = 1;
				num++;
			}
		}
		else
			ch_printf(ch, "You see %s.\r\n", bld->description);
	}

	if (num)
	{
		for (counter = 0; counter < num; counter++)
		{
			if (locate_list[counter] > 1)
				ch_printf(ch, "You see %s (%d).\r\n", blist[counter]->description, locate_list[counter]);
			else
				ch_printf(ch, "You see %s.\r\n", blist[counter]->description);
		}
	}

	if (ROOM_FLAGGED(ch->in_room, ROOM_BUILDING_WORKS))
		send_to_char("There are building works here.\r\n", ch);
}

/* ================================================================= */
/* Code for handling chars in & out of a building                    */    
/* ================================================================= */

/* place a char inside a building */
void char_to_building(CHAR_DATA *ch, BUILDING_DATA *bld, int room)
{
	int room_num = room;

	if (room_num < 0 || room_num >= bld->size)
		room_num = bld->type->entrance;

	ch->in_building	= bld;
	char_to_room(ch, bld->rooms[room_num]);

	ch->next_in_building	= bld->people;
	bld->people				= ch;
}

/* take out a char from the building he's inside */
void char_from_building(CHAR_DATA *ch)
{
	CHAR_DATA *temp;

	if (!ch || !ch->in_building)
		return;

	REMOVE_FROM_LIST(ch, ch->in_building->people, next_in_building);
	ch->in_building			= NULL;
	ch->next_in_building	= NULL;
}


/* ================================================================= */
/* Code for handling authorization                                   */    
/* ================================================================= */

/* search the authorized ppl array for the idnum */
AUTH_DATA *get_bld_auth(BUILDING_DATA *bld, long idnum)
{
	AUTH_DATA *bAuth;

	if (!bld || !bld->auth)
		return (NULL);

	for (bAuth = bld->auth; bAuth != NULL; bAuth = bAuth->next)
	{
		if (bAuth->id_num == idnum)
			break;
	}

	return (bAuth);
}

/*
 * if bCanAuthCheck is FALSE, check if char can enter the building
 *
 * if bCanAuthCheck is TRUE, check if char can toggle entrance authorization
 * to other people
 *
 */
bool can_enter_bld(CHAR_DATA *ch, BUILDING_DATA *bld, bool bCanAuthCheck)
{
	if ( !ch || !bld )
		return (FALSE);

	if (IS_IMMORTAL(ch))
		return (TRUE);

	if (bld->can_enter == BLD_ENT_FREE)
	{
		if (bCanAuthCheck)
		{
			send_to_char("You don't need to set/revoke authorization for free entrance buildings.\r\n", ch);
			return (FALSE);
		}
		return (TRUE);
	}

	/* owner of the building */
	if (bld->owner_type == BLD_OWN_CHAR && GET_IDNUM(ch) == bld->owner_id)
		return (TRUE);

	/* member of the clan */
	if (bld->owner_type == BLD_OWN_CLAN && GET_CLAN(ch) == bld->owner_id)
	{
		if (bCanAuthCheck)
		{
			// only privileged members can authorize to enter
			if (GET_CLAN_RANK(ch) >= RANK_PRIVILEGES)
				return (TRUE);
			else
				return (FALSE);
		}

		if (GET_CLAN_RANK(ch) < RANK_MEMBER_FIRST)
			return (FALSE);

		return (TRUE);
	}

	if (bCanAuthCheck)
		return (FALSE);

	/* ppl authorized to enter */
	if (get_bld_auth(bld, GET_IDNUM(ch)))
		return (TRUE);

	return (FALSE);
}

/* entering a building.. called from do_enter in act.movement.c */
void enter_building(CHAR_DATA *ch, BUILDING_DATA *bld)
{
	EXIT_DATA *pexit;

	if (!ch || !bld)
		return;

	if (BUILDING_FLAGGED(bld, BLD_F_RUIN))
	{
		send_to_char("You cannot enter in a ruined building.\r\n", ch);
		return;
	}
	
	// special case
	if (bld->type->vnum != BLD_STABLE)
	{
		if (RIDING(ch))
		{
			send_to_char("You cannot enter a building while mounted.\r\n", ch );
			return;
		}
		if (WAGONER(ch))
		{
			send_to_char("You cannot enter a building while driving a vehicle.\r\n", ch );
			return;
		}
	}

	if (!(pexit = get_exit(bld->rooms[bld->type->entrance], SOUTH)))
	{
		log( "SYSERR: enter_building() - cannot find entrance to building %d.", bld->vnum );
		return;
	}

	if (EXIT_FLAGGED(pexit, EX_CLOSED))
	{
		if (pexit->keyword)
			ch_printf(ch, "The %s is closed.\r\n", pexit->keyword );
		else
			send_to_char("It's closed.\r\n", ch );
		return;
	}

	if (!can_enter_bld(ch, bld, FALSE))
	{
		ch_printf(ch, "You are not authorized to enter %s.\r\n", bld->description);
		return;
	}

	act("You enter $b.\r\n", FALSE, ch, NULL, bld, TO_CHAR);
	act("$n enters $b.", FALSE, ch, NULL, bld, TO_ROOM); 

	char_from_room(ch);
	char_to_building(ch, bld, NOWHERE);

	if (RIDING(ch))
	{
		char_from_room(RIDING(ch));
		char_to_building(RIDING(ch), bld, NOWHERE);
	}

	act("$n enters.", FALSE, ch, NULL, NULL, TO_ROOM); 

	look_at_room(ch, TRUE);
}

/*
 * give vict the authorization to enter bld
 * or
 * revoke to vict the authorization to enter bld
 */
void auth_enter(BUILDING_DATA *bld, CHAR_DATA *ch, CHAR_DATA *vict, int mode)
{
	AUTH_DATA *bAuth;

	if ( !bld || !vict )
		return;

	if (bld->can_enter == BLD_ENT_FREE)
	{
		send_to_char("You don't need to set/revoke authorization for free entrance buildings.\r\n", ch);
		return;
	}

	/* authorize */
	if (mode == 1)
	{
		if (get_bld_auth(bld, GET_IDNUM(vict)))
		{
			ch_printf(ch, "%s is already authorized to enter.\r\n", PERS(vict, ch));
			return;
		}

		CREATE(bAuth, AUTH_DATA, 1);
		bAuth->id_num	= GET_IDNUM(vict);
		bAuth->name		= str_dup(GET_NAME(vict));

		// add to the list
		bAuth->next		= bld->auth;
		bld->auth		= bAuth;

		ch_printf(ch, "%s is now authorized to enter %s.\r\n",
			PERS(vict, ch), bld->description);
		act("You are now authorized to enter $b.", FALSE, vict, NULL, bld, TO_CHAR);
	}
	/* revoke authorization */
	else
	{
		AUTH_DATA *temp;

		if (!(bAuth = get_bld_auth(bld, GET_IDNUM(vict))))
		{
			ch_printf(ch, "%s is already not authorized to enter.\r\n", PERS(vict, ch));
			return;
		}

		if (vict->in_building && vict->in_building->vnum == bld->vnum)
		{
			send_to_char("You cannot revoke authorization to someone that's currently inside.\r\n", ch);
			return;
		}

		REMOVE_FROM_LIST(bAuth, bld->auth, next);
		STRFREE(bAuth->name);
		DISPOSE(bAuth);

		ch_printf(ch, "%s now is not authorized to enter %s.\r\n",
			PERS(vict, ch), bld->description);
		act("You no longer can enter $b.", FALSE, vict, NULL, bld, TO_CHAR);
	}
}

/* ================================================================= */
/* Code for finding buildings, buildings rooms, buildings doors, etc */    
/* ================================================================= */

/* find a building in the entire world, by vnum */
BUILDING_DATA *find_building(int vnum)
{
	BUILDING_DATA *bld;
	int iHash;

	if (vnum < 0)
		return (NULL);

	iHash = vnum % BLD_HASH;

	for (bld = building_list[iHash]; bld; bld = bld->next)
	{
		if (bld->vnum == vnum)
			break;
	}

	return (bld);
}

/* find a building in the given room, by building name */
BUILDING_DATA *find_building_in_room_by_name(ROOM_DATA *pRoom, char *arg)
{
	BUILDING_DATA *bld = NULL;
	int fnum = 1;

	if (!pRoom || !pRoom->buildings || !*arg)
		return (NULL);

	if (!(fnum = get_number(&arg)))
		return (NULL);

	for (bld = pRoom->buildings; bld; bld = bld->next_in_room)
	{
		if (bld->keyword)
			if (isname(arg, bld->keyword))
				if (--fnum == 0)
					break;
	}

	return (bld);
}

/* find the first building owned by char in char room */
BUILDING_DATA *find_char_owned_building(CHAR_DATA *ch)
{
	BUILDING_DATA *bld;

	if (!ch || !ch->in_room || !ch->in_room->buildings)
		return (NULL);

	for (bld = ch->in_room->buildings; bld; bld = bld->next_in_room)
	{
		if (bld->owner_type == BLD_OWN_CHAR)
			if (bld->owner_id == GET_IDNUM(ch))
				break;
	}

	return (bld);
}

/* find a room inside a building */
ROOM_DATA *find_room_building(BUILDING_DATA *bld, int room)
{
	if (room < 0 || room > bld->size)
		return (NULL);

	if (BUILDING_FLAGGED(bld, BLD_F_RUIN))
		return (NULL);

	return (bld->rooms[room]);
}

/* find the room that has the entrance/exit from the building */
EXIT_DATA *find_building_door(CHAR_DATA *ch, char *doorname, char *building)
{
	BUILDING_DATA *bld;
	EXIT_DATA *pexit;

	if (!ch || !ch->in_room->buildings)
		return (NULL);

	if (!(bld = find_building_in_room_by_name(ch->in_room, building)))
		return (NULL);

	if (BUILDING_FLAGGED(bld, BLD_F_RUIN))
		return (NULL);

	pexit = find_exit(bld->rooms[bld->type->entrance], SOUTH);

	if (pexit && isname(doorname, pexit->keyword))
		return (pexit);

	return (NULL);
}

/* find the coord of the room where the building is placed */
COORD_DATA *get_bld_coord(int vnum)
{
	COORD_DATA *here;
	BUILDING_DATA *bld = find_building(vnum);

	if (!bld)
		return (NULL);

	CREATE(here, COORD_DATA, 1);

	if (IS_WILD(bld->in_room))
		*here = *bld->in_room->coord;
	else
	{
		*here = zone_table[bld->in_room->zone].wild.z_start;
		if (here->y == 0 && here->x == 0)
			here = NULL;
	}

	return (here);
}

/* ================================================================= */
/* Code for managing building templates                              */    
/* ================================================================= */

/* display a list of valid buildings templates */
void show_valid_list(CHAR_DATA *ch)
{
	char buf[MAX_STRING_LENGTH];
	int t;

	strcpy(buf, "Valids building type names are:\r\n");
	for (t = 0; t < num_of_bld_templ; t++)
		sprintf(buf + strlen(buf), "%s\r\n", building_template[t]->name);
	send_to_char(buf, ch);
}

/* get the building template, by vnum */
BUILDING_TYPE *get_bld_template(int tnum)
{
	int t;

	for (t = 0; t < num_of_bld_templ; t++)
	{
		if (building_template[t])
			if (building_template[t]->vnum == tnum)
				break;
	}

	if (t >= num_of_bld_templ)
		return (NULL);

	return (building_template[t]);
}

/* get the building template index, by name */
int get_bld_template_by_name(char *bname)
{
	int t;

	for (t = 0; t < num_of_bld_templ; t++)
	{
		if (building_template[t])
			if (is_abbrev(bname, building_template[t]->name))
				break;
	}

	if (t >= num_of_bld_templ)
		return (NOTHING);

	return (t);
}


/* read from file one building template */
BUILDING_TYPE *fread_bld_template(FILE *fp)
{
	BUILDING_TYPE *bType;
	char *word;
	bool fMatch;
	sh_int vnmob;

	CREATE(bType, BUILDING_TYPE, 1);

	bType->name			= NULL;
	bType->cmds_list	= NULL;
	bType->cost			= 0;
	bType->entrance		= 0;
	bType->size			= 0;
	bType->vnum			= NOTHING;

	for (vnmob = 0; vnmob < BLD_MAX_MOB; vnmob++)
		bType->vmob[vnmob]	= NOBODY;

	vnmob = 0;

	for ( ; ; )
	{
		word   = feof( fp ) ? "End" : fread_word( fp );
		fMatch = FALSE;
		
		switch (UPPER(word[0]))
		{
		case '*':
			fMatch = TRUE;
			fread_to_eol(fp);
			break;

		case 'C':
			KEY("Cost",			bType->cost,			fread_number(fp));
			if (!strcmp(word, "Cmd"))
			{
				BUILDING_CMD *bCmd;
				fMatch = TRUE;

				CREATE(bCmd, BUILDING_CMD, 1);

				bCmd->cmd	= fread_letter(fp);

				if (strchr("MO", bCmd->cmd) == NULL)
				{
					log("SYSERR: fread_bld_template() - invalid command in building template %d.", bType->vnum);
					exit(1);
				}

				bCmd->arg[0]	= fread_number(fp);
				bCmd->arg[1]	= fread_number(fp);
				bCmd->arg[2]	= fread_number(fp);
				bCmd->arg[3]	= fread_number(fp);

				// add to the list
				bCmd->next		= bType->cmds_list;
				bType->cmds_list	= bCmd;

				break;
			}
			break;

		case 'E':
			KEY("Entrance",		bType->entrance,		fread_number(fp));
			if (!strcmp(word, "End"))
			{
				if (!bType->name || !bType->size || bType->vnum == NOTHING)
				{
					if (bType->name)
						free(bType->name);
					DISPOSE(bType);
					return (NULL);
				}

				return (bType);
			}
			break;

		case 'N':
			KEY("Name",			bType->name,			fread_string_nospace(fp));
			break;

		case 'R':
			if (!strcmp(word, "Rng_Defense"))
			{
				bType->range_min.defense	= fread_number(fp);
				bType->range_max.defense	= fread_number(fp);
				fMatch = TRUE;
				break;
			}
			if (!strcmp(word, "Rng_Health"))
			{
				bType->range_min.health		= fread_number(fp);
				bType->range_max.health		= fread_number(fp);
				fMatch = TRUE;
				break;
			}
			if (!strcmp(word, "Rng_Att_MOB"))
			{
				bType->range_min.mob_attack	= fread_number(fp);
				bType->range_max.mob_attack	= fread_number(fp);
				fMatch = TRUE;
				break;
			}
			if (!strcmp(word, "Rng_Att_NPC"))
			{
				bType->range_min.npc_attack	= fread_number(fp);
				bType->range_max.npc_attack	= fread_number(fp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'S':
			KEY("Size",			bType->size,			fread_number(fp));
			break;

		case 'V':
			KEY("Vnum",			bType->vnum,			fread_number(fp));
			if (!strcmp(word, "Vmob"))
			{
				mob_vnum vnmobnum = fread_number(fp);

				if ( vnmob >= BLD_MAX_MOB )
					log("SYSERR: fread_bld_template() - too many defending mobs.");
				else
					bType->vmob[vnmob++]	= vnmobnum;

				fMatch = TRUE;
				break;
			}
			break;
		}

		if (!fMatch)
			log("fread_bld_template: no match: %s", word);
	}

	if (bType->name)
		free(bType->name);
	DISPOSE(bType);

	return (NULL);
}

/*
 * load all buildings templates
 *
 * called from boot_world() in db.c
 */
void LoadBldTemplate(void)
{
	FILE *fp;
	char indname[256];
	char letter;
	char *word;

	sprintf(indname, "%sbuildings.txt", BLD_PREFIX);
	if (!(fp = fopen(indname, "r")))
	{
		log("SYSERR: Cannot open buildings index file %s.", indname);
		return;
	}

	for ( ; ; )
	{
		letter = fread_letter(fp);
		if (letter == '*')
		{
			fread_to_eol(fp);
			continue;
		}
		
		if (letter != '#')
		{
			log("LoadBldTemplate(): # not found.");
			break;
		}
		
		word = fread_word(fp);

		if (!str_cmp(word, "BUILDING"))
		{
			if (num_of_bld_templ >= NUM_BUILDING_TYPE)
			{
				log("LoadBldTemplate: more template than NUM_BUILDING_TYPE %d", NUM_BUILDING_TYPE);
				fclose(fp);
				return;
			}
			building_template[num_of_bld_templ++] = fread_bld_template(fp);
		}
		else if (!str_cmp(word, "END"))
			break;
		else
		{
			log("LoadBldTemplate: bad section %s.", word);
			continue;
		}
	}

	fclose(fp);
}


/* ================================================================= */
/* Code for placing and removing buildings in/from room              */    
/* ================================================================= */

/* place a building into a wild or zone room */
void building_to_room(BUILDING_DATA *bld, ROOM_DATA *pRoom)
{
	ROOM_DATA *room;

	if (!bld)
		return;

	if (!pRoom && !bld->coord && bld->vnum_room == NOWHERE)
		return;

	// a room exists.. place the building there..
	if (pRoom)
	{
		bld->vnum_room		= NOTHING;
		bld->coord			= NULL;

		if (IS_WILD(pRoom))
		{
			if (!bld->coord)
				CREATE(bld->coord, COORD_DATA, 1);
			
			bld->coord->y	= GET_RY(pRoom);
			bld->coord->x	= GET_RX(pRoom);
			
			SET_BIT(ROOM_FLAGS(pRoom), ROOM_WILD_STATIC);
		}
		else
			bld->vnum_room	= pRoom->number;

		/* add to the room list */
		bld->next_in_room	= pRoom->buildings;
		pRoom->buildings	= bld;

		bld->in_room		= pRoom;

		return;
	}

	// zone room
	if (bld->vnum_room != NOWHERE)
	{
		if (!(room = get_room(bld->vnum_room)))
		{
			log("SYSERR: Cannot place building to room %d.", bld->vnum_room);
			return;
		}
		bld->coord	= NULL;
	}
	// wild room
	else
	{
		if (!(room = create_wild_room(bld->coord, TRUE)))
		{
			log("SYSERR: Cannot place building to coord %d %d.",
				bld->coord->y, bld->coord->x);
			return;
		}
		bld->vnum_room = NOWHERE;
	}

	/* add to the room list */
	bld->next_in_room	= room->buildings;
	room->buildings		= bld;

	bld->in_room		= room;
}


/* ================================================================= */
/* Code for creating new buildings                                   */    
/* ================================================================= */

/* allocate memory and initialize data for a new building */
BUILDING_DATA *new_building(void)
{
	BUILDING_DATA *bld;
	int xm;

	CREATE(bld, BUILDING_DATA, 1);

	bld->vnum			= NOWHERE;
	bld->type			= NULL;
	bld->next			= NULL;
	bld->next_in_room	= NULL;
	bld->in_room		= NULL;
	bld->coord			= NULL;
	bld->people			= NULL;
	bld->auth			= NULL;
	bld->keyword		= NULL;
	bld->description	= NULL;
	bld->trp			= NULL;
	bld->cmds_list		= NULL;
	bld->vnum_room		= NOWHERE;
	bld->can_enter		= NOTHING;
	bld->owner_type		= NOTHING;
	bld->owner_id		= NOTHING;
	bld->flags			= 0;

	for (xm = 0; xm < BLD_MAX_MOB; xm++)
		bld->vmob[xm]	= NOBODY;

	return (bld);
}

/* make the building inside */
bool create_building_rooms(BUILDING_DATA *bld)
{
	FILE *fp;
	EXTRA_DESCR *new_descr;
	char fname[128];
	char letter, *flags;
	int room, door;

	sprintf(fname, "%s%d.bld", BLD_PREFIX, bld->type->vnum);
	if (!(fp = fopen(fname, "r")))
	{
		log("SYSERR: Unable to open building template file %s.", fname);
		return (FALSE);
	}

	CREATE(bld->rooms, ROOM_DATA *, bld->type->size);
	for (room = 0; room < bld->type->size; room++)
	{
		CREATE(bld->rooms[room], ROOM_DATA, 1);
		bld->rooms[room]->number = room;
		bld->rooms[room]->zone = BUILDING_ZONE;

		CREATE(bld->rooms[room]->extra_data, ROOM_EXTRA, 1);
		bld->rooms[room]->extra_data->vnum = bld->vnum;
	}

	// in entrance room must always be light
	bld->rooms[0]->light = 1;

	for ( ;; )
	{
		letter = fread_letter(fp);

		if (feof(fp))
			break;

		if (letter == '$')
			break;

		if (letter == '*')
		{
			fread_to_eol(fp);
			continue;
		}
		
		if (letter != '#')
		{
			log("create_building_rooms(): # not found.");
			break;
		}
		
		room = fread_number(fp);

		if (room < 0 || room > bld->type->size)
			break;

		bld->rooms[room]->number		= room;
		bld->rooms[room]->name			= fread_string_nospace(fp);
		bld->rooms[room]->description	= fread_string_nospace(fp);
		flags							= fread_word(fp);
		bld->rooms[room]->room_flags	= asciiflag_conv(flags);
		bld->rooms[room]->sector_type	= fread_number(fp);

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

			switch (letter)
			{
			case 'D':
				door = fread_number(fp);
				if ( door < 0 || door > NUM_OF_DIRS )
				{
					log( "create_building_rooms: vnum %d has bad door number %d.", room, door );
					break;
				}
				else
				{
					EXIT_DATA *pexit;
					char flags[128];
					char *ln;
					int x1, x2;
					
					pexit = make_exit(bld->rooms[room], NULL, door);
					pexit->description	= fread_string_nospace(fp);
					pexit->keyword		= fread_string_nospace(fp);
					ln					= fread_line(fp);
					x1=x2=0;
					sscanf( ln, "%s %d %d ", &flags, &x1, &x2 );
					
					pexit->key			= x1;
					pexit->vnum			= x2;
					pexit->vdir			= door;
					pexit->exit_info	= asciiflag_conv(flags);
					
					pexit->to_room		= bld->rooms[pexit->vnum];

					/* sanity check */
					if (pexit->exit_info && !EXIT_FLAGGED(pexit, EX_ISDOOR))
						SET_BIT(pexit->exit_info, EX_ISDOOR);
				}
				break;

			case 'E':
				CREATE(new_descr, EXTRA_DESCR, 1);
				new_descr->keyword		= fread_string(fp, buf2);
				new_descr->description	= fread_string(fp, buf2);
				new_descr->next			= bld->rooms[room]->ex_description;
				bld->rooms[room]->ex_description = new_descr;
				break;
			/* Room Trigger */
			case 'T':
				setup_trigger( bld->rooms[room], fp );
				break;
			}
		}

		if (letter == '$')
			break;
	}

	fclose(fp);
	return (TRUE);
}


void exec_bld_cmds(BUILDING_DATA *bld)
{
	BUILDING_CMD *bCmd;
	CHAR_DATA *mob;
	OBJ_DATA *obj;
	ROOM_DATA *bRoom;
	int x;
	
	for (bCmd = bld->type->cmds_list; bCmd; bCmd = bCmd->next)
	{
		switch (bCmd->cmd)
		{
		default:
			log("SYSERR: exec_bld_cmds() - unknown command in building %d.", bld->vnum);
			bCmd->cmd = '*';
			continue;
		case '*':
			continue;
		case 'M':
			if (!(bRoom = bld->rooms[bCmd->arg[2]]))
			{
				log("SYSERR: exec_bld_cmds() - invalid room vnum %d in building %d.", bCmd->arg[2], bld->vnum);
				bCmd->cmd = '*';
				continue;
			}
			for (x = 0; x < bCmd->arg[1]; x++)
			{
				if (!(mob = read_mobile(bCmd->arg[0], VIRTUAL)))
				{
					log("SYSERR: exec_bld_cmds() - invalid mob vnum %d in building %d.", bCmd->arg[0], bld->vnum);
					bCmd->cmd = '*';
					break;
				}
				char_to_room(mob, bRoom);
			}
			break;

		case 'O':
			if (!(bRoom = bld->rooms[bCmd->arg[2]]))
			{
				log("SYSERR: exec_bld_cmds() - invalid room vnum %d in building %d.", bCmd->arg[2], bld->vnum);
				bCmd->cmd = '*';
				continue;
			}
			
			for (x = 0; x < bCmd->arg[1]; x++)
			{
				if (!(obj = read_object(bCmd->arg[0], VIRTUAL)))
				{
					log("SYSERR: exec_bld_cmds() - invalid obj vnum %d in building %d.", bCmd->arg[0], bld->vnum);
					bCmd->cmd = '*';
					break;
				}
				obj_to_room(obj, bRoom);
			}
			break;
		}
	}
}

/* setup data for a yet-to-be created building */
BUILDING_DATA *setup_new_building(BUILDING_TYPE *bType, ROOM_DATA *pRoom, long owner_id, char owner_type)
{
	BUILDING_DATA *bld;
	EXIT_DATA *pexit;
	char buf[MAX_STRING_LENGTH];
	int iHash;

	if (!bType || !pRoom)
	{
		log("SYSERR: setup_new_building() - invalid pointer.");
		return (NULL);
	}

	bld = new_building();

	bld->vnum			= ++top_of_buildings;
	bld->type			= bType;
	bld->size			= bType->size;

	bld->keyword		= str_dup(bType->name);
	sprintf(buf, "%s %s", AN(bType->name), bType->name);
	bld->description	= str_dup(buf);

	/* setup building rooms */
	if (!create_building_rooms(bld))
	{
		log("SYSERR: Setup_new_building() - cannot create building rooms.");
		bld->type = NULL;
		DISPOSE(bld);
		return (NULL);
	}

	if (owner_type == BLD_OWN_CHAR)
	{
		bld->owner_id	= owner_id;
		bld->owner_type	= BLD_OWN_CHAR;
		bld->can_enter	= BLD_ENT_CHAR;
	}
	else
	{
		CLAN_DATA *pClan = get_clan(owner_id);

		if (!pClan)
		{
			log("SYSERR: setup_new_building() - invalid clan number %d.", owner_id);
			bld->type = NULL;
			DISPOSE(bld);
			return (NULL);
		}

		bld->owner_id	= owner_id;
		bld->owner_type	= BLD_OWN_CLAN;
		bld->can_enter	= BLD_ENT_CLAN;

		if (bType->vnum == BLD_CASTLE)
		{
			if (pClan->hall == NOWHERE)
			{
				SET_BIT(bld->flags, BLD_F_CLANHALL);
				pClan->hall = bld->vnum;
			}
		}
	}

	// everyone should be allowed to enter a trading post or a stable
	if (bld->type->vnum == BLD_STORE || bld->type->vnum == BLD_STABLE)
		bld->can_enter	= BLD_ENT_FREE;

	/* set up defense pts */
	bld->max_val.defense	= number(bType->range_min.defense, bType->range_max.defense);
	bld->curr_val.defense	= bld->max_val.defense;
	/* set up health pts */
	bld->max_val.health		= number(bType->range_min.health, bType->range_max.health);
	bld->curr_val.health	= bld->max_val.health;
	/* set up mob_attack pts */
	if (bType->range_max.mob_attack)
	{
		int xm;

		bld->max_val.mob_attack		= number(bType->range_min.mob_attack, bType->range_max.mob_attack);
		bld->curr_val.mob_attack	= bld->max_val.mob_attack;

		for (xm = 0; xm < BLD_MAX_MOB; xm++)
		{
			if (bType->vmob[xm] == NOBODY)
				continue;
			bld->vmob[xm] = bType->vmob[xm];
		}
	}
	/* set up npc_attack pts */
	if (bType->range_max.npc_attack)
	{
		bld->max_val.npc_attack		= number(bType->range_min.npc_attack, bType->range_max.npc_attack);
		bld->curr_val.npc_attack	= bld->max_val.npc_attack;
	}

	// execute building commands (load objs & mobs)
	exec_bld_cmds(bld);

	/* add to the global hash table */
	iHash					= bld->vnum % BLD_HASH;
	bld->next				= building_list[iHash];
	building_list[iHash]	= bld;

	/* place building into room */
	building_to_room(bld, pRoom);

	/* exit building --> room */
	pexit = make_exit(bld->rooms[bld->type->entrance], NULL, SOUTH);
	pexit->to_room = bld->in_room;
	pexit->keyword = str_dup("door");
	SET_BIT(EXIT_FLAGS(pexit), EX_ISDOOR);

	// forced save
	SaveBuildings();

	return (bld);
}


/* ======================================================================= */
/* Code for building Buildings                                             */
/* ======================================================================= */

/* time needed in each phase for each building rooms.. in mud hours */
int bwks_timing[] =
{
  0,
  4,
  2,
  3
};


/* setup build works a new building */
int build_works_start(BUILDING_TYPE *bType, ROOM_DATA *pRoom, long owner_id, char owner_type)
{
	BLD_WORKS *pWorks;

	CREATE(pWorks, BLD_WORKS, 1);

	pWorks->next			= NULL;
	pWorks->prev			= NULL;

	pWorks->num				= ++last_works_num;

	pWorks->owner_type		= owner_type;
	pWorks->owner_id		= owner_id;
	pWorks->authorized		= FALSE;
	pWorks->phase			= BWKS_BASE;
	pWorks->timer			= bwks_timing[BWKS_BASE] * bType->size;
	pWorks->type			= bType->vnum;

	pWorks->coord			= NULL;
	pWorks->in_room			= NULL;

	if (IS_WILD(pRoom))
	{
		CREATE(pWorks->coord, COORD_DATA, 1);
		pWorks->coord->y	= GET_RY(pRoom);
		pWorks->coord->x	= GET_RX(pRoom);
		SET_BIT(ROOM_FLAGS(pRoom), ROOM_BUILDING_WORKS | ROOM_WILD_STATIC);
	}
	else
	{
		pWorks->in_room		= pRoom;
		SET_BIT(ROOM_FLAGS(pRoom), ROOM_BUILDING_WORKS);
	}

	LINK(pWorks, first_build_work, last_build_work, next, prev);

	return (pWorks->num);
}

void build_works_process(void)
{
	BLD_WORKS *pWorks, *pWorks_next;

	if (!first_build_work)
		return;

	for (pWorks = first_build_work; pWorks; pWorks = pWorks_next)
	{
		pWorks_next	= pWorks->next;

		if (--pWorks->timer <= 0)
		{
			BUILDING_TYPE *bType = get_bld_template(pWorks->type);

			if (++pWorks->phase > BWKS_FINAL)
			{
				BUILDING_DATA *bld;

				if (pWorks->coord)
					pWorks->in_room = get_wild_room(pWorks->coord);

				bld = setup_new_building(bType, pWorks->in_room, pWorks->owner_id, pWorks->owner_type);

				UNLINK(pWorks, first_build_work, last_build_work, next, prev);

				REMOVE_BIT(ROOM_FLAGS(pWorks->in_room), ROOM_BUILDING_WORKS);

				pWorks->in_room = NULL;
				pWorks->next	= NULL;
				pWorks->prev	= NULL;
				DISPOSE(pWorks->coord);
				DISPOSE(pWorks);
			}
			else
				pWorks->timer	= bwks_timing[pWorks->phase] * bType->size;
		}
	}
}

const char *bwks_phase_descr[] =
{
  "none",
  "basement",
  "walls",
  "roof",
  "\n"
};

void build_works_list(CHAR_DATA *ch)
{
	BLD_WORKS *pWorks;

	if (!first_build_work)
	{
		send_to_char("Currently, there are no building works in progress.\r\n", ch);
		return;
	}

	send_to_char("List of build works currently in progress:\r\n", ch);
	
	for ( pWorks = first_build_work; pWorks; pWorks = pWorks->next )
	{
		ch_printf(ch, " %30s   Phase: [%-10s]  Hours left: %d\r\n",
			building_template[pWorks->type]->name,
			bwks_phase_descr[pWorks->phase], pWorks->timer);
	}
}


ACMD(do_listworks)
{
	build_works_list(ch);
}

/* ============================================================= */

void SaveWorksList(void)
{
	FILE *fp;
	BLD_WORKS *pWorks;
	char fname[128];

	if (!first_build_work)
		return;

	sprintf(fname, "%sworkslist.txt", LIB_BUILDINGS);
	if (!(fp = fopen(fname, "w")))
	{
		log("SYSERR: SaveWorksList() - cannot create file %s.", fname);
		return;
	}

	for (pWorks = first_build_work; pWorks; pWorks = pWorks->next)
	{
		fprintf(fp, "Work %d %hd %d %hd %hd %hd %d ",
			pWorks->num,
			pWorks->owner_type, pWorks->owner_id, (pWorks->authorized ? 1 : 0),
			pWorks->phase, pWorks->timer, pWorks->type);
		if (pWorks->coord)
			fprintf(fp, "C %hd %hd", pWorks->coord->y, pWorks->coord->x);
		else
			fprintf(fp, "R %d", pWorks->in_room->number);
		fprintf(fp, "\n");
	}

	fprintf(fp, "$\n");
	fclose(fp);
}


void ReadWorksList(void)
{
	FILE *fp;
	BLD_WORKS *pWorks;
	char fname[128], *word, letter;

	// list already loaded....
	if (first_build_work)
		return;

	// nullify head & tail
	first_build_work	= NULL;
	last_build_work		= NULL;

	last_works_num		= 0;

	sprintf(fname, "%sworkslist.txt", LIB_BUILDINGS);
	if (!(fp = fopen(fname, "r")))
		return;

	for ( ; ; )
	{
		word   = feof(fp) ? "$" : fread_word(fp);
		
		switch (UPPER(word[0]))
		{
		case '*':
			fread_to_eol( fp );
			break;

		case '$':
			fclose(fp);
			return;

		case 'W':
			CREATE(pWorks, BLD_WORKS, 1);

			pWorks->next			= NULL;
			pWorks->prev			= NULL;

			pWorks->num				= fread_number(fp);
			pWorks->owner_type		= fread_number(fp);
			pWorks->owner_id		= fread_number(fp);
			pWorks->authorized		= fread_number(fp);
			pWorks->phase			= fread_number(fp);
			pWorks->timer			= fread_number(fp);
			pWorks->type			= fread_number(fp);

			letter = fread_letter(fp);

			pWorks->coord			= NULL;
			pWorks->in_room			= NULL;

			if (letter == 'C')
			{
				CREATE(pWorks->coord, COORD_DATA, 1);
				pWorks->coord->y	= fread_number(fp);
				pWorks->coord->x	= fread_number(fp);

				pWorks->in_room		= create_wild_room(pWorks->coord, TRUE);
			}
			else
				pWorks->in_room		= get_room(fread_number(fp));

			LINK(pWorks, first_build_work, last_build_work, next, prev);

			if (last_works_num < pWorks->num)
				last_works_num = pWorks->num;
		}
	}

	fclose(fp);
}

/* ============================================================= */

/* create a new building of the given type */
void create_building(CHAR_DATA *ch, char *arg, int clan_num)
{
	char owner_type;
	bool can_build = TRUE;
	int vnum, gold, cost;
	long owner_id;


	if (!num_of_bld_templ)
	{
		send_to_char("No building can be built right now.\r\n", ch);
		return;
	}

	if (!*arg)
	{
		send_to_char("Usage: build <building type>\r\n", ch);
		show_valid_list(ch);
		return;
	}

	if ((vnum = get_bld_template_by_name(arg)) == NOTHING)
	{
		ch_printf(ch, "Unknown building type '%s'.\r\n", arg);
		show_valid_list(ch);
		return;
	}

	if (!terrain_type[SECT(ch->in_room)])
	{
		send_to_char("You cannot build in this room.\r\n", ch);
		return;
	}

	/* check invalid room sector */
	switch (SECT(ch->in_room))
	{
	case SECT_INSIDE:
	case SECT_WATER_SWIM:
	case SECT_WATER_NOSWIM:
	case SECT_UNDERWATER:
	case SECT_FLYING:
	case SECT_MOUNTAIN_PEAK:
	case SECT_REEF:
	case SECT_SEA:
	case SECT_RIVER:
	case SECT_ROAD:
	case SECT_BRIDGE:
	case SECT_WILD_EXIT:
	case SECT_ZONE_BORDER:
	case SECT_ZONE_INSIDE:
	case SECT_RIVER_NAVIGABLE:
	case SECT_SHIP_STERN:
	case SECT_SHIP_DECK:
	case SECT_SHIP_BOW:
	case SECT_SHIP_UNDERDK:
	case SECT_SHIP_CABIN:
	case SECT_SHIP_HOLD:
	case SECT_FERRY_DECK:
	case SECT_FORD:
	case SECT_SHALLOWS:
		can_build = FALSE;
		break;
	}

	if (!can_build)
	{
		ch_printf(ch, "You cannot build in '%s' rooms.\r\n",
			terrain_type[SECT(ch->in_room)]->name);
		return;
	}
	
	cost = building_template[vnum]->cost;

	if (clan_num == NOTHING)
	{
		owner_id	= GET_IDNUM(ch);
		owner_type	= BLD_OWN_CHAR;

		gold = get_gold(ch) + GET_BANK_GOLD(ch);
		
		if (gold < cost)
			gold = -1;
		else
		{
			if ( get_gold(ch) < cost )
			{
				int diff = cost - get_gold(ch);

				sub_gold(ch, get_gold(ch));
				GET_BANK_GOLD(ch) -= diff;
			}
			else
				sub_gold(ch, cost);
		}
	}
	else
	{
		CLAN_DATA *pClan = get_clan(clan_num);

		if ( !pClan )
		{
			send_to_char("Ooopss.. clan error: contact an immortal.\r\n", ch);
			log("SYSERR: create_building() - invalid clan number %d.", clan_num);
			return;
		}

		owner_id	= clan_num;
		owner_type	= BLD_OWN_CLAN;

		gold		= pClan->treasure;

		if ( gold < cost )
			gold = -1;
		else
			pClan->treasure -= cost;
	}

	if (gold == -1 && IS_MORTAL(ch))
	{
		ch_printf(ch, "You need %d gold coins to build %s %s.\r\n",
			building_template[vnum]->cost, AN(building_template[vnum]->name),
			building_template[vnum]->name);
		return;
	}

	ch_printf(ch, "You pay %d gold coins of building expenses.\r\n",
		building_template[vnum]->cost);

	if (IS_IMMORTAL(ch))
		setup_new_building(building_template[vnum], ch->in_room, owner_id, owner_type);
	else
		build_works_start(building_template[vnum], ch->in_room, owner_id, owner_type);
}


/* the build command */
ACMD(do_build)
{
	char arg[MAX_INPUT_LENGTH];

	argument = one_argument(argument, arg);
	
	if (!*arg)
	{
		send_to_char("Usage: build <building type>\r\n", ch);
		return;
	}

	argument = one_argument(argument, arg);
	if (!*arg)
	{
		show_valid_list(ch);
		return;
	}
	create_building(ch, arg, NOTHING);
}


/* show the status of a building - IMMS */
void do_stat_building(CHAR_DATA *ch, BUILDING_DATA *bld)
{
	char bflags[128];

	ch_printf(ch, "Vnum       : %d\r\n", bld->vnum);
	ch_printf(ch, "Type       : %s\r\n", bld_type_descr[bld->type->vnum]);
	ch_printf(ch, "Name       : %s\r\n", bld->description);

	if (BUILDING_FLAGGED(bld, BLD_F_RUIN))
		return;

	ch_printf(ch, "Size       : %d\r\n", bld->size);

	if (bld->coord)
		ch_printf(ch, "Location   : %d %d\r\n", GET_RY(bld), GET_RX(bld));
	else
		ch_printf(ch, "Location   : room %d\r\n", bld->vnum_room);

	sprintbit(bld->flags, bld_flags_descr, bflags);
	ch_printf(ch, "Flags      : %s\r\n", bflags);

	ch_printf(ch, "Defense    : %d / %d\r\n", bld->curr_val.defense, bld->max_val.defense);
	ch_printf(ch, "Health     : %d / %d\r\n", bld->curr_val.health, bld->max_val.health);

	ch_printf(ch, "Owner type : %s\r\n", bld_owner_descr[bld->owner_type]);
	ch_printf(ch, "Owner Id   : %d\r\n", bld->owner_id);

	ch_printf(ch, "Enter auth : %s\r\n", bld_enter_descr[bld->can_enter]);

	if (bld->max_val.npc_attack > 0)
		ch_printf(ch, "NPC damage : %d / %d.\r\n",
			bld->curr_val.npc_attack, bld->max_val.npc_attack);

	if (bld->max_val.mob_attack > 0)
	{
		int x;

		ch_printf(ch, "Mobs loaded: %d / %d.\r\n",
			bld->curr_val.mob_attack, bld->max_val.mob_attack);
		for (x = 0; x < BLD_MAX_MOB; x++)
		{
			if (bld->vmob[x] == NOTHING)
				continue;
			ch_printf(ch, "  Mob vnum : %d\r\n", bld->vmob[x]);
		}
	}
}


ACMD(do_bldlist)
{
	BUILDING_DATA *bld;
	char sbaf[MAX_STRING_LENGTH];
	int iHash;

	if (!building_loaded)
	{
		send_to_char("No buildings at all.\r\n", ch);
		return;
	}

	send_to_char("List of building in world:\r\n", ch);
	for (iHash = 0; iHash < BLD_HASH; iHash++)
	{
		for (bld = building_list[iHash]; bld; bld = bld->next)
		{
			if (IS_WILD(bld->in_room))
				sprintf(sbaf, "Wild [%d %d]", GET_Y(bld), GET_X(bld));
			else
				sprintf(sbaf, "Room [%d]", bld->in_room->number);

			ch_printf(ch, "[%d] %-30s  Location: %s\r\n",
				bld->vnum, bld->description, sbaf);
		}
	}
}

/* ================================================================= */
/* Code for saving buildings                                         */    
/* ================================================================= */

/* write on file the building inside */
void fwrite_building_rooms(BUILDING_DATA *bld, FILE *fp)
{
	ROOM_DATA *room;
	EXIT_DATA *pexit;
	char dflags[128];
	int num;

	for (num = 0; num < bld->size; num++)
	{
		room = bld->rooms[num];

		if (!room) break;

		/* Copy the description and strip off trailing newlines */
		strcpy(buf, room->description ? room->description : "Empty room.");
		strip_cr(buf);

		sprintascii(buf2, room->room_flags);

		fprintf(fp,
			"#%d\n"
			"%s~\n"
			"%s~\n"
			"%s %d %d %d\n",
			room->number,
			room->name ? room->name : "Untitled",
			buf, buf2, room->sector_type,
			room->extra_data->curr_hp, room->extra_data->max_hp
			);

		/* Now you write out the exits for the room */
		for (pexit = room->first_exit; pexit; pexit = pexit->next)
		{
			/* check for non-building exits */
			if (!IS_BUILDING(pexit->to_room))
				continue;
			
			if (pexit->description)
			{
				strcpy(buf, pexit->description);
				strip_cr(buf);
			}
			else
				*buf = '\0';
			
			if (pexit->keyword)
				strcpy(buf1, pexit->keyword);
			else
				*buf1 = '\0';
			
			/* New door flags handling -- Fab 2000 */
			REMOVE_BIT(EXIT_FLAGS(pexit), EX_LOCKED);
			REMOVE_BIT(EXIT_FLAGS(pexit), EX_CLOSED);
			REMOVE_BIT(EXIT_FLAGS(pexit), EX_HIDDEN);

			/* sanity check */
			if (EXIT_FLAGS(pexit) && !EXIT_FLAGGED(pexit, EX_ISDOOR))
				SET_BIT(EXIT_FLAGS(pexit), EX_ISDOOR);
			
			sprintascii(dflags, pexit->exit_info);
			
			fprintf(fp,
				"D%d\n"
				"%s~\n"
				"%s~\n"
				"%s %d %d\n", pexit->vdir, buf, buf1, dflags,
				pexit->key, ( pexit->to_room != NULL ? pexit->to_room->number : NOWHERE ) );
		}

		if (room->ex_description)
		{
			EXTRA_DESCR *xdesc;
			
			for (xdesc = room->ex_description; xdesc; xdesc = xdesc->next)
			{
				strcpy(buf, xdesc->description);
				strip_cr(buf);
				fprintf(fp,
					"E\n"
					"%s~\n"
					"%s~\n", xdesc->keyword, buf);
			}
		}
		
		fprintf(fp, "S\n");
	}

	/* Write the final line */
	fprintf(fp, "$\n\n");
}

/* write on file one building */
void fwrite_building(BUILDING_DATA *bld)
{
	FILE *fp;
	char fname[128];
	int num, xm;

	sprintf(fname, "%s%d.bld", LIB_BUILDINGS, bld->vnum);
	if (!(fp = fopen(fname, "w")))
	{
		log("SYSERR: fwrite_building() - unable to create file %s.", fname);
		return;
	}

	fprintf(fp, "#BUILDING\n");
	fprintf(fp, "Vnum          %d\n",	bld->vnum);
	fprintf(fp, "Type          %d\n",	bld->type->vnum);
	if (bld->coord)
		fprintf(fp, "Coord         %hd %hd\n",	GET_RY(bld), GET_RX(bld));
	if (bld->vnum_room != NOWHERE)
		fprintf(fp, "Vnum_room     %d\n",	bld->vnum_room);
	fprintf(fp, "Size          %hd\n",	bld->size);
	fprintf(fp, "Owner_Id      %ld\n",	bld->owner_id);
	fprintf(fp, "Owner_Type    %d\n",	bld->owner_type);
	fprintf(fp, "Enter_Type    %d\n",	bld->can_enter);

	fprintf(fp, "Defense       %hd %hd\n",	bld->curr_val.defense, bld->max_val.defense);
	fprintf(fp, "Health        %hd %hd\n",	bld->curr_val.health, bld->max_val.health);
	if (bld->max_val.mob_attack > 0)
		fprintf(fp, "Attack_MOB    %hd %hd\n",	bld->curr_val.mob_attack, bld->max_val.mob_attack);
	if (bld->max_val.npc_attack > 0)
		fprintf(fp, "Attack_NPC    %hd %hd\n",	bld->curr_val.npc_attack, bld->max_val.npc_attack);
	if (bld->flags)
		fprintf(fp, "Flags         %d\n",	bld->flags);

	for (xm = 0; xm < BLD_MAX_MOB; xm++)
	{
		if (bld->vmob[xm] == NOBODY)
			continue;

		fprintf(fp, "Vmob          %d\n",	bld->vmob[xm]);
	}

	if (bld->keyword)
		fprintf(fp, "Keywords      %s~\n",	bld->keyword);
	if (bld->description)
		fprintf(fp, "Description   %s~\n",	bld->description);

	if (bld->auth)
	{
		AUTH_DATA *bAuth;

		for (bAuth = bld->auth; bAuth; bAuth = bAuth->next)
			fprintf(fp, "IdAuth          %ld %s\n", bAuth->id_num, bAuth->name);
	}

	if (bld->trp)
		fprintf(fp, "TradingPost   %d\n", bld->trp->vnum);

	fprintf(fp, "End\n\n");

	if (!BUILDING_FLAGGED(bld, BLD_F_RUIN))
	{
		fprintf(fp, "#ROOMS\n");
		
		fwrite_building_rooms(bld, fp);
		
		for (num = 0; num < bld->size; num++)
		{
			if (bld->rooms[num] && bld->rooms[num]->first_content)
			{
				fprintf(fp, "#ROOMCONTENT\n");
				fprintf(fp, "Room          %d\n", num);
				save_objs( bld->rooms[num]->last_content, fp, 0, 0);
				fprintf(fp, "End\n\n");
			}
		}
	}
	
	fprintf(fp, "#END\n");
	fclose(fp);
}

/*
 * save all buildings
 *
 * called from heartbeat() in comm.c
 */
void SaveBuildings(void)
{
	FILE *fp;
	BUILDING_DATA *bld;
	char fname[128];
	int i;

	if (!top_of_buildings)
		return;

	sprintf(fname, "%sindex", LIB_BUILDINGS);
	if (!(fp = fopen(fname, "w")))
	{
		log("SYSERR: SaveBuildings() - unable to create index file %s.", fname);
		return;
	}

	for (i = 0; i < BLD_HASH; i++)
	{
		for (bld = building_list[i]; bld; bld = bld->next)
		{
			if (bld->vnum == NOTHING || !bld->type || bld->type->vnum == NOTHING)
				continue;
			
			fprintf(fp, "%d.bld\n", bld->vnum);
			
			fwrite_building(bld);
		}
	}

	fprintf(fp, "$\n");
	fclose(fp);

	SaveWorksList();
}

/* forced save of all buildings - IMMS */
ACMD(do_bldsave)
{
	SaveBuildings();
	send_to_char(OK, ch);
}


/* ================================================================= */
/* Code for loading saved buildings                                  */    
/* ================================================================= */

#define MAX_BAG_ROWS	5

/* load contents of a building room from file */
void fread_building_contents(BUILDING_DATA *bld, FILE *fp)
{
	OBJ_DATA *obj, *obj2, *cont_row[MAX_BAG_ROWS];
	int location, j, roomnum;
	char *word;
	bool fMatch;

	/* Empty all of the container lists (you never know ...) */
	for (j = 0; j < MAX_BAG_ROWS; j++)
		cont_row[j] = NULL;

	for ( ; ; )
	{
		word   = feof( fp ) ? "End" : fread_word( fp );
		fMatch = FALSE;
		
		switch (UPPER(word[0]))
		{
		case '*':
			fMatch = TRUE;
			fread_to_eol(fp);
			break;

		case 'E':
			if (!strcmp(word, "End"))
				return;

		case 'R':
			KEY("Room",		roomnum,		fread_number(fp));
			break;

		case '#':
			// skip #OBJECT line..
			fread_to_eol( fp );
			
			if ((obj = fread_one_obj(fp, &location)) == NULL)
				continue;
			
			obj_to_room(obj, bld->rooms[roomnum]);
			
			for (j = MAX_BAG_ROWS - 1; j > -location; j--)
			{
				if (cont_row[j])	/* No container, back to inventory. */
				{
					for (; cont_row[j]; cont_row[j] = obj2)
					{
						obj2 = cont_row[j]->next_content;
						obj_to_room( cont_row[j], bld->rooms[roomnum] );
					}
					cont_row[j] = NULL;
				}
			}
			if (j == -location && cont_row[j])	/* Content list exists. */
			{
				if (GET_OBJ_TYPE(obj) == ITEM_CONTAINER ||
					GET_OBJ_TYPE(obj) == ITEM_MISSILE_CONT)
				{
					/* Take the item, fill it, and give it back. */
					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, bld->rooms[roomnum]);
				}
				else	/* Object isn't container, empty content list. */
				{
					for (; cont_row[j]; cont_row[j] = obj2)
					{
						obj2 = cont_row[j]->next_content;
						obj_to_room(cont_row[j], bld->rooms[roomnum]);
					}
					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;
			}
			break;
		}
	}
}

/* load from file the building inside */
void fread_building_rooms(BUILDING_DATA *bld, FILE *fp)
{
	EXTRA_DESCR *new_descr;
	char letter, *flags;
	int room, door;

	CREATE(bld->rooms, ROOM_DATA *, bld->size);

	for (room = 0; room < bld->size; room++)
	{
		CREATE(bld->rooms[room], ROOM_DATA, 1);
		bld->rooms[room]->zone = BUILDING_ZONE;

		CREATE(bld->rooms[room]->extra_data, ROOM_EXTRA, 1);
		bld->rooms[room]->extra_data->vnum = bld->vnum;
	}

	// in entrance room must always be light
	bld->rooms[0]->light = 1;

	if (bld->trp)
	{
		// assign to first building room the special procedure
		bld->rooms[0]->func	= tradingpost;
		// save room in TP data for reference..
		bld->trp->in_room	= bld->rooms[0];
	}

	for ( ;; )
	{
		letter = fread_letter(fp);

		if (feof(fp))
			break;

		if (letter == '$')
			break;

		if (letter == '*')
		{
			fread_to_eol(fp);
			continue;
		}
		
		if (letter != '#')
		{
			log("create_building_rooms(): # not found.");
			break;
		}
		
		room = fread_number(fp);

		if (room < 0 || room > bld->type->size)
			break;

		bld->rooms[room]->number				= room;

		bld->rooms[room]->name					= fread_string_nospace(fp);
		bld->rooms[room]->description			= fread_string_nospace(fp);
		flags									= fread_word(fp);
		bld->rooms[room]->room_flags			= asciiflag_conv(flags);
		bld->rooms[room]->sector_type			= fread_number(fp);

		bld->rooms[room]->extra_data->curr_hp	= fread_number(fp);
		bld->rooms[room]->extra_data->max_hp	= fread_number(fp);

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

			switch (letter)
			{
			case 'D':
				door = fread_number(fp);
				if ( door < 0 || door > NUM_OF_DIRS )
				{
					log("create_building_rooms: vnum %d has bad door number %d.", room, door);
					break;
				}
				else
				{
					EXIT_DATA *pexit;
					char flags[128];
					char *ln;
					int x1, x2;
					
					pexit = make_exit( bld->rooms[room], NULL, door );
					pexit->description	= fread_string_nospace(fp);
					pexit->keyword		= fread_string_nospace(fp);
					ln					= fread_line(fp);
					x1=x2=0;
					sscanf( ln, "%s %d %d ", &flags, &x1, &x2 );
					
					pexit->key			= x1;
					pexit->vnum			= x2;
					pexit->vdir			= door;
					pexit->exit_info	= asciiflag_conv( flags );
					
					pexit->to_room		= bld->rooms[pexit->vnum];

					/* sanity check */
					if ( pexit->exit_info && !EXIT_FLAGGED( pexit, EX_ISDOOR ) )
						SET_BIT( pexit->exit_info, EX_ISDOOR );
				}
				break;

			case 'E':
				CREATE(new_descr, EXTRA_DESCR, 1);
				new_descr->keyword		= fread_string(fp, buf2);
				new_descr->description	= fread_string(fp, buf2);
				new_descr->next			= bld->rooms[room]->ex_description;
				bld->rooms[room]->ex_description = new_descr;
				break;

			/* Room Trigger */
			case 'T':
				setup_trigger( bld->rooms[room], fp );
				break;
			}
		}

		if (letter == '$')
			break;
	}
}

/* load from file the building data */
BUILDING_DATA *fread_building_data( FILE *fp )
{
	BUILDING_DATA *bld;
	char *word;
	bool fMatch;
	sh_int vnmob = 0;

	bld = new_building();

	for ( ; ; )
	{
		word   = feof( fp ) ? "End" : fread_word( fp );
		fMatch = FALSE;
		
		switch (UPPER(word[0]))
		{
		case '*':
			fMatch = TRUE;
			fread_to_eol( fp );
			break;

		case 'A':
			if (!strcmp(word, "Attack_MOB"))
			{
				bld->curr_val.mob_attack	= fread_number(fp);
				bld->max_val.mob_attack		= fread_number(fp);
				fMatch = TRUE;
				break;
			}
			if (!strcmp(word, "Attack_NPC"))
			{
				bld->curr_val.npc_attack	= fread_number(fp);
				bld->max_val.npc_attack		= fread_number(fp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'C':
			if (!strcmp(word, "Coord"))
			{
				CREATE(bld->coord, COORD_DATA, 1);

				bld->coord->y				= fread_number(fp);
				bld->coord->x				= fread_number(fp);

				fMatch = TRUE;
				break;
			}
			break;

		case 'D':
			KEY("Description",	bld->description,		fread_string_nospace(fp));
			if (!strcmp(word, "Defense"))
			{
				bld->curr_val.defense		= fread_number(fp);
				bld->max_val.defense		= fread_number(fp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'E':
			KEY("Enter_Type",	bld->can_enter,			fread_number(fp));
			if (!strcmp(word, "End"))
			{
				if (!bld->type || (!bld->coord && !bld->vnum_room))
				{
					bld->type	= NULL;
					if (bld->coord)
						DISPOSE(bld->coord);
					DISPOSE(bld);
					return (NULL);
				}

				return (bld);
			}
			break;

		case 'F':
			KEY("Flags",		bld->flags,				fread_number(fp));
			break;

		case 'H':
			if (!strcmp(word, "Health"))
			{
				bld->curr_val.health	= fread_number(fp);
				bld->max_val.health		= fread_number(fp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'I':
			if (!strcmp(word, "IdAuth"))
			{
				AUTH_DATA *bAuth;

				CREATE(bAuth, AUTH_DATA, 1);
				bAuth->id_num		= fread_number(fp);
				bAuth->name			= str_dup(fread_word(fp));

				bAuth->next			= bld->auth;
				bld->auth			= bAuth;
				
				fMatch = TRUE;
				break;
			}
			break;

		case 'K':
			KEY("Keywords",		bld->keyword,			fread_string_nospace(fp));
			break;

		case 'O':
			KEY("Owner_Id",		bld->owner_id,			fread_number(fp));
			KEY("Owner_Type",	bld->owner_type,		fread_number(fp));
			break;

		case 'S':
			KEY("Size",			bld->size,				fread_number(fp));
			break;

		case 'T':
			KEY("Type",			bld->type,				get_bld_template(fread_number(fp)));
			if (!strcmp(word, "TradingPost"))
			{
				TRADING_POST *pTp;
				int tpnum;

				fMatch = TRUE;
				tpnum = fread_number(fp);

				if (!(pTp = find_trading_post(tpnum)))
				{
					log("SYSERR: fread_building_data() - invalid trading post vnum %d.", tpnum);
					break;
				}

				// give building trading post status
				bld->trp = pTp;

				break;
			}
			break;

		case 'V':
			KEY("Vnum",			bld->vnum,				fread_number(fp));
			KEY("Vnum_room",	bld->vnum_room,			fread_number(fp));
			if (!strcmp(word, "Vmob"))
			{
				mob_vnum vnmobnum = fread_number(fp);

				if (vnmob >= BLD_MAX_MOB)
					log("SYSERR: fread_building_data() - too many defending mobs.");
				else
					bld->vmob[vnmob++]	= vnmobnum;

				fMatch = TRUE;
				break;
			}
			break;
		}
	}

	return (NULL);
}

/* load from file one building */
BUILDING_DATA *fread_building(char *bname)
{
	FILE *fp;
	BUILDING_DATA *bld = NULL;
	char fname[128];
	char letter;
	char *word;

	sprintf(fname, "%s%s", LIB_BUILDINGS, bname);
	if (!(fp = fopen(fname, "r")))
	{
		log("SYSERR: fread_building() - unable to open file %s.", fname);
		return (NULL);
	}

	for ( ;; )
	{
		letter = fread_letter(fp);
		if (letter == '*')
		{
			fread_to_eol( fp );
			continue;
		}
		
		if (letter != '#')
		{
			log( "fread_building: # not found." );
			break;
		}
		
		word = fread_word(fp);

		if (!strcmp(word, "BUILDING"))
		{
			if (!(bld = fread_building_data(fp)))
			{
				log("SYSERR: fread_building() - error in reading data for building file %s.", bname);
				break;
			}
		}
		else if (!strcmp(word, "ROOMS"))
			fread_building_rooms(bld, fp);
		else if (!strcmp(word, "ROOMCONTENT"))
			fread_building_contents(bld, fp);
		else if (!strcmp(word, "END"))
			break;
		else
		{
			log("fread_building: bad section %s.", word);
			continue;
		}
	}

	fclose(fp);
	return (bld);
}

/*
 * load all buildings
 *
 * called from ???
 */
void LoadBuildings(void)
{
	FILE *fp;
	BUILDING_DATA *bld;
	EXIT_DATA *pexit;
	char fname[128];
	int iHash;

	if (building_loaded)
	{
		log("SYSERR: LoadBuildings() - already loaded.");
		return;
	}

	sprintf(fname, "%sindex", LIB_BUILDINGS);
	if (!(fp = fopen(fname, "r")))
	{
		log("   No building templates defined.");
		return;
	}

	building_loaded				= TRUE;
	top_of_buildings			= 0;

	while (!feof(fp))
	{
		char *bldfname = fread_word( fp );

		if (bldfname[0] == '$')
			break;

		if (!(bld = fread_building(bldfname)))
			continue;

		building_to_room(bld, NULL);

		if (!BUILDING_FLAGGED(bld, BLD_F_RUIN))
		{
			/* exit building --> wilderness */
			pexit = make_exit(bld->rooms[bld->type->entrance], NULL, SOUTH);
			if (bld->vnum_room != NOWHERE)
				pexit->to_room	= get_room(bld->vnum_room);
			else
				pexit->to_room	= get_wild_room(bld->coord);
			pexit->keyword		= str_dup("door");
			SET_BIT(EXIT_FLAGS(pexit), EX_ISDOOR);
		}

		// execute building commands (load objs & mobs)
		exec_bld_cmds(bld);

		/* add to the global hash table */
		iHash					= bld->vnum % BLD_HASH;
		bld->next				= building_list[iHash];
		building_list[iHash]	= bld;

		top_of_buildings++;
	}

	fclose(fp);

	ReadWorksList();
}

/* ======================================================================= */
/* Code for repairing buildings                                            */
/* ======================================================================= */

/* make some reparations */
void repair_building(CHAR_DATA *ch, BUILDING_DATA *bld)
{
	if (BUILDING_FLAGGED(bld, BLD_F_RUIN))
	{
		act("$b: you cannot repair ruins.", FALSE, ch, NULL, bld, TO_CHAR);
		return;
	}

	if (bld->max_val.health == bld->curr_val.health)
	{
		act("$b does not need reparations.", FALSE, ch, NULL, bld, TO_CHAR);
		return;
	}

	if (!roll(GET_DEX(ch)))
	{
		send_to_char("You fail.\r\n", ch);
		return;
	}

	bld->curr_val.health++;
	act("You make sone reparations to $b.", FALSE, ch, NULL, bld, TO_CHAR);
	act("$n makes sone reparations to $b.", FALSE, ch, NULL, bld, TO_ROOM);
}


/* ============================================================================ */
/* Code for attacking, damaging and repairing buildings, plus buildings defense */
/* ============================================================================ */

/*
 * some buildings, in response of an attack,
 * can "generate" some mobs to fight the attacker
 *
 * this code pick a defender mob at random from
 * the list of possible defenders mobs
 */
CHAR_DATA *get_bld_defmob(BUILDING_DATA *bld)
{
	CHAR_DATA *mob;
	int xn, nlast, num;

	for (nlast = -1, xn = 0; xn < BLD_MAX_MOB; xn++)
	{
		if (bld->vmob[xn] == NOBODY)
			break;
		nlast = xn;
	}

	/* no mobs.. hmmmm */
	if (nlast == -1)
	{
		log("SYSERR: get_bld_defmob() - cannot find a valid mob vnum.");
		return (NULL);
	}

	if (nlast == 0)	num = nlast;				// single mob
	else			num = number(0, nlast);		// two or more mobs

	if (!(mob = read_mobile(bld->vmob[num], VIRTUAL)))
	{
		log("SYSERR: get_bld_defmob() - invalid mob vnum %d.", bld->vmob[num]);
		return (NULL);
	}

	return (mob);
}

/*
 * called when a building is destroyed
 */
void damage_people_inside(BUILDING_DATA *bld)
{
	CHAR_DATA *tch, *tch_next = NULL;
	EXIT_DATA *pexit;
	bool saved = FALSE;
	
	pexit = get_exit(bld->rooms[bld->type->entrance], SOUTH);
	if (pexit && !valid_exit(pexit))
		pexit = NULL;

	for (tch = bld->people; tch; tch = tch_next)
	{
		tch_next = tch->next_in_building;

		act("$b collapses and crumbles.", FALSE, tch, NULL, bld, TO_CHAR);

		saved = FALSE;
		/*
		 * if tch is in the entrance room, and if the door is open, and if he makes
		 * a check on dex at -4, arf, he can jump out of the building..
		 */
		if (tch->in_room == bld->rooms[bld->type->entrance] && pexit &&
			roll(GET_DEX(tch) - 4))
		{
			act("You quickly leap out of $b's door one moment before it crumbles.",
				FALSE, tch, NULL, bld, TO_CHAR);
			act("$n quickly leaps out of $b's door one moment before it crumbles.",
				FALSE, tch, NULL, bld, TO_ROOM);
			char_from_building(tch);
			char_from_room(tch);
			char_to_room(tch, bld->in_room);
			act("$n leaps out of $b'door one moment before it crumbles.",
				FALSE, tch, NULL, bld, TO_ROOM);
			continue;
		}

		// take him out so, if he die, we place the corpse OUTSIDE the building
		char_from_building(tch);
		char_from_room(tch);
		char_to_room(tch, bld->in_room);
		
		send_to_char("You are hit by falling rubbles.\r\n", tch);

		// char suffer massive damage.. :-(((
		damage(tch, tch, number(50,100) * (bld->type->vnum + 1), -1);
	}
}

/*
 * adjust destroyed building descriptions with
 * ruins of the correct type
 */
void make_ruins(BUILDING_DATA *bld)
{
	char dbuf[MAX_STRING_LENGTH];

	SET_BIT(BUILDING_FLAGS(bld), BLD_F_RUIN);

	sprintf(dbuf, "ruins %s", bld->type->name);
	if (bld->keyword)
		DISPOSE(bld->keyword);
	bld->keyword = str_dup(dbuf);

	sprintf(dbuf, "the ruins of %s %s", AN(bld->type->name), bld->type->name);
	if (bld->description)
		DISPOSE(bld->description);
	bld->description = str_dup(dbuf);
}

/*
 * return 0 if building is destroyed, >0 otherwise
 */
int damage_building(BUILDING_DATA *bld, int dam)
{
	bld->curr_val.health -= dam;

	if (bld->curr_val.health <= 0)
	{
		bld->curr_val.health = 0;
		damage_people_inside(bld);
		make_ruins(bld);
		fwrite_building(bld);		
		return (0);
	}

	return (1);
}

/* one hit to a building */
void hit_building(CHAR_DATA *ch, BUILDING_DATA *bld)
{
	OBJ_DATA *weapon;
	char dbuf[MAX_STRING_LENGTH];
	int dam;

	if (!ch || !bld)
		return;

	if (!(weapon = GET_EQ(ch, WEAR_WIELD)))
	{
		send_to_char("You cannot damage a building with your bare hands.\r\n", ch);
		return;
	}

	if (GET_OBJ_TYPE(weapon) != ITEM_WEAPON)
	{
		send_to_char("Using a weapon would surely help.\r\n", ch);
		return;
	}

	// calc damage
	dam = GET_REAL_DAMROLL(ch);
	dam += dice(GET_OBJ_VAL(weapon, 1), GET_OBJ_VAL(weapon, 2));

	// subtract building defense
	dam -= bld->curr_val.defense;

	// damage goes from 0 to 100
	dam = URANGE(0, dam, 100);

	// check for damaging the weapon used
	check_damage_obj(ch, weapon, 8);

	/* no damage done */
	if (!dam)
	{
		// message to attacker
		act("You strike $b, but make no damages.", FALSE, ch, NULL, bld, TO_CHAR);
		// message to room
		act("$n strikes $b but nothing happens.", FALSE, ch, NULL, bld, TO_ROOM);

		/*
		 * some buildings can auto-respond to attacks..
		 * ie: towers have npc archers which fires upon attacking ppl
		 */
		if (bld->curr_val.npc_attack)
		{
			damage(ch, ch, bld->curr_val.npc_attack, -1);
			act("You are hit by arrows fired at you from $b.", FALSE, ch, NULL, bld, TO_CHAR);
			act("$n is being hit by arrows fired at $m from $b.", FALSE, ch, NULL, bld, TO_ROOM);
		}
		
		return;
	}
	
	/* damage the building */

	// if 0 the building has been destroyed
	if (!damage_building(bld, dam))
	{
		// message to attacker
		act("Your attack DESTROY $b.", FALSE, ch, NULL, bld, TO_CHAR);
		// message to room
		act("$n DESTROYS $b with $s attack.", FALSE, ch, NULL, bld, TO_ROOM);
		return;
	}
	
	// message to attacker
	sprintf(dbuf, "You strike $b, causing %d damages.", dam);
	act(dbuf, FALSE, ch, NULL, bld, TO_CHAR);
	// message to room
	act("$n strike $b, causing some damage.", FALSE, ch, NULL, bld, TO_ROOM);
	
	/*
	 * some buildings can auto-respond to attacks..
	 * ie: towers could have npc archers which fires upon attacking ppl
	 */
	if (bld->curr_val.npc_attack)
	{
		damage(ch, ch, bld->curr_val.npc_attack, -1);
		act("You are hit by arrows fired at you from $b.", FALSE, ch, NULL, bld, TO_CHAR);
		act("$n is being hit by arrows fired at $m from $b.", FALSE, ch, NULL, bld, TO_ROOM);
	}
	
	/*
	 * some buildings, in response of an attack,
	 * can "generate" mobs to fight the attacker
	 */
	if (bld->curr_val.mob_attack > 0)
	{
		CHAR_DATA *mob = get_bld_defmob(bld);
		
		if (mob)
		{
			bld->curr_val.mob_attack--;
			// message to vict
			sprintf(dbuf, "$N exit from %s and attacks you.", bld->description);
			act(dbuf, FALSE, ch, NULL, mob, TO_CHAR);
			// message to room
			sprintf(dbuf, "$N exit from %s and attacks $n.", bld->description);
			act(dbuf, FALSE, ch, NULL, mob, TO_ROOM);
			
			char_to_room(mob, ch->in_room);
			hit(mob, ch, -1);
		}
	}
}


/* one single attack to a building */
bool attack_building(CHAR_DATA *ch, char *arg)
{
	BUILDING_DATA *bld;

	if (!*arg)
		return (FALSE);

	if (!(bld = find_building_in_room_by_name(ch->in_room, arg)))
		return (FALSE);

	if (BUILDING_FLAGGED(bld, BLD_F_RUIN))
	{
		ch_printf(ch, "%s %s has already been destroyed.\r\n",
			AN(bld->type->name), bld->type->name);
		return (TRUE);
	}

	hit_building(ch, bld);
	return (TRUE);
}

/* ============================================================================ */
/* AUTHORIZE Code                                                               */
/* ============================================================================ */

AUTH_DATA *get_ship_auth( SHIP_DATA *ship, long idnum );
void auth_aboard( SHIP_DATA *ship, CHAR_DATA *ch, CHAR_DATA *vict, int mode );

ACMD(do_authorize)
{
	BUILDING_DATA *pBld = NULL;
	SHIP_DATA *pShip = NULL;
	AUTH_DATA *bAuthList = NULL, *bAuth = NULL;
	CHAR_DATA *vict;
	char arg[MAX_INPUT_LENGTH], sbuf[MAX_STRING_LENGTH];

	if (!ch) return;

	if (!(pBld = ch->in_building) && !(pShip = ch->in_ship))
	{
		send_to_char("You can use this command only when inside a building or aboard a ship.\r\n", ch);
		return;
	}

	argument = one_argument(argument, arg);
	
	if (!*arg)
	{
		send_to_char(
			"Usage:\r\n"
			"-------------------------------------------------------------\r\n"
			"authorize list    show list of authorized people\r\n"
			"authorize <name>  set or revoke authorization to player 'name'\r\n"
			"-------------------------------------------------------------\r\n"
			, ch);
		return;
	}

	if (!str_cmp(arg, "list"))
	{
		if (pBld)
		{
			if (!can_enter_bld(ch, pBld, TRUE))
			{
				send_to_char("You have no rights, go away.\r\n", ch);
				return;
			}
			bAuthList = pBld->auth;
		}
		else if (pShip)
		{
			if (GET_IDNUM(ch) != pShip->idcaptain && GET_IDNUM(ch) != pShip->idowner)
			{
				send_to_char("You must be the captain or the owner to authorize people.\r\n", ch );
				return;
			}
			bAuthList = pShip->authorized;
		}
		else
			bAuthList = NULL;

		if (!bAuthList)
		{
			send_to_char("No one is authorized.\r\n", ch);
			return;
		}

		strcpy(sbuf, "List of authorized people:\r\n");
		for (bAuth = bAuthList; bAuth; bAuth = bAuth->next)
			sprintf(sbuf + strlen(sbuf), "%s\r\n", bAuth->name);

		page_string(ch->desc, sbuf, 0);
		return;
	}

	if (pBld)
	{
		if (!(vict = get_player_vis(ch, arg, NULL, FIND_CHAR_WORLD)))
		{
			ch_printf(ch, "No one called '%' is in the game at the moment.\r\n", arg);
			return;
		}

		/* new introduction system */
		if ( !is_char_known(ch, vict) )
		{
			send_to_char("You cannot authorize someone you don't know.\r\n", ch );
			return;
		}

		if (!can_enter_bld(ch, pBld, TRUE))
		{
			send_to_char("You have no rights, go away.\r\n", ch);
			return;
		}

		if (get_bld_auth(pBld, GET_IDNUM(vict)))
			auth_enter(pBld, ch, vict, 0);
		else
			auth_enter(pBld, ch, vict, 1);
	}
	else if (pShip)
	{
		ROOM_DATA *pRoom;

		if (!SHIP_FLAGGED(pShip, SHIP_IN_PORT) || !(pRoom = get_wild_room(pShip->port)))
		{
			send_to_char("You can authorize people only when ship is at one port.\r\n", ch);
			return;
		}

		if (!(vict = get_char_elsewhere_vis(ch, pRoom, arg, NULL)))
		{
			ch_printf( ch, "No one called '%s' is in the port.\r\n", arg );
			return;
		}

		/* new introduction system */
		if (!is_char_known(ch, vict))
		{
			send_to_char("You cannot authorize someone you don't know.\r\n", ch );
			return;
		}

		if (GET_IDNUM(ch) != pShip->idcaptain && GET_IDNUM(ch) != pShip->idowner)
		{
			send_to_char("You must be the captain or the owner to authorize people.\r\n", ch );
			return;
		}

		/* toggle authorization */
		if (get_ship_auth(pShip, GET_IDNUM(vict)))
			auth_aboard(pShip, ch, vict, 0);
		else
			auth_aboard(pShip, ch, vict, 1);
	}
}