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: goods.c                                                          *
 *                                                                        *
 * Goods code                                                             *
 * Trading Post code                                                      *
 * Market code                                                            *
 * Market Affections code                                                 *
 *                                                                        *
 **************************************************************************/

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

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

#define IS_VALID_GOOD(sn)							\
				((sn) >= 0 && (sn) < top_goods		\
					&& goods_table[(sn)]			\
					&& goods_table[(sn)]->name)

#define IS_VALID_TYPE(sn)							\
				((sn) >= 0 && (sn) < top_good_type	\
					&& good_types[(sn)]				\
					&& good_types[(sn)]->name)

#if defined(KEY)
#undef KEY
#endif

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

/* external funcs */
SPECIAL(tradingpost);
char	*get_spell_name(char *argument);
int		calc_trade_value(int gnum, int mnum);
int		Season(void);
void	fwrite_building(BUILDING_DATA *bld);
void	calc_prod(MARKET_DATA *pMk, GOOD_DATA *pGood, TRP_GOOD *tGood, MARKET_GOOD *pGM);
void	calc_price(MARKET_DATA *pMk, GOOD_DATA *pGood, MARKET_GOOD *pGM);


/* globals */
MARKET_DATA		*first_market		= NULL;
MARKET_DATA		*last_market		= NULL;
TRADING_POST	*first_trading_post	= NULL;
TRADING_POST	*last_trading_post	= NULL;
GOOD_TYPE		*good_types[MAX_TYPE_CODE];
GOOD_DATA		*goods_table[MAX_GOOD];
MARKET_GOOD		*GoodsMarkets[MAX_GOOD][MAX_MARKET];

int		top_goods		= 0;
int		top_good_type	= 0;
int		top_tp_vnum		= 0;
int		reset_markets	= 0;

/* local functions */
MARKET_DATA *find_market(int vnum);
void SaveGoodsMarketsTable(bool bTime);
void set_mk_aff(int mnum, int nwhat, int bitv, int durat, float value);


/* =========================================================================== */
/* Goods Type Code                                                             */
/* =========================================================================== */

/* *************************************************************************** */
/* Goods Type finding code                                                     */
/* *************************************************************************** */

GOOD_TYPE *get_good_type(int vnum)
{
	int gtn;

	for (gtn = 0; gtn < top_good_type; gtn++)
	{
		if (!IS_VALID_TYPE(gtn))
			continue;

		if (good_types[gtn]->vnum == vnum)
			break;
	}

	if (gtn >= top_good_type)
		return (NULL);

	return (good_types[gtn]);
}

/* *************************************************************************** */
/* Goods Type creation code                                                    */
/* *************************************************************************** */

GOOD_TYPE *new_good_type(void)
{
	GOOD_TYPE *gtype;
	int x;

	CREATE(gtype, GOOD_TYPE, 1);

	gtype->name			= NULL;
	gtype->vnum			= NOTHING;
	gtype->elasticity	= 1;
	gtype->cons_speed	= 1.00;

	for (x = 0; x < NUM_SEASONS; x++)
		gtype->prod_avg[x] = 0;

	return (gtype);
}


/* *************************************************************************** */
/* Goods Type load code                                                        */
/* *************************************************************************** */

GOOD_TYPE *fread_one_gtype(FILE *fp)
{
	GOOD_TYPE *gtype = new_good_type();
	char *word;
	bool fMatch;

	for ( ; ; )
	{
		word   = feof(fp) ? "End" : fread_word(fp);
		fMatch = FALSE;

		switch (UPPER(word[0]))
		{
		case '*':
			fMatch = TRUE;
			fread_to_eol( fp );
			break;

		case 'C':
			if (!str_cmp(word, "Cons_speed"))
			{
				int num = fread_number(fp);
				float calc = (float)num / 100;

				gtype->cons_speed = calc;

				fMatch = TRUE;
				break;
			}
			break;

		case 'E':
			KEY("Elast",		gtype->elasticity,	fread_number(fp));
			if (!strcmp(word, "End"))
				return (gtype);

		case 'N':
			KEY("Name",			gtype->name,		str_dup(fread_word(fp)));
			break;

		case 'P':
			if (!strcmp(word, "Prod"))
			{
				int x;

				for (x = 0; x < NUM_SEASONS; x++)
					gtype->prod_avg[x] = fread_number(fp);

				fMatch = TRUE;
				break;
			}
			break;

		case 'V':
			KEY("Vnum",			gtype->vnum,		fread_number(fp));
			break;

		}
	}

	return (NULL);
}

void LoadGoodTypes(void)
{
	FILE *fp;
	char fname[128];
	char letter;
	char *word;
	int i;

	sprintf(fname, "%sgoodtypes.data", LIB_GOODS);
	if (!(fp = fopen(fname, "r")))
	{
		log("SYSERR: LoadGoodTypes() - cannot open good types file %s.", fname);
		return;
	}

	/* Pre-initialize goods_table with blanks */
	for (i = 0; i < MAX_TYPE_CODE; i++)
		good_types[i] = NULL;

	top_good_type = 0;

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

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

		if (!strcmp(word, "TYPE"))
		{
			if (top_good_type >= MAX_TYPE_CODE)
			{
				log("SYSERR: LoadGoodTypes() - more good types than MAX_TYPE_CODE %d", MAX_TYPE_CODE);
				fclose(fp);
				exit(1);
			}
			good_types[top_good_type] = fread_one_gtype(fp);
			top_good_type++;
			continue;
		}
		else if (!strcmp(word, "END"))	// Done
			break;
		else
		{
			log("SYSERR: LoadGoodTypes() - bad section %s", word);
			continue;
		}
	}

	fclose(fp);
}


/* =========================================================================== */
/* Goods Code                                                                  */
/* =========================================================================== */

/* *************************************************************************** */
/* Goods creation code                                                         */
/* *************************************************************************** */

GOOD_DATA *new_good(void)
{
	GOOD_DATA *pGood;

	CREATE(pGood, GOOD_DATA, 1);
	pGood->name			= NULL;
	pGood->unit			= NULL;
	pGood->short_descr	= NULL;
	pGood->gtype		= NULL;
	pGood->code			= 0;
	pGood->cost			= 0;
	pGood->life			= -1;
	pGood->vnum			= 0;
	pGood->mkvnum		= 0;
	pGood->weight		= 0;
	pGood->docg			= 0;

	return (pGood);
}

void clear_goods(GOOD_DATA *pGood)
{
	if (pGood->name)		STRFREE(pGood->name);
	if (pGood->unit)		STRFREE(pGood->unit);
	if (pGood->short_descr)	STRFREE(pGood->short_descr);
	pGood->gtype = NULL;
}


/* *************************************************************************** */
/* Sort Goods Table                                                            */
/* *************************************************************************** */

/* Function used by qsort to sort goods */
int goods_comp(GOOD_DATA **g1, GOOD_DATA **g2)
{
	GOOD_DATA *pGood1 = (*g1);
	GOOD_DATA *pGood2 = (*g2);
	
	if (!pGood1 && pGood2)
		return (1);
	if (pGood1 && !pGood2)
		return (-1);
	if (!pGood1 && !pGood2)
		return (0);
	return (str_cmp(pGood1->name, pGood2->name));
}

/*
 * Ordina la tabella delle abilita' con qsort
 */
void sort_goods_table(void)
{
	log("  Sorting Goods Table.");
	qsort( &goods_table[0], MAX_GOOD - 1, sizeof(GOOD_DATA *),
		(int(*)(const void *, const void *)) goods_comp );
}

/* *************************************************************************** */
/* Load Goods from file code                                                   */
/* *************************************************************************** */

int parse_g_code(char *cname)
{
	if (!str_cmp(cname, "corn"))	return (TYPE_CORN);
	if (!str_cmp(cname, "flour"))	return (TYPE_FLOUR);
	if (!str_cmp(cname, "fabric"))	return (TYPE_FABRIC);
	if (!str_cmp(cname, "hide"))	return (TYPE_HIDE);
	if (!str_cmp(cname, "dye"))		return (TYPE_DYE);
	if (!str_cmp(cname, "metal"))	return (TYPE_METAL);
	if (!str_cmp(cname, "ore"))		return (TYPE_ORE);
	if (!str_cmp(cname, "spice"))	return (TYPE_SPICE);
	if (!str_cmp(cname, "sugar"))	return (TYPE_SUGAR);
	if (!str_cmp(cname, "oil"))		return (TYPE_OIL);
	if (!str_cmp(cname, "wood"))	return (TYPE_WOOD);

	return (NOTHING);
}

