1stMUD/corefiles/
1stMUD/gods/
1stMUD/notes/
1stMUD/player/
1stMUD/win32/
1stMUD/win32/ROM/
/**************************************************************************
*  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-2002 by Ryan Jennings              *
*            http://1stmud.dlmud.com/  <r-jenn@shaw.ca>                   *
***************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <sys/types.h>
#if !defined(WIN32)
#include <sys/time.h>
#endif
#include <stdarg.h>
#include "merc.h"
#include "db.h"
#include "tables.h"
#include "lookup.h"
#include "interp.h"
#include "magic.h"
#include "recycle.h"
#include "olc.h"

/* values for db2.c */

extern AREA_DATA *current_area;
int top_oprog_index;
int top_rprog_index;
int nAllocPerm;
long sAllocPerm;

/*
 * Snarf a mob section.  new style
 */
void load_mobiles(FILE * fp)
{
	MOB_INDEX_DATA *pMobIndex;

	if (!current_area)			/* OLC */
	{
		bug("Load_mobiles: no #AREA seen yet.", 0);
		exit(1);
	}

	for (;;)
	{
		vnum_t vnum;
		char letter;
		int iHash;
		const char *tmp;

		letter = fread_letter(fp);
		if (letter != '#')
		{
			bug("Load_mobiles: # not found.", 0);
			exit(1);
		}

		vnum = fread_number(fp);
		if (vnum == 0)
			break;

		fBootDb = FALSE;
		if (get_mob_index(vnum) != NULL)
		{
			bug("Load_mobiles: vnum %d duplicated.", vnum);
			exit(1);
		}
		fBootDb = TRUE;

		pMobIndex = new_mob_index();
		pMobIndex->vnum = vnum;
		pMobIndex->area = current_area;	/* OLC */
		pMobIndex->new_format = TRUE;
		newmobs++;
		free_string(pMobIndex->player_name);
		pMobIndex->player_name = fread_string(fp);
		free_string(pMobIndex->short_descr);
		pMobIndex->short_descr = fread_string(fp);
		free_string(pMobIndex->long_descr);
		pMobIndex->long_descr = fread_string(fp);
		free_string(pMobIndex->description);
		pMobIndex->description = fread_string(fp);
		tmp = fread_string(fp);
		pMobIndex->race = race_lookup(tmp);
		free_string(tmp);

		pMobIndex->act = fread_flag(fp) | ACT_IS_NPC | pMobIndex->race->act;
		pMobIndex->affected_by = fread_flag(fp) | pMobIndex->race->aff;
		pMobIndex->pShop = NULL;
		pMobIndex->alignment = fread_number(fp);
		pMobIndex->group = fread_number(fp);

		pMobIndex->level = fread_number(fp);
		pMobIndex->hitroll = fread_number(fp);

		/* read hit dice */
		pMobIndex->hit[DICE_NUMBER] = fread_number(fp);
		/* 'd'          */ fread_letter(fp);
		pMobIndex->hit[DICE_TYPE] = fread_number(fp);
		/* '+'          */ fread_letter(fp);
		pMobIndex->hit[DICE_BONUS] = fread_number(fp);

		/* read mana dice */
		pMobIndex->mana[DICE_NUMBER] = fread_number(fp);
		fread_letter(fp);
		pMobIndex->mana[DICE_TYPE] = fread_number(fp);
		fread_letter(fp);
		pMobIndex->mana[DICE_BONUS] = fread_number(fp);

		/* read damage dice */
		pMobIndex->damage[DICE_NUMBER] = fread_number(fp);
		fread_letter(fp);
		pMobIndex->damage[DICE_TYPE] = fread_number(fp);
		fread_letter(fp);
		pMobIndex->damage[DICE_BONUS] = fread_number(fp);
		pMobIndex->dam_type = attack_lookup(fread_word(fp));

		/* read armor class */
		pMobIndex->ac[AC_PIERCE] = fread_number(fp) * 10;
		pMobIndex->ac[AC_BASH] = fread_number(fp) * 10;
		pMobIndex->ac[AC_SLASH] = fread_number(fp) * 10;
		pMobIndex->ac[AC_EXOTIC] = fread_number(fp) * 10;

		/* read flags and add in data from the race table */
		pMobIndex->off_flags = fread_flag(fp) | pMobIndex->race->off;
		pMobIndex->imm_flags = fread_flag(fp) | pMobIndex->race->imm;
		pMobIndex->res_flags = fread_flag(fp) | pMobIndex->race->res;
		pMobIndex->vuln_flags = fread_flag(fp) | pMobIndex->race->vuln;

		/* vital statistics */
		pMobIndex->start_pos = position_lookup(fread_word(fp));
		pMobIndex->default_pos = position_lookup(fread_word(fp));
		pMobIndex->sex = sex_lookup(fread_word(fp));

		pMobIndex->wealth = fread_number(fp);

		pMobIndex->form = fread_flag(fp) | pMobIndex->race->form;
		pMobIndex->parts = fread_flag(fp) | pMobIndex->race->parts;
		/* size */
		CHECK_POS(pMobIndex->size, size_lookup(fread_word(fp)), "size");
/*	pMobIndex->size			= size_lookup(fread_word(fp)); */
		pMobIndex->material = str_dup(fread_word(fp));

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

			if (letter == 'F')
			{
				char *word;
				flag_t vector;

				word = fread_word(fp);
				vector = fread_flag(fp);

				if (!str_prefix(word, "act"))
					REMOVE_BIT(pMobIndex->act, vector);
				else if (!str_prefix(word, "aff"))
					REMOVE_BIT(pMobIndex->affected_by, vector);
				else if (!str_prefix(word, "off"))
					REMOVE_BIT(pMobIndex->off_flags, vector);
				else if (!str_prefix(word, "imm"))
					REMOVE_BIT(pMobIndex->imm_flags, vector);
				else if (!str_prefix(word, "res"))
					REMOVE_BIT(pMobIndex->res_flags, vector);
				else if (!str_prefix(word, "vul"))
					REMOVE_BIT(pMobIndex->vuln_flags, vector);
				else if (!str_prefix(word, "for"))
					REMOVE_BIT(pMobIndex->form, vector);
				else if (!str_prefix(word, "par"))
					REMOVE_BIT(pMobIndex->parts, vector);
				else
				{
					bug("Flag remove: flag not found.", 0);
					exit(1);
				}
			}
			else if (letter == 'M')
			{
				PROG_LIST *pMprog;
				char *word;
				int trigger = 0;

				pMprog = new_prog();
				word = fread_word(fp);
				if ((trigger = flag_value(mprog_flags, word)) == NO_FLAG)
				{
					bug("MOBprogs: invalid trigger.", 0);
					exit(1);
				}
				SET_BIT(pMobIndex->mprog_flags, trigger);
				pMprog->trig_type = trigger;
				pMprog->vnum = fread_number(fp);
				free_string(pMprog->trig_phrase);
				pMprog->trig_phrase = fread_string(fp);
				LINK(pMprog, pMobIndex->first_mprog, pMobIndex->last_mprog,
					 next, prev);
			}
			else
			{
				ungetc(letter, fp);
				break;
			}
		}

		iHash = vnum % MAX_KEY_HASH;
		LINK_SINGLE(pMobIndex, next, mob_index_hash[iHash]);
		top_mob_index++;
		top_vnum_mob = top_vnum_mob < vnum ? vnum : top_vnum_mob;	/* OLC */
		assign_area_vnum(vnum);	/* OLC */
		kill_table[URANGE(0, pMobIndex->level, MAX_LEVEL - 1)].number++;
	}

	return;
}

