/*
 * store1.c: store code, updating store inventory, pricing objects 
 *
 * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke 
 *
 * This software may be copied and distributed for educational, research, and
 * not for profit purposes provided that this copyright and statement are
 * included in all such copies. 
 */

#include "constant.h"
#include "config.h"
#include "types.h"
#include "externs.h"

#ifdef USG
#ifndef ATARIST_MWC
#include <string.h>
#endif
#else
#include <strings.h>
#endif

#ifndef NO_LINT_ARGS
#ifdef __STDC__
static void special_offer(inven_type *);
static void insert_store(int, int, int32, struct inven_type *);
static void store_create(int);
#else
static void special_offer();
static void insert_store();
static void store_create();
#endif
#endif

/* Returns the value for any given object		-RAK-	 */
int32 
item_value(i_ptr)
register inven_type *i_ptr;
{
    register int32      value;

    value = i_ptr->cost;
/* don't purchase known cursed items */
    if (i_ptr->ident & ID_DAMD)
	value = 0;
		/* Weapons and armor	 */
    else if (((i_ptr->tval >= TV_BOW) && (i_ptr->tval <= TV_SWORD)) ||
	     ((i_ptr->tval >= TV_BOOTS) && (i_ptr->tval <= TV_SOFT_ARMOR))) {
	if (!known2_p(i_ptr))
	    value = object_list[i_ptr->index].cost;
	else if ((i_ptr->tval >= TV_BOW) && (i_ptr->tval <= TV_SWORD)) {
	    if (i_ptr->tohit < 0)
		value = 0;
	    else if (i_ptr->todam < 0)
		value = 0;
	    else if (i_ptr->toac < 0)
		value = 0;
	    else
		value = i_ptr->cost + (i_ptr->tohit + i_ptr->todam + i_ptr->toac) * 100;
	} else {
	    if (i_ptr->toac < 0)
		value = 0;
	    else
		value = i_ptr->cost + i_ptr->toac * 100;
	}
    } else if (((i_ptr->tval >= TV_SLING_AMMO) && (i_ptr->tval <= TV_ARROW))
	       || (i_ptr->tval == TV_SPIKE)) {	/* Ammo			 */
	if (!known2_p(i_ptr))
	    value = object_list[i_ptr->index].cost;
	else {
	    if (i_ptr->tohit < 0)
		value = 0;
	    else if (i_ptr->todam < 0)
		value = 0;
	    else if (i_ptr->toac < 0)
		value = 0;
	    else

	    /* use 5, because missiles generally appear in groups of 20, so
	     * 20 * 5 == 100, which is comparable to weapon bonus above 
	     */
		value = i_ptr->cost + (i_ptr->tohit + i_ptr->todam + i_ptr->toac) * 5;
	}
				/* Potions, Scrolls, and Food */
    } else if ((i_ptr->tval == TV_SCROLL1) || (i_ptr->tval == TV_SCROLL2) ||
	       (i_ptr->tval == TV_POTION1) || (i_ptr->tval == TV_POTION2)) {
	if (!known1_p(i_ptr))
	    value = 20;
    } else if (i_ptr->tval == TV_FOOD) {
	if ((i_ptr->subval < (ITEM_SINGLE_STACK_MIN + MAX_MUSH))
	    && !known1_p(i_ptr))
	    value = 1;
				/* Rings and amulets */
    } else if ((i_ptr->tval == TV_AMULET) || (i_ptr->tval == TV_RING)) {
	/* player does not know what type of ring/amulet this is */
	if (!known1_p(i_ptr))
	    value = 45;
	else if (!known2_p(i_ptr))
	/* player knows what type of ring, but does not know whether it is
	 * cursed or not, if refuse to buy cursed objects here, then player
	 * can use this to 'identify' cursed objects 
	 */
	    value = object_list[i_ptr->index].cost;
				/* Wands and staffs */
    } else if ((i_ptr->tval == TV_STAFF) || (i_ptr->tval == TV_WAND)) {
	if (!known1_p(i_ptr)) {

	    if (i_ptr->tval == TV_WAND)
		value = 50;
	    else
		value = 70;
	} else if (known2_p(i_ptr))
	    value = i_ptr->cost + (i_ptr->cost / 20) * i_ptr->p1;
    }
				/* picks and shovels */
    else if (i_ptr->tval == TV_DIGGING) {
	if (!known2_p(i_ptr))
	    value = object_list[i_ptr->index].cost;
	else {
	    if (i_ptr->p1 < 0)
		value = 0;
	    else {

	    /* some digging tools start with non-zero p1 values, so only
	     * multiply the plusses by 100, make sure result is positive 
	     * no longer; have adjusted costs in treasure.c -CWS
	     */
		value = i_ptr->cost + i_ptr->p1;
		if (value < 0)
		    value = 0;
	    }
	}
    }
/* multiply value by number of items if it is a group stack item */
    if (i_ptr->subval > ITEM_GROUP_MIN)	/* do not include torches here */
	value = value * i_ptr->number;
    return (value);
}