GOOD_DATA *fread_one_good(FILE *fp)
{
	GOOD_DATA *pGood = new_good();
	char *word;
	bool fMatch;

	for ( ; ; )
	{
		word   = feof(fp) ? "End" : fread_word(fp);
		fMatch = FALSE;

		switch (UPPER(word[0]))
		{
		case '*':
			fMatch = TRUE;
			fread_to_eol( fp );
			break;

		case 'C':
			if (!strcmp(word, "Code"))
			{
				//pGood->code = parse_g_code(fread_word(fp));
				pGood->code		= fread_number(fp);
				pGood->gtype	= get_good_type(pGood->code);
				fMatch = TRUE;
				break;
			}
			KEY("Cost",		pGood->cost,		fread_number(fp));
			break;

		case 'E':
			if (!strcmp(word, "End"))
			{
				if (!pGood->gtype)
				{
					log("SYSERR: good %d without type.", pGood->vnum);
					exit(1);
					return (NULL);
				}

				if (!pGood->short_descr || !*pGood->short_descr)
				{
					char gname[MAX_STRING_LENGTH];

					sprintf(gname, "%s %s", pGood->unit, pGood->name);
					pGood->short_descr = str_dup(gname);
				}

				return (pGood);
			}
			break;

		case 'L':
			KEY("Life",			pGood->life,		fread_number(fp));
			break;

		case 'M':
			KEY("Market",		pGood->mkvnum,		fread_number(fp));
			break;

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

		case 'S':
			KEY("Short_descr",	pGood->short_descr,	fread_string_nospace(fp));
			break;

		case 'U':
			KEY("Unit",			pGood->unit,		str_dup(fread_word(fp)));
			break;

		case 'V':
			KEY("Vnum",			pGood->vnum,		fread_number(fp));
			break;

		case 'W':
			KEY("Weight",		pGood->weight,		fread_number(fp));
			break;
		}

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

	return (NULL);
}

void LoadGoods(void)
{
	FILE *fp;
	char fname[128];
	char letter;
	char *word;
	int i;

	sprintf(fname, "%sgoods.data", LIB_GOODS);
	if (!(fp = fopen(fname, "r")))
	{
		log("SYSERR: LoadGoods() - cannot open goods file %s.", fname);
		return;
	}

	/* Pre-init the goods_table with blank goods */
	for (i = 0; i < MAX_GOOD; i++)
		goods_table[i] = NULL;

	top_goods = 0;

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

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

		if (!strcmp(word, "GOODS"))
		{
			if (top_goods >= MAX_GOOD)
			{
				log("SYSERR: LoadGoods() - more goods than MAX_GOOD %d", MAX_GOOD);
				fclose(fp);
				return;
			}
			goods_table[top_goods] = fread_one_good(fp);

			top_goods++;
			continue;
		}
		else if (!strcmp(word, "END"))	// Done
			break;
		else
		{
			log("SYSERR: LoadGoods() - bad section %s", word);
			continue;
		}
	}

	fclose(fp);
}


/* *************************************************************************** */
/* Goods finding code                                                          */
/* *************************************************************************** */

/* Check for exact matches only */
int bsearch_goods_exact(const char *name, int first, int top)
{
	int sn;
	
	for (;;)
	{
		sn = (first + top) >> 1;
		if (!IS_VALID_GOOD(sn))
			return (-1);
		if (!str_cmp(name, goods_table[sn]->name))
			return (sn);
		if (first >= top)
			return (-1);
		if (strcmp(name, goods_table[sn]->name) < 1)
			top = sn - 1;
		else
			first = sn + 1;
	}

	return (-1);
}

GOOD_DATA *get_good(int gnum)
{
	int gn;

	for (gn = 0; gn < top_goods; gn++)
	{
		if (!IS_VALID_GOOD(gn))
			continue;

		if (goods_table[gn]->vnum == gnum)
			break;
	}

	if (gn >= top_goods)
		return (NULL);

	return (goods_table[gn]);
}


GOOD_DATA *get_good_by_name(char *gname)
{
	int gn;

	if ((gn = bsearch_goods_exact(gname, 0, top_goods - 1)) == -1)
		return (NULL);

	return (goods_table[gn]);
}

/*
 * recursive reverse search for goods object <gnum>
 */
OBJ_DATA *get_good_object(OBJ_DATA *list, int gnum)
{
	OBJ_DATA *obj = NULL;

	for (obj = list; obj; obj = obj->prev_content)
	{
		if (GET_OBJ_TYPE(obj) == ITEM_GOODS)
		{
			if (GET_OBJ_VAL(obj, 0) == gnum)
				break;
		}

		if (GET_OBJ_TYPE(obj) == ITEM_CONTAINER)
			get_good_object(obj->last_content, gnum);
	}

	return (obj);
}

/* *************************************************************************** */
/* Goods Display Routines                                                      */
/* *************************************************************************** */

void list_goods(CHAR_DATA *ch, int mode)
{
	char gbuf[MAX_STRING_LENGTH*2];
	int gn;

	strcpy(gbuf,
		"List of goods table:\r\n"
		"-----------------------------------------------\r\n"
		"Name                       Unit        Cost\r\n"
		"-----------------------------------------------\r\n"
		);

	for (gn = 0; gn < top_goods; gn++)
	{
		if (!IS_VALID_GOOD(gn))
			continue;

		if (mode)
		{
			if (goods_table[gn]->cost < mode)
				continue;
		}

		sprintf(gbuf + strlen(gbuf),
			"%-25s  %-10s  %4d  %5d  %5d  %5d\r\n",
			goods_table[gn]->name, goods_table[gn]->unit,
			goods_table[gn]->cost);
	}

	page_string(ch->desc, gbuf, 1);
}

/* *************************************************************************** */
/* Goods object creation code                                                  */
/* *************************************************************************** */

OBJ_DATA *create_good_obj(GOOD_DATA *pGood, int amount)
{
	OBJ_DATA *obj;
	char buf[MAX_STRING_LENGTH];
	
	if (amount < 0)
		amount = 1;

	obj = create_obj();
	
	// setup goods name
	sprintf(buf, "%s %s", pGood->name, pGood->unit);
	if (obj->name)
		free(obj->name);
	obj->name = str_dup(buf);
	
	// setup goods short description
	sprintf(buf, "%s %s of %s", AN(pGood->unit), pGood->unit, pGood->name);
	if (obj->short_description)
		free(obj->short_description);
	obj->short_description = str_dup(buf);
	
	// setup goods description
	sprintf(buf, "%s %s of %s was placed here.",
		UAN(pGood->unit), pGood->unit, pGood->name);
	if (obj->description)
		free(obj->description);
	obj->description = str_dup(buf);
	
	obj->action_description	= NULL;

	SET_BIT(GET_OBJ_EXTRA(obj), ITEM_UNIQUE);
	SET_BIT(GET_OBJ_WEAR(obj), ITEM_WEAR_TAKE);
	
	GET_OBJ_TYPE(obj)		= ITEM_GOODS;
	GET_OBJ_WEIGHT(obj)		= pGood->weight;
	GET_OBJ_COST(obj)		= pGood->cost;
	GET_OBJ_TIMER(obj)		= pGood->life;
	GET_OBJ_QUALITY(obj)	= number(30, 80);

	obj->count				= amount;

	GET_OBJ_VAL(obj, 0)	= pGood->vnum;
	
	return (obj);
}


ACMD(do_newgoods)
{
	OBJ_DATA *obj;
	GOOD_DATA *pGood;
	char *g;

	/* get goods name */
	g = get_spell_name(argument);
	if (g == NULL)
	{
		send_to_char("Goods names must be enclosed in the Holy Magic Symbols: '\r\n", ch);
		return;
	}
	argument = strtok(NULL, "\0");

	// show the entire list of goods
	if (!str_cmp(g, "list"))
	{
		int mode;

		if (*argument)
		{
			one_argument(argument, arg);
			if ( !*arg || !is_number(arg) )
				mode = 0;
			else
				mode = atoi(arg);
		}
		else
			mode = 0;

		list_goods(ch, mode);
		return;
	}

	if (!(pGood = get_good_by_name(g)))
	{
		ch_printf(ch, "There is no goods called '%s'.\r\n", g);
		return;
	}

	obj = create_good_obj(pGood, 1);
	obj = obj_to_room(obj, ch->in_room);

	ch_printf(ch, "You create %s %s of %s out of the blue.\r\n",
		AN(pGood->unit), pGood->unit, pGood->name);
	sprintf(buf, "$n creates %s %s of %s out of the blue.\r\n",
		AN(pGood->unit), pGood->unit, pGood->name);

	act(buf, FALSE, ch, NULL, NULL, TO_ROOM);
}


/* =========================================================================== */
/* Trading Post Code                                                           */
/* =========================================================================== */

/* *************************************************************************** */
/* Trading Post finding code                                                   */
/* *************************************************************************** */