/*
 * Snarf an obj section. new style
 */
void load_objects(FILE * fp)
{
	OBJ_INDEX_DATA *pObjIndex;

	if (!current_area)			/* OLC */
	{
		bug("Load_objects: no #AREA seen yet.", 0);
		exit(1);
	}

	for (;;)
	{
		vnum_t vnum;
		char letter;
		int iHash;

		letter = fread_letter(fp);
		if (letter != '#')
		{
			bug("Load_objects: # not found.", 0);
			exit(1);
		}

		vnum = fread_number(fp);
		if (vnum == 0)
			break;

		fBootDb = FALSE;
		if (get_obj_index(vnum) != NULL)
		{
			bug("Load_objects: vnum %d duplicated.", vnum);
			exit(1);
		}
		fBootDb = TRUE;

		pObjIndex = new_obj_index();
		pObjIndex->vnum = vnum;
		pObjIndex->area = current_area;	/* OLC */
		pObjIndex->new_format = TRUE;
		pObjIndex->reset_num = 0;
		newobjs++;
		free_string(pObjIndex->name);
		pObjIndex->name = fread_string(fp);
		free_string(pObjIndex->short_descr);
		pObjIndex->short_descr = fread_string(fp);
		free_string(pObjIndex->description);
		pObjIndex->description = fread_string(fp);
		free_string(pObjIndex->material);
		pObjIndex->material = fread_string(fp);

		CHECK_POS(pObjIndex->item_type, item_lookup(fread_word(fp)),
				  "item_type");
		pObjIndex->extra_flags = fread_flag(fp);
		pObjIndex->wear_flags = fread_flag(fp);
		switch (pObjIndex->item_type)
		{
		case ITEM_WEAPON:
			pObjIndex->value[0] = weapon_typ(fread_word(fp));
			pObjIndex->value[1] = fread_number(fp);
			pObjIndex->value[2] = fread_number(fp);
			pObjIndex->value[3] = attack_lookup(fread_word(fp));
			pObjIndex->value[4] = fread_flag(fp);
			break;
		case ITEM_CONTAINER:
			pObjIndex->value[0] = fread_number(fp);
			pObjIndex->value[1] = fread_flag(fp);
			pObjIndex->value[2] = fread_number(fp);
			pObjIndex->value[3] = fread_number(fp);
			pObjIndex->value[4] = fread_number(fp);
			break;
		case ITEM_DRINK_CON:
		case ITEM_FOUNTAIN:
			pObjIndex->value[0] = fread_number(fp);
			pObjIndex->value[1] = fread_number(fp);
			CHECK_POS(pObjIndex->value[2],
					  liq_lookup(fread_word(fp)), "liq_lookup");
			pObjIndex->value[3] = fread_number(fp);
			pObjIndex->value[4] = fread_number(fp);
			break;
		case ITEM_WAND:
		case ITEM_STAFF:
			pObjIndex->value[0] = fread_number(fp);
			pObjIndex->value[1] = fread_number(fp);
			pObjIndex->value[2] = fread_number(fp);
			pObjIndex->value[3] = skill_lookup(fread_word(fp));
			pObjIndex->value[4] = fread_number(fp);
			break;
		case ITEM_POTION:
		case ITEM_PILL:
		case ITEM_SCROLL:
			pObjIndex->value[0] = fread_number(fp);
			pObjIndex->value[1] = skill_lookup(fread_word(fp));
			pObjIndex->value[2] = skill_lookup(fread_word(fp));
			pObjIndex->value[3] = skill_lookup(fread_word(fp));
			pObjIndex->value[4] = skill_lookup(fread_word(fp));
			break;
		default:
			pObjIndex->value[0] = fread_flag(fp);
			pObjIndex->value[1] = fread_flag(fp);
			pObjIndex->value[2] = fread_flag(fp);
			pObjIndex->value[3] = fread_flag(fp);
			pObjIndex->value[4] = fread_flag(fp);
			break;
		}
		pObjIndex->level = fread_number(fp);
		pObjIndex->weight = fread_number(fp);
		pObjIndex->cost = fread_number(fp);

		/* condition */
		letter = fread_letter(fp);
		switch (letter)
		{
		case ('P'):
			pObjIndex->condition = 100;
			break;
		case ('G'):
			pObjIndex->condition = 90;
			break;
		case ('A'):
			pObjIndex->condition = 75;
			break;
		case ('W'):
			pObjIndex->condition = 50;
			break;
		case ('D'):
			pObjIndex->condition = 25;
			break;
		case ('B'):
			pObjIndex->condition = 10;
			break;
		case ('R'):
			pObjIndex->condition = 0;
			break;
		default:
			pObjIndex->condition = 100;
			break;
		}

		for (;;)
		{
			char pletter;

			pletter = fread_letter(fp);

			if (pletter == 'A')
			{
				AFFECT_DATA *paf;

				paf = new_affect();
				paf->where = TO_OBJECT;
				paf->type = -1;
				paf->level = pObjIndex->level;
				paf->duration = -1;
				paf->location = fread_number(fp);
				paf->modifier = fread_number(fp);
				paf->bitvector = 0;
				LINK(paf, pObjIndex->first_affect, pObjIndex->last_affect, next,
					 prev);
				top_affect++;
			}

			else if (pletter == 'F')
			{
				AFFECT_DATA *paf;

				paf = new_affect();
				pletter = fread_letter(fp);
				switch (pletter)
				{
				case 'A':
					paf->where = TO_AFFECTS;
					break;
				case 'I':
					paf->where = TO_IMMUNE;
					break;
				case 'R':
					paf->where = TO_RESIST;
					break;
				case 'V':
					paf->where = TO_VULN;
					break;
				default:
					bug("Load_objects: Bad where on flag set.", 0);
					exit(1);
				}
				paf->type = -1;
				paf->level = pObjIndex->level;
				paf->duration = -1;
				paf->location = fread_number(fp);
				paf->modifier = fread_number(fp);
				paf->bitvector = fread_flag(fp);
				LINK(paf, pObjIndex->first_affect, pObjIndex->last_affect, next,
					 prev);
				top_affect++;
			}

			else if (pletter == 'E')
			{
				EXTRA_DESCR_DATA *ed;

				ed = new_extra_descr();
				free_string(ed->keyword);
				ed->keyword = fread_string(fp);
				free_string(ed->description);
				ed->description = fread_string(fp);
				LINK(ed, pObjIndex->first_extra_descr,
					 pObjIndex->last_extra_descr, next, prev);
				top_ed++;
			}
			else if (pletter == 'O')
			{
				PROG_LIST *pOprog;
				const char *word;
				flag_t trigger = 0;

				pOprog = new_prog();
				word = fread_word(fp);
				if ((trigger = flag_value(oprog_flags, word)) == NO_FLAG)
				{
					bug("OBJprogs: invalid trigger.", 0);
					exit(1);
				}
				SET_BIT(pObjIndex->oprog_flags, trigger);
				pOprog->trig_type = trigger;
				pOprog->vnum = fread_number(fp);
				free_string(pOprog->trig_phrase);
				pOprog->trig_phrase = fread_string(fp);
				LINK(pOprog, pObjIndex->first_oprog, pObjIndex->last_oprog,
					 next, prev);
			}

			else
			{
				ungetc(pletter, fp);
				break;
			}
		}

		iHash = vnum % MAX_KEY_HASH;
		LINK_SINGLE(pObjIndex, next, obj_index_hash[iHash]);
		top_obj_index++;
		top_vnum_obj = top_vnum_obj < vnum ? vnum : top_vnum_obj;	/* OLC */
		assign_area_vnum(vnum);	/* OLC */
	}

	return;
}

