00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00036 #include <assert.h>
00037 #include <global.h>
00038 #include <spells.h>
00039 #include <skills.h>
00040 #include <living.h>
00041 #include <newclient.h>
00042 #ifndef __CEXTRACT__
00043 #include <sproto.h>
00044 #endif
00045 #include <math.h>
00046
00055 #define SPECIALISATION_EFFECT 0.5
00056
00058 #define DISAPPROVAL_RATIO 0.2
00059
00061 #define NEUTRAL_RATIO 0.8
00062
00063 static uint64 pay_from_container(object *pl, object *pouch, uint64 to_pay);
00064 static uint64 value_limit(uint64 val, int quantity, const object *who, int isshop);
00065 static double shop_specialisation_ratio(const object *item, const mapstruct *map);
00066 static double shop_greed(const mapstruct *map);
00067
00068 #define NUM_COINS 5
00070 #define LARGEST_COIN_GIVEN 2
00073 static const char *const coins[] = {
00074 "ambercoin",
00075 "jadecoin",
00076 "platinacoin",
00077 "goldcoin",
00078 "silvercoin",
00079 NULL
00080 };
00081
00121 uint64 query_cost(const object *tmp, object *who, int flag) {
00122 uint64 val;
00123 int number;
00124 int no_bargain;
00125 int identified;
00126 int not_cursed;
00127 int approximate;
00128 int shop;
00129 float diff;
00130 float ratio;
00131 const char *key;
00132
00133 no_bargain = flag&F_NO_BARGAIN;
00134 identified = flag&F_IDENTIFIED;
00135 not_cursed = flag&F_NOT_CURSED;
00136 approximate = flag&F_APPROX;
00137 shop = flag&F_SHOP;
00138 flag &= ~(F_NO_BARGAIN|F_IDENTIFIED|F_NOT_CURSED|F_APPROX|F_SHOP);
00139
00140 number = tmp->nrof;
00141 if (number == 0)
00142 number = 1;
00143
00144 if ((key = get_ob_key_value(tmp, "price_adjustment")) != NULL) {
00145 ratio = atof(key);
00146 return tmp->value*number*ratio;
00147 }
00148 if ((flag == F_BUY) && ((key = get_ob_key_value(tmp, "price_adjustment_buy")) != NULL)) {
00149 ratio = atof(key);
00150 return tmp->value*number*ratio;
00151 }
00152 if ((flag == F_SELL) && ((key = get_ob_key_value(tmp, "price_adjustment_sell")) != NULL)) {
00153 ratio = atof(key);
00154 return tmp->value*number*ratio;
00155 }
00156
00157 if (tmp->type == MONEY)
00158 return (tmp->nrof*tmp->value);
00159 if (tmp->type == GEM) {
00160 if (flag == F_TRUE)
00161 return number*tmp->value;
00162 if (flag == F_BUY)
00163 return (1.03*tmp->nrof*tmp->value);
00164 if (flag == F_SELL)
00165 return (0.97*tmp->nrof*tmp->value);
00166 LOG(llevError, "Query_cost: Gem type with unknown flag : %d\n", flag);
00167 return 0;
00168 }
00169 if (QUERY_FLAG(tmp, FLAG_IDENTIFIED)
00170 || !need_identify(tmp)
00171 || identified) {
00172 if (!not_cursed
00173 && (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)))
00174 return 0;
00175 else
00176 val = tmp->value*number;
00177
00178 } else {
00179 if (tmp->arch != NULL) {
00180 if (flag == F_BUY) {
00181 LOG(llevError, "Asking for buy-value of unidentified object.\n");
00182 val = tmp->arch->clone.value*50*number;
00183 } else {
00184 if (tmp->type == POTION)
00185 val = number*1000;
00186 else {
00187
00188
00189
00190 if (QUERY_FLAG(tmp, FLAG_BEEN_APPLIED))
00191 val = number*tmp->arch->clone.value*2/3;
00192 else
00193 val = number*tmp->arch->clone.value/3;
00194 }
00195 }
00196 } else {
00197 LOG(llevDebug, "In sell item: Have object with no archetype: %s\n", tmp->name);
00198 if (flag == F_BUY) {
00199 LOG(llevError, "Asking for buy-value of unidentified object without arch.\n");
00200 val = number*tmp->value*10;
00201 } else
00202 val = number*tmp->value/5;
00203 }
00204 }
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215 if ((QUERY_FLAG(tmp, FLAG_IDENTIFIED) || !need_identify(tmp) || identified || QUERY_FLAG(tmp, FLAG_BEEN_APPLIED))
00216 && tmp->magic
00217 && (tmp->arch == NULL || !tmp->arch->clone.magic)) {
00218 if (tmp->magic > 0)
00219 val *= (3*tmp->magic*tmp->magic*tmp->magic);
00220 else
00221
00222
00223
00224 val /= (1-tmp->magic);
00225 }
00226
00227 if (tmp->type == WAND) {
00228
00229
00230
00231
00232 if (QUERY_FLAG(tmp, FLAG_IDENTIFIED)
00233 || !need_identify(tmp)
00234 || identified)
00235 val = (val*tmp->stats.food)/50;
00236 else
00237 val /= 50;
00238 }
00239
00240
00241 if (flag == F_TRUE || flag == F_SELL)
00242 val = value_limit(val, number, who, shop);
00243
00244
00245
00246
00247
00248
00249 val *= 4;
00250
00251
00252
00253
00254
00255
00256
00257
00258 if (who != NULL && who->type == PLAYER) {
00259 int lev_bargain = 0;
00260 int lev_identify = 0;
00261 int idskill1 = 0;
00262 int idskill2 = 0;
00263 const typedata *tmptype;
00264
00265
00266
00267
00268
00269 ratio = 0.5;
00270 tmptype = get_typedata(tmp->type);
00271
00272 if (find_skill_by_number(who, SK_BARGAINING)) {
00273 lev_bargain = find_skill_by_number(who, SK_BARGAINING)->level;
00274 }
00275 if (tmptype) {
00276 idskill1 = tmptype->identifyskill;
00277 if (idskill1) {
00278 idskill2 = tmptype->identifyskill2;
00279 if (find_skill_by_number(who, idskill1)) {
00280 lev_identify = find_skill_by_number(who, idskill1)->level;
00281 }
00282 if (idskill2 && find_skill_by_number(who, idskill2)) {
00283 lev_identify += find_skill_by_number(who, idskill2)->level;
00284 }
00285 }
00286 } else
00287 LOG(llevError, "Query_cost: item %s hasn't got a valid type\n", tmp->name);
00288 if (!no_bargain && (lev_bargain > 0))
00289 diff = (0.8-0.6*((lev_bargain+settings.max_level*0.05)/(settings.max_level*1.05)));
00290 else
00291 diff = 0.8;
00292
00293 diff *= 1-ratio;
00294
00295
00296 diff += (cha_bonus[who->stats.Cha]-1)/(1+cha_bonus[who->stats.Cha])*ratio;
00297
00298 if (flag == F_BUY)
00299 val = (val*(long)(1000*(1+diff)))/1000;
00300 else if (flag == F_SELL)
00301 val = (val*(long)(1000*(1-diff)))/1000;
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313 if (approximate)
00314 val = (sint64)val+(sint64)((sint64)val*(sin(tmp->count)/sqrt(lev_bargain+lev_identify*2+1.0)));
00315 }
00316
00317
00318
00319
00320
00321
00322 if ((sint64)val < 0)
00323 val = 0;
00324
00325
00326 if (flag == F_SELL
00327 && !QUERY_FLAG(tmp, FLAG_IDENTIFIED)
00328 && need_identify(tmp)
00329 && !identified) {
00330 val = MIN(val, 600);
00331 }
00332
00333
00334 if (shop && who) {
00335 if (flag == F_SELL)
00336 val = (sint64)val*shop_specialisation_ratio(tmp, who->map)
00337 *shopkeeper_approval(who->map, who)/shop_greed(who->map);
00338 else if (flag == F_BUY) {
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354 if (QUERY_FLAG(tmp, FLAG_PLAYER_SOLD))
00355 val = (sint64)val*shop_greed(who->map)
00356 *shop_specialisation_ratio(tmp, who->map)
00357 /shopkeeper_approval(who->map, who);
00358 else
00359 val = (sint64)val*shop_greed(who->map)
00360 /(shop_specialisation_ratio(tmp, who->map)
00361 *shopkeeper_approval(who->map, who));
00362 }
00363
00364
00365
00366
00367
00368 if (who->map->path != NULL && val > 50)
00369 val = (sint64)val+0.05*(sint64)val*cos(tmp->count+strlen(who->map->path));
00370 }
00371 return val;
00372 }
00373
00385 static archetype *find_next_coin(uint64 c, int *cointype) {
00386 archetype *coin;
00387
00388 do {
00389 if (coins[*cointype] == NULL)
00390 return NULL;
00391 coin = find_archetype(coins[*cointype]);
00392 if (coin == NULL)
00393 return NULL;
00394 *cointype += 1;
00395 } while (coin->clone.value > c);
00396
00397 return coin;
00398 }
00399
00417 static StringBuffer *cost_string_from_value(uint64 cost, StringBuffer *buf) {
00418 archetype *coin, *next_coin;
00419 uint32 num;
00420 int cointype = LARGEST_COIN_GIVEN;
00421
00422 if (!buf)
00423 buf = stringbuffer_new();
00424
00425 coin = find_next_coin(cost, &cointype);
00426 if (coin == NULL) {
00427 stringbuffer_append_string(buf, "nothing");
00428 return buf;
00429 }
00430
00431 num = cost/coin->clone.value;
00432
00433
00434
00435
00436 if ((cost/coin->clone.value) > UINT32_MAX) {
00437 stringbuffer_append_string(buf, "an unimaginable sum of money.");
00438 return buf;
00439 }
00440
00441 cost -= (uint64)num*(uint64)coin->clone.value;
00442 if (num == 1)
00443 stringbuffer_append_printf(buf, "1 %s", coin->clone.name);
00444 else
00445 stringbuffer_append_printf(buf, "%u %ss", num, coin->clone.name);
00446
00447 next_coin = find_next_coin(cost, &cointype);
00448 if (next_coin == NULL)
00449 return buf;
00450
00451 do {
00452 coin = next_coin;
00453 num = cost/coin->clone.value;
00454 cost -= (uint64)num*(uint64)coin->clone.value;
00455
00456 if (cost == 0)
00457 next_coin = NULL;
00458 else
00459 next_coin = find_next_coin(cost, &cointype);
00460
00461 if (next_coin) {
00462
00463
00464
00465 stringbuffer_append_string(buf, ", ");
00466 } else {
00467 stringbuffer_append_string(buf, " and ");
00468 }
00469 if (num == 1)
00470 stringbuffer_append_printf(buf, "1 %s", coin->clone.name);
00471 else
00472 stringbuffer_append_printf(buf, "%u %ss", num, coin->clone.name);
00473 } while (next_coin);
00474
00475 return buf;
00476 }
00477
00488 static StringBuffer *real_money_value(const object *coin, StringBuffer *buf) {
00489 assert(coin->type == MONEY);
00490 assert(buf);
00491
00492 stringbuffer_append_printf(buf, "%ld %s", coin->nrof, coin->nrof == 1 ? coin->name : coin->name_pl);
00493 return buf;
00494 }
00495
00511 StringBuffer *query_cost_string(const object *tmp, object *who, int flag, StringBuffer *buf) {
00512 uint64 real_value = query_cost(tmp, who, flag);
00513 int idskill1 = 0;
00514 int idskill2 = 0;
00515 const typedata *tmptype;
00516
00517 if (!buf)
00518 buf = stringbuffer_new();
00519
00520
00521 if (tmp->type == MONEY) {
00522 return real_money_value(tmp, buf);
00523 }
00524
00525 tmptype = get_typedata(tmp->type);
00526 if (tmptype) {
00527 idskill1 = tmptype->identifyskill;
00528 idskill2 = tmptype->identifyskill2;
00529 }
00530
00531
00532
00533
00534
00535
00536 if (flag&F_APPROX) {
00537 if (!idskill1 || !find_skill_by_number(who, idskill1)) {
00538 if (!idskill2 || !find_skill_by_number(who, idskill2)) {
00539 if (!find_skill_by_number(who, SK_BARGAINING)) {
00540 int num;
00541 int cointype = LARGEST_COIN_GIVEN;
00542 archetype *coin = find_next_coin(real_value, &cointype);
00543
00544 if (coin == NULL) {
00545 stringbuffer_append_string(buf, "nothing");
00546 return buf;
00547 }
00548
00549 num = real_value/coin->clone.value;
00550 if (num == 1)
00551 stringbuffer_append_printf(buf, "about one %s", coin->clone.name);
00552 else if (num < 5)
00553 stringbuffer_append_printf(buf, "a few %s", coin->clone.name_pl);
00554 else if (num < 10)
00555 stringbuffer_append_printf(buf, "several %s", coin->clone.name_pl);
00556 else if (num < 25)
00557 stringbuffer_append_printf(buf, "a moderate amount of %s", coin->clone.name_pl);
00558 else if (num < 100)
00559 stringbuffer_append_printf(buf, "lots of %s", coin->clone.name_pl);
00560 else if (num < 1000)
00561 stringbuffer_append_printf(buf, "a great many %s", coin->clone.name_pl);
00562 else
00563 stringbuffer_append_printf(buf, "a vast quantity of %s", coin->clone.name_pl);
00564 return buf;
00565 }
00566 }
00567 }
00568 }
00569 return cost_string_from_value(real_value, buf);
00570 }
00571
00581 uint64 query_money(const object *op) {
00582 object *tmp;
00583 uint64 total = 0;
00584
00585 if (op->type != PLAYER && op->type != CONTAINER) {
00586 LOG(llevError, "Query money called with non player/container\n");
00587 return 0;
00588 }
00589 for (tmp = op->inv; tmp; tmp = tmp->below) {
00590 if (tmp->type == MONEY) {
00591 total += (uint64)tmp->nrof*(uint64)tmp->value;
00592 } else if (tmp->type == CONTAINER
00593 && QUERY_FLAG(tmp, FLAG_APPLIED)
00594 && (tmp->race == NULL || strstr(tmp->race, "gold"))) {
00595 total += query_money(tmp);
00596 }
00597 }
00598 return total;
00599 }
00600
00613 int pay_for_amount(uint64 to_pay, object *pl) {
00614 object *pouch;
00615
00616 if (to_pay == 0)
00617 return 1;
00618 if (to_pay > query_money(pl))
00619 return 0;
00620
00621 to_pay = pay_from_container(pl, pl, to_pay);
00622
00623 for (pouch = pl->inv; (pouch != NULL) && (to_pay > 0); pouch = pouch->below) {
00624 if (pouch->type == CONTAINER
00625 && QUERY_FLAG(pouch, FLAG_APPLIED)
00626 && (pouch->race == NULL || strstr(pouch->race, "gold"))) {
00627 to_pay = pay_from_container(pl, pouch, to_pay);
00628 }
00629 }
00630 if (to_pay > 0) {
00631 LOG(llevError, "pay_for_amount: Cannot remove enough money -- %"FMT64U" remains\n", to_pay);
00632 }
00633
00634 fix_object(pl);
00635 return 1;
00636 }
00637
00652 int pay_for_item(object *op, object *pl) {
00653 uint64 to_pay = query_cost(op, pl, F_BUY|F_SHOP);
00654 object *pouch;
00655 uint64 saved_money;
00656
00657 if (to_pay == 0)
00658 return 1;
00659 if (to_pay > query_money(pl))
00660 return 0;
00661
00662
00663
00664
00665
00666 saved_money = query_cost(op, pl, F_BUY|F_NO_BARGAIN|F_SHOP)-to_pay;
00667
00668 if (saved_money > 0)
00669 change_exp(pl, saved_money, "bargaining", SK_EXP_NONE);
00670
00671 to_pay = pay_from_container(pl, pl, to_pay);
00672
00673 for (pouch = pl->inv; (pouch != NULL) && (to_pay > 0); pouch = pouch->below) {
00674 if (pouch->type == CONTAINER
00675 && QUERY_FLAG(pouch, FLAG_APPLIED)
00676 && (pouch->race == NULL || strstr(pouch->race, "gold"))) {
00677 to_pay = pay_from_container(pl, pouch, to_pay);
00678 }
00679 }
00680 if (to_pay > 0) {
00681 LOG(llevError, "pay_for_item: Cannot remove enough money -- %"FMT64U" remains\n", to_pay);
00682 }
00683 if (settings.real_wiz == FALSE && QUERY_FLAG(pl, FLAG_WAS_WIZ))
00684 SET_FLAG(op, FLAG_WAS_WIZ);
00685 fix_object(pl);
00686 return 1;
00687 }
00688
00700 static sint64 remove_value(object *coin_objs[], sint64 remain) {
00701 int i;
00702
00703 for (i = 0; i < NUM_COINS; i++) {
00704 int count;
00705 sint64 num_coins;
00706
00707 if (coin_objs[i]->nrof*coin_objs[i]->value > remain) {
00708 num_coins = remain/coin_objs[i]->value;
00709 if ((uint64)num_coins*(uint64)coin_objs[i]->value < remain) {
00710 num_coins++;
00711 }
00712 } else {
00713 num_coins = coin_objs[i]->nrof;
00714 }
00715 remain -= (sint64)num_coins*(sint64)coin_objs[i]->value;
00716 coin_objs[i]->nrof -= num_coins;
00717
00718
00719
00720
00721 count = i-1;
00722 while (remain < 0 && count >= 0) {
00723 num_coins = -remain/coin_objs[count]->value;
00724 coin_objs[count]->nrof += num_coins;
00725 remain += num_coins*coin_objs[count]->value;
00726 count--;
00727 }
00728 }
00729
00730 return remain;
00731 }
00732
00741 static void add_value(object *coin_objs[], sint64 value) {
00742 int i;
00743
00744 for (i = NUM_COINS-LARGEST_COIN_GIVEN-1; i >= 0; i--) {
00745 uint32 nrof;
00746
00747 nrof = (uint32)(value/coin_objs[i]->value);
00748 value -= nrof*coin_objs[i]->value;
00749 coin_objs[i]->nrof += nrof;
00750 }
00751 }
00752
00765 static void insert_objects(object *pl, object *container, object *objects[], int objects_len) {
00766 int i, one = 0;
00767
00768 for (i = 0; i < objects_len; i++) {
00769 if (objects[i]->nrof > 0) {
00770 insert_ob_in_ob(objects[i], container);
00771 one = 1;
00772 } else {
00773 free_object(objects[i]);
00774 }
00775 }
00776 if (one)
00777 esrv_update_item(UPD_WEIGHT, pl, container);
00778 }
00779
00796 static uint64 pay_from_container(object *pl, object *pouch, uint64 to_pay) {
00797 int i;
00798 sint64 remain;
00799 object *tmp, *coin_objs[NUM_COINS], *next;
00800 object *other_money[16];
00801 size_t other_money_len;
00802 archetype *at;
00803
00804 if (pouch->type != PLAYER && pouch->type != CONTAINER)
00805 return to_pay;
00806
00807 remain = to_pay;
00808 for (i = 0; i < NUM_COINS; i++)
00809 coin_objs[i] = NULL;
00810
00811
00812 other_money_len = 0;
00813 for (tmp = pouch->inv; tmp; tmp = next) {
00814 next = tmp->below;
00815 if (tmp->type == MONEY) {
00816 for (i = 0; i < NUM_COINS; i++) {
00817 if (!strcmp(coins[NUM_COINS-1-i], tmp->arch->name)
00818 && (tmp->value == tmp->arch->clone.value)) {
00819
00820
00821
00822
00823 if (coin_objs[i] != NULL) {
00824 LOG(llevError, "%s has two money entries of (%s)\n", pouch->name, coins[NUM_COINS-1-i]);
00825 remove_ob(tmp);
00826 coin_objs[i]->nrof += tmp->nrof;
00827 free_object(tmp);
00828 } else {
00829 remove_ob(tmp);
00830 coin_objs[i] = tmp;
00831 }
00832 break;
00833 }
00834 }
00835 if (i == NUM_COINS) {
00836 if (other_money_len >= sizeof(other_money)/sizeof(*other_money)) {
00837 LOG(llevError, "pay_for_item: Cannot store non-standard money object %s\n", tmp->arch->name);
00838 } else {
00839 remove_ob(tmp);
00840 other_money[other_money_len++] = tmp;
00841 }
00842 }
00843 }
00844 }
00845
00846
00847
00848 for (i = 0; i < NUM_COINS; i++)
00849 if (coin_objs[i] == NULL) {
00850 at = find_archetype(coins[NUM_COINS-1-i]);
00851 if (at == NULL)
00852 LOG(llevError, "Could not find %s archetype\n", coins[NUM_COINS-1-i]);
00853 coin_objs[i] = get_object();
00854 copy_object(&at->clone, coin_objs[i]);
00855 coin_objs[i]->nrof = 0;
00856 }
00857
00858
00859 remain = remove_value(coin_objs, remain);
00860
00861
00862 for (i = 0; i < other_money_len && remain > 0; i++) {
00863 uint32 nrof;
00864 object *coin;
00865
00866 coin = other_money[i];
00867
00868
00869
00870
00871 nrof = (remain+coin->value-1)/coin->value;
00872 if (nrof > coin->nrof) {
00873 nrof = coin->nrof;
00874 }
00875 coin->nrof -= nrof;
00876 add_value(coin_objs, nrof*coin->value);
00877
00878 remain = remove_value(coin_objs, remain);
00879 }
00880
00881
00882 insert_objects(pl, pouch, coin_objs, NUM_COINS);
00883 insert_objects(pl, pouch, other_money, other_money_len);
00884
00885 return(remain);
00886 }
00887
00904 static void count_unpaid(object *pl, object *item, int *unpaid_count, uint64 *unpaid_price, uint32 *coincount) {
00905 int i;
00906
00907 for (; item; item = item->below) {
00908 if QUERY_FLAG(item, FLAG_UNPAID) {
00909 (*unpaid_count)++;
00910 (*unpaid_price) += query_cost(item, pl, F_BUY|F_SHOP);
00911 }
00912
00913
00914
00915
00916 for (i = 0; i < NUM_COINS; i++)
00917 if (!strcmp(coins[i], item->arch->name)) {
00918 coincount[i] += item->nrof;
00919 break;
00920 }
00921 if (item->inv)
00922 count_unpaid(pl, item->inv, unpaid_count, unpaid_price, coincount);
00923 }
00924 }
00925
00938 int can_pay(object *pl) {
00939 int unpaid_count = 0, i;
00940 uint64 unpaid_price = 0;
00941 uint64 player_wealth = query_money(pl);
00942 uint32 coincount[NUM_COINS];
00943
00944 if (!pl || pl->type != PLAYER) {
00945 LOG(llevError, "can_pay(): called against something that isn't a player\n");
00946 return 0;
00947 }
00948
00949 for (i = 0; i < NUM_COINS; i++)
00950 coincount[i] = 0;
00951
00952 count_unpaid(pl, pl->inv, &unpaid_count, &unpaid_price, coincount);
00953
00954 if (unpaid_price > player_wealth) {
00955 char buf[MAX_BUF], coinbuf[MAX_BUF];
00956 int denominations = 0;
00957 char *value = stringbuffer_finish(cost_string_from_value(unpaid_price, NULL));
00958
00959 snprintf(buf, sizeof(buf), "You have %d unpaid items that would cost you %s, ", unpaid_count, value);
00960 free(value);
00961 for (i = 0; i < NUM_COINS; i++) {
00962 if (coincount[i] > 0 && coins[i]) {
00963 if (denominations == 0)
00964 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "but you only have");
00965 denominations++;
00966 snprintf(coinbuf, sizeof(coinbuf), " %u %s,", coincount[i], find_archetype(coins[i])->clone.name_pl);
00967 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s", coinbuf);
00968 }
00969 }
00970 if (denominations == 0)
00971 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "but you don't have any money.");
00972 else if (denominations > 1)
00973 make_list_like(buf);
00974 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SHOP,
00975 MSG_TYPE_SHOP_PAYMENT, buf, NULL);
00976 return 0;
00977 } else
00978 return 1;
00979 }
00980
00992 int get_payment(object *pl, object *op) {
00993 char name_op[MAX_BUF];
00994 int ret = 1;
00995
00996 if (op != NULL && op->inv)
00997 ret = get_payment(pl, op->inv);
00998
00999 if (!ret)
01000 return 0;
01001
01002 if (op != NULL && op->below)
01003 ret = get_payment(pl, op->below);
01004
01005 if (!ret)
01006 return 0;
01007
01008 if (op != NULL && QUERY_FLAG(op, FLAG_UNPAID)) {
01009 if (!pay_for_item(op, pl)) {
01010 uint64 i = query_cost(op, pl, F_BUY|F_SHOP)-query_money(pl);
01011 char *missing = stringbuffer_finish(cost_string_from_value(i, NULL));
01012
01013 CLEAR_FLAG(op, FLAG_UNPAID);
01014 query_name(op, name_op, MAX_BUF);
01015 draw_ext_info_format(NDI_UNIQUE, 0, pl,
01016 MSG_TYPE_SHOP, MSG_TYPE_SHOP_PAYMENT,
01017 "You lack %s to buy %s.",
01018 "You lack %s to buy %s.",
01019 missing, name_op);
01020 free(missing);
01021 SET_FLAG(op, FLAG_UNPAID);
01022 return 0;
01023 } else {
01024 object *tmp;
01025 char *value = stringbuffer_finish(query_cost_string(op, pl, F_BUY|F_SHOP, NULL));
01026
01027 CLEAR_FLAG(op, FLAG_UNPAID);
01028 CLEAR_FLAG(op, FLAG_PLAYER_SOLD);
01029 query_name(op, name_op, MAX_BUF);
01030 draw_ext_info_format(NDI_UNIQUE, 0, pl,
01031 MSG_TYPE_SHOP, MSG_TYPE_SHOP_PAYMENT,
01032 "You paid %s for %s.",
01033 "You paid %s for %s.",
01034 value, name_op);
01035 free(value);
01036 tmp = merge_ob(op, NULL);
01037 if (pl->type == PLAYER && !tmp) {
01038
01039 esrv_update_item(UPD_FLAGS|UPD_NAME, pl, op);
01040 }
01041 }
01042 }
01043 return 1;
01044 }
01045
01059 void sell_item(object *op, object *pl) {
01060 uint64 i = query_cost(op, pl, F_SELL|F_SHOP), extra_gain;
01061 int count;
01062 object *tmp, *pouch;
01063 archetype *at;
01064 char name_op[MAX_BUF], *value;
01065
01066 if (pl == NULL || pl->type != PLAYER) {
01067 LOG(llevDebug, "Object other than player tried to sell something.\n");
01068 return;
01069 }
01070
01071 if (op->custom_name)
01072 FREE_AND_CLEAR_STR(op->custom_name);
01073
01074 if (!i) {
01075 query_name(op, name_op, MAX_BUF);
01076 draw_ext_info_format(NDI_UNIQUE, 0, pl,
01077 MSG_TYPE_SHOP, MSG_TYPE_SHOP_SELL,
01078 "We're not interested in %s.",
01079 "We're not interested in %s.",
01080 name_op);
01081
01082
01083
01084
01085 if (op->value) {
01086 SET_FLAG(op, FLAG_UNPAID);
01087 SET_FLAG(op, FLAG_PLAYER_SOLD);
01088 }
01089 identify(op);
01090 return;
01091 }
01092
01093
01094
01095
01096
01097
01098 extra_gain = i-query_cost(op, pl, F_SELL|F_NO_BARGAIN|F_SHOP);
01099
01100 if (extra_gain > 0)
01101 change_exp(pl, extra_gain/10, "bargaining", SK_EXP_NONE);
01102
01103 for (count = LARGEST_COIN_GIVEN; coins[count] != NULL; count++) {
01104 at = find_archetype(coins[count]);
01105 if (at == NULL)
01106 LOG(llevError, "Could not find %s archetype\n", coins[count]);
01107 else if ((i/at->clone.value) > 0) {
01108 for (pouch = pl->inv; pouch; pouch = pouch->below) {
01109 if (pouch->type == CONTAINER
01110 && QUERY_FLAG(pouch, FLAG_APPLIED)
01111 && pouch->race
01112 && strstr(pouch->race, "gold")) {
01113 int w = at->clone.weight*(100-pouch->stats.Str)/100;
01114 int n = i/at->clone.value;
01115
01116 if (w == 0)
01117 w = 1;
01118 if (n > 0
01119 && (!pouch->weight_limit || pouch->carrying+w <= pouch->weight_limit)) {
01120 if (pouch->weight_limit
01121 && (pouch->weight_limit-pouch->carrying)/w < n)
01122 n = (pouch->weight_limit-pouch->carrying)/w;
01123
01124 tmp = get_object();
01125 copy_object(&at->clone, tmp);
01126 tmp->nrof = n;
01127 i -= (uint64)tmp->nrof*(uint64)tmp->value;
01128 tmp = insert_ob_in_ob(tmp, pouch);
01129 esrv_update_item(UPD_WEIGHT, pl, pl);
01130 }
01131 }
01132 }
01133 if (i/at->clone.value > 0) {
01134 tmp = get_object();
01135 copy_object(&at->clone, tmp);
01136 tmp->nrof = i/tmp->value;
01137 i -= (uint64)tmp->nrof*(uint64)tmp->value;
01138 tmp = insert_ob_in_ob(tmp, pl);
01139 esrv_update_item(UPD_WEIGHT, pl, pl);
01140 }
01141 }
01142 }
01143
01144 if (i != 0)
01145 #ifndef WIN32
01146 LOG(llevError, "Warning - payment not zero: %llu\n", i);
01147 #else
01148 LOG(llevError, "Warning - payment not zero: %I64u\n", i);
01149 #endif
01150
01151 query_name(op, name_op, MAX_BUF);
01152 value = stringbuffer_finish(query_cost_string(op, pl, F_SELL|F_SHOP, NULL));
01153
01154 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SHOP, MSG_TYPE_SHOP_SELL,
01155 "You receive %s for %s.",
01156 "You receive %s for %s.",
01157 value,
01158 name_op);
01159
01160 free(value);
01161
01162 SET_FLAG(op, FLAG_UNPAID);
01163 identify(op);
01164 }
01165
01179 static double shop_specialisation_ratio(const object *item, const mapstruct *map) {
01180 shopitems *items = map->shopitems;
01181 double ratio = SPECIALISATION_EFFECT, likedness = 0.001;
01182 int i;
01183
01184 if (item == NULL) {
01185 LOG(llevError, "shop_specialisation_ratio: passed a NULL item for map %s\n", map->path);
01186 return 0;
01187 }
01188 if (!item->type) {
01189 LOG(llevError, "shop_specialisation_ratio: passed an item with an invalid type\n");
01190
01191
01192
01193
01194
01195 return ratio;
01196 }
01197 if (map->shopitems) {
01198 for (i = 0; i < items[0].index; i++)
01199 if (items[i].typenum == item->type || (!items[i].typenum && likedness == 0.001))
01200 likedness = items[i].strength/100.0;
01201 }
01202 if (likedness > 1.0) {
01203 LOG(llevDebug, "shop_specialisation ratio: item type %d on map %s is above 100%%\n", item->type, map->path);
01204 likedness = 1.0;
01205 }
01206 if (likedness < -1.0) {
01207 LOG(llevDebug, "shop_specialisation ratio: item type %d on map %s is below -100%%\n", item->type, map->path);
01208 likedness = -1.0;
01209 }
01210 ratio = ratio+(1.0-ratio)*likedness;
01211 if (ratio <= 0.1)
01212 ratio = 0.1;
01213 return ratio;
01214 }
01215
01224 static double shop_greed(const mapstruct *map) {
01225 double greed = 1.0;
01226
01227 if (map->shopgreed)
01228 return map->shopgreed;
01229 return greed;
01230 }
01231
01243 double shopkeeper_approval(const mapstruct *map, const object *player) {
01244 double approval = 1.0;
01245
01246 if (map->shoprace) {
01247 approval = NEUTRAL_RATIO;
01248 if (player->race && !strcmp(player->race, map->shoprace))
01249 approval = 1.0;
01250 }
01251 return approval;
01252 }
01253
01273 static uint64 value_limit(uint64 val, int quantity, const object *who, int isshop) {
01274 uint64 newval, unit_price;
01275 mapstruct *map;
01276
01277 unit_price = val/quantity;
01278 if (!isshop || !who) {
01279 if (unit_price > 10000)
01280 newval = 8000+isqrt(unit_price)*20;
01281 else
01282 newval = unit_price;
01283 } else {
01284 if (!who->map) {
01285 LOG(llevError, "value_limit: asked shop price for ob %s on NULL map\n", who->name);
01286 return val;
01287 }
01288 map = who->map;
01289 if (map->shopmin && unit_price < map->shopmin)
01290 return 0;
01291 else if (map->shopmax && unit_price > map->shopmax/2)
01292 newval = MIN((map->shopmax/2)+isqrt(unit_price-map->shopmax/2), map->shopmax);
01293 else if (unit_price > 10000)
01294 newval = 8000+isqrt(unit_price)*20;
01295 else
01296 newval = unit_price;
01297 }
01298 newval *= quantity;
01299 return newval;
01300 }
01301
01311 int describe_shop(const object *op) {
01312 mapstruct *map = op->map;
01313
01314 int pos = 0, i;
01315 double opinion = 0;
01316 char tmp[MAX_BUF] = "\0", *value;
01317
01318 if (op->type != PLAYER)
01319 return 0;
01320
01321
01322 if (map->shopitems
01323 || map->shopgreed
01324 || map->shoprace
01325 || map->shopmin
01326 || map->shopmax) {
01327 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SHOP, MSG_TYPE_SHOP_LISTING,
01328 "From looking at the nearby shop you determine that it trades in:",
01329 NULL);
01330
01331 if (map->shopitems) {
01332 for (i = 0; i < map->shopitems[0].index; i++) {
01333 if (map->shopitems[i].name && map->shopitems[i].strength > 10) {
01334 snprintf(tmp+pos, sizeof(tmp)-pos, "%s, ", map->shopitems[i].name_pl);
01335 pos += strlen(tmp+pos);
01336 }
01337 }
01338 }
01339 if (!pos)
01340 strcpy(tmp, "a little of everything.");
01341
01342
01343 make_list_like(tmp);
01344 draw_ext_info(NDI_UNIQUE, 0, op,
01345 MSG_TYPE_SHOP, MSG_TYPE_SHOP_LISTING, tmp, NULL);
01346
01347 if (map->shopmax) {
01348 value = stringbuffer_finish(cost_string_from_value(map->shopmax, NULL));
01349 draw_ext_info_format(NDI_UNIQUE, 0, op,
01350 MSG_TYPE_SHOP, MSG_TYPE_SHOP_MISC,
01351 "It won't trade for items above %s.",
01352 "It won't trade for items above %s.",
01353 value);
01354 free(value);
01355 }
01356
01357 if (map->shopmin) {
01358 value = stringbuffer_finish(cost_string_from_value(map->shopmin, NULL));
01359 draw_ext_info_format(NDI_UNIQUE, 0, op,
01360 MSG_TYPE_SHOP, MSG_TYPE_SHOP_MISC,
01361 "It won't trade in items worth less than %s.",
01362 "It won't trade in items worth less than %s.",
01363 value);
01364 free(value);
01365 }
01366
01367 if (map->shopgreed) {
01368 if (map->shopgreed > 2.0)
01369 draw_ext_info(NDI_UNIQUE, 0, op,
01370 MSG_TYPE_SHOP, MSG_TYPE_SHOP_MISC,
01371 "It tends to overcharge massively.", NULL);
01372 else if (map->shopgreed > 1.5)
01373 draw_ext_info(NDI_UNIQUE, 0, op,
01374 MSG_TYPE_SHOP, MSG_TYPE_SHOP_MISC,
01375 "It tends to overcharge substantially.", NULL);
01376 else if (map->shopgreed > 1.1)
01377 draw_ext_info(NDI_UNIQUE, 0, op,
01378 MSG_TYPE_SHOP, MSG_TYPE_SHOP_MISC,
01379 "It tends to overcharge slightly.", NULL);
01380 else if (map->shopgreed < 0.9)
01381 draw_ext_info(NDI_UNIQUE, 0, op,
01382 MSG_TYPE_SHOP, MSG_TYPE_SHOP_MISC,
01383 "It tends to undercharge.", NULL);
01384 }
01385 if (map->shoprace) {
01386 opinion = shopkeeper_approval(map, op);
01387 if (opinion > 0.8)
01388 draw_ext_info(NDI_UNIQUE, 0, op,
01389 MSG_TYPE_SHOP, MSG_TYPE_SHOP_MISC,
01390 "You think the shopkeeper likes you.", NULL);
01391 else if (opinion > 0.5)
01392 draw_ext_info(NDI_UNIQUE, 0, op,
01393 MSG_TYPE_SHOP, MSG_TYPE_SHOP_MISC,
01394 "The shopkeeper seems unconcerned by you.", NULL);
01395 else
01396 draw_ext_info(NDI_UNIQUE, 0, op,
01397 MSG_TYPE_SHOP, MSG_TYPE_SHOP_MISC,
01398 "The shopkeeper seems to have taken a dislike to you.", NULL);
01399 }
01400 } else draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SHOP, MSG_TYPE_SHOP_MISC,
01401 "There is no shop nearby.", NULL);
01402
01403 return 1;
01404 }
01405
01414 int is_in_shop(object *ob) {
01415 if (!ob->map)
01416 return 0;
01417 return coords_in_shop(ob->map, ob->x, ob->y);
01418 }
01419
01429 int coords_in_shop(mapstruct *map, int x, int y) {
01430 object *floor;
01431
01432 for (floor = GET_MAP_OB(map, x, y); floor; floor = floor->above)
01433 if (floor->type == SHOP_FLOOR)
01434 return 1;
01435 return 0;
01436 }