TRADING_POST *find_trading_post(int vnum)
{
	TRADING_POST *pTp;

	if (vnum < 1)
		return (NULL);

	for (pTp = first_trading_post; pTp; pTp = pTp->next)
	{
		if (pTp->vnum == vnum)
			break;
	}

	return (pTp);
}

TRP_GOOD *tp_has_goods(TRADING_POST *pTp, int gnum)
{
	TRP_GOOD *tGood;

	for (tGood = pTp->first_tpgood; tGood; tGood = tGood->next)
		if (tGood->goods_vnum == gnum)
			break;

	return (tGood);
}

/* *************************************************************************** */
/* Trading Post creation code                                                  */
/* *************************************************************************** */

TRADING_POST *new_trading_post(void)
{
	TRADING_POST *pTp;

	CREATE(pTp, TRADING_POST, 1);
	pTp->next			= NULL;
	pTp->prev			= NULL;
	pTp->next_in_market	= NULL;
	pTp->market			= NULL;
	pTp->first_tpgood	= NULL;
	pTp->last_tpgood	= NULL;
	pTp->vnum			= NOTHING;
	pTp->type			= TP_GCLOSE;

	// add to the list
	LINK(pTp, first_trading_post, last_trading_post, next, prev);

	return (pTp);
}

/* *************************************************************************** */
/* Load Trading Post from file code                                            */
/* *************************************************************************** */

TRADING_POST *fread_one_tp(FILE *fp)
{
	TRADING_POST *tp = new_trading_post();
	char *word;
	bool fMatch;

	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 (tp);
			break;

		case 'G':
			if (!strcmp(word, "Good"))
			{
				TRP_GOOD *tGood;
				GOOD_DATA *pGood;
				int vnum;

				fMatch = TRUE;

				vnum = fread_number(fp);

				if (!(pGood = get_good(vnum)))
				{
					log("SYSERR: fread_one_tp() - wrong good vnum %d.", vnum);
					exit(1);
				}

				CREATE(tGood, TRP_GOOD, 1);
				tGood->next			= NULL;
				tGood->prev			= NULL;
				tGood->goods_vnum	= vnum;

				tGood->quantity		= fread_number(fp);
				tGood->prev_qty		= fread_number(fp);
				tGood->stock		= fread_number(fp);

				// add good to tp
				LINK(tGood, tp->first_tpgood, tp->last_tpgood, next, prev);

				// assign unassigned goods to the tp market
				if (tGood->stock && tp->market && !pGood->mkvnum)
					pGood->mkvnum	= tp->market->vnum;

				// setup GM if needed
				if (!GoodsMarkets[pGood->vnum][tp->market->vnum])
				{
					log("SYSERR: fread_one_tp() - missing GoodsMarkets data for good %d - market %d.", pGood->vnum, tp->market->vnum);
					exit(1);
				//	CREATE(GoodsMarkets[pGood->vnum][tp->market->vnum], MARKET_GOOD, 1);
				}

				// setup GM.total_tp
				GoodsMarkets[pGood->vnum][tp->market->vnum]->total_tp++;
				// increase GM.qty
				GoodsMarkets[pGood->vnum][tp->market->vnum]->qty += tGood->quantity;

				break;
			}
			break;

		case 'M':
			if (!strcmp(word, "Market"))
			{
				int mnum = fread_number(fp);

				fMatch = TRUE;

				if (mnum == -1)
				{
					log("ECONOMY: Trading Post %d without market vnum.", tp->vnum);
					exit(1);
				}

				if (!(tp->market = find_market(mnum)))
				{
					log("ECONOMY: Trading Post %d unknown market vnum %d.", tp->vnum, mnum);
					exit(1);
				}

				// increase count of tp
				tp->market->num_of_tp++;

				// add to market tp list
				tp->next_in_market	= tp->market->tp_list;
				tp->market->tp_list	= tp;

				break;
			}
			break;

		case 'T':
			KEY("Type",	tp->type,	fread_number(fp));
			break;

		case 'V':
			KEY("Vnum",	tp->vnum,	fread_number(fp));
			break;

		}

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

	return (NULL);
}

void LoadTradingPost(void)
{
	FILE *fp;
	char fname[128];
	char letter;
	char *word;

	sprintf(fname, "%stradingpost.data", LIB_GOODS);
	if (!(fp = fopen(fname, "r")))
	{
		log("SYSERR: LoadTradingPost() - cannot open file %s.", fname);
		return;
	}

	top_tp_vnum = 0;

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

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

		if (!strcmp(word, "TRADINGPOST"))
		{
			TRADING_POST *pTp;

			if (!(pTp = fread_one_tp(fp)))
			{
				log("SYSERR: LoadTradingPost() - error in reading trading post.");
				fclose(fp);
				exit(1);
			}

			if (pTp->vnum > top_tp_vnum)
				top_tp_vnum = pTp->vnum;
		}
		else if (!strcmp( word, "END"))		// Done
			break;
		else
		{
			log("SYSERR: LoadTradingPost() - bad section %s", word);
			continue;
		}
	}

	fclose(fp);

	if (reset_markets)
		SaveGoodsMarketsTable(0);
}

/* *************************************************************************** */
/* Save Trading Post to file code                                              */
/* *************************************************************************** */

void SaveTradingPost(void)
{
	FILE *fp;
	TRADING_POST *tp;
	TRP_GOOD *tGood;
	char fname[128];

	if (!first_trading_post)
		return;

	sprintf(fname, "%stradingpost.data", LIB_GOODS);
	if (!(fp = fopen(fname, "w")))
	{
		log("SYSERR: SaveTradingPost() - cannot open file %s.", fname);
		return;
	}

	for (tp = first_trading_post; tp; tp = tp->next)
	{
		fprintf(fp, "#TRADINGPOST\n");
		fprintf(fp, "Vnum          %d\n", tp->vnum);
		fprintf(fp, "Market        %d\n", tp->market ? tp->market->vnum : -1);
		fprintf(fp, "Type          %d\n", tp->type);

		for (tGood = tp->first_tpgood; tGood; tGood = tGood->next)
		{
			if (tGood->quantity <= 0 && tGood->prev_qty <= 0 && !tGood->stock)
				continue;

			fprintf(fp, "Good          %d %d %d %d\n",
				tGood->goods_vnum, tGood->quantity, tGood->prev_qty, tGood->stock);
		}
		fprintf(fp, "End\n\n");
	}

	fprintf(fp, "#END\n");
	fclose(fp);
}


/* *************************************************************************** */
/* IMMS Command                                                                */
/* *************************************************************************** */

const char *tp_type_desc[] =
{
  "closed",
  "restricted",
  "limited",
  "free"
};

SHIP_DATA *find_ship( sh_int vnum );

MARKET_DATA *locate_bld_market(BUILDING_DATA *pBld)
{
	MARKET_DATA *pMk = NULL;
	int iDist = 0;

	if (!pBld->in_room)
		return (NULL);

	if (IS_WILD(pBld->in_room))
	{
		for (pMk = first_market; pMk; pMk = pMk->next)
		{
			iDist = (int) distance(GET_X(pBld), GET_Y(pBld), pMk->heart.x, pMk->heart.y);

			if (iDist <= pMk->size)
				break;
		}
	}
	else
	{
		COORD_DATA coord;

		coord = zone_table[pBld->in_room->zone].wild.z_start;

		if (coord.y == 0 && coord.x == 0)
		{
			log("SYSERR: cannot retrieve wild position for zone %d.", zone_table[pBld->in_room->zone].number);
			return (NULL);
		}

		for (pMk = first_market; pMk; pMk = pMk->next)
		{
			iDist = (int) distance(coord.x, coord.y, pMk->heart.x, pMk->heart.y);

			if (iDist <= pMk->size)
				break;
		}
	}

	return (pMk);
}