/* Asking price for an item				-RAK-	 */
int32 
sell_price(snum, max_sell, min_sell, item)
    int                 snum;
    int32              *max_sell, *min_sell;
    inven_type         *item;
{
    register int32      i;
    register store_type *s_ptr;

    s_ptr = &store[snum];
    i = item_value(item);
/* check item->cost in case it is cursed, check i in case it is damaged */
    if ((item->cost > 0) && (i > 0)) {
	i = i * rgold_adj[owners[s_ptr->owner].owner_race][py.misc.prace] / 100;
	if (i < 1)
	    i = 1;
	*max_sell = i * owners[s_ptr->owner].max_inflate / 100;
	*min_sell = i * owners[s_ptr->owner].min_inflate / 100;
	if (snum == 6) {
	    (*max_sell) *= 2;
	    (*min_sell) *= 2;
	}
	if (*min_sell > *max_sell)
	    *min_sell = *max_sell;
	return (i);
    } else
    /* don't let the item get into the store inventory */
	return (0);
}


/* Check to see if he will be carrying too many objects	-RAK-	 */
int 
store_check_num(t_ptr, store_num)
    inven_type         *t_ptr;
    int                 store_num;
{
    register int        store_check, i;
    register store_type *s_ptr;
    register inven_type *i_ptr;

    store_check = FALSE;
    s_ptr = &store[store_num];
    if (s_ptr->store_ctr < STORE_INVEN_MAX)
	store_check = TRUE;
    else if (t_ptr->subval >= ITEM_SINGLE_STACK_MIN)
	for (i = 0; i < s_ptr->store_ctr; i++) {
	    i_ptr = &s_ptr->store_inven[i].sitem;

	/* note: items with subval of gte ITEM_SINGLE_STACK_MAX only stack if
	 * their subvals match 
	 */
	    if (i_ptr->tval == t_ptr->tval && i_ptr->subval == t_ptr->subval
		&& ((int)i_ptr->number + (int)t_ptr->number < 256)
		&& (t_ptr->subval < ITEM_GROUP_MIN
		    || (i_ptr->p1 == t_ptr->p1)))
		store_check = TRUE;
	}

/* But, wait.  If at home, don't let player drop 25th item, or he will lose it. -CFT */
    if (is_home && (t_ptr->subval >= ITEM_SINGLE_STACK_MIN))
	for (i = 0; i < s_ptr->store_ctr; i++) {
	    i_ptr = &s_ptr->store_inven[i].sitem;
	/*
	 * note: items with subval of gte ITEM_SINGLE_STACK_MAX only stack if
	 * their subvals match 
	 */
	    if (i_ptr->tval == t_ptr->tval && i_ptr->subval == t_ptr->subval
		&& ((int)i_ptr->number + (int)t_ptr->number > 24)
		&& (t_ptr->subval < ITEM_GROUP_MIN
		    || (i_ptr->p1 == t_ptr->p1)))
		store_check = FALSE;
	}
    return (store_check);
}


/* Insert INVEN_MAX at given location	 */
static void 
insert_store(store_num, pos, icost, i_ptr)
    register int        pos;
    int                 store_num;
    int32               icost;
    inven_type         *i_ptr;
{
    register int        i;
    register store_type *s_ptr;

    s_ptr = &store[store_num];
    for (i = s_ptr->store_ctr - 1; i >= pos; i--)
	s_ptr->store_inven[i + 1] = s_ptr->store_inven[i];
    s_ptr->store_inven[pos].sitem = *i_ptr;
    s_ptr->store_inven[pos].scost = (-icost);
    s_ptr->store_ctr++;
}


