/**************************************************************************
*  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-2003 by Ryan Jennings              *
*            http://1stmud.dlmud.com/  <r-jenn@shaw.ca>                   *
***************************************************************************/
#include "merc.h"
#include "recycle.h"
#include "olc.h"
#include "tables.h"
#include "interp.h"
PROTOTYPE(void free_channel_history, (PC_DATA *));
PROTOTYPE(void init_channel_history, (PC_DATA *));
/* stuff for recycling ban structures */
BAN_DATA *ban_free;
BAN_DATA *new_ban(void)
{
	static BAN_DATA ban_zero;
	BAN_DATA *ban;
	GET_FREE(ban, BAN_DATA, next, ban_free);
	*ban = ban_zero;
	VALIDATE(ban);
	ban->name = &str_empty[0];
	return ban;
}
void free_ban(BAN_DATA * ban)
{
	if (!IS_VALID(ban))
		return;
	free_string(ban->name);
	INVALIDATE(ban);
	PUT_FREE(ban, next, ban_free);
}
/* stuff for recycling descriptors */
DESCRIPTOR_DATA *new_descriptor(void)
{
	static DESCRIPTOR_DATA d_zero;
	DESCRIPTOR_DATA *d;
	GET_FREE(d, DESCRIPTOR_DATA, next, descriptor_free);
	*d = d_zero;
	VALIDATE(d);
	d->connected = CON_GET_TERM;
	d->showstr_head = NULL;
	d->showstr_point = NULL;
	d->run_buf = NULL;
	d->run_head = NULL;
	d->outsize = 2000;
	d->scr_width = DEFAULT_SCR_WIDTH;
	d->scr_height = DEFAULT_SCR_HEIGHT;
	d->bytes_normal = 0;
#if !defined(NO_MCCP)
	d->bytes_compressed = 0;
	d->mccp_version = 0;
#endif
	d->editor = ED_NONE;
	d->pEdit = NULL;
	alloc_mem(d->outbuf, char, d->outsize);
	d->mxp.supports = &str_empty[0];
	d->mxp.mxp_ver = 0.0;
	d->mxp.client_ver = 0.0;
	d->mxp.style_ver = 0.0;
	d->mxp.client = &str_empty[0];
	return d;
}
void free_descriptor(DESCRIPTOR_DATA * d)
{
	if (!IS_VALID(d))
		return;
	free_string(d->host);
	free_string(d->showstr_head);
	free_mem(d->outbuf);
	free_string(d->mxp.supports);
	free_string(d->mxp.client);
	INVALIDATE(d);
	PUT_FREE(d, next, descriptor_free);
}
/* stuff for recycling gen_data */
GEN_DATA *gen_data_free;
GEN_DATA *new_gen_data(void)
{
	static GEN_DATA gen_zero;
	GEN_DATA *gen;
	GET_FREE(gen, GEN_DATA, next, gen_data_free);
	*gen = gen_zero;
	VALIDATE(gen);
	alloc_mem(gen->skill_chosen, bool, maxSkill);
	alloc_mem(gen->group_chosen, bool, maxGroup);
	return gen;
}
void free_gen_data(GEN_DATA * gen)
{
	if (!IS_VALID(gen))
		return;
	INVALIDATE(gen);
	free_mem(gen->skill_chosen);
	free_mem(gen->group_chosen);
	PUT_FREE(gen, next, gen_data_free);
}
/* stuff for recycling extended descs */
EXTRA_DESCR_DATA *extra_descr_free;
EXTRA_DESCR_DATA *new_extra_descr(void)
{
	EXTRA_DESCR_DATA *ed;
	GET_FREE(ed, EXTRA_DESCR_DATA, next, extra_descr_free);
	ed->keyword = &str_empty[0];
	ed->description = &str_empty[0];
	VALIDATE(ed);
	return ed;
}
void free_extra_descr(EXTRA_DESCR_DATA * ed)
{
	if (!IS_VALID(ed))
		return;
	free_string(ed->keyword);
	free_string(ed->description);
	INVALIDATE(ed);
	PUT_FREE(ed, next, extra_descr_free);
}
/* stuff for recycling affects */
AFFECT_DATA *new_affect(void)
{
	static AFFECT_DATA af_zero;
	AFFECT_DATA *af;
	GET_FREE(af, AFFECT_DATA, next, affect_free);
	*af = af_zero;
	VALIDATE(af);
	return af;
}
void free_affect(AFFECT_DATA * af)
{
	if (!IS_VALID(af))
		return;
	INVALIDATE(af);
	PUT_FREE(af, next, affect_free);
}
/* stuff for recycling objects */
OBJ_DATA *new_obj(void)
{
	static OBJ_DATA obj_zero;
	OBJ_DATA *obj;
	GET_FREE(obj, OBJ_DATA, next, obj_free);
	*obj = obj_zero;
	VALIDATE(obj);
	return obj;
}
void free_obj(OBJ_DATA * obj)
{
	AFFECT_DATA *paf, *paf_next;
	EXTRA_DESCR_DATA *ed, *ed_next;
	if (!IS_VALID(obj))
		return;
	for (paf = obj->first_affect; paf != NULL; paf = paf_next)
	{
		paf_next = paf->next;
		free_affect(paf);
	}
	obj->first_affect = NULL;
	for (ed = obj->first_extra_descr; ed != NULL; ed = ed_next)
	{
		ed_next = ed->next;
		free_extra_descr(ed);
	}
	obj->first_extra_descr = NULL;
	free_string(obj->name);
	free_string(obj->description);
	free_string(obj->short_descr);
	free_string(obj->owner);
	INVALIDATE(obj);
	PUT_FREE(obj, next, obj_free);
}
/* stuff for recyling characters */
CHAR_DATA *new_char(void)
{
	static CHAR_DATA ch_zero;
	CHAR_DATA *ch;
	int i;
	GET_FREE(ch, CHAR_DATA, next, char_free);
	*ch = ch_zero;
	VALIDATE(ch);
	ch->name = &str_empty[0];
	ch->short_descr = &str_empty[0];
	ch->long_descr = &str_empty[0];
	ch->description = &str_empty[0];
	ch->prompt = &str_empty[0];
	ch->prefix = &str_empty[0];
	ch->logon = current_time;
	ch->lines = 0;
	ch->columns = 0;
	ch->clan = NULL;
	ch->invited = NULL;
	ch->rank = 0;
	ch->deity = NULL;
	for (i = 0; i < 4; i++)
		ch->armor[i] = 100;
	ch->position = POS_STANDING;
	ch->hit = 20;
	ch->max_hit = 20;
	ch->mana = 100;
	ch->max_mana = 100;
	ch->move = 100;
	ch->max_move = 100;
	for (i = 0; i < MAX_STATS; i++)
	{
		ch->perm_stat[i] = 13;
		ch->mod_stat[i] = 0;
	}
	for (i = 0; i < MAX_STANCE; i++)
		ch->stance[i] = 0;
	return ch;
}
void free_char(CHAR_DATA * ch)
{
	OBJ_DATA *obj;
	OBJ_DATA *obj_next;
	AFFECT_DATA *paf;
	AFFECT_DATA *paf_next;
	if (!IS_VALID(ch))
		return;
	if (IS_NPC(ch))
		mobile_count--;
	for (obj = ch->first_carrying; obj != NULL; obj = obj_next)
	{
		obj_next = obj->next_content;
		extract_obj(obj);
	}
	for (paf = ch->first_affect; paf != NULL; paf = paf_next)
	{
		paf_next = paf->next;
		affect_remove(ch, paf);
	}
	free_string(ch->name);
	free_string(ch->short_descr);
	free_string(ch->long_descr);
	free_string(ch->description);
	free_string(ch->prompt);
	free_string(ch->prefix);
	free_pcdata(ch->pcdata);
	PUT_FREE(ch, next, char_free);
	INVALIDATE(ch);
	return;
}
PC_DATA *new_pcdata(void)
{
	int alias, bud, ign;
	static PC_DATA pcdata_zero;
	PC_DATA *pcdata;
	GET_FREE(pcdata, PC_DATA, next, pcdata_free);
	*pcdata = pcdata_zero;
	for (alias = 0; alias < MAX_ALIAS; alias++)
	{
		pcdata->alias[alias] = NULL;
		pcdata->alias_sub[alias] = NULL;
	}
	for (bud = 0; bud < MAX_BUDDY; bud++)
	{
		pcdata->buddies[bud] = NULL;
	}
	for (ign = 0; ign < MAX_IGNORE; ign++)
	{
		pcdata->ignore[ign] = NULL;
		pcdata->ignore_flags[ign] = 0;
	}
	init_channel_history(pcdata);
	pcdata->buffer = new_buf();
	alloc_mem(pcdata->learned, int, maxSkill);
	alloc_mem(pcdata->group_known, bool, maxGroup);
	pcdata->str_ed_key = '/';
	VALIDATE(pcdata);
	return pcdata;
}
void free_pcdata(PC_DATA * pcdata)
{
	int alias, pos, ignore;
	if (!IS_VALID(pcdata))
		return;
	free_string(pcdata->pwd);
	free_string(pcdata->bamfin);
	free_string(pcdata->bamfout);
	free_string(pcdata->title);
	free_string(pcdata->who_descr);
	free_string(pcdata->webpass);
	free_string(pcdata->afk_msg);
	free_buf(pcdata->buffer);
	free_mem(pcdata->learned);
	free_mem(pcdata->group_known);
	for (alias = 0; alias < MAX_ALIAS; alias++)
	{
		free_string(pcdata->alias[alias]);
		free_string(pcdata->alias_sub[alias]);
	}
	for (pos = 0; pos < MAX_BUDDY; pos++)
		free_string(pcdata->buddies[pos]);
	for (ignore = 0; ignore < MAX_IGNORE; ignore++)
		free_string(pcdata->ignore[ignore]);
	free_channel_history(pcdata);
	INVALIDATE(pcdata);
	PUT_FREE(pcdata, next, pcdata_free);
	return;
}
/* stuff for setting ids */
long last_pc_id;
long last_mob_id;
long get_pc_id(void)
{
	int val;
	val = (current_time <= last_pc_id) ? last_pc_id + 1 : current_time;
	last_pc_id = val;
	return val;
}
long get_mob_id(void)
{
	last_mob_id++;
	return last_mob_id;
}
/* procedures and constants needed for buffering */
BUFFER *buf_free;
/* buffer sizes */
const int buf_size[MAX_BUF_LIST] = {
	16, 32, 64, 128, 256, 1024, 2048, 4096, 8192, 16384
};
/* local procedure for finding the next acceptable size */
/* -1 indicates out-of-boundary error */
int get_size(int val)
{
	int i;
	for (i = 0; i < MAX_BUF_LIST; i++)
		if (buf_size[i] >= val)
		{
			return buf_size[i];
		}
	return -1;
}
BUFFER *new_buf()
{
	BUFFER *buffer;
	GET_FREE(buffer, BUFFER, next, buf_free);
	buffer->next = NULL;
	buffer->state = BUFFER_SAFE;
	buffer->size = get_size(BASE_BUF);
	alloc_mem(buffer->string, char, buffer->size);
	buffer->string[0] = '\0';
	VALIDATE(buffer);
	return buffer;
}
BUFFER *new_buf_size(int size)
{
	BUFFER *buffer;
	GET_FREE(buffer, BUFFER, next, buf_free);
	buffer->next = NULL;
	buffer->state = BUFFER_SAFE;
	buffer->size = get_size(size);
	if (buffer->size == -1)
	{
		bugf("new_buf: buffer size %d too large.", size);
		exit(1);
	}
	alloc_mem(buffer->string, char, buffer->size);
	buffer->string[0] = '\0';
	VALIDATE(buffer);
	return buffer;
}
void free_buf(BUFFER * buffer)
{
	if (!IS_VALID(buffer))
		return;
	free_mem(buffer->string);
	buffer->string = NULL;
	buffer->size = 0;
	buffer->state = BUFFER_FREED;
	INVALIDATE(buffer);
	PUT_FREE(buffer, next, buf_free);
}
bool bprint(BUFFER * buffer, const char *string)
{
	int len;
	char *oldstr;
	int oldsize;
	oldstr = buffer->string;
	oldsize = buffer->size;
	if (buffer->state == BUFFER_OVERFLOW)	/* don't waste time on bad strings! */
		return FALSE;
	len = strlen(buffer->string) + strlen(string) + 1;
	while (len >= buffer->size)	/* increase the buffer size */
	{
		buffer->size = get_size(buffer->size + 1);
		{
			if (buffer->size == -1)	/* overflow */
			{
				buffer->size = oldsize;
				buffer->state = BUFFER_OVERFLOW;
				bugf("buffer overflow past size %d", buffer->size);
				return FALSE;
			}
		}
	}
	if (buffer->size != oldsize)
	{
		alloc_mem(buffer->string, char, buffer->size);
		strcpy(buffer->string, oldstr);
		free_mem(oldstr);
	}
	strcat(buffer->string, string);
	return TRUE;
}
void bprintf(BUFFER * buffer, const char *messg, ...)
{
	char buf[MSL * 3];
	va_list args;
	if (IS_NULLSTR(messg))
		return;
	va_start(args, messg);
	vsnprintf(buf, sizeof(buf), messg, args);
	va_end(args);
	bprint(buffer, buf);
}
void bprintlnf(BUFFER * buffer, const char *messg, ...)
{
	va_list args;
	char send_buf[MAX_STRING_LENGTH * 3];
	if (IS_NULLSTR(messg))
		return;
	va_start(args, messg);
	vsnprintf(send_buf, sizeof(send_buf), messg, args);
	va_end(args);
	bprint(buffer, send_buf);
	bprint(buffer, "\n\r");
}
void bprintln(BUFFER * buffer, const char *messg)
{
	bprint(buffer, messg);
	bprint(buffer, "\n\r");
}
void clear_buf(BUFFER * buffer)
{
	buffer->string[0] = '\0';
	buffer->state = BUFFER_SAFE;
}
char *buf_string(BUFFER * buffer)
{
	return buffer->string;
}
/* stuff for recycling mobprograms */
PROG_LIST *prog_free;
PROG_LIST *new_prog(void)
{
	static PROG_LIST mp_zero;
	PROG_LIST *mp;
	GET_FREE(mp, PROG_LIST, next, prog_free);
	*mp = mp_zero;
	mp->vnum = 0;
	mp->trig_type = 0;
	mp->code = str_dup("");
	VALIDATE(mp);
	return mp;
}
void free_prog(PROG_LIST * mp)
{
	if (!IS_VALID(mp))
		return;
	INVALIDATE(mp);
	PUT_FREE(mp, next, prog_free);
}
HELP_DATA *help_free;
HELP_DATA *new_help(void)
{
	HELP_DATA *help;
	GET_FREE(help, HELP_DATA, next, help_free);
	help->keyword = &str_empty[0];
	help->text = &str_empty[0];
	return help;
}
void free_help(HELP_DATA * help)
{
	free_string(help->keyword);
	free_string(help->text);
	PUT_FREE(help, next, help_free);
}
/*
 * stuff for recycling statlist structures 
 */