ACMD(do_tp)
{
	TRADING_POST *pTp;
	GOOD_DATA *pGood;
	char arg[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH];
	char buf[MAX_STRING_LENGTH];
	char bloc[MAX_STRING_LENGTH];
	int tnum, gnum;

	argument = one_argument(argument, arg);

	if (!*arg)
	{
		send_to_char(
			"Usage:\r\n"
			"-----------------------------------------------------------------------\r\n"
			" command                         description\r\n"
			"-----------------------------------------------------------------------\r\n"
			" tp virtual                      List of loaded Trading Posts\r\n"
			" tp list <market vnum>           List of Trading Posts in game (1)\r\n"
			" tp <vnum>                       Detailed infos\r\n"
			" tp <vnum> add <good> <stock>    Add good to the TP goods list (2)\r\n"
			" tp <vnum> remove <good>         Remove good from the TP goods list\r\n"
			" tp new <type>                   Setup a new TP of the given type(3)\r\n"
			" tp delete <vnum>                Destroy given Trading Post\r\n"
			"-----------------------------------------------------------------------\r\n"
			" (1) <market vnum> filter TP listing to those of the given market.\r\n"
			" (2) <stock> can be 0 or 1, depending if the good should be produced in\r\n"
			"     the Trading Post's market. If 0, you can omit the parameter.\r\n"
			" (3) to create a new Trading Post, you must be in a STORE type building\r\n"
			"-----------------------------------------------------------------------\r\n"
			, ch);
		return;
	}

	if (is_abbrev(arg, "virtual"))
	{
		char buf[MAX_STRING_LENGTH];

		strcpy(buf, "List of loaded Trading Posts.\r\n");
		for (pTp = first_trading_post; pTp; pTp = pTp->next)
		{
			sprintf(buf+strlen(buf), " Vnum: [%d]  Market: [%d]  Type: %s\r\n",
				pTp->vnum, (pTp->market ? pTp->market->vnum : NOWHERE), tp_type_desc[pTp->type]);
		}
		page_string(ch->desc, buf, 0);
		return;
	}

	if (is_abbrev(arg, "list"))
	{
		MARKET_DATA *pMk = NULL;
		int mnum;
		
		argument = one_argument(argument, arg);

		if (!*arg || !is_number(arg))
			mnum = 0;
		else
		{
			mnum = atoi(arg);

			if (mnum < 1 || mnum > MAX_MARKET)
				mnum = 0;
			else if	(!(pMk = find_market(mnum)))
				mnum = 0;
		}

		strcpy(buf, "List of Trading Posts in");
		if (mnum)
			sprintf(buf + strlen(buf), " market [%d] '%s'.\r\n", mnum, pMk->name);
		else
			strcat(buf, " game.\r\n");
		send_to_char(buf, ch);

		*buf = '\0';
		for (pTp = first_trading_post; pTp; pTp = pTp->next)
		{
			if (!pTp->market)
				continue;

			if (!pTp->in_room)
				continue;

			if (mnum && pTp->market->vnum != mnum)
				continue;

			// where is this TP ??
			if (IS_BUILDING(pTp->in_room))
				sprintf(bloc, "Building [%d]", pTp->in_room->extra_data->vnum);
			else if (IS_WILD(pTp->in_room))
				sprintf(bloc, "Wilderness [%d %d]", GET_Y(pTp), GET_X(pTp));
			else if (pTp->in_room->number != NOWHERE)
				sprintf(bloc, "Room [%d] '%s'", pTp->in_room->number, ROOM_NAME(pTp));
			else
				strcpy(bloc, "NOWHERE");

			sprintf(buf+strlen(buf), "  [%d]   Type: %s   Location: %s\r\n",
				pTp->vnum, tp_type_desc[pTp->type], bloc);
		}

		if (!*buf)
			send_to_char("No Trading Post found.\r\n", ch);
		else
			page_string(ch->desc, buf, 0);

		return;
	}

	if (is_abbrev(arg, "new"))
	{
		BUILDING_DATA *pBld;
		int nt;

		// building checks....
		if (!IN_BUILDING(ch))
		{
			send_to_char("You must be in a STORE type building to create a new TP.\r\n", ch);
			return;
		}

		pBld = ch->in_building;
		if (!pBld || pBld->type->vnum != BLD_STORE)
		{
			send_to_char("You must be in a STORE type building to create a new TP.\r\n", ch);
			return;
		}

		if (pBld->trp)
		{
			send_to_char("This building already host a Trading Post.\r\n", ch);
			return;
		}

		// TP type checks....
		argument = one_argument(argument, arg);

		if (!*arg)
		{
			send_to_char("You must specify a TP type.\r\nPossible types are:\r\n", ch);

			for (nt = 0; nt < NUM_TP_TYPES; nt++)
				ch_printf(ch, " - %s\r\n", tp_type_desc[nt]);

			return;
		}

		for (nt = 0; nt < NUM_TP_TYPES; nt++)
		{
			if (is_abbrev(arg, tp_type_desc[nt]))
				break;
		}

		if (nt >= NUM_TP_TYPES)
		{
			send_to_char("Unknown TP type.\r\nPossible types are:\r\n", ch);

			for (nt = 0; nt < NUM_TP_TYPES; nt++)
				ch_printf(ch, " - %s\r\n", tp_type_desc[nt]);

			return;
		}

		// setup the new TP
		pTp = new_trading_post();
		pTp->vnum			= ++top_tp_vnum;
		pTp->type			= nt;
		pTp->in_room		= pBld->rooms[ch->in_room->number];
		pTp->market			= locate_bld_market(pBld);
		
		if (pTp->market)
		{
			pTp->next_in_market		= pTp->market->tp_list;
			pTp->market->tp_list	= pTp;
		}

		// assing building tp status
		pBld->trp		= pTp;
		// assign to current building room the special procedure
		pBld->rooms[ch->in_room->number]->func	= tradingpost;

		fwrite_building(pBld);
		SaveTradingPost();

		return;
	}

	if (!str_cmp(arg, "delete"))
	{
		send_to_char("Not yet implemented, sorry.\r\n", ch);
		return;
	}

	if (!is_number(arg))
		return;

	tnum = atoi(arg);

	if (tnum < 0 || tnum > top_tp_vnum)
	{
		ch_printf(ch, "Invalid TP vnum %d.\r\n", tnum);
		return;
	}

	if (!(pTp = find_trading_post(tnum)))
	{
		ch_printf(ch, "Cannot find any TP with vnum %d.\r\n", tnum);
		return;
	}

	argument = two_arguments(argument, arg, arg2);

	// print detailed info about TP
	if (!*arg)
	{
		TRP_GOOD *tGood;
		GOOD_DATA *pGood;
		int price;

		if (pTp->in_room)
		{
			// where is this TP ??
			if (IS_BUILDING(pTp->in_room))
				sprintf(bloc, "Building [%d]", pTp->in_room->extra_data->vnum);
			else if (IS_WILD(pTp->in_room))
				sprintf(bloc, "Wilderness [%d %d]", GET_Y(pTp), GET_X(pTp));
			else if (IS_SHIP(pTp->in_room))
			{
				SHIP_DATA *pShip;
				
				if (!(pShip = find_ship(pTp->in_room->extra_data->vnum)))
					strcpy(bloc, "Ship <ERROR>");
				else
					sprintf(bloc, "Ship [%d] '%s'", pShip->vnum, pShip->name);
			}
			else if (pTp->in_room->number != NOWHERE)
				sprintf(bloc, "Room [%d]", pTp->in_room->number);
			else
				strcpy(bloc, "NOWHERE");
		}
		else
			strcpy(bloc, "NOWHERE");
		
		ch_printf(ch, "Trading Post %d    -- Market: %d '%s'\r\n",
			pTp->vnum,
			(pTp->market ? pTp->market->vnum : NOWHERE),
			(pTp->market ? pTp->market->name : "<ERROR>"));
		ch_printf(ch, "Type: %s   Location: %s\r\n", tp_type_desc[pTp->type], bloc);

		send_to_char("List of Goods in TP:\r\n", ch);
		*buf = '\0';
		for (tGood = pTp->first_tpgood; tGood; tGood = tGood->next)
		{
			if (!(pGood = get_good(tGood->goods_vnum)))
				continue;

			price = 0;

			if (GoodsMarkets[pGood->vnum][pTp->market->vnum])
				price = GoodsMarkets[pGood->vnum][pTp->market->vnum]->price;

			sprintf(buf+strlen(buf), "  Good: [%3d] %-20s - %s  Qty: %4d  Price: %6d\r\n",
				pGood->vnum, pGood->short_descr,
				(tGood->stock ? "X" : " "), tGood->quantity, price);
		}
		page_string(ch->desc, buf, 0);

		return; 
	}

	if (!isname(arg, "add free") || !*arg2 || !is_number(arg2))
	{
		send_to_char("Valid commands for TP are: add, free\r\n", ch);
		return;
	}

	gnum = atoi(arg2);
	if (!(pGood = get_good(gnum)))
	{
		ch_printf(ch, "Invalid good vnum %d.\r\n", gnum);
		return;
	}

	if (is_abbrev(arg, "add"))
	{
		MARKET_GOOD *pGM;
		TRP_GOOD *tGood;
		int stock, x;
	
		if (tp_has_goods(pTp, pGood->vnum))
		{
			send_to_char("Good already present in TP.\r\n", ch);
			return;
		}

		argument = one_argument(argument, arg);

		// get stock flag
		if (!*arg || !is_number(arg))
			stock = 0;
		else
			stock = atoi(arg);

		if (stock != 1)
			stock = 0;

		if (stock && pGood->mkvnum && pGood->mkvnum != pTp->market->vnum)
			stock = 0;

		CREATE(tGood, TRP_GOOD, 1);
		tGood->next				= NULL;
		tGood->prev				= NULL;
		tGood->goods_vnum		= pGood->vnum;
		tGood->prev_qty			= 0;
		tGood->quantity			= pGood->gtype->prod_avg[Season()] * pTp->market->var_mod.prod_var / pGood->gtype->cons_speed;
		tGood->stock			= stock;

		// add good to TP good list
		LINK(tGood, pTp->first_tpgood, pTp->last_tpgood, next, prev);

		// good previously unused.. initialize.
		if (!pGood->mkvnum)
			pGood->mkvnum		= pTp->market->vnum;

		// if not present, create GM data
		if (!(pGM = GoodsMarkets[pGood->vnum][pTp->market->vnum]))
		{ 
			CREATE(pGM, MARKET_GOOD, 1);
			pGM->comm_closure	= 1.00;
			pGM->comm_prod		= 1.00;
			pGM->demand			= 0;
			pGM->good_appet		= 1;
			pGM->price			= pGood->cost;
			pGM->qty			= 0;
			pGM->total_tp		= 0;
			
			GoodsMarkets[pGood->vnum][pTp->market->vnum] = pGM;
		}

		pGM->total_tp++;
		pGM->qty += tGood->quantity;

		// perform ten resets
		for (x = 0; x < 10; x++)
		{
			calc_prod(pTp->market, pGood, tGood, pGM);
			calc_price(pTp->market, pGood, pGM);
		}

		// save new data
		SaveGoodsMarketsTable(0);
		SaveTradingPost();

		send_to_char("Good succesfully added.\r\n", ch);
		return;
	}

	if (is_abbrev(arg, "remove"))
	{
		//MARKET_DATA *pMk;
		TRADING_POST *tpp;
		MARKET_GOOD *pGM;
		TRP_GOOD *tGood;

		if (!(tGood = tp_has_goods(pTp, pGood->vnum)))
		{
			ch_printf(ch, "Good %d is not present in TP %d.\r\n", pGood->vnum, pTp->vnum);
			return;
		}

		if (!(pGM = GoodsMarkets[pGood->vnum][pTp->market->vnum]))
		{
			log("SYSERR: good %d was in TP %d without GM data.\r\n", pGood->vnum, pTp->vnum);
			ch_printf(ch, "Good %d cannot be removed from TP %d.\r\n", pGood->vnum, pTp->vnum);
			return;
		}

		// look in every TP in this market for this good
		for (tpp = pTp->market->tp_list; tpp; tpp = tpp->next_in_market)
		{
			if ( tp_has_goods(tpp, pGood->vnum) )
				break;
		}

		// good is present elsewhere, do nothing..
		if (tpp)
			return;

		// remove GM info
		DISPOSE(GoodsMarkets[pGood->vnum][pTp->market->vnum]);

		// take care of GOOD info


		send_to_char("Good succesfully removed.\r\n", ch);
		return;
	}
}


