1 | /* |
1 | /* |
2 | * static char *rcsid_shop_c = |
2 | * static char *rcsid_shop_c = |
3 | * "$Id: shop.c,v 1.4 2006/05/24 01:59:09 pippijn Exp $"; |
3 | * "$Id: shop.c,v 1.13 2006/07/05 21:02:50 root Exp $"; |
4 | */ |
4 | */ |
5 | |
5 | |
6 | /* |
6 | /* |
7 | CrossFire, A Multiplayer game for X-windows |
7 | CrossFire, A Multiplayer game for X-windows |
8 | |
8 | |
… | |
… | |
42 | * therefore making this number higher, makes specialisation less effective. |
42 | * therefore making this number higher, makes specialisation less effective. |
43 | * setting this value above 1 or to a negative value would have interesting, |
43 | * setting this value above 1 or to a negative value would have interesting, |
44 | * (though not useful) effects. |
44 | * (though not useful) effects. |
45 | */ |
45 | */ |
46 | #define SPECIALISATION_EFFECT 0.5 |
46 | #define SPECIALISATION_EFFECT 0.5 |
47 | |
|
|
48 | /* price a shopkeeper will give to someone they disapprove of.*/ |
|
|
49 | #define DISAPPROVAL_RATIO 0.2 |
|
|
50 | |
47 | |
51 | /* price a shopkeeper will give someone they neither like nor dislike */ |
48 | /* price a shopkeeper will give someone they neither like nor dislike */ |
52 | #define NEUTRAL_RATIO 0.8 |
49 | #define NEUTRAL_RATIO 0.8 |
53 | |
50 | |
54 | static uint64 pay_from_container(object *pl, object *pouch, uint64 to_pay); |
51 | static uint64 pay_from_container(object *pl, object *pouch, uint64 to_pay); |
… | |
… | |
81 | * divisions took place, the value got rounded to 0 (Being an int), and |
78 | * divisions took place, the value got rounded to 0 (Being an int), and |
82 | * thus remained 0. |
79 | * thus remained 0. |
83 | * |
80 | * |
84 | * Mark Wedel (mwedel@pyramid.com) |
81 | * Mark Wedel (mwedel@pyramid.com) |
85 | */ |
82 | */ |
|
|
83 | |
|
|
84 | static uint64 approx_range; |
|
|
85 | |
86 | uint64 query_cost(const object *tmp, object *who, int flag) { |
86 | uint64 query_cost(const object *tmp, object *who, int flag) { |
87 | uint64 val; |
87 | uint64 val; |
88 | int number; /* used to better calculate value */ |
88 | int number; /* used to better calculate value */ |
89 | int no_bargain; |
89 | int no_bargain; |
90 | int identified; |
90 | int identified; |
91 | int not_cursed; |
91 | int not_cursed; |
92 | int approximate; |
92 | int approximate; |
93 | int shop; |
93 | int shop; |
94 | float diff; |
94 | double diff; |
95 | float ratio; |
95 | |
|
|
96 | approx_range = 0; |
96 | |
97 | |
97 | no_bargain = flag & F_NO_BARGAIN; |
98 | no_bargain = flag & F_NO_BARGAIN; |
98 | identified = flag & F_IDENTIFIED; |
99 | identified = flag & F_IDENTIFIED; |
99 | not_cursed = flag & F_NOT_CURSED; |
100 | not_cursed = flag & F_NOT_CURSED; |
100 | approximate = flag & F_APPROX; |
101 | approximate = flag & F_APPROX; |
… | |
… | |
179 | else /* if not identified, presume one charge */ |
180 | else /* if not identified, presume one charge */ |
180 | val/=50; |
181 | val/=50; |
181 | } |
182 | } |
182 | |
183 | |
183 | /* Limit amount of money you can get for really great items. */ |
184 | /* Limit amount of money you can get for really great items. */ |
184 | if (flag==F_TRUE || flag==F_SELL) |
185 | if (flag==F_SELL) |
185 | val=value_limit(val, number, who, shop); |
186 | val=value_limit(val, number, who, shop); |
186 | |
187 | |
187 | /* we need to multiply these by 4.0 to keep buy costs roughly the same |
188 | // use a nonlinear price adjustment. as my predecessor said, don't change |
188 | * (otherwise, you could buy a potion of charisma for around 400 pp. |
189 | // the archetypes, its work required for balancing, and we don't care. |
189 | * Arguable, the costs in the archetypes should be updated to better |
190 | val = pow (val, 1.2); |
190 | * reflect values (potion charisma list for 1250 gold) |
|
|
191 | */ |
|
|
192 | val *= 4; |
|
|
193 | |
191 | |
194 | /* This modification is for bargaining skill. |
192 | /* This modification is for bargaining skill. |
195 | * Now only players with max level in bargaining |
193 | * Now only players with max level in bargaining |
196 | * AND Cha = 30 will get optimal price. |
194 | * AND Cha = 30 will get optimal price. |
197 | * Thus charisma will never get useless. |
195 | * Thus charisma will never get useless. |
… | |
… | |
203 | int lev_identify = 0; |
201 | int lev_identify = 0; |
204 | int idskill1=0; |
202 | int idskill1=0; |
205 | int idskill2=0; |
203 | int idskill2=0; |
206 | const typedata *tmptype; |
204 | const typedata *tmptype; |
207 | |
205 | |
208 | /* ratio determines how much of the price modification |
|
|
209 | * will come from the basic stat charisma |
|
|
210 | * the rest will come from the level in bargaining skill |
|
|
211 | */ |
|
|
212 | ratio = 0.5; |
|
|
213 | tmptype=get_typedata(tmp->type); |
206 | tmptype=get_typedata(tmp->type); |
214 | |
207 | |
215 | if (find_skill_by_number(who,SK_BARGAINING)) { |
208 | if (find_skill_by_number(who,SK_BARGAINING)) { |
216 | lev_bargain = find_skill_by_number(who,SK_BARGAINING)->level; |
209 | lev_bargain = find_skill_by_number(who,SK_BARGAINING)->level; |
217 | } |
210 | } |
… | |
… | |
226 | lev_identify += find_skill_by_number(who,idskill2)->level; |
219 | lev_identify += find_skill_by_number(who,idskill2)->level; |
227 | } |
220 | } |
228 | } |
221 | } |
229 | } |
222 | } |
230 | else LOG(llevError, "Query_cost: item %s hasn't got a valid type\n", tmp->name); |
223 | else LOG(llevError, "Query_cost: item %s hasn't got a valid type\n", tmp->name); |
231 | if ( !no_bargain && (lev_bargain>0) ) |
224 | |
232 | diff = (0.8 - 0.6*((lev_bargain+settings.max_level*0.05) |
225 | /* ratio determines how much of the price modification |
233 | /(settings.max_level*1.05))); |
226 | * will come from the basic stat charisma |
234 | else |
227 | * the rest will come from the level in bargaining skill |
235 | diff = 0.8; |
228 | */ |
|
|
229 | const double cha_ratio = 0.45; |
|
|
230 | |
|
|
231 | diff = no_bargain ? 1.0 : 1. - pow (lev_bargain / (double)settings.max_level, 0.25); |
236 | |
232 | |
237 | diff *= 1-ratio; |
233 | diff = (1. - cha_ratio) * diff |
|
|
234 | + cha_ratio * (cha_bonus[who->stats.Cha] - 1.) / (cha_bonus[who->stats.Cha] + 1.); |
|
|
235 | |
|
|
236 | diff = .02 + (0.80. - .02) * diff; |
238 | |
237 | |
239 | /* Diff is now a float between 0.2 and 0.8 */ |
238 | if (flag == F_BUY) val += val * diff; |
240 | diff+=(cha_bonus[who->stats.Cha]-1)/(1+cha_bonus[who->stats.Cha])*ratio; |
239 | else if (flag == F_SELL) val -= val * diff; |
241 | |
240 | |
242 | if(flag==F_BUY) |
241 | // now find a price range. the less good we can judge, the larger the range is |
243 | val=(val*(long)(1000*(1+diff)))/1000; |
242 | // then the range is adjusted randomly around the correct value |
244 | else if (flag==F_SELL) |
|
|
245 | val=(val*(long)(1000*(1-diff)))/1000; |
|
|
246 | |
|
|
247 | /* If we are approximating, then the value returned should be allowed to be wrong |
|
|
248 | * however merely using a random number each time will not be sufficiant, as then |
|
|
249 | * multiple examinations would give different answers, so we'll use the count |
|
|
250 | * instead. By taking the sine of the count, a value between -1 and 1 is |
|
|
251 | * generated, we then divide by the square root of the bargaining skill and the |
|
|
252 | * appropriate identification skills, so that higher level players get better estimates. |
|
|
253 | * (we need a +1 there in case we would otherwise be dividing by zero. |
|
|
254 | */ |
|
|
255 | if (approximate) |
243 | if (approximate) |
256 | val = (sint64)val + (sint64)((sint64)val*(sin(tmp->count)/sqrt(lev_bargain+lev_identify*2+1.0))); |
244 | approx_range = val / sqrt (lev_identify * 3 + 1); |
257 | } |
245 | } |
258 | |
246 | |
259 | /* I don't think this should really happen - if it does, it indicates and |
247 | /* I don't think this should really happen - if it does, it indicates and |
260 | * overflow of diff above. That shoudl only happen if |
248 | * overflow of diff above. That shoudl only happen if |
261 | * we are selling objects - in that case, the person just |
249 | * we are selling objects - in that case, the person just |
… | |
… | |
331 | * have so much money that they have more than 2 billion platinum |
319 | * have so much money that they have more than 2 billion platinum |
332 | * coins, there are certainly issues - the easiest fix at that |
320 | * coins, there are certainly issues - the easiest fix at that |
333 | * time is to add a higher denomination (mithril piece with |
321 | * time is to add a higher denomination (mithril piece with |
334 | * 10,000 silver or something) |
322 | * 10,000 silver or something) |
335 | */ |
323 | */ |
336 | static const char *cost_string_from_value(uint64 cost) |
324 | static const char *cost_string_from_value(uint64 cost, int approx) |
337 | { |
325 | { |
338 | static char buf[MAX_BUF]; |
326 | static char buf[MAX_BUF]; |
339 | archetype *coin, *next_coin; |
327 | archetype *coin, *next_coin; |
340 | char *endbuf; |
|
|
341 | int num, cointype = 0; |
328 | int num, cointype = 0; |
342 | |
329 | |
343 | coin = find_next_coin(cost, &cointype); |
330 | coin = find_next_coin(cost, &cointype); |
344 | if (coin == NULL) |
331 | if (coin == NULL) |
345 | return "nothing"; |
332 | return "nothing"; |
… | |
… | |
353 | strcpy(buf,"an unimaginable sum of money."); |
340 | strcpy(buf,"an unimaginable sum of money."); |
354 | return buf; |
341 | return buf; |
355 | } |
342 | } |
356 | |
343 | |
357 | cost -= (uint64)num * (uint64)coin->clone.value; |
344 | cost -= (uint64)num * (uint64)coin->clone.value; |
358 | if (num == 1) |
345 | sprintf(buf, "%d %s", num, num > 1 ? coin->clone.name_pl : coin->clone.name); |
359 | sprintf(buf, "1 %s", coin->clone.name); |
|
|
360 | else |
|
|
361 | sprintf(buf, "%d %s", num, coin->clone.name_pl); |
|
|
362 | |
346 | |
363 | next_coin = find_next_coin(cost, &cointype); |
347 | next_coin = find_next_coin(cost, &cointype); |
364 | if (next_coin == NULL) |
348 | if (next_coin == NULL || approx) |
365 | return buf; |
349 | return buf; |
366 | |
350 | |
367 | do { |
|
|
368 | endbuf = buf + strlen(buf); |
|
|
369 | |
|
|
370 | coin = next_coin; |
351 | coin = next_coin; |
371 | num = cost / coin->clone.value; |
352 | num = cost / coin->clone.value; |
372 | cost -= (uint64)num * (uint64)coin->clone.value; |
353 | cost -= (uint64)num * (uint64)coin->clone.value; |
373 | |
354 | |
374 | if (cost == 0) |
355 | sprintf (buf + strlen (buf), " and %d %s", num, num > 1 ? coin->clone.name_pl : coin->clone.name); |
375 | next_coin = NULL; |
356 | |
376 | else |
|
|
377 | next_coin = find_next_coin(cost, &cointype); |
|
|
378 | |
|
|
379 | if (next_coin) { |
|
|
380 | /* There will be at least one more string to add to the list, |
|
|
381 | * use a comma. |
|
|
382 | */ |
|
|
383 | strcat(endbuf, ", "); endbuf += 2; |
|
|
384 | } else { |
|
|
385 | strcat(endbuf, " and "); endbuf += 5; |
|
|
386 | } |
|
|
387 | if (num == 1) |
|
|
388 | sprintf(endbuf, "1 %s", coin->clone.name); |
|
|
389 | else |
|
|
390 | sprintf(endbuf, "%d %ss", num, coin->clone.name); |
|
|
391 | } while (next_coin); |
|
|
392 | |
|
|
393 | return buf; |
357 | return buf; |
394 | } |
358 | } |
395 | |
359 | |
396 | const char *query_cost_string(const object *tmp,object *who,int flag) { |
360 | const char *query_cost_string(const object *tmp,object *who,int flag) { |
397 | uint64 real_value = query_cost(tmp,who,flag); |
361 | uint64 real_value = query_cost(tmp,who,flag); |
… | |
… | |
437 | sprintf(buf, "a vast quantity of %s", coin->clone.name_pl); |
401 | sprintf(buf, "a vast quantity of %s", coin->clone.name_pl); |
438 | return buf; |
402 | return buf; |
439 | } |
403 | } |
440 | } |
404 | } |
441 | } |
405 | } |
|
|
406 | |
|
|
407 | int hash = ((unsigned int)tmp->count * 174364621) & 1023; |
|
|
408 | |
|
|
409 | if (approx_range) |
|
|
410 | { |
|
|
411 | uint64 lo = (sint64)real_value - (approx_range * hash >> 10); |
|
|
412 | static char buf[MAX_BUF]; |
|
|
413 | |
|
|
414 | sprintf (buf, "between %s", cost_string_from_value (lo, 1)); |
|
|
415 | sprintf (buf + strlen (buf), " and %s", cost_string_from_value (lo + approx_range, 1)); |
|
|
416 | |
|
|
417 | return buf; |
|
|
418 | } |
442 | } |
419 | } |
|
|
420 | |
443 | return cost_string_from_value(real_value); |
421 | return cost_string_from_value (real_value, 0); |
444 | } |
422 | } |
445 | |
423 | |
446 | /* This function finds out how much money the player is carrying, |
424 | /* This function finds out how much money the player is carrying, |
447 | * including what is in containers. |
425 | * including what is in containers. |
448 | */ |
426 | */ |
… | |
… | |
669 | } |
647 | } |
670 | if (unpaid_price > player_wealth) { |
648 | if (unpaid_price > player_wealth) { |
671 | char buf[MAX_BUF], coinbuf[MAX_BUF]; |
649 | char buf[MAX_BUF], coinbuf[MAX_BUF]; |
672 | int denominations = 0; |
650 | int denominations = 0; |
673 | int has_coins = NUM_COINS; |
651 | int has_coins = NUM_COINS; |
674 | sprintf(buf, "You have %d unpaid items that would cost you %s, but you", |
652 | char cost[MAX_BUF]; |
|
|
653 | char missing[MAX_BUF]; |
|
|
654 | |
675 | unpaid_count, cost_string_from_value(unpaid_price)); |
655 | sprintf(cost, "%s", cost_string_from_value (unpaid_price, 0)); |
676 | for (i=0; i< NUM_COINS; i++) { |
656 | sprintf(missing, "%s", cost_string_from_value (unpaid_price - player_wealth, 0)); |
677 | if (coincount[i] == 0) { |
657 | |
678 | has_coins--; |
658 | sprintf(buf, "You have %d unpaid items that would cost you %s. You need another %s to be able to afford that.", |
679 | } |
659 | unpaid_count, cost, missing); |
680 | } |
|
|
681 | if (has_coins == 0) { |
|
|
682 | strcat (buf, " have nothing to spend."); |
|
|
683 | new_draw_info(NDI_UNIQUE, 0, pl, buf); |
|
|
684 | return 0; |
|
|
685 | } else { |
|
|
686 | strcat (buf, " only have"); |
|
|
687 | } |
|
|
688 | for (i=0; i< NUM_COINS; i++) { |
|
|
689 | if (coincount[i] > 0 && coins[i]) { |
|
|
690 | denominations++; |
|
|
691 | if (coincount[i+1] == 0 || !coins[i+1]) { |
|
|
692 | sprintf(coinbuf, " %d %s.", coincount[i], find_archetype(coins[i])->clone.name_pl); |
|
|
693 | } else { |
|
|
694 | sprintf(coinbuf, " %d %s,", coincount[i], find_archetype(coins[i])->clone.name_pl); |
|
|
695 | } |
|
|
696 | strcat (buf, coinbuf); |
|
|
697 | } |
|
|
698 | } |
|
|
699 | if (denominations > 1) make_list_like(buf); |
|
|
700 | new_draw_info(NDI_UNIQUE, 0, pl, buf); |
660 | new_draw_info(NDI_UNIQUE, 0, pl, buf); |
701 | return 0; |
661 | return 0; |
702 | } |
662 | } |
703 | else return 1; |
663 | else return 1; |
704 | } |
664 | } |
… | |
… | |
734 | buf[MAX_BUF-1] = '\0'; |
694 | buf[MAX_BUF-1] = '\0'; |
735 | if(!pay_for_item(op,pl)) { |
695 | if(!pay_for_item(op,pl)) { |
736 | uint64 i=query_cost(op,pl,F_BUY | F_SHOP) - query_money(pl); |
696 | uint64 i=query_cost(op,pl,F_BUY | F_SHOP) - query_money(pl); |
737 | CLEAR_FLAG(op, FLAG_UNPAID); |
697 | CLEAR_FLAG(op, FLAG_UNPAID); |
738 | new_draw_info_format(NDI_UNIQUE, 0, pl, |
698 | new_draw_info_format(NDI_UNIQUE, 0, pl, |
739 | "You lack %s to buy %s.", cost_string_from_value(i), |
699 | "You lack %s to buy %s.", cost_string_from_value (i, 0), |
740 | query_name(op)); |
700 | query_name(op)); |
741 | SET_FLAG(op, FLAG_UNPAID); |
701 | SET_FLAG(op, FLAG_UNPAID); |
742 | return 0; |
702 | return 0; |
743 | } else { |
703 | } else { |
744 | object *tmp; |
704 | object *tmp; |
… | |
… | |
932 | static uint64 value_limit(uint64 val, int quantity, const object *who, int isshop) { |
892 | static uint64 value_limit(uint64 val, int quantity, const object *who, int isshop) { |
933 | uint64 newval, unit_price; |
893 | uint64 newval, unit_price; |
934 | mapstruct *map; |
894 | mapstruct *map; |
935 | unit_price=val/quantity; |
895 | unit_price=val/quantity; |
936 | if (!isshop || !who) { |
896 | if (!isshop || !who) { |
937 | if (unit_price > 10000) |
897 | if (unit_price > 250000) |
938 | newval=8000+isqrt(unit_price)*20; |
898 | newval = 250000. - pow (250000., .75) * 65. + pow (unit_price, .75) * 65.; |
939 | else |
899 | else |
940 | newval=unit_price; |
900 | newval=unit_price; |
941 | } else { |
901 | } else { |
942 | if (!who->map) { |
902 | if (!who->map) { |
943 | LOG(llevError, "value_limit: asked shop price for ob %s on NULL map\n", who->name); |
903 | LOG(llevError, "value_limit: asked shop price for ob %s on NULL map\n", who->name); |
… | |
… | |
945 | } |
905 | } |
946 | map=who->map; |
906 | map=who->map; |
947 | if (map->shopmin && unit_price < map->shopmin) return 0; |
907 | if (map->shopmin && unit_price < map->shopmin) return 0; |
948 | else if (map->shopmax && unit_price > map->shopmax/2) |
908 | else if (map->shopmax && unit_price > map->shopmax/2) |
949 | newval=MIN((map->shopmax/2)+isqrt(unit_price-map->shopmax/2), map->shopmax); |
909 | newval=MIN((map->shopmax/2)+isqrt(unit_price-map->shopmax/2), map->shopmax); |
950 | else if (unit_price>10000) |
910 | else if (unit_price > 250000) |
951 | newval=8000+isqrt(unit_price)*20; |
911 | newval = 250000. - pow (250000., .75) * 65. + pow (unit_price, .75) * 65.; |
952 | else |
912 | else |
953 | newval=unit_price; |
913 | newval=unit_price; |
954 | } |
914 | } |
955 | newval *= quantity; |
915 | newval *= quantity; |
956 | return newval; |
916 | return newval; |
… | |
… | |
982 | make_list_like(tmp); |
942 | make_list_like(tmp); |
983 | new_draw_info_format(NDI_UNIQUE, 0, op, "%s", tmp); |
943 | new_draw_info_format(NDI_UNIQUE, 0, op, "%s", tmp); |
984 | |
944 | |
985 | if (map->shopmax) |
945 | if (map->shopmax) |
986 | new_draw_info_format(NDI_UNIQUE,0,op,"It won't trade for items above %s.", |
946 | new_draw_info_format(NDI_UNIQUE,0,op,"It won't trade for items above %s.", |
987 | cost_string_from_value(map->shopmax)); |
947 | cost_string_from_value(map->shopmax, 0)); |
988 | if (map->shopmin) |
948 | if (map->shopmin) |
989 | new_draw_info_format(NDI_UNIQUE,0,op,"It won't trade in items worth less than %s.", |
949 | new_draw_info_format(NDI_UNIQUE,0,op,"It won't trade in items worth less than %s.", |
990 | cost_string_from_value(map->shopmin)); |
950 | cost_string_from_value(map->shopmin, 0)); |
991 | if (map->shopgreed) { |
951 | if (map->shopgreed) { |
992 | if (map->shopgreed >2.0) |
952 | if (map->shopgreed >2.0) |
993 | new_draw_info(NDI_UNIQUE,0,op,"It tends to overcharge massively."); |
953 | new_draw_info(NDI_UNIQUE,0,op,"It tends to overcharge massively."); |
994 | else if (map->shopgreed >1.5) |
954 | else if (map->shopgreed >1.5) |
995 | new_draw_info(NDI_UNIQUE,0,op,"It tends to overcharge substantially."); |
955 | new_draw_info(NDI_UNIQUE,0,op,"It tends to overcharge substantially."); |