STAT_DATA *stat_free;
STAT_DATA *new_stat_data(void)
{
	static STAT_DATA stat_zero;
	STAT_DATA *stat;
	GET_FREE(stat, STAT_DATA, next, stat_free);
	*stat = stat_zero;
	VALIDATE(stat);
	stat->name = &str_empty[0];
	return stat;
}
void free_stat_data(STAT_DATA * stat)
{
	if (!IS_VALID(stat))
		return;
	free_string(stat->name);
	INVALIDATE(stat);
	PUT_FREE(stat, next, stat_free);
}
CORPSE_DATA *corpse_free;
CORPSE_DATA *new_corpse(void)
{
	static CORPSE_DATA corpse_zero;
	CORPSE_DATA *corpse;
	GET_FREE(corpse, CORPSE_DATA, next, corpse_free);
	*corpse = corpse_zero;
	corpse->corpse = NULL;
	return corpse;
}
void free_corpse(CORPSE_DATA * corpse)
{
	if (corpse == NULL)
		return;
	if (corpse->corpse != NULL)
		free_obj(corpse->corpse);
	PUT_FREE(corpse, next, corpse_free);
	return;
}
AUCTION_DATA *auction_free;
AUCTION_DATA *new_auction(void)
{
	static AUCTION_DATA auc_zero;
	AUCTION_DATA *auction;
	GET_FREE(auction, AUCTION_DATA, next, auction_free);
	*auction = auc_zero;
	VALIDATE(auction);
	return auction;
}
void free_auction(AUCTION_DATA * auction)
{
	if (!IS_VALID(auction))
		return;
	auction->high_bidder = NULL;
	auction->item = NULL;
	auction->owner = NULL;
	INVALIDATE(auction);
	PUT_FREE(auction, next, auction_free);
}
WPWD_DATA *WPWD_free;
WPWD_DATA *new_pwd(void)
{
	static WPWD_DATA WPWD_zero;
	WPWD_DATA *pwd;
	GET_FREE(pwd, WPWD_DATA, next, WPWD_free);
	*pwd = WPWD_zero;
	pwd->name = &str_empty[0];
	pwd->passw = &str_empty[0];
	VALIDATE(pwd);
	return pwd;
}
void free_pwd(WPWD_DATA * pwd)
{
	if (!IS_VALID(pwd))
		return;
	free_string(pwd->name);
	free_string(pwd->passw);
	INVALIDATE(pwd);
	PUT_FREE(pwd, next, WPWD_free);
}
RACE_DATA *race_free;
RACE_DATA *new_race(void)
{
	static RACE_DATA race_zero;
	RACE_DATA *race;
	int x;
	GET_FREE(race, RACE_DATA, next, race_free);
	*race = race_zero;
	race->name = &str_empty[0];
	race->pc_race = FALSE;
	race->act = 0;
	race->aff = 0;
	race->off = 0;
	race->imm = 0;
	race->res = 0;
	race->vuln = 0;
	race->form = 0;
	race->parts = 0;
	for (x = 0; x < 5; x++)
		race->skills[x] = NULL;
	for (x = 0; x < STAT_MAX; x++)
	{
		race->stats[x] = 0;
		race->max_stats[x] = 0;
	}
	alloc_mem(race->class_mult, int, maxClass);
	race->points = 0;
	race->size = SIZE_MEDIUM;
	maxRace++;
	VALIDATE(race);
	return race;
}
void free_race(RACE_DATA * race)
{
	int x;
	if (!IS_VALID(race))
		return;
	free_string(race->name);
	for (x = 0; x < 5; x++)
		free_string(race->skills[x]);
	free_mem(race->class_mult);
	maxRace--;
	INVALIDATE(race);
	PUT_FREE(race, next, race_free);
}
MBR_DATA *new_mbr(void)
{
	static MBR_DATA mbr_zero;
	MBR_DATA *mbr;
	GET_FREE(mbr, MBR_DATA, next, mbr_free);
	*mbr = mbr_zero;
	mbr->name = &str_empty[0];
	mbr->clan = NULL;
	mbr->rank = 0;
	VALIDATE(mbr);
	return mbr;
}
void free_mbr(MBR_DATA * mbr)
{
	if (!IS_VALID(mbr))
		return;
	free_string(mbr->name);
	INVALIDATE(mbr);
	PUT_FREE(mbr, next, mbr_free);
}
SOCIAL_DATA *social_free;
SOCIAL_DATA *new_social(void)
{
	static SOCIAL_DATA social_zero;
	SOCIAL_DATA *soc;
	GET_FREE(soc, SOCIAL_DATA, next, social_free);
	*soc = social_zero;
	soc->name = &str_empty[0];
	soc->char_no_arg = &str_empty[0];
	soc->others_no_arg = &str_empty[0];
	soc->char_found = &str_empty[0];
	soc->others_found = &str_empty[0];
	soc->vict_found = &str_empty[0];
	soc->char_not_found = &str_empty[0];
	soc->char_auto = &str_empty[0];
	soc->others_auto = &str_empty[0];
	maxSocial++;
	VALIDATE(soc);
	return soc;
}
void free_social(SOCIAL_DATA * soc)
{
	if (!IS_VALID(soc))
		return;
	free_string(soc->name);
	free_string(soc->char_no_arg);
	free_string(soc->others_no_arg);
	free_string(soc->char_found);
	free_string(soc->others_found);
	free_string(soc->vict_found);
	free_string(soc->char_not_found);
	free_string(soc->char_auto);
	free_string(soc->others_auto);
	maxSocial--;
	INVALIDATE(soc);
	PUT_FREE(soc, next, social_free);
}
DEITY_DATA *deity_free;
DEITY_DATA *new_deity(void)
{
	static DEITY_DATA deity_zero;
	DEITY_DATA *deity;
	GET_FREE(deity, DEITY_DATA, next, deity_free);
	*deity = deity_zero;
	deity->name = &str_empty[0];
	deity->desc = &str_empty[0];
	deity->skillname = &str_empty[0];
	maxDeity++;
	VALIDATE(deity);
	return deity;
}
void free_deity(DEITY_DATA * deity)
{
	if (!IS_VALID(deity))
		return;
	free_string(deity->name);
	free_string(deity->desc);
	free_string(deity->skillname);
	maxDeity--;
	INVALIDATE(deity);
	PUT_FREE(deity, next, deity_free);
}
CLAN_DATA *clan_free;
CLAN_DATA *new_clan(void)
{
	static CLAN_DATA clan_zero;
	CLAN_DATA *clan;
	int x;
	GET_FREE(clan, CLAN_DATA, next, clan_free);
	*clan = clan_zero;
	clan->name = &str_empty[0];
	clan->who_name = &str_empty[0];
	for (x = 0; x < MAX_RANK; x++)
		clan->rank[x].rankname = &str_empty[0];
	maxClan++;
	VALIDATE(clan);
	return clan;
}
void free_clan(CLAN_DATA * clan)
{
	int x;
	if (!IS_VALID(clan))
		return;
	free_string(clan->name);
	free_string(clan->who_name);
	for (x = 0; x < MAX_RANK; x++)
		free_string(clan->rank[x].rankname);
	maxClan--;
	INVALIDATE(clan);
	PUT_FREE(clan, next, clan_free);
}
CMD_DATA *cmd_free;
CMD_DATA *new_command(void)
{
	static CMD_DATA cmd_zero;
	CMD_DATA *cmd;
	GET_FREE(cmd, CMD_DATA, next, cmd_free);
	*cmd = cmd_zero;
	cmd->name = &str_empty[0];
	cmd->do_fun = do_null;
	cmd->level = 0;
	cmd->position = POS_DEAD;
	cmd->show = TRUE;
	cmd->log = LOG_NORMAL;
	maxCommands++;
	VALIDATE(cmd);
	return cmd;
}
void free_command(CMD_DATA * cmd)
{
	if (!IS_VALID(cmd))
		return;
	free_string(cmd->name);
	maxCommands--;
	INVALIDATE(cmd);
	PUT_FREE(cmd, next, cmd_free);
}