/* =========================================================================== */
/* Markets Code                                                                */
/* =========================================================================== */

/* *************************************************************************** */
/* Market finding code                                                         */
/* *************************************************************************** */

MARKET_DATA *find_market(int vnum)
{
	MARKET_DATA *pMk = NULL;

	for (pMk = first_market; pMk; pMk = pMk->next)
	{
		if (pMk->vnum == vnum)
			break;
	}

	return (pMk);
}


MARKET_DATA *find_market_by_name(char *mname)
{
	MARKET_DATA *pMk = NULL;

	for (pMk = last_market; pMk; pMk = pMk->prev)
	{
		if (isname(mname, pMk->name))
			break;
	}

	return (pMk);
}

/* return the lowest price of a given good in a given market */
bool get_good_in_market(MARKET_DATA *pMk, int gnum)
{
	TRADING_POST *pTp;
	bool found = FALSE;

	// loop thru each market's trading post
	for (pTp = pMk->tp_list; pTp; pTp = pTp->next_in_market)
	{
		if (tp_has_goods(pTp, gnum))
		{
			found = TRUE;
			break;
		}
	}

	return (found);
}

/*
 * trova il numero di merci del tipo specificato che
 * vengono prodotte nel mercato indicato
 */
int get_market_gtype_prod(MARKET_DATA *pMk, int gtype)
{
	TRADING_POST *pTp;
	TRP_GOOD *tGood;
	GOOD_DATA *pGood;
	int amount = 0;

	// loop thru each trading post in given market
	for (pTp = pMk->tp_list; pTp; pTp = pTp->next_in_market)
	{
		// loop thru goods in trading post
		for (tGood = pTp->first_tpgood; tGood; tGood = tGood->next)
		{
			if (!(pGood = get_good(tGood->goods_vnum)))
				continue;

			if (pGood->code == gtype && tGood->stock)
				amount++;
		}
	}

	return (amount);
}

/* *************************************************************************** */
/* Market creation code                                                        */
/* *************************************************************************** */

MARKET_DATA *new_market(void)
{
	MARKET_DATA *pMk = NULL;

	CREATE(pMk, MARKET_DATA, 1);

	pMk->next					= NULL;
	pMk->prev					= NULL;
	pMk->name					= NULL;
	pMk->tp_list				= NULL;
	pMk->affect					= NULL;
	pMk->vnum					= NOTHING;
	pMk->affections				= 0;
	pMk->num_of_tp				= 0;
	pMk->var_real.prod_var		= 1.00;
	pMk->var_real.price_var		= 1.00;
	pMk->var_real.closure_var	= 1.00;
	pMk->var_mod.prod_var		= 1.00;
	pMk->var_mod.price_var		= 1.00;
	pMk->var_mod.closure_var	= 1.00;

	// add to the global list
	LINK(pMk, first_market, last_market, next, prev);

	return (pMk);
}

/* *************************************************************************** */
/* Load Market from file code                                                  */
/* *************************************************************************** */

MARKET_DATA *fread_one_market(FILE *fp)
{
	MARKET_DATA *pMk = new_market();
	char *word;
	bool fMatch;

	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, "Affection"))
			{
				int bitv, nwhat, dur, tmp;
				float value;

				bitv		= fread_number(fp);
				nwhat		= fread_number(fp);
				dur			= fread_number(fp);
				tmp			= fread_number(fp);
				value = (float) tmp / 100;

				set_mk_aff(pMk->vnum, nwhat, bitv, dur, value);

				fMatch = TRUE;
				break;
			}
			break;

		case 'H':
			if (!strcmp(word, "Heart"))
			{
				pMk->heart.y	= fread_number(fp);
				pMk->heart.x	= fread_number(fp);
				fMatch = TRUE;
				break;
			}
			break;

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

		case 'N':
			KEY("Name",				pMk->name,				str_dup(fread_word(fp)));
			break;

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

		case 'V':
			KEY("Vnum",				pMk->vnum,				fread_number(fp));
			if (!strcmp(word, "Var_real_clos"))
			{
				int num;
				float calc;

				num = fread_number(fp);
				calc = (float)num / 100;
				pMk->var_real.closure_var	= calc;
				fMatch = TRUE;
				break;
			}
			if (!strcmp(word, "Var_real_prod"))
			{
				int num;
				float calc;

				num = fread_number(fp);
				calc = (float)num / 100;
				pMk->var_real.prod_var		= calc;
				fMatch = TRUE;
				break;
			}
			if (!strcmp(word, "Var_real_price"))
			{
				int num;
				float calc;

				num = fread_number(fp);
				calc = (float)num / 100;
				pMk->var_real.price_var		= calc;
				fMatch = TRUE;
				break;
			}
			/*
			if (!strcmp(word, "Var_mod_clos"))
			{
				int num;
				float calc;

				num = fread_number(fp);
				calc = (float)num / 100;
				pMk->var_mod.closure_var	= calc;
				fMatch = TRUE;
				break;
			}
			if (!strcmp(word, "Var_mod_prod"))
			{
				int num;
				float calc;

				num = fread_number(fp);
				calc = (float)num / 100;
				pMk->var_mod.prod_var		= calc;
				fMatch = TRUE;
				break;
			}
			if (!strcmp(word, "Var_mod_price"))
			{
				int num;
				float calc;

				num = fread_number(fp);
				calc = (float)num / 100;
				pMk->var_mod.price_var		= calc;
				fMatch = TRUE;
				break;
			}
			*/
			break;
		}

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

	return (NULL);
}

void LoadMarkets(void)
{
	FILE *fp;
	char fname[128];
	char letter;
	char *word;

	sprintf(fname, "%smarkets.data", LIB_GOODS);
	if (!(fp = fopen(fname, "r")))
	{
		log("SYSERR: LoadMarkets() - cannot open file %s.", fname);
		return;
	}

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

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

		if (!strcmp(word, "MARKET"))
		{
			if (!fread_one_market(fp))
			{
				log("SYSERR: LoadMarkets() - error in reading market.");
				fclose(fp);
				exit(1);
			}
		}
		else if (!strcmp( word, "END"))		// Done
			break;
		else
		{
			log("SYSERR: LoadMarkets() - bad section %s", word);
			continue;
		}
	}

	fclose(fp);
}

