1 /* source/store1.c: store code, updating store inventory, pricing objects
2 
3    Copyright (c) 1989-94 James E. Wilson, Robert A. Koeneke
4 
5    This software may be copied and distributed for educational, research, and
6    not for profit purposes provided that this copyright and statement are
7    included in all such copies. */
8 
9 #include "config.h"
10 #include "constant.h"
11 #include "types.h"
12 #include "externs.h"
13 
14 #ifdef USG
15 #ifndef ATARIST_MWC
16 #include <string.h>
17 #endif
18 #else
19 #include <strings.h>
20 #endif
21 
22 #if defined(LINT_ARGS)
23 static void insert_store(int, int, int32, struct inven_type *);
24 static void store_create(int);
25 #else
26 static void insert_store();
27 static void store_create();
28 #endif
29 
30 
31 /* Returns the value for any given object		-RAK-	*/
item_value(i_ptr)32 int32 item_value(i_ptr)
33 register inven_type *i_ptr;
34 {
35   register int32 value;
36 
37   value = i_ptr->cost;
38   /* don't purchase known cursed items */
39   if (i_ptr->ident & ID_DAMD)
40     value = 0;
41   else if (((i_ptr->tval >= TV_BOW) && (i_ptr->tval <= TV_SWORD)) ||
42 	   ((i_ptr->tval >= TV_BOOTS) && (i_ptr->tval <= TV_SOFT_ARMOR)))
43     {	/* Weapons and armor	*/
44       if (!known2_p(i_ptr))
45 	value = object_list[i_ptr->index].cost;
46       else if ((i_ptr->tval >= TV_BOW) && (i_ptr->tval <= TV_SWORD))
47 	{
48 	  if (i_ptr->tohit < 0)
49 	    value = 0;
50 	  else if (i_ptr->todam < 0)
51 	    value = 0;
52 	  else if (i_ptr->toac < 0)
53 	    value = 0;
54 	  else
55 	    value = i_ptr->cost+(i_ptr->tohit+i_ptr->todam+i_ptr->toac)*100;
56 	}
57       else
58 	{
59 	  if (i_ptr->toac < 0)
60 	    value = 0;
61 	  else
62 	    value = i_ptr->cost+i_ptr->toac*100;
63 	}
64     }
65   else if ((i_ptr->tval >= TV_SLING_AMMO) && (i_ptr->tval <= TV_SPIKE))
66     {	/* Ammo			*/
67       if (!known2_p(i_ptr))
68 	value = object_list[i_ptr->index].cost;
69       else
70 	{
71 	  if (i_ptr->tohit < 0)
72 	    value = 0;
73 	  else if (i_ptr->todam < 0)
74 	    value = 0;
75 	  else if (i_ptr->toac < 0)
76 	    value = 0;
77 	  else
78 	    /* use 5, because missiles generally appear in groups of 20,
79 	       so 20 * 5 == 100, which is comparable to weapon bonus above */
80 	    value = i_ptr->cost+(i_ptr->tohit+i_ptr->todam+i_ptr->toac)*5;
81 	}
82     }
83   else if ((i_ptr->tval == TV_SCROLL1) || (i_ptr->tval == TV_SCROLL2) ||
84 	   (i_ptr->tval == TV_POTION1) || (i_ptr->tval == TV_POTION2))
85     {	/* Potions, Scrolls, and Food	*/
86       if (!known1_p(i_ptr))
87 	value = 20;
88     }
89   else if (i_ptr->tval == TV_FOOD)
90     {
91       if ((i_ptr->subval < (ITEM_SINGLE_STACK_MIN + MAX_MUSH))
92 	  && !known1_p(i_ptr))
93 	value = 1;
94     }
95   else if ((i_ptr->tval == TV_AMULET) || (i_ptr->tval == TV_RING))
96     {	/* Rings and amulets	*/
97       if (!known1_p(i_ptr))
98 	/* player does not know what type of ring/amulet this is */
99 	value = 45;
100       else if (!known2_p(i_ptr))
101 	/* player knows what type of ring, but does not know whether it is
102 	   cursed or not, if refuse to buy cursed objects here, then
103 	   player can use this to 'identify' cursed objects */
104 	value = object_list[i_ptr->index].cost;
105     }
106   else if ((i_ptr->tval == TV_STAFF) || (i_ptr->tval == TV_WAND))
107     {	/* Wands and staffs*/
108       if (!known1_p(i_ptr))
109 	{
110 	  if (i_ptr->tval == TV_WAND)
111 	    value = 50;
112 	  else
113 	    value = 70;
114 	}
115       else if (known2_p(i_ptr))
116 	value = i_ptr->cost + (i_ptr->cost / 20) * i_ptr->p1;
117     }
118   /* picks and shovels */
119   else if (i_ptr->tval == TV_DIGGING)
120     {
121       if (!known2_p(i_ptr))
122 	value = object_list[i_ptr->index].cost;
123       else
124 	{
125 	  if (i_ptr->p1 < 0)
126 	    value = 0;
127 	  else
128 	    {
129 	      /* some digging tools start with non-zero p1 values, so only
130 		 multiply the plusses by 100, make sure result is positive */
131 	      value = i_ptr->cost
132 		+ (i_ptr->p1 - object_list[i_ptr->index].p1) * 100;
133 	      if (value < 0)
134 		value = 0;
135 	    }
136 	}
137     }
138   /* multiply value by number of items if it is a group stack item */
139   if (i_ptr->subval > ITEM_GROUP_MIN) /* do not include torches here */
140     value = value * i_ptr->number;
141   return(value);
142 }
143 
144 
145 /* Asking price for an item				-RAK-	*/
sell_price(snum,max_sell,min_sell,item)146 int32 sell_price(snum, max_sell, min_sell, item)
147 int snum;
148 int32 *max_sell, *min_sell;
149 inven_type *item;
150 {
151   register int32 i;
152   register store_type *s_ptr;
153 
154   s_ptr = &store[snum];
155   i = item_value(item);
156   /* check item->cost in case it is cursed, check i in case it is damaged */
157   if ((item->cost > 0) && (i > 0))
158     {
159       i = i * rgold_adj[owners[s_ptr->owner].owner_race][py.misc.prace] / 100;
160       if (i < 1)  i = 1;
161       *max_sell = i * owners[s_ptr->owner].max_inflate / 100;
162       *min_sell = i * owners[s_ptr->owner].min_inflate / 100;
163       if (*min_sell > *max_sell) *min_sell = *max_sell;
164       return(i);
165     }
166   else
167     /* don't let the item get into the store inventory */
168     return(0);
169 }
170 
171 
172 /* Check to see if he will be carrying too many objects	-RAK-	*/
store_check_num(t_ptr,store_num)173 int store_check_num(t_ptr, store_num)
174 inven_type *t_ptr;
175 int store_num;
176 {
177   register int store_check, i;
178   register store_type *s_ptr;
179   register inven_type *i_ptr;
180 
181   store_check = FALSE;
182   s_ptr = &store[store_num];
183   if (s_ptr->store_ctr < STORE_INVEN_MAX)
184     store_check = TRUE;
185   else if (t_ptr->subval >= ITEM_SINGLE_STACK_MIN)
186     for (i = 0; i < s_ptr->store_ctr; i++)
187       {
188 	i_ptr = &s_ptr->store_inven[i].sitem;
189 	/* note: items with subval of gte ITEM_SINGLE_STACK_MAX only stack
190 	   if their subvals match */
191 	if (i_ptr->tval == t_ptr->tval && i_ptr->subval == t_ptr->subval
192 	    && ((int)i_ptr->number + (int)t_ptr->number < 256)
193 	    && (t_ptr->subval < ITEM_GROUP_MIN
194 		|| (i_ptr->p1 == t_ptr->p1)))
195 	  store_check = TRUE;
196       }
197   return(store_check);
198 }
199 
200 
201 /* Insert INVEN_MAX at given location	*/
insert_store(store_num,pos,icost,i_ptr)202 static void insert_store(store_num, pos, icost, i_ptr)
203 register int pos;
204 int store_num;
205 int32 icost;
206 inven_type *i_ptr;
207 {
208   register int i;
209   register store_type *s_ptr;
210 
211   s_ptr = &store[store_num];
212   for (i = s_ptr->store_ctr-1; i >= pos; i--)
213     s_ptr->store_inven[i+1] = s_ptr->store_inven[i];
214   s_ptr->store_inven[pos].sitem = *i_ptr;
215   s_ptr->store_inven[pos].scost = -icost;
216   s_ptr->store_ctr++;
217 }
218 
219 
220 /* Add the item in INVEN_MAX to stores inventory.	-RAK-	*/
store_carry(store_num,ipos,t_ptr)221 void store_carry(store_num, ipos, t_ptr)
222 int store_num;
223 int *ipos;
224 inven_type *t_ptr;
225 {
226   int item_num, item_val, flag;
227   register int typ, subt;
228   int32 icost, dummy;
229   register inven_type *i_ptr;
230   register store_type *s_ptr;
231 
232   *ipos = -1;
233   if (sell_price(store_num, &icost, &dummy, t_ptr) > 0)
234     {
235       s_ptr = &store[store_num];
236       item_val = 0;
237       item_num = t_ptr->number;
238       flag = FALSE;
239       typ  = t_ptr->tval;
240       subt = t_ptr->subval;
241       do
242 	{
243 	  i_ptr = &s_ptr->store_inven[item_val].sitem;
244 	  if (typ == i_ptr->tval)
245 	    {
246 	      if (subt == i_ptr->subval && /* Adds to other item	*/
247 		  subt >= ITEM_SINGLE_STACK_MIN
248 		  && (subt < ITEM_GROUP_MIN || i_ptr->p1 == t_ptr->p1))
249 		{
250 		  *ipos = item_val;
251 		  i_ptr->number += item_num;
252 		  /* must set new scost for group items, do this only for items
253 		     strictly greater than group_min, not for torches, this
254 		     must be recalculated for entire group */
255 		  if (subt > ITEM_GROUP_MIN)
256 		    {
257 		      (void) sell_price (store_num, &icost, &dummy, i_ptr);
258 		      s_ptr->store_inven[item_val].scost = -icost;
259 		    }
260 		  /* must let group objects (except torches) stack over 24
261 		     since there may be more than 24 in the group */
262 		  else if (i_ptr->number > 24)
263 		    i_ptr->number = 24;
264 		  flag = TRUE;
265 		}
266 	    }
267 	  else if (typ > i_ptr->tval)
268 	    {		/* Insert into list		*/
269 	      insert_store(store_num, item_val, icost, t_ptr);
270 	      flag = TRUE;
271 	      *ipos = item_val;
272 	    }
273 	  item_val++;
274 	}
275       while ((item_val < s_ptr->store_ctr) && (!flag));
276       if (!flag)	/* Becomes last item in list	*/
277 	{
278 	  insert_store(store_num, (int)s_ptr->store_ctr, icost, t_ptr);
279 	  *ipos = s_ptr->store_ctr - 1;
280 	}
281     }
282 }
283 
284 /* Destroy an item in the stores inventory.  Note that if	*/
285 /* "one_of" is false, an entire slot is destroyed	-RAK-	*/
store_destroy(store_num,item_val,one_of)286 void store_destroy(store_num, item_val, one_of)
287 int store_num, item_val;
288 int one_of;
289 {
290   register int j, number;
291   register store_type *s_ptr;
292   register inven_type *i_ptr;
293 
294   s_ptr = &store[store_num];
295   i_ptr = &s_ptr->store_inven[item_val].sitem;
296 
297   /* for single stackable objects, only destroy one half on average,
298      this will help ensure that general store and alchemist have
299      reasonable selection of objects */
300   if ((i_ptr->subval >= ITEM_SINGLE_STACK_MIN) &&
301       (i_ptr->subval <= ITEM_SINGLE_STACK_MAX))
302     {
303       if (one_of)
304 	number = 1;
305       else
306 	number = randint((int)i_ptr->number);
307     }
308   else
309     number = i_ptr->number;
310 
311   if (number != i_ptr->number)
312     i_ptr->number -= number;
313   else
314     {
315       for (j = item_val; j < s_ptr->store_ctr-1; j++)
316 	s_ptr->store_inven[j] = s_ptr->store_inven[j+1];
317       invcopy(&s_ptr->store_inven[s_ptr->store_ctr-1].sitem, OBJ_NOTHING);
318       s_ptr->store_inven[s_ptr->store_ctr-1].scost = 0;
319       s_ptr->store_ctr--;
320     }
321 }
322 
323 
324 /* Initializes the stores with owners			-RAK-	*/
store_init()325 void store_init()
326 {
327   register int i, j, k;
328   register store_type *s_ptr;
329 
330   i = MAX_OWNERS / MAX_STORES;
331   for (j = 0; j < MAX_STORES; j++)
332     {
333       s_ptr = &store[j];
334       s_ptr->owner = MAX_STORES*(randint(i)-1) + j;
335       s_ptr->insult_cur = 0;
336       s_ptr->store_open = 0;
337       s_ptr->store_ctr	= 0;
338       s_ptr->good_buy = 0;
339       s_ptr->bad_buy = 0;
340       for (k = 0; k < STORE_INVEN_MAX; k++)
341 	{
342 	  invcopy(&s_ptr->store_inven[k].sitem, OBJ_NOTHING);
343 	  s_ptr->store_inven[k].scost = 0;
344 	}
345     }
346 }
347 
348 
349 /* Creates an item and inserts it into store's inven	-RAK-	*/
store_create(store_num)350 static void store_create(store_num)
351 int store_num;
352 {
353   register int i, tries;
354   int cur_pos, dummy;
355   register store_type *s_ptr;
356   register inven_type *t_ptr;
357 
358   tries = 0;
359   cur_pos = popt();
360   s_ptr = &store[store_num];
361   do
362     {
363       i = store_choice[store_num][randint(STORE_CHOICES)-1];
364       invcopy(&t_list[cur_pos], i);
365       magic_treasure(cur_pos, OBJ_TOWN_LEVEL);
366       t_ptr = &t_list[cur_pos];
367       if (store_check_num(t_ptr, store_num))
368 	{
369 	  if ((t_ptr->cost > 0) &&	/* Item must be good	*/
370 	      (t_ptr->cost < owners[s_ptr->owner].max_cost))
371 	    {
372 	      /* equivalent to calling ident_spell(), except will not
373 		 change the object_ident array */
374 	      store_bought(t_ptr);
375 	      store_carry(store_num, &dummy, t_ptr);
376 	      tries = 10;
377 	    }
378 	}
379       tries++;
380     }
381   while (tries <= 3);
382   pusht((int8u)cur_pos);
383 }
384 
385 
386 /* Initialize and up-keep the store's inventory.		-RAK-	*/
store_maint()387 void store_maint()
388 {
389   register int i, j;
390   register store_type *s_ptr;
391 
392   for (i = 0; i < MAX_STORES; i++)
393     {
394       s_ptr = &store[i];
395       s_ptr->insult_cur = 0;
396       if (s_ptr->store_ctr >= STORE_MIN_INVEN)
397 	{
398 	  j = randint(STORE_TURN_AROUND);
399 	  if (s_ptr->store_ctr >= STORE_MAX_INVEN)
400 	    j += 1 + s_ptr->store_ctr - STORE_MAX_INVEN;
401 	  while (--j >= 0)
402 	    store_destroy(i, randint((int)s_ptr->store_ctr)-1, FALSE);
403 	}
404 
405       if (s_ptr->store_ctr <= STORE_MAX_INVEN)
406 	{
407 	  j = randint(STORE_TURN_AROUND);
408 	  if (s_ptr->store_ctr < STORE_MIN_INVEN)
409 	    j += STORE_MIN_INVEN - s_ptr->store_ctr;
410 	  while (--j >= 0)
411 	    store_create(i);
412 	}
413     }
414 }
415 
416 /* eliminate need to bargain if player has haggled well in the past   -DJB- */
noneedtobargain(store_num,minprice)417 int noneedtobargain(store_num, minprice)
418 int store_num;
419 int32 minprice;
420 {
421   register int flagnoneed;
422   int bargain_record;
423   register store_type *s_ptr;
424 
425   s_ptr = &store[store_num];
426   if (s_ptr->good_buy == MAX_SHORT)
427     return TRUE;
428   bargain_record = (s_ptr->good_buy - 3 * s_ptr->bad_buy - 5);
429   flagnoneed = ((bargain_record > 0)
430 		&& ((long)bargain_record * (long)bargain_record
431 		    > minprice/50));
432   return (flagnoneed);
433 }
434 
435 
436 /* update the bargin info					-DJB- */
updatebargain(store_num,price,minprice)437 void updatebargain(store_num, price, minprice)
438 int store_num;
439 int32 price, minprice;
440 {
441   register store_type *s_ptr;
442 
443   s_ptr = &store[store_num];
444   if (minprice > 9)
445     if (price == minprice)
446       {
447 	if (s_ptr->good_buy < MAX_SHORT)
448 	  s_ptr->good_buy++;
449       }
450     else
451       {
452 	if (s_ptr->bad_buy < MAX_SHORT)
453 	  s_ptr->bad_buy++;
454       }
455 }
456