/* Add the item in INVEN_MAX to stores inventory.	-RAK-	 */
void 
store_carry(store_num, ipos, t_ptr)
    int                 store_num;
    int                *ipos;
    inven_type         *t_ptr;
{
    int                 item_num, item_val, flag;
    register int        typ, subt;
    int32               icost, dummy;
    register inven_type *i_ptr;
    register store_type *s_ptr;
    int stacked = FALSE; /* from inven_carry() -CFT */

    *ipos = -1;
    if (sell_price(store_num, &icost, &dummy, t_ptr) > 0 || is_home)
    {
	s_ptr = &store[store_num];
	item_val = 0;
	item_num = t_ptr->number;
	flag = FALSE;
	typ  = t_ptr->tval;
	subt = t_ptr->subval;
	if (subt >= ITEM_SINGLE_STACK_MIN) { /* try to stack in store's inven */
	    do {
		i_ptr = &s_ptr->store_inven[item_val].sitem;
		if (typ == i_ptr->tval)
		{
		    if (subt == i_ptr->subval && /* Adds to other item        */
			subt >= ITEM_SINGLE_STACK_MIN
			&& (subt < ITEM_GROUP_MIN || i_ptr->p1 == t_ptr->p1))
		    {
			stacked = TRUE; /* remember that we did stack it... -CFT */
			*ipos = item_val;
			i_ptr->number += item_num;
			/* must set new scost for group items, do this only for items
			   strictly greater than group_min, not for torches, this
			   must be recalculated for entire group */
			if (subt > ITEM_GROUP_MIN)
			{
			    (void) sell_price (store_num, &icost, &dummy, i_ptr);
			    s_ptr->store_inven[item_val].scost = -icost;
			}
			/* must let group objects (except torches) stack over 24
			   since there may be more than 24 in the group */
			else if (i_ptr->number > 24)
			    i_ptr->number = 24;
			flag = TRUE;
		    }
		}
		item_val ++;
	    } while (!stacked && (item_val < s_ptr->store_ctr));
	} /* if might stack... -CFT */
	if (!stacked) {		/* either never stacks, or didn't find a place to stack */
	    item_val = 0;
	    do {
		i_ptr = &s_ptr->store_inven[item_val].sitem;
		if ((typ > i_ptr->tval) || /* sort by desc tval, */
		    ((typ == i_ptr->tval) &&
		     ((t_ptr->level < i_ptr->level) || /* then by inc level, */
		      ((t_ptr->level == i_ptr->level) &&
		       (subt < i_ptr->subval))))) /* and finally by inc subval -CFT */
		{		/* Insert into list             */
		    insert_store(store_num, item_val, icost, t_ptr);
		    flag = TRUE;
		    *ipos = item_val;
		}
		item_val++;
	    } while ((item_val < s_ptr->store_ctr) && (!flag));
	} /* if didn't already stack it... */
	if (!flag)		/* Becomes last item in list    */
	{
	    insert_store(store_num, (int)s_ptr->store_ctr, icost, t_ptr);
	    *ipos = s_ptr->store_ctr - 1;
	}
    }
}


/* Destroy an item in the stores inventory.  Note that if	 */
/* "one_of" is false, an entire slot is destroyed	-RAK-	 */
void 
store_destroy(store_num, item_val, one_of)
int store_num, item_val;
int one_of;
{
    register int         j, number;
    register store_type *s_ptr;
    register inven_type *i_ptr;

    s_ptr = &store[store_num];
    i_ptr = &s_ptr->store_inven[item_val].sitem;

/* for single stackable objects, only destroy one half on average, this will
 * help ensure that general store and alchemist have reasonable selection of
 * objects 
 */
    if ((i_ptr->subval >= ITEM_SINGLE_STACK_MIN) &&
	(i_ptr->subval <= ITEM_SINGLE_STACK_MAX)) {
	if (one_of)
	    number = 1;
	else
	    number = randint((int)i_ptr->number);
    } else
	number = i_ptr->number;

    if (number != i_ptr->number)
	i_ptr->number -= number;
    else {
	for (j = item_val; j < s_ptr->store_ctr - 1; j++)
	    s_ptr->store_inven[j] = s_ptr->store_inven[j + 1];
	invcopy(&s_ptr->store_inven[s_ptr->store_ctr - 1].sitem, OBJ_NOTHING);
	s_ptr->store_inven[s_ptr->store_ctr - 1].scost = 0;
	s_ptr->store_ctr--;
    }
}


/* Initializes the stores with owners			-RAK-	 */
void 
store_init()
{
    register int         i, j, k;
    register store_type *s_ptr;

    i = MAX_OWNERS / MAX_STORES;
    for (j = 0; j < MAX_STORES; j++) {
	s_ptr = &store[j];
	s_ptr->owner = MAX_STORES * (randint(i) - 1) + j;
	s_ptr->insult_cur = 0;
	s_ptr->store_open = 0;
	s_ptr->store_ctr = 0;
	s_ptr->good_buy = 0;
	s_ptr->bad_buy = 0;
	for (k = 0; k < STORE_INVEN_MAX; k++) {
	    invcopy(&s_ptr->store_inven[k].sitem, OBJ_NOTHING);
	    s_ptr->store_inven[k].scost = 0;
	}
    }
}