/* *************************************************************************** */
/* Save Market to file code                                                    */
/* *************************************************************************** */

void SaveMarkets(void)
{
	FILE *fp;
	MARKET_DATA *pMk;
	char fname[128];

	if (!first_market)
		return;

	sprintf(fname, "%smarkets.data", LIB_GOODS);
	if (!(fp = fopen(fname, "w")))
	{
		log("SYSERR: SaveMarkets() - cannot open file %s.", fname);
		return;
	}

	for (pMk = first_market; pMk; pMk = pMk->next)
	{
		fprintf(fp, "#MARKET\n");
		fprintf(fp, "Vnum             %d\n",	pMk->vnum);
		fprintf(fp, "Name             %s\n",	pMk->name);
		fprintf(fp, "Heart            %hd %hd\n",	pMk->heart.y, pMk->heart.x);
		fprintf(fp, "Size             %d\n",	pMk->size);
		fprintf(fp, "Var_real_clos    %d\n",	(int)(pMk->var_real.closure_var * 100));
		fprintf(fp, "Var_real_prod    %d\n",	(int)(pMk->var_real.prod_var * 100));
		fprintf(fp, "Var_real_price   %d\n",	(int)(pMk->var_real.price_var * 100));
//		fprintf(fp, "Var_mod_clos     %d\n",	(int)(pMk->var_mod.closure_var * 100));
//		fprintf(fp, "Var_mod_prod     %d\n",	(int)(pMk->var_mod.prod_var * 100));
//		fprintf(fp, "Var_mod_price    %d\n",	(int)(pMk->var_mod.price_var * 100));

		if (pMk->affect)
		{
			MARKET_AFF *pAff = NULL;

			for (pAff = pMk->affect; pAff; pAff = pAff->next)
			{
				fprintf(fp, "Affection        %d %hd %hd %d\n", 
					pAff->bitvector, pAff->what, pAff->duration, (int)(pAff->modifier * 100));
			}
		}
		fprintf(fp, "End\n\n");
	}

	fprintf(fp, "#END\n");
	fclose(fp);
}

/* *************************************************************************** */
/* Market Affections code                                                      */
/* *************************************************************************** */


NAME_NUMBER mk_gdr_aff[] =
{
  {"pestilence",	MKT_PESTILENCE},
  {"insects",		MKT_INSECT},
  {"drought",		MKT_DROUGHT},
  {"flood",			MKT_FLOOD},
  {"war",			MKT_WAR},
  {"\n",			0}
};

NAME_NUMBER mk_gdr_modif[] =
{
  {"production",	MOD_PROD},
  {"price",			MOD_PRICE},
  {"closure",		MOD_CLOSURE},
  {"\n",			0}
};


const char *mk_aff_descr[] =
{
  "EXTRA_PROD",
  "MALUS_PROD",
  "EXTRA_PRICE",
  "MALUS_PRICE",
  "PESTILENCE",
  "INSECTS",
  "DROUGHT",
  "FLOOD",
  "WAR",
  "\n"
};

const char *mk_aff_what[] =
{
  "NONE",
  "PRODUCTION",
  "PRICE",
  "CLOSURE",
  "\n"
};


void affect_to_market(MARKET_DATA *pMk, MARKET_AFF *pAff)
{
	// set affection bitvector
	SET_BIT(pMk->affections, pAff->bitvector);

	// apply value modification
	switch (pAff->what)
	{
	case MOD_CLOSURE:
		pMk->var_mod.closure_var	+= pAff->modifier;
		break;
	case MOD_PROD:
		pMk->var_mod.prod_var		+= pAff->modifier;
		break;
	case MOD_PRICE:
		pMk->var_mod.price_var		+= pAff->modifier;
		break;
	}

	// add to the list
	pAff->next	= pMk->affect;
	pMk->affect	= pAff;
}

void affect_from_market(MARKET_DATA *pMk, MARKET_AFF *pAff)
{
	MARKET_AFF *temp = NULL;

	// remove affection bitvector
	REMOVE_BIT(pMk->affections, pAff->bitvector);

	// undo value modification
	switch (pAff->what)
	{
	case MOD_CLOSURE:
		pMk->var_mod.closure_var	-= pAff->modifier;
		break;
	case MOD_PROD:
		pMk->var_mod.prod_var		-= pAff->modifier;
		break;
	case MOD_PRICE:
		pMk->var_mod.price_var		-= pAff->modifier;
		break;
	}

	// remove from the list
	REMOVE_FROM_LIST(pAff, pMk->affect, next);
	pAff->next		= NULL;
	// take care
	DISPOSE(pAff);
}

MARKET_AFF *market_aff_by_bitv(MARKET_DATA *pMk, int bitv)
{
	MARKET_AFF *pAff = NULL;

	for (pAff = pMk->affect; pAff; pAff = pAff->next)
		if (pAff->bitvector == bitv)
			break;

	return (pAff);
}

void set_mk_aff(int mnum, int nwhat, int bitv, int durat, float value)
{
	MARKET_DATA *pMk;
	MARKET_AFF *pAff = NULL;

	if (!(pMk = find_market(mnum)))
		return;

	pAff = market_aff_by_bitv(pMk, bitv);

	// it's not there... setup a new one
	if (!pAff || pAff->what != nwhat)
	{
		CREATE(pAff, MARKET_AFF, 1);
		pAff->next			= NULL;
		pAff->bitvector		= bitv;
		pAff->duration		= durat;
		pAff->modifier		= value;
		pAff->what			= nwhat;
		
		affect_to_market(pMk, pAff);
	}
	// update existing affection
	else
	{
		if (durat != MUD_DAYS_PER_YEAR)
			pAff->duration	+= durat;
		pAff->modifier		+= value;

		// apply value modification
		switch (pAff->what)
		{
		case MOD_CLOSURE:
			pMk->var_mod.closure_var	+= value;
			break;
		case MOD_PROD:
			pMk->var_mod.prod_var		+= value;
			break;
		case MOD_PRICE:
			pMk->var_mod.price_var		+= value;
			break;
		}
	}
}

// called every mud day
void UpdateMarketAffections(void)
{
	MARKET_DATA *pMk;
	MARKET_AFF *pAff = NULL, *pAff_next = NULL;

	for (pMk = first_market; pMk; pMk = pMk->next)
	{
		for (pAff = pMk->affect; pAff; pAff = pAff_next)
		{
			pAff_next = pAff->next;

			if (pAff->duration == -1)
				continue;

			if (--pAff->duration <= 0)
				affect_from_market(pMk, pAff);
		}
	}

	SaveMarkets();
}

/* automatic affections.. rolled once every three mud months... */
void RollMarketAffections(void)
{
	MARKET_DATA *pMk;
	int type, bitv, dur, nwhat;
	float value = 0;
	//char dflags[128], swhat[128];

	for (pMk = first_market; pMk; pMk = pMk->next)
	{
		if (!pMk->size)
			continue;

		for (type = 0; type < 4; type++)
		{
			if (number(1, 12) > 1)
				continue;
			
			bitv	= (1 << type);
			dur		= MUD_DAYS_PER_YEAR;

			switch (type)
			{
			case 0:	nwhat = MOD_PROD;	value = 0.1F;	break;
			case 1:	nwhat = MOD_PROD;	value = -0.1F;	break;
			case 2:	nwhat = MOD_PRICE;	value = 0.1F;	break;
			case 3:	nwhat = MOD_PRICE;	value = -0.1F;	break;
			}

			set_mk_aff(pMk->vnum, nwhat, bitv, dur, value);

			//sprintbit(bitv, mk_aff_descr, dflags);
			//sprinttype(nwhat, mk_aff_what, swhat);
			//log("Economy - settata auto affection %s (mod %s) sul mercato %d.",
			//	dflags, swhat, pMk->vnum);
		}
	}
}