/*****************************************************************************
 Name:	        convert_objects
 Purpose:	Converts all old format objects to new format
 Called by:	boot_db (db.c).
 Note:          Loops over all resets to find the level of the mob
                loaded before the object to determine the level of
                the object.
		It might be better to update the levels in load_resets().
		This function is not pretty.. Sorry about that :)
 Author:        Hugin
 ****************************************************************************/
void convert_objects(void)
{
	vnum_t vnum;
	AREA_DATA *pArea;
	RESET_DATA *pReset;
	MOB_INDEX_DATA *pMob = NULL;
	OBJ_INDEX_DATA *pObj;
	ROOM_INDEX_DATA *pRoom;

	if (newobjs == top_obj_index)
		return;					/* all objects in new format */

	for (pArea = area_first; pArea; pArea = pArea->next)
	{
		for (vnum = pArea->min_vnum; vnum <= pArea->max_vnum; vnum++)
		{
			if (!(pRoom = get_room_index(vnum)))
				continue;

			for (pReset = pRoom->reset_first; pReset; pReset = pReset->next)
			{
				switch (pReset->command)
				{
				case 'M':
					if (!(pMob = get_mob_index(pReset->arg1)))
						bug("Convert_objects: 'M': bad vnum %d.", pReset->arg1);
					break;

				case 'O':
					if (!(pObj = get_obj_index(pReset->arg1)))
					{
						bug("Convert_objects: 'O': bad vnum %d.", pReset->arg1);
						break;
					}

					if (pObj->new_format)
						continue;

					if (!pMob)
					{
						bug("Convert_objects: 'O': No mob reset yet.", 0);
						break;
					}

					pObj->level =
						pObj->level <
						1 ? pMob->level -
						2 : UMIN(pObj->level, pMob->level - 2);
					break;

				case 'P':
					{
						OBJ_INDEX_DATA *ppObj, *pObjTo;

						if (!(ppObj = get_obj_index(pReset->arg1)))
						{
							bug("Convert_objects: 'P': bad vnum %d.",
								pReset->arg1);
							break;
						}

						if (ppObj->new_format)
							continue;

						if (!(pObjTo = get_obj_index(pReset->arg3)))
						{
							bug("Convert_objects: 'P': bad vnum %d.",
								pReset->arg3);
							break;
						}

						ppObj->level =
							ppObj->level <
							1 ? pObjTo->level : UMIN(ppObj->level,
													 pObjTo->level);
					}
					break;

				case 'G':
				case 'E':
					if (!(pObj = get_obj_index(pReset->arg1)))
					{
						bug("Convert_objects: 'E' or 'G': bad vnum %d.",
							pReset->arg1);
						break;
					}

					if (!pMob)
					{
						bug
							("Convert_objects: 'E' or 'G': null mob for vnum %d.",
							pReset->arg1);
						break;
					}

					if (pObj->new_format)
						continue;

					if (pMob->pShop)
					{
						switch (pObj->item_type)
						{
						default:
							pObj->level = UMAX(0, pObj->level);
							break;
						case ITEM_PILL:
						case ITEM_POTION:
							pObj->level = UMAX(5, pObj->level);
							break;
						case ITEM_SCROLL:
						case ITEM_ARMOR:
						case ITEM_WEAPON:
							pObj->level = UMAX(10, pObj->level);
							break;
						case ITEM_WAND:
						case ITEM_TREASURE:
							pObj->level = UMAX(15, pObj->level);
							break;
						case ITEM_STAFF:
							pObj->level = UMAX(20, pObj->level);
							break;
						}
					}
					else
						pObj->level =
							pObj->level <
							1 ? pMob->level : UMIN(pObj->level, pMob->level);
					break;
				}				/* switch ( pReset->command ) */
			}
		}
	}

	/* do the conversion: */

	for (pArea = area_first; pArea; pArea = pArea->next)
		for (vnum = pArea->min_vnum; vnum <= pArea->max_vnum; vnum++)
			if ((pObj = get_obj_index(vnum)))
				if (!pObj->new_format)
					convert_object(pObj);

	return;
}