/* Creates an item and inserts it into store's inven	-RAK-	 */
static void 
store_create(store_num)
int store_num;
{
    register int         i, tries;
    int                  cur_pos, dummy;
    register store_type *s_ptr;
    register inven_type *t_ptr;

    tries = 0;
    cur_pos = popt();
    s_ptr = &store[store_num];
    object_level = OBJ_TOWN_LEVEL;
    do {
	if (store_num != 6) {
	    i = store_choice[store_num][randint(STORE_CHOICES) - 1];
	    invcopy(&t_list[cur_pos], i);
	    magic_treasure(cur_pos, OBJ_TOWN_LEVEL, FALSE, TRUE);
	    t_ptr = &t_list[cur_pos];
	    if (store_check_num(t_ptr, store_num)) {
		if ((t_ptr->cost > 0) &&	/* Item must be good	 */
		    (t_ptr->cost < owners[s_ptr->owner].max_cost)) {

/* equivalent to calling ident_spell(), except will not change the object_ident array */
		    store_bought(t_ptr);
		    special_offer(t_ptr);
		    store_carry(store_num, &dummy, t_ptr);
		    tries = 10;
		}
	    }
	    tries++;
	} else {
	    i = get_obj_num(40, FALSE);
	    invcopy(&t_list[cur_pos], i);
	    magic_treasure(cur_pos, 40, FALSE, TRUE);
	    t_ptr = &t_list[cur_pos];
	    if (store_check_num(t_ptr, store_num)) {
		if (t_ptr->cost > 0) {	/* Item must be good	 */
		/*
		 * equivalent to calling ident_spell(), except will not
		 * change the object_ident array 
		 */
		    store_bought(t_ptr);
		    special_offer(t_ptr);
		    store_carry(store_num, &dummy, t_ptr);
		    tries = 10;
		}
	    }
	    tries++;
	}
    }
    while (tries <= 3);
    pusht(cur_pos);
}

static void 
special_offer(i_ptr)
inven_type *i_ptr;
{
    int32 orig_cost = i_ptr->cost;

    if (randint(30) == 1) {
	i_ptr->cost = (i_ptr->cost * 3) / 4;
	if (i_ptr->cost < 1)
	    i_ptr->cost = 1;
	if (i_ptr->cost < orig_cost)
	    inscribe(i_ptr, "25% discount");
    } else if (randint(150) == 1) {
	i_ptr->cost /= 2;
	if (i_ptr->cost < 1)
	    i_ptr->cost = 1;
	if (i_ptr->cost < orig_cost)
	    inscribe(i_ptr, "50% discount");
    } else if (randint(300) == 1) {
	i_ptr->cost /= 4;
	if (i_ptr->cost < 1)
	    i_ptr->cost = 1;
	if (i_ptr->cost < orig_cost)
	    inscribe(i_ptr, "75% discount");
    } else if (randint(500) == 1) {
	i_ptr->cost /= 10;
	if (i_ptr->cost < 1)
	    i_ptr->cost = 1;
	if (i_ptr->cost < orig_cost)
	    inscribe(i_ptr, "to clear");
    }
}

/* Initialize and up-keep the store's inventory.		-RAK-	 */
void 
store_maint()
{
    register int         i, j;
    register store_type *s_ptr;

    for (i = 0; i < (MAX_STORES - 1); i++) {
	s_ptr = &store[i];
	s_ptr->insult_cur = 0;
	if (s_ptr->store_ctr >= STORE_MIN_INVEN) {
	    j = randint(STORE_TURN_AROUND);
	    if (s_ptr->store_ctr >= STORE_MAX_INVEN)
		j += 1 + s_ptr->store_ctr - STORE_MAX_INVEN;
	    while (--j >= 0)
		store_destroy(i, randint((int)s_ptr->store_ctr) - 1, FALSE);
	}
	if (s_ptr->store_ctr <= STORE_MAX_INVEN) {
	    j = randint(STORE_TURN_AROUND);
	    if (s_ptr->store_ctr < STORE_MIN_INVEN)
		j += STORE_MIN_INVEN - s_ptr->store_ctr;
	    while (--j >= 0)
		store_create(i);
	}
    }
}

/* eliminate need to bargain if player has haggled well in the past   -DJB- */
int 
noneedtobargain(store_num, minprice)
int store_num;
int32 minprice;
{
    register int         flagnoneed;
    register store_type *s_ptr;

    if (no_haggle_flag)
	return (TRUE);

    s_ptr = &store[store_num];
    flagnoneed = ((s_ptr->good_buy == MAX_SHORT)
		  || ((s_ptr->good_buy - 3 * s_ptr->bad_buy) > (5 + (minprice/50))));
    return (flagnoneed);
}


/* update the bargin info					-DJB- */
void 
updatebargain(store_num, price, minprice)
int   store_num;
int32 price, minprice;
{
    register store_type *s_ptr;

    s_ptr = &store[store_num];
    if (minprice > 9)
	if (price == minprice) {
	    if (s_ptr->good_buy < MAX_SHORT)
		s_ptr->good_buy++;
	} else {
	    if (s_ptr->bad_buy < MAX_SHORT)
		s_ptr->bad_buy++;
	}
}