// print info on markets
ACMD(do_market)
{
	MARKET_DATA *pMk;
	MARKET_GOOD *pGM;
	char arg[MAX_INPUT_LENGTH], buf[MAX_STRING_LENGTH];
	int gn;

	argument = one_argument(argument, arg);

	if (!*arg)
	{
		send_to_char(
			"Usage:\r\n"
			"-----------------------------------------------------------------------\r\n"
			" command                          description\r\n"
			"-----------------------------------------------------------------------\r\n"
			" market list                      List of markets in game.\r\n"
			" market info                      Show global trade and production info\r\n"
			" market <vnum> <gm>               Detailed info for market <vnum>\r\n"
			" market <name> <gm>               Detailed info for market <name>\r\n"
			"-----------------------------------------------------------------------\r\n"
			" <gm> if any data is present print Goods/Markets info\r\n"
			"-----------------------------------------------------------------------\r\n"
			, ch);
		return;
	}

	if (!str_cmp(arg, "info"))
	{
		extern char *newspaper;

		if (newspaper)
			page_string(ch->desc, newspaper, 0);
		else
			send_to_char("Trade Newspaper not present.\r\n", ch);
		return;
	}

	if (!str_cmp(arg, "list"))
	{
		send_to_char("Current markets in game:\r\n", ch);
		for (pMk = first_market; pMk; pMk = pMk->next)
		{
			if (!pMk->size)
				continue;

			ch_printf(ch, 
				"[%d] %-30s - Center: [%4d %4d]\r\n",
				pMk->vnum, pMk->name, pMk->heart.y, pMk->heart.x);
		}
		return;
	}

	if (is_number(arg))
	{
		if (!(pMk = find_market(atoi(arg))))
		{
			ch_printf(ch, "There isn't a market with vnum %d.\r\n", atoi(arg));
			return;
		}
	}
	else
	{
		if (!(pMk = find_market_by_name(arg)))
		{
			ch_printf(ch, "There isn't a market called '%s'.\r\n", arg);
			return;
		}
	}

	//extra check
	if (!pMk)	return;

	sprintf(buf, "Market [%d] %s\r\n", pMk->vnum, pMk->name);
	sprintf(buf + strlen(buf), "Heart coord: [%4d %4d]  Size: %d\r\n",
		pMk->heart.y, pMk->heart.x, pMk->size);
	sprintf(buf + strlen(buf), "Number of Trading Posts: %d\r\n",
		pMk->num_of_tp);
	sprintf(buf + strlen(buf), "Default Modifier - Prod: [%5.2f]  Price: [%5.2f]  Clos: [%5.2f]\r\n",
		pMk->var_real.prod_var, pMk->var_real.price_var, pMk->var_real.closure_var);
	sprintf(buf + strlen(buf), "Current Modifier - Prod: [%5.2f]  Price: [%5.2f]  Clos: [%5.2f]\r\n",
		pMk->var_mod.prod_var, pMk->var_mod.price_var, pMk->var_mod.closure_var);
	send_to_char(buf, ch);

	if (pMk->affect)
	{
		MARKET_AFF *pAff = NULL;
		char dflags[128], swhat[128];

		sprintf(buf, "Affections:\r\n");

		for (pAff = pMk->affect; pAff; pAff = pAff->next)
		{
			sprintbit(pAff->bitvector, mk_aff_descr, dflags);
			sprinttype(pAff->what, mk_aff_what, swhat);
			sprintf(buf+strlen(buf), "  Bit: [%-12s]  Modif: [%-12s]  Duration: [%4d]  Value: [%5.2f]\r\n",
				dflags, swhat, pAff->duration, pAff->modifier);
		}

		send_to_char(buf, ch);
	}

	argument = one_argument(argument, arg);

	if (!*arg)
		return;

	sprintf(buf, "Goods / Market Info:\r\n");

	for (gn = 0; gn < MAX_GOOD; gn++)
	{
		if (!(pGM = GoodsMarkets[gn][pMk->vnum]))
			continue;

		sprintf(buf+strlen(buf), " Good: [%3d] - Qty:[%5d]  Price:[%5d]  Demand:[%5.2f]\r\n",
			gn, pGM->qty, pGM->price, pGM->demand);
	}

	send_to_char(buf, ch);

}

// allow imms to set gdr affections on markets
ACMD(do_mkaff)
{
	MARKET_DATA *pMk;
	char arg[MAX_INPUT_LENGTH];
	int nr;
	int bitv = 0, modif = 0, dur = 0;
	float value = 0;

	argument = one_argument(argument, arg);

	if (!*arg)
	{
		send_to_char("Usage: mkaff <market> <affection> <modif> <duration> <value>\r\n", ch);
		return;
	}

	// get market
	if (is_number(arg))
	{
		if (!(pMk = find_market(atoi(arg))))
		{
			ch_printf(ch, "There isn't a market with vnum %d.\r\n", atoi(arg));
			return;
		}
	}
	else
	{
		if (!(pMk = find_market_by_name(arg)))
		{
			ch_printf(ch, "There isn't a market called '%s'.\r\n", arg);
			return;
		}
	}

	// get affection
	argument = one_argument(argument, arg);
	if (!*arg)
	{
		send_to_char("Available affections are:\r\n", ch);
		
		for (nr = 0; *mk_gdr_aff[nr].name != '\n'; nr++)
			ch_printf(ch, "%s\r\n", mk_gdr_aff[nr].name);
		return;
	}
	
	for (nr = 0; *mk_gdr_aff[nr].name != '\n'; nr++)
	{
		if ( isname(arg, mk_gdr_aff[nr].name) )
			break;
	}
	
	if (*mk_gdr_aff[nr].name == '\n')
	{
		send_to_char("Unknown affection.\r\nAvailable affections are:\r\n", ch);
		
		for (nr = 0; *mk_gdr_aff[nr].name != '\n'; nr++)
			ch_printf(ch, "%s\r\n", mk_gdr_aff[nr].name);
		return;
	}

	bitv = mk_gdr_aff[nr].number;


	// get modifier
	argument = one_argument(argument, arg);
	if (!*arg)
	{
		send_to_char("Available modifiers are:\r\n", ch);
		
		for (nr = 0; *mk_gdr_modif[nr].name != '\n'; nr++)
			ch_printf(ch, "%s\r\n", mk_gdr_modif[nr].name);
		return;
	}
	
	for (nr = 0; *mk_gdr_modif[nr].name != '\n'; nr++)
	{
		if ( isname(arg, mk_gdr_modif[nr].name) )
			break;
	}
	
	if (*mk_gdr_modif[nr].name == '\n')
	{
		send_to_char("Unknown modifier.\r\nAvailable modifiers are:\r\n", ch);
		
		for (nr = 0; *mk_gdr_modif[nr].name != '\n'; nr++)
			ch_printf(ch, "%s\r\n", mk_gdr_modif[nr].name);
		return;
	}

	modif = mk_gdr_modif[nr].number;


	// get duration
	argument = one_argument(argument, arg);
	if (!*arg || !is_number(arg))
	{
		ch_printf(ch, "Duration must be a number from 1 to %d\r\n", MUD_DAYS_PER_YEAR);
		return;
	}

	dur = atoi(arg);
	if (dur < 1 || dur > MUD_DAYS_PER_YEAR)
	{
		ch_printf(ch, "Duration must be a number from 1 to %d\r\n", MUD_DAYS_PER_YEAR);
		return;
	}

	// get value.. handle decimals..
	argument = one_argument(argument, arg);
	if (!*arg)
	{
		send_to_char("Value must be a number from -2.0 to 2.0\r\n", ch);
		return;
	}

	value = atof(arg);

	if (value < -2.0 || value > 2.0)
	{
		send_to_char("Value must be a number from -2.0 to 2.0\r\n", ch);
		return;
	}

	set_mk_aff(pMk->vnum, modif, bitv, dur, value);	
	send_to_char("Done.\r\n", ch);

	SaveMarkets();
}


ACMD(do_goodinfo)
{
	GOOD_DATA *pGood;
	MARKET_DATA *pMk;
	MARKET_GOOD *pGM;
	char arg[MAX_INPUT_LENGTH], buf[MAX_STRING_LENGTH];
	int gn, mn, price;

	one_argument(argument, arg);

	if (!*arg || !is_number(arg))
	{
		send_to_char("Usage: ginfo <good vnum>\r\n", ch);
		return;
	}

	gn = atoi(arg);
	
	if (!(pGood = get_good(gn)))
	{
		ch_printf(ch, "There is no good with vnum %d.\r\n", gn);
		return;
	}

	sprintf(buf, "Good: '%s' [%d]\r\n", pGood->name, gn);
	sprintf(buf+strlen(buf), "Unit: %s  Weigth: %d  Cost: %d\r\n",
		pGood->unit, pGood->weight, pGood->cost);
	sprintf(buf+strlen(buf), "Type: (%d) %s\r\n", pGood->gtype->vnum, pGood->gtype->name);
	sprintf(buf+strlen(buf), "Cons_speed: %6.2f   Elasticity: %d\r\n",
		pGood->gtype->cons_speed, pGood->gtype->elasticity);
	sprintf(buf+strlen(buf), "Production: Wn:%d  Sp:%d  Sm:%d  At:%d\r\n",
		pGood->gtype->prod_avg[0], pGood->gtype->prod_avg[1],
		pGood->gtype->prod_avg[2], pGood->gtype->prod_avg[3]);
	send_to_char(buf, ch);

	ch_printf(ch, "Commercial data:\r\n", pGood->name, gn);

	for (mn = 0; mn < MAX_MARKET; mn++)
	{
		if (!(pMk = find_market(mn)))
			continue;
		
		if (!str_cmp(pMk->name, "noname") || !pMk->size)
			continue;
		
		price = calc_trade_value(gn, mn);

		if (!(pGM = GoodsMarkets[gn][mn]))
			sprintf(buf, "  Market: [%3d] %-15s - Qty: [%5d] Sell Price: [%4d] Buy Price: [%4d] Demand: [%6.2f]\r\n",
				mn, pMk->name, 0, 0, price, 0);
		else
			sprintf(buf, "  Market: [%3d] %-15s - Qty: [%5d] Sell Price: [%4d] Buy Price: [%4d] Demand: [%6.2f]\r\n",
				mn, pMk->name, pGM->qty, pGM->price, price, pGM->demand);
		send_to_char(buf, ch);
	}
}