/*****************************************************************************
 Name:		convert_object
 Purpose:	Converts an old_format obj to new_format
 Called by:	convert_objects (db2.c).
 Note:          Dug out of create_obj (db.c)
 Author:        Hugin
 ****************************************************************************/
void convert_object(OBJ_INDEX_DATA * pObjIndex)
{
	int level;
	int number, type;			/* for dice-conversion */

	if (!pObjIndex || pObjIndex->new_format)
		return;

	level = pObjIndex->level;

	pObjIndex->level = UMAX(0, pObjIndex->level);	/* just to be sure */
	pObjIndex->cost = 10 * level;

	switch (pObjIndex->item_type)
	{
	default:
		bug("Obj_convert: vnum %d bad type.", pObjIndex->item_type);
		break;

	case ITEM_LIGHT:
	case ITEM_TREASURE:
	case ITEM_FURNITURE:
	case ITEM_TRASH:
	case ITEM_CONTAINER:
	case ITEM_DRINK_CON:
	case ITEM_KEY:
	case ITEM_FOOD:
	case ITEM_BOAT:
	case ITEM_CORPSE_NPC:
	case ITEM_CORPSE_PC:
	case ITEM_FOUNTAIN:
	case ITEM_MAP:
	case ITEM_CLOTHING:
	case ITEM_SCROLL:
		break;

	case ITEM_WAND:
	case ITEM_STAFF:
		pObjIndex->value[2] = pObjIndex->value[1];
		break;

	case ITEM_WEAPON:

		/*
		 * The conversion below is based on the values generated
		 * in one_hit() (fight.c).  Since I don't want a lvl 50 
		 * weapon to do 15d3 damage, the min value will be below
		 * the one in one_hit, and to make up for it, I've made 
		 * the max value higher.
		 * (I don't want 15d2 because this will hardly ever roll
		 * 15 or 30, it will only roll damage close to 23.
		 * I can't do 4d8+11, because one_hit there is no dice-
		 * bounus value to set...)
		 *
		 * The conversion below gives:

		 level:   dice      min      max      mean
		 1:     1d8      1( 2)    8( 7)     5( 5)
		 2:     2d5      2( 3)   10( 8)     6( 6)
		 3:     2d5      2( 3)   10( 8)     6( 6)
		 5:     2d6      2( 3)   12(10)     7( 7)
		 10:     4d5      4( 5)   20(14)    12(10)
		 20:     5d5      5( 7)   25(21)    15(14)
		 30:     5d7      5(10)   35(29)    20(20)
		 50:     5d11     5(15)   55(44)    30(30)

		 */

		number = UMIN(level / 4 + 1, 5);
		type = (level + 7) / number;

		pObjIndex->value[1] = number;
		pObjIndex->value[2] = type;
		break;

	case ITEM_ARMOR:
		pObjIndex->value[0] = level / 5 + 3;
		pObjIndex->value[1] = pObjIndex->value[0];
		pObjIndex->value[2] = pObjIndex->value[0];
		break;

	case ITEM_POTION:
	case ITEM_PILL:
		break;

	case ITEM_MONEY:
		pObjIndex->value[0] = pObjIndex->cost;
		break;
	}

	pObjIndex->new_format = TRUE;
	++newobjs;

	return;
}

/*****************************************************************************
 Name:		convert_mobile
 Purpose:	Converts an old_format mob into new_format
 Called by:	load_old_mob (db.c).
 Note:          Dug out of create_mobile (db.c)
 Author:        Hugin
 ****************************************************************************/
void convert_mobile(MOB_INDEX_DATA * pMobIndex)
{
	int i;
	int type, number, bonus;
	int level;

	if (!pMobIndex || pMobIndex->new_format)
		return;

	level = pMobIndex->level;

	pMobIndex->act |= ACT_WARRIOR;

	/*
	 * Calculate hit dice.  Gives close to the hitpoints
	 * of old format mobs created with create_mobile()  (db.c)
	 * A high number of dice makes for less variance in mobiles
	 * hitpoints.
	 * (might be a good idea to reduce the max number of dice)
	 *
	 * The conversion below gives:

	 level:     dice         min         max        diff       mean
	 1:       1d2+6       7(  7)     8(   8)     1(   1)     8(   8)
	 2:       1d3+15     16( 15)    18(  18)     2(   3)    17(  17)
	 3:       1d6+24     25( 24)    30(  30)     5(   6)    27(  27)
	 5:      1d17+42     43( 42)    59(  59)    16(  17)    51(  51)
	 10:      3d22+96     99( 95)   162( 162)    63(  67)   131(    )
	 15:     5d30+161    166(159)   311( 311)   145( 150)   239(    )
	 30:    10d61+416    426(419)  1026(1026)   600( 607)   726(    )
	 50:    10d169+920   930(923)  2610(2610)  1680(1688)  1770(    )

	 The values in parenthesis give the values generated in create_mobile.
	 Diff = max - min.  Mean is the arithmetic mean.
	 (hmm.. must be some roundoff error in my calculations.. smurfette got
	 1d6+23 hp at level 3 ? -- anyway.. the values above should be
	 approximately right..)
	 */
	type = level * level * 27 / 40;
	number = UMIN(type / 40 + 1, 10);	/* how do they get 11 ??? */
	type = UMAX(2, type / number);
	bonus = UMAX(0, level * (8 + level) * 9 / 10 - number * type);

	pMobIndex->hit[DICE_NUMBER] = number;
	pMobIndex->hit[DICE_TYPE] = type;
	pMobIndex->hit[DICE_BONUS] = bonus;

	pMobIndex->mana[DICE_NUMBER] = level;
	pMobIndex->mana[DICE_TYPE] = 10;
	pMobIndex->mana[DICE_BONUS] = 100;

	/*
	 * Calculate dam dice.  Gives close to the damage
	 * of old format mobs in damage()  (fight.c)
	 */
	type = level * 7 / 4;
	number = UMIN(type / 8 + 1, 5);
	type = UMAX(2, type / number);
	bonus = UMAX(0, level * 9 / 4 - number * type);

	pMobIndex->damage[DICE_NUMBER] = number;
	pMobIndex->damage[DICE_TYPE] = type;
	pMobIndex->damage[DICE_BONUS] = bonus;

	switch (number_range(1, 3))
	{
	case (1):
		pMobIndex->dam_type = 3;
		break;					/* slash  */
	case (2):
		pMobIndex->dam_type = 7;
		break;					/* pound  */
	case (3):
		pMobIndex->dam_type = 11;
		break;					/* pierce */
	}

	for (i = 0; i < 3; i++)
		pMobIndex->ac[i] = interpolate(level, 100, -100);
	pMobIndex->ac[3] = interpolate(level, 100, 0);	/* exotic */

	pMobIndex->wealth /= 100;
	pMobIndex->size = SIZE_MEDIUM;
	pMobIndex->material = str_dup("none");

	pMobIndex->new_format = TRUE;
	++newmobs;

	return;
}

/*
 * Memory management.
 * Increase MAX_STRING if you have too.
 * Tune the others only if you understand what you're doing.
 */

#define IMP_COUNTER( type, start, px) \
  int px##type();                      \
  int px##type() { type * p = start; int n = 0; while( p!=NULL ) { p= p->next; n++; } return n; }