/* =========================================================================== */
/* Trading Code                                                                */
/* =========================================================================== */

/* *************************************************************************** */
/* Boot Setup Market-Goods data code                                           */
/* *************************************************************************** */

void SaveGoodsMarketsTable(bool bTime)
{
	FILE *fp;
	MARKET_GOOD *pGM;
	GOOD_DATA *pGood;
	MARKET_DATA *pMk;
	char fname[128];
	int gn, mn;

	if (bTime)
	{
		sprintf(fname, "%sgoodsmarkets.txt", LIB_GOODS);
		fp = fopen(fname, "a+");
	}
	else
	{
		sprintf(fname, "%sgoodsmarkets.data", LIB_GOODS);
		fp = fopen(fname, "w");
	}

	if (!fp)
	{
		log("SYSERR: SaveGoodsMarketsTable() - cannot open file %s.", fname);
		return;
	}

	for (mn = 0; mn < MAX_MARKET; mn++)
	{
		pMk = find_market(mn);

		for (gn = 0; gn < MAX_GOOD; gn++)
		{
			if (!(pGM = GoodsMarkets[gn][mn]))
				continue;

			if (!(pGood = get_good(gn)))
				continue;

			if (bTime)
			{
				double calc = 0;
				
				if (pGM->qty && pGM->total_tp)
					calc = (double) pGM->qty / pGM->total_tp;

				fprintf(fp,
				 "G:%ld M:%ld [C:%4.2f P:%4.2f PP:%4.2f] (Stock=%d) : Qty=[%-4ld] Price=[%-4ld] Qty/TP=[%7.2f] Comm_prod=[%7.2f] Demand=[%7.2f]\n",
				 gn, mn, pMk->var_mod.closure_var, pMk->var_mod.prod_var, pMk->var_mod.price_var,
				 (pGood->mkvnum == mn ? 1 : 0),
				 pGM->qty, pGM->price, calc, pGM->comm_prod, pGM->demand);
			}
			else
			{
				fprintf(fp,
				 "#GM %d %d %d %d %d %hd %d %d\n",
				 mn, gn,
				 (int)(pGM->demand * 100), 0, pGM->price, pGM->good_appet,
				 (int)(pGM->comm_prod * 100), (int)(pGM->comm_closure * 100) );
			}
		}
	}

	if (!bTime)
	{
		fprintf(fp, "#END\n");
		fclose(fp);
		return;
	}

	for (mn = 0; mn < MAX_MARKET; mn++)
	{
		for (gn = 0; gn < MAX_GOOD; gn++)
		{
			if (!(pGM = GoodsMarkets[gn][mn]))
				continue;

			{
				double calc = 0;
				
				if (pGM->qty && pGM->total_tp)
					calc = (double) pGM->qty / pGM->total_tp;

				fprintf(fp, "%ld;%ld;%ld;%ld;%f;%f\n",
				 mn, gn,
				 pGM->qty, pGM->price, calc, pGM->comm_prod);
			}
		}
	}

	fprintf(fp, "#END\n");
	fclose(fp);
}


void LoadGoodsMarketsTable(void)
{
	FILE *fp;
	char fname[128];
	char letter;
	char *word;
	int gn, mn;

	sprintf(fname, "%sgoodsmarkets.data", LIB_GOODS);
	if (!(fp = fopen(fname, "r")))
	{
		log("SYSERR: LoadGoodsMarketsTable() - cannot open file %s.", fname);
		reset_markets = 1;
		return;
	}

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

		if (letter != '#')
		{
			log("SYSERR: LoadGoodsMarketsTable() - # not found.");
			break;
		}

		word = fread_word(fp);

		if (!strcmp(word, "GM"))
		{
			int num;
			float calc;

			mn	= fread_number(fp);
			if (mn < 0 || mn > MAX_MARKET)
			{
				log("SYSERR: invalid market number %d.", mn);
				exit(1);
			}

			gn	= fread_number(fp);
			if (gn < 0 || gn > MAX_GOOD)
			{
				log("SYSERR: invalid good number %d.", gn);
				exit(1);
			}

			if (GoodsMarkets[gn][mn])
			{
				log("SYSERR: LoadGoodsMarketsTable() - duplicate GoodsMarkets info for G:%d M:%d.", gn, mn);
				GoodsMarkets[gn][mn]->demand		= 0.0;
				GoodsMarkets[gn][mn]->qty			= 0;
				GoodsMarkets[gn][mn]->price			= 0;
				GoodsMarkets[gn][mn]->comm_closure	= 0.0;
				GoodsMarkets[gn][mn]->comm_prod		= 0.0;
				GoodsMarkets[gn][mn]->good_appet	= 0;
			}
			else
				CREATE(GoodsMarkets[gn][mn], MARKET_GOOD, 1);

			num = fread_number(fp);
			calc = (float)num / 100;
			GoodsMarkets[gn][mn]->demand		= calc;
			GoodsMarkets[gn][mn]->qty			= fread_number(fp);
			GoodsMarkets[gn][mn]->price			= fread_number(fp);
			GoodsMarkets[gn][mn]->good_appet	= fread_number(fp);
			num = fread_number(fp);
			calc = (float)num / 100;
			GoodsMarkets[gn][mn]->comm_prod		= calc;
			num = fread_number(fp);
			calc = (float)num / 100;
			GoodsMarkets[gn][mn]->comm_closure	= calc;
		}
		else if (!strcmp( word, "END"))		// Done
			break;
		else
		{
			log("SYSERR: LoadGoodsMarketsTable() - bad section %s", word);
			continue;
		}
	}

	fclose(fp);
}


void BootMarkets(void)
{
	reset_markets	= 0;

	LoadMarkets();
	LoadGoodsMarketsTable();
}


/*
 * su "mercato x" la produzione e' "0-1 = buona, 1-2= alta, 3-4 = altissima,
 * -1,0 = bassa, -1 -2 = bassissima, -2 -4 = pessima" ed i prezzi sono "0-1 = buoni,
 * 1-2= alti, 3-4 = altissimi, -1,0 = bassi, -1 -2 = bassissimi, -2 -4 = pessimi"...
 * o qualcosa di simile...
 */

extern char *newspaper;

const char *trade_descr[] =
{
	"bad",				// -4 -2
	"low",				// -2 -1
	"poor",				// -1 0
	"normal",			// 0 1
	"good",				// 1 2
	"high"				// 2 4
};

int trade_descr_index(float val)
{
	int idx;

	if		(val < -2)		idx = 0;
	else if (val < -1)		idx = 1;
	else if (val < 0)		idx = 2;
	else if (val <= 1)		idx = 3;
	else if (val < 2)		idx = 4;
	else					idx = 5;

	return (idx);
}

void update_newspaper(void)
{
	FILE *fp;
	MARKET_DATA *pMk;
	int iprod, iprice;

	if (!(fp = fopen(NEWSPAPER_FILE, "w")))
		return;

	fprintf(fp,
		"Market                    Production               Prices\n"
		"---------------------------------------------------------------------------\n");

	for (pMk = first_market; pMk; pMk = pMk->next)
	{
		// skip unused markets
		if (!pMk->size)	continue;

		iprod	= trade_descr_index(pMk->var_mod.prod_var);
		iprice	= trade_descr_index(pMk->var_mod.price_var);

		fprintf(fp, "%-25s %-15s [%6.2f] %-15s [%6.2f]\n",
			pMk->name,
			trade_descr[iprod], pMk->var_mod.prod_var,
			trade_descr[iprice], pMk->var_mod.price_var
			);
	}

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

int file_to_string_alloc(const char *name, char **buf);

ACMD(do_newspaper)
{
	update_newspaper();
	file_to_string_alloc(NEWSPAPER_FILE, &newspaper);
}