#define IMP_LIST_COUNTERS( type, prefix ) \
		IMP_COUNTER( type, prefix##first, n ) \
        IMP_COUNTER( type, prefix##free, f )

// these object types have no global list
// *INDENT-OFF*

IMP_COUNTER(PC_DATA, pcdata_free, f) 
IMP_COUNTER(AFFECT_DATA, affect_free, f)

// these do
#define object_free obj_free
IMP_LIST_COUNTERS(OBJ_DATA, object_);
IMP_LIST_COUNTERS(CHAR_DATA, char_);
IMP_LIST_COUNTERS(DESCRIPTOR_DATA, descriptor_);
IMP_LIST_COUNTERS(MBR_DATA, mbr_);

// *INDENT-ON*

int nNOTE_DATA(void)
{
	NOTE_DATA *p;
	int board;
	int n = 0;
	for (board = 0; board < MAX_BOARD; board++)
	{
		BOARD_DATA *b = &boards[board];

		for (p = b->note_first; p != NULL; p = p->next)
			n++;
	}
	return n;
}

#define rpt( name, n, obj ) chprintf( ch, "%5d %-14s *%4d Bytes =%6d kB\n\r", \
	                                 n, name, sizeof(obj), (n) * sizeof(obj)/1024 )
		// report on an object type

#define rpt_dy( name, type ) { int n= n##type(); rpt( name, n, type ); }
		// report object with dynamic counting of list -
		// calls rpt() without evaluating n twice 

#define rptsd( st, sz )	chprintf( ch, "%-34s %5d kB\n\r", st, sz )
		// report string and decimal

#define rptdsd(n,st,sz)	chprintf( ch, "%5d %-28s %5d kB\n\r", n, st, sz )
		// report decimal, string, decimal

const char separator[] = "-------------------------------------------\n\r";

CH_CMD(do_memory_perms)
{
	chprint(ch, separator);
	chprintln(ch, "Permanent memory objects in use");
	chprint(ch, separator);
	rpt_dy("Characters", CHAR_DATA);
	rpt("PCs",
		nCHAR_DATA() -
		mobile_count /* should be an accurate shortcut */ , PC_DATA);
	rpt_dy("Descriptors", DESCRIPTOR_DATA);
	rpt("Mob Prototypes", top_mob_index, MOB_INDEX_DATA);
	rpt_dy("Objects", OBJ_DATA);
	rpt("Obj Prototypes", top_obj_index, OBJ_INDEX_DATA);
	rpt("Extra Descs", top_ed, EXTRA_DESCR_DATA);
	rpt("Affects", top_affect, AFFECT_DATA);
	rpt("Rooms", top_room, ROOM_INDEX_DATA);
	rpt("Exits", top_exit, EXIT_DATA);
	rpt("Shops", top_shop, SHOP_DATA);
	rpt("Areas", top_area, AREA_DATA);
	rpt("Resets", top_reset, RESET_DATA);
	rpt("Help entries", top_help, HELP_DATA);
	rpt_dy("Members", MBR_DATA);
	rpt_dy("Notes", NOTE_DATA);
	rpt("Socials", maxSocial, struct social_type);
	chprint(ch, separator);
}

#define rptf( name, type ) { int n= f##type(); rpt( name, n, type ); }

CH_CMD(do_memory_freelists)
{
	chprint(ch, separator);
	chprintln(ch, "Memory objects waiting to be recycled");
	chprint(ch, separator);
	rptf("Characters", CHAR_DATA);
	rptf("PCs", PC_DATA);
	rptf("Descriptors", DESCRIPTOR_DATA);
	rptf("Objects", OBJ_DATA);
	rptf("Affects", AFFECT_DATA);
	rptf("Members", MBR_DATA);
	chprint(ch, separator);
}

CH_CMD(do_memory_heap)
{
	chprint(ch, separator);
#if !defined(NO_STRHASH)
	rptsd("String Space allocated at DB boot", MAX_STRING / 1024);
	rptdsd(nAllocString, "Strings in string space", sAllocString / 1024);
	rptsd("Excess string space", MAX_STRING / 1024 - sAllocString / 1024);
#endif
	chprintlnf(ch, " Perms %d blocks of %ld kb.",
			   nAllocPerm, sAllocPerm / 1024);

	chprint(ch, separator);
}

CH_CMD(do_memory_formats)
{
	chprint(ch, separator);
	chprintf(ch, "Old format mobs: %d\n\rOld format objects: %d\n\r",
			 top_mob_index - newmobs, top_obj_index - newobjs);
	chprint(ch, separator);
}

#undef rpt
#undef rpt_dy
#undef rptsd
#undef rptdsd
	/* Those macros are not for general use */

CH_CMD(do_memory_help)
{
	chprintln(ch, "usage: memory <command>\n\r\ravailable commands:\n\r"
			  "heap                Report heap memory allocated by the memory manager\n\r"
			  "perms               Report managed memory objects in use by other modules\n\r"
			  "freelists           Report memory objects waiting to be recycled\n\r"
			  "formats             Report old format prototypes\n\r"
			  "?                   This message");
}

CH_CMD(do_memory)
{
	vinterpret(ch, argument, "perms", do_memory_perms, "heap",
			   do_memory_heap, "freelists", do_memory_freelists, "formats",
			   do_memory_formats, NULL, do_memory_help);
}

int strswitch(const char *arg, ...)
{
	int i = 0;
	char *p;
	va_list caselist;
	if (arg[0])
	{
		va_start(caselist, arg);
		while ((p = va_arg(caselist, char *)) != NULL)
		{
			i++;
			if (!str_prefix(arg, p))
				return i;
		}
	}
	return 0;
}

void vinterpret(CHAR_DATA * ch, const char *argument, ...)
{
	char arg[MAX_INPUT_LENGTH];
	char *iStr;
	DO_FUN *iFun;
	va_list caselist;
	va_start(caselist, argument);
	argument = one_argument(argument, arg);
	do
	{
		iStr = va_arg(caselist, char *);
		iFun = va_arg(caselist, DO_FUN *);
	}
	while (iStr != NULL && (!arg[0] || str_prefix(arg, iStr)));
	if (iFun != NULL)
		(*iFun) (ch, argument);
}

void strspace_alloc()
{
#if !defined(NO_STRHASH)
	extern char *top_string;

	if ((string_space = (char *) calloc(1, MAX_STRING)) == NULL)
	{
		logf("Unable to allocate %d kB string space from system.",
			 MAX_STRING / 1024);
		exit(1);
	}
	top_string = string_space;
#endif
}

/* Function Name: FILE *file_open (const char *mode, char *fmt,...)

 * this function closes fpReserve (if open), and opens a stream to the
 * file specified, with the specified modes.
 */
FILE *file_open(const char *file, const char *mode)
{
	FILE *fp = NULL;

/* fpReserve open? */
	if (fpReserve != NULL)
	{
		/* Assuming that fpReserve *will* get re-opened sometime */
		fclose(fpReserve);
		fpReserve = NULL;
	}

/* Open the new stream */
	fp = fopen(file, mode);

	return fp;
}

/* Function Name: bool file_close (FILE *fp)

 * this function closes the stream specified, and attempts to re-open
 * fpReserve, if it's not already open.
 */
bool file_close(FILE * fp)
{
	if (fp != NULL)
		fclose(fp);

/* fpReserve already open? */
	if (fpReserve == NULL)
		/* Nope, let's open */
		fpReserve = fopen(NULL_FILE, "r");

	return TRUE;
}

const struct dofun_type dofun_table[] = {
#define MAKE_COMMAND_TABLE
#include "dofun.h"
#undef MAKE_COMMAND_TABLE
	{NULL, NULL}
};

const char *do_fun_name(DO_FUN * fun)
{
	int i;

	for (i = 0; dofun_table[i].fun != NULL; i++)
		if (fun == dofun_table[i].fun)
			return dofun_table[i].name;

	return "do_null";
}

DO_FUN *do_fun_lookup(const char *name)
{
	int i;

	for (i = 0; dofun_table[i].name != NULL; i++)
		if (!str_cmp(name, dofun_table[i].name))
			return dofun_table[i].fun;

	if (fBootDb)
		bugf("Unknown command %s", name);

	return &do_null;
}

const struct gsn_type gsn_table[] = {
#undef GSN_H
#define MAKE_GSN_TABLE
#include "gsn.h"
#undef MAKE_GSN_TABLE
	{NULL, NULL}
};

const struct spfun_type spell_table[] = {
#define MAKE_SPELL_TABLE
#include "spells.h"
#undef MAKE_SPELL_TABLE
	{NULL, NULL}
};

int *gsn_lookup(const char *word)
{
	int i;

	for (i = 0; gsn_table[i].name != NULL; i++)
	{
		if (!str_cmp(word, gsn_table[i].name))
			return gsn_table[i].pgsn;
	}
	return &gsn_null;
}

const char *skill_gsn_name(int *pgsn)
{
	int i;

	for (i = 0; gsn_table[i].pgsn != NULL; i++)
	{
		if (pgsn == gsn_table[i].pgsn)
			return gsn_table[i].name;
	}
	return "gsn_null";
}

SPELL_FUN *spell_lookup(const char *word)
{
	int i;

	for (i = 0; spell_table[i].name != NULL; i++)
		if (!str_cmp(word, spell_table[i].name))
			return spell_table[i].fun;

	return &spell_null;
}

const char *spell_fun_name(SPELL_FUN * fun)
{
	int i;

	for (i = 0; spell_table[i].fun != NULL; i++)
		if (spell_table[i].fun == fun)
			return spell_table[i].name;

	return "spell_null";
}

void free_runbuf(DESCRIPTOR_DATA * d)
{
	if (d && d->run_buf)
	{
		free_string(d->run_buf);
		d->run_buf = NULL;
		d->run_head = NULL;
	}
	return;
}

void load_bank_data(void)
{
	FILE *fp;
	extern int share_value;

	if ((fp = file_open(BANK_FILE, "r")) != NULL)
	{
		share_value = fread_number(fp);
	}
	file_close(fp);
}

void load_objprogs(FILE * fp)
{
	PROG_CODE *pOprog;

	if (current_area == NULL)
	{
		bug("Load_objprogs: no #AREA seen yet.", 0);
		exit(1);
	}

	for (;;)
	{
		vnum_t vnum;
		char letter;

		letter = fread_letter(fp);
		if (letter != '#')
		{
			bug("Load_objprogs: # not found.", 0);
			exit(1);
		}

		vnum = fread_number(fp);
		if (vnum == 0)
			break;

		fBootDb = FALSE;
		if (get_prog_index(vnum, PRG_OPROG) != NULL)
		{
			bug("Load_objprogs: vnum %d duplicated.", vnum);
			exit(1);
		}
		fBootDb = TRUE;

		pOprog = new_pcode();
		pOprog->vnum = vnum;
		free_string(pOprog->code);
		pOprog->code = fread_string(fp);
		LINK(pOprog, oprog_first, oprog_last, next, prev);
		top_oprog_index++;
	}
	return;
}

void load_roomprogs(FILE * fp)
{
	PROG_CODE *pRprog;

	if (current_area == NULL)
	{
		bug("Load_roomprogs: no #AREA seen yet.", 0);
		exit(1);
	}

	for (;;)
	{
		vnum_t vnum;
		char letter;

		letter = fread_letter(fp);
		if (letter != '#')
		{
			bug("Load_roomprogs: # not found.", 0);
			exit(1);
		}

		vnum = fread_number(fp);
		if (vnum == 0)
			break;

		fBootDb = FALSE;
		if (get_prog_index(vnum, PRG_RPROG) != NULL)
		{
			bugf("Load_roomprogs: vnum %ld duplicated.", vnum);
			exit(1);
		}
		fBootDb = TRUE;

		pRprog = new_pcode();
		pRprog->vnum = vnum;
		free_string(pRprog->code);
		pRprog->code = fread_string(fp);
		LINK(pRprog, rprog_first, rprog_last, next, prev);
		top_rprog_index++;
	}
	return;
}

void fix_objprogs(void)
{
	OBJ_INDEX_DATA *pObjIndex;
	PROG_LIST *list;
	PROG_CODE *prog;
	int iHash;

	for (iHash = 0; iHash < MAX_KEY_HASH; iHash++)
	{
		for (pObjIndex = obj_index_hash[iHash];
			 pObjIndex != NULL; pObjIndex = pObjIndex->next)
		{
			for (list = pObjIndex->first_oprog; list != NULL; list = list->next)
			{
				if ((prog = get_prog_index(list->vnum, PRG_OPROG)) != NULL)
					replace_string(list->code, prog->code);
				else
				{
					bugf("Fix_objprogs: code vnum %ld not found.", list->vnum);
					exit(1);
				}
			}
		}
	}
}

void fix_roomprogs(void)
{
	ROOM_INDEX_DATA *pRoomIndex;
	PROG_LIST *list;
	PROG_CODE *prog;
	int iHash;

	for (iHash = 0; iHash < MAX_KEY_HASH; iHash++)
	{
		for (pRoomIndex = room_index_hash[iHash];
			 pRoomIndex != NULL; pRoomIndex = pRoomIndex->next)
		{
			for (list = pRoomIndex->first_rprog; list != NULL;
				 list = list->next)
			{
				if ((prog = get_prog_index(list->vnum, PRG_RPROG)) != NULL)
					replace_string(list->code, prog->code);
				else
				{
					bugf("Fix_roomprogs: code vnum %ld not found.", list->vnum);
					exit(1);
				}
			}
		}
	}
}

int srt_skills(const void *p1, const void *p2)
{
	struct skill_type sk1;
	struct skill_type sk2;

	sk1 = *(struct skill_type *) p1;
	sk2 = *(struct skill_type *) p2;

	return str_cmp(sk1.name, sk2.name);
}

void add_social(SOCIAL_DATA * social)
{
	SOCIAL_DATA *tmp;

	hash_social(social);

	for (tmp = social_first; tmp; tmp = tmp->next)
	{
		if (str_cmp(social->name, tmp->name) < 0)
		{
			INSERT(social, tmp, social_first, next, prev);
			break;
		}
	}

	if (!tmp)
		LINK(social, social_first, social_last, next, prev);
}

void unlink_social(SOCIAL_DATA * social)
{
	unhash_social(social);
	UNLINK(social, social_first, social_last, next, prev);
}

void add_help(HELP_DATA * help)
{
	HELP_DATA *tmp;

	for (tmp = help_first; tmp; tmp = tmp->next)
	{
		if (str_cmp(help->keyword, tmp->keyword) < 0)
		{
			INSERT(help, tmp, help_first, next, prev);
			break;
		}
	}

	if (!tmp)
		LINK(help, help_first, help_last, next, prev);
}

void newcmd_insert_name_sort(CMD_DATA * a)
{
	CMD_DATA *lsort = cmd_first_sorted;
	CMD_DATA *lsort_prev = NULL;

	if (!cmd_first_sorted)
	{
		cmd_first_sorted = a;
		return;
	}

	// sort cmds by level
	for (; lsort; lsort_prev = lsort, lsort = lsort->next_sort)
	{
		if (str_cmp(a->name, lsort->name) > 0)
		{
			continue;
		}

		// add cmd to this point in the list
		if (lsort_prev)
		{						// insert in the list 
			a->next_sort = lsort;
			lsort_prev->next_sort = a;
		}
		else					// we are at the head 
		{
			// insert at the head 
			a->next_sort = cmd_first_sorted;
			cmd_first_sorted = a;
		}
		return;
	}

	lsort_prev->next_sort = a;
}

void add_command(CMD_DATA * command)
{
	hash_command(command);
	newcmd_insert_name_sort(command);
	LINK(command, cmd_first, cmd_last, next, prev);
}

void unlink_command(CMD_DATA * command)
{
	unhash_command(command);
	UNLINK_SINGLE(command, next_sort, CMD_DATA, cmd_first_sorted);
	UNLINK(command, cmd_first, cmd_last, next, prev);
}

#define                 MAX_PERM_BLOCK  131072
/*
 * Allocate some permanent memory.
 * Permanent memory is never freed,
 *   pointers into it may be copied safely.
 */
void *alloc_perm(size_t sMem)
{
	static char *pMemPerm;
	static int iMemPerm;
	void *pMem;

	while (sMem % sizeof(long) != 0)
		sMem++;
	if (sMem > MAX_PERM_BLOCK)
	{
		bugf("%d too large.", sMem);
		exit(1);
	}

	if (pMemPerm == NULL || iMemPerm + sMem > MAX_PERM_BLOCK)
	{
		iMemPerm = 0;
		if ((pMemPerm = (char *) calloc(1, MAX_PERM_BLOCK)) == NULL)
		{
			perror("Alloc_perm");
			exit(1);
		}
	}

	pMem = pMemPerm + iMemPerm;
	iMemPerm += sMem;
	nAllocPerm += 1;
	sAllocPerm += sMem;

	return pMem;
}

void hash_command(CMD_DATA * command)
{
	int hash;

	if (!command)
	{
		bug("add_command: NULL command", 0);
		return;
	}

	if (!command->name)
	{
		bug("add_command: NULL command->name", 0);
		return;
	}

	if (!command->do_fun)
	{
		bug("add_command: NULL command->do_fun", 0);
		return;
	}

	hash = LOWER(command->name[0]) % 126;

	LINK_LAST(command, next_hash, CMD_DATA, command_hash[hash]);

	return;
}

void unhash_command(CMD_DATA * command)
{
	int hash;

	if (!command)
	{
		bug("unlink_command: NULL command", 0);
		return;
	}

	hash = LOWER(command->name[0]) % 126;

	UNLINK_SINGLE(command, next_hash, CMD_DATA, command_hash[hash]);
}

void unhash_social(SOCIAL_DATA * social)
{
	int hash;

	if (!social)
	{
		bug("unlink_social: NULL social", 0);
		return;
	}

	if (LOWER(social->name[0]) < 'a' || LOWER(social->name[0]) > 'z')
		hash = 0;
	else
		hash = (LOWER(social->name[0]) - 'a') + 1;

	UNLINK_SINGLE(social, next_hash, SOCIAL_DATA, social_hash[hash]);
}

void hash_social(SOCIAL_DATA * social)
{
	int hash;

	if (!social)
	{
		bug("add_social: NULL social", 0);
		return;
	}

	if (!social->name)
	{
		bug("add_social: NULL social->name", 0);
		return;
	}

	if (LOWER(social->name[0]) < 'a' || LOWER(social->name[0]) > 'z')
		hash = 0;
	else
		hash = (LOWER(social->name[0]) - 'a') + 1;

	LINK_LAST(social, next_hash, SOCIAL_DATA, social_hash[hash]);

	return;
}

int convert_level(char *arg)
{
	if (IS_NULLSTR(arg))
		return 0;
	else if (is_number(arg))
		return atoi(arg);
	else if (is_name("IMM", arg))
		return LEVEL_IMMORTAL;
	else if (is_name("HERO", arg) || is_name("HRO", arg))
		return LEVEL_HERO;
	else
		return 0;
}

void convert_area_credits(AREA_DATA * pArea)
{
	char high[MAX_STRING_LENGTH], low[MAX_STRING_LENGTH], builder[MSL];

	if (!pArea || pArea->version > 0)
		return;

	if (3 != sscanf(pArea->credits, "{ %[^} ] %[^} ] } %s", low, high, builder)
		&& 3 != sscanf(pArea->credits, "{ %[^} ]-%[^} ] } %s", low, high,
					   builder)
		&& 3 != sscanf(pArea->credits, "[ %[^] ] %[^] ] ] %s", low, high,
					   builder)
		&& 3 != sscanf(pArea->credits, "[ %[^] ]-%[^] ] ] %s", low, high,
					   builder))
	{
		if (2 == sscanf(pArea->credits, "{ %[^} ] } %s", low, builder)
			|| 2 == sscanf(pArea->credits, "[ %[^] ] ] %s", low, builder))
		{
			replace_string(pArea->lvl_comment, low);
			replace_string(pArea->credits, builder);
		}
		return;
	}
	replace_string(pArea->lvl_comment, "");
	replace_string(pArea->credits, builder);
	pArea->min_level = convert_level(low);
	pArea->max_level = convert_level(high);
}