Crossfire Server, Trunk  1.75.0
shop.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
21 #include "global.h"
22 
23 #include <assert.h>
24 #include <cmath>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "shop.h"
29 #include "sproto.h"
30 
39 #define SPECIALISATION_EFFECT 0.5
40 
42 #define DISAPPROVAL_RATIO 0.2
43 
45 #define NEUTRAL_RATIO 0.8
46 
48 #define MAX_BUY_REDUCTION 0.1f
49 
50 #define MAX_SELL_EXTRA 0.1f
51 
52 static uint64_t pay_from_container(object *pl, object *pouch, uint64_t to_pay);
53 static uint64_t value_limit(uint64_t val, int quantity, const object *who, int isshop);
54 static double shop_specialisation_ratio(const object *item, const mapstruct *map);
55 static double shop_greed(const mapstruct *map);
56 
57 #define NUM_COINS 5
59 #define LARGEST_COIN_GIVEN 2
62 static const char *const coins[] = {
63  "ambercoin",
64  "jadecoin",
65  "platinacoin",
66  "goldcoin",
67  "silvercoin",
68  NULL
69 };
70 
71 uint64_t price_approx(const object *tmp, object *who) {
72  uint64_t val = price_base(tmp);
73 
74  /* If we are approximating, then the value returned should
75  * be allowed to be wrong however merely using a random number
76  * each time will not be sufficient, as then multiple examinations
77  * would give different answers, so we'll use the count instead.
78  * By taking the sine of the count, a value between -1 and 1 is
79  * generated, we then divide by the square root of the bargaining
80  * skill and the appropriate identification skills, so that higher
81  * level players get better estimates. (We need a +1 there to
82  * prevent dividing by zero.)
83  */
84  const typedata *tmptype = get_typedata(tmp->type);
85  int lev_identify = 0;
86 
87  if (tmptype) {
88  int idskill1 = tmptype->identifyskill;
89  if (idskill1) {
90  int idskill2 = tmptype->identifyskill2;
91  if (find_skill_by_number(who, idskill1)) {
92  lev_identify = find_skill_by_number(who, idskill1)->level;
93  }
94  if (idskill2 && find_skill_by_number(who, idskill2)) {
95  lev_identify += find_skill_by_number(who, idskill2)->level;
96  }
97  }
98  } else {
99  LOG(llevError, "Query_cost: item %s hasn't got a valid type\n", tmp->name);
100  }
101  val += val * (sin(tmp->count) / sqrt(lev_identify * 3 + 1.0));
102 
103  return val;
104 }
105 
111 static float shop_cha_modifier(int charisma) {
112  return tanh((charisma+15.0)/20);
113 }
114 
122 float shop_efficiency(const object *player) {
123  return shop_greed(player->map)
124  * shop_approval(player->map, player)
125  * shop_cha_modifier(player->stats.Cha);
126 }
127 
128 uint64_t shop_price_buy(const object *tmp, object *who) {
129  assert(who != NULL && who->type == PLAYER);
130  // price tags override any buy price
131  if (object_value_set(tmp, "price")) {
132  const char *price = object_get_value(tmp, "price");
133  return NROF(tmp)*strtol(price, NULL, 10);
134  }
135  const uint64_t val = price_base(tmp);
136  const char *key = object_get_value(tmp, "price_adjustment_buy");
137  float adj = 1;
138  if (key != NULL) {
139  adj = atof(key);
140  }
141  float E = shop_efficiency(who);
142  const float adj_val = val * adj / E;
143  if (getenv("CF_DEBUG_SHOP")) {
144  LOG(llevDebug, "price_buy %s %lu*adj(%.2f)/E(%.2f) = %.2f\n",
145  tmp->arch->name, val, adj, E, adj_val);
146  }
147  if (std::isfinite(adj_val)) {
148  return adj_val;
149  } else {
150  return UINT64_MAX;
151  }
152 }
153 
154 uint64_t shop_price_sell(const object *tmp, object *who) {
155  assert(who != NULL && who->type == PLAYER);
156  const uint64_t val = price_base(tmp);
157  const char *key = object_get_value(tmp, "price_adjustment_sell");
158  float adj = 1;
159  if (key != NULL) {
160  adj = atof(key);
161  }
162  float spec = shop_specialisation_ratio(tmp, who->map);
163  float E = shop_efficiency(who);
164  const uint64_t adj_val = val * adj * spec * E;
165 
166  /* Limit amount of money you can get for really great items. */
167  int number = NROF(tmp);
168  uint64_t limval = value_limit(adj_val, number, who, 1);
169 
170  if (getenv("CF_DEBUG_SHOP")) {
171  LOG(llevDebug, "price_sell %s %lu*adj(%.2f)*s(%.2f)*E(%.2f) = %lu limited to %lu\n",
172  tmp->arch->name, val, adj, spec, E, adj_val, limval);
173  }
174  return limval;
175 }
176 
188 static archetype *find_next_coin(uint64_t c, int *cointype) {
189  archetype *coin;
190 
191  do {
192  if (coins[*cointype] == NULL)
193  return NULL;
194  coin = find_archetype(coins[*cointype]);
195  if (coin == NULL)
196  return NULL;
197  *cointype += 1;
198  } while (coin->clone.value > (int64_t) c);
199 
200  return coin;
201 }
202 
220 char* cost_string_from_value(uint64_t cost, int largest_coin) {
221  archetype *coin, *next_coin;
222  uint32_t num;
223  int cointype = largest_coin;
224 
225  if (cointype < 0)
226  cointype = 0;
227  else if (cointype >= NUM_COINS)
228  cointype = NUM_COINS - 1;
229 
231  if (cost == UINT64_MAX) {
232  stringbuffer_append_string(buf, "an unimaginable sum of money");
233  goto done;
234  }
235 
236  coin = find_next_coin(cost, &cointype);
237  if (coin == NULL) {
238  stringbuffer_append_string(buf, "nothing");
239  goto done;
240  }
241 
242  num = cost/coin->clone.value;
243  /* so long as nrof is 32 bit, this is true.
244  * If it takes more coins than a person can possibly carry, this
245  * is basically true.
246  */
247  if ((cost/coin->clone.value) > UINT32_MAX) {
248  stringbuffer_append_string(buf, "an unimaginable sum of money");
249  goto done;
250  }
251 
252  cost -= (uint64_t)num*(uint64_t)coin->clone.value;
253  if (num == 1)
254  stringbuffer_append_printf(buf, "1 %s", coin->clone.name);
255  else
256  stringbuffer_append_printf(buf, "%u %ss", num, coin->clone.name);
257 
258  next_coin = find_next_coin(cost, &cointype);
259  if (next_coin == NULL)
260  goto done;
261 
262  do {
263  coin = next_coin;
264  num = cost/coin->clone.value;
265  cost -= (uint64_t)num*(uint64_t)coin->clone.value;
266 
267  if (cost == 0)
268  next_coin = NULL;
269  else
270  next_coin = find_next_coin(cost, &cointype);
271 
272  if (next_coin) {
273  /* There will be at least one more string to add to the list,
274  * use a comma.
275  */
277  } else {
279  }
280  if (num == 1)
281  stringbuffer_append_printf(buf, "1 %s", coin->clone.name);
282  else
283  stringbuffer_append_printf(buf, "%u %ss", num, coin->clone.name);
284  } while (next_coin);
285 
286 done:
287  return stringbuffer_finish(buf);
288 }
289 
300 static StringBuffer *real_money_value(const object *coin, StringBuffer *buf) {
301  assert(coin->type == MONEY);
302  assert(buf);
303 
304  stringbuffer_append_printf(buf, "%ld %s", (long)coin->nrof, coin->nrof == 1 ? coin->name : coin->name_pl);
305  return buf;
306 }
307 
308 char *cost_str(uint64_t cost) {
310 }
311 
312 char *cost_approx_str(const object *tmp, object *who) {
313  uint64_t approx_val = price_approx(tmp, who);
314  int idskill1 = 0;
315  int idskill2 = 0;
316  const typedata *tmptype;
317 
319 
320  /* money it's pretty hard to not give the exact price, so skip all logic and just return the real value. */
321  if (tmp->type == MONEY) {
323  }
324 
325  tmptype = get_typedata(tmp->type);
326  if (tmptype) {
327  idskill1 = tmptype->identifyskill;
328  idskill2 = tmptype->identifyskill2;
329  }
330 
331  /* we show an approximate price if
332  * 1) we are approximating
333  * 2) there either is no id skill(s) for the item, or we don't have them
334  * 3) we don't have bargaining skill either
335  */
336  if (!idskill1 || !find_skill_by_number(who, idskill1)) {
337  if (!idskill2 || !find_skill_by_number(who, idskill2)) {
338  if (!find_skill_by_number(who, SK_BARGAINING)) {
339  int num;
340  int cointype = LARGEST_COIN_GIVEN;
341  archetype *coin = find_next_coin(approx_val, &cointype);
342 
343  if (coin == NULL) {
344  stringbuffer_append_string(buf, "nothing");
345  return stringbuffer_finish(buf);
346  }
347 
348  num = approx_val/coin->clone.value;
349  if (num == 1)
350  stringbuffer_append_printf(buf, "about one %s", coin->clone.name);
351  else if (num < 5)
352  stringbuffer_append_printf(buf, "a few %s", coin->clone.name_pl);
353  else if (num < 10)
354  stringbuffer_append_printf(buf, "several %s", coin->clone.name_pl);
355  else if (num < 25)
356  stringbuffer_append_printf(buf, "a moderate amount of %s", coin->clone.name_pl);
357  else if (num < 100)
358  stringbuffer_append_printf(buf, "lots of %s", coin->clone.name_pl);
359  else if (num < 1000)
360  stringbuffer_append_printf(buf, "a great many %s", coin->clone.name_pl);
361  else
362  stringbuffer_append_printf(buf, "a vast quantity of %s", coin->clone.name_pl);
363  return stringbuffer_finish(buf);
364  }
365  }
366  }
367 
368  // If we get here, return the price we guessed.
370  return cost_str(approx_val);
371 }
372 
373 uint64_t query_money(const object *op) {
374  uint64_t total = 0;
375 
376  if (op->type != PLAYER && op->type != CONTAINER) {
377  LOG(llevError, "Query money called with non player/container\n");
378  return 0;
379  }
380  FOR_INV_PREPARE(op, tmp) {
381  if (tmp->type == MONEY) {
382  total += (uint64_t)tmp->nrof*(uint64_t)tmp->value;
383  } else if (tmp->type == CONTAINER && QUERY_FLAG(tmp, FLAG_APPLIED)) {
384  total += query_money(tmp);
385  }
386  } FOR_INV_FINISH();
387  return total;
388 }
389 
402 int pay_for_amount(uint64_t to_pay, object *pl) {
403  if (to_pay == 0)
404  return 1;
405  if (to_pay > query_money(pl))
406  return 0;
407 
408  to_pay = pay_from_container(pl, pl, to_pay);
409 
410  FOR_INV_PREPARE(pl, pouch) {
411  if (to_pay <= 0)
412  break;
413  if (pouch->type == CONTAINER && QUERY_FLAG(pouch, FLAG_APPLIED)) {
414  to_pay = pay_from_container(pl, pouch, to_pay);
415  }
416  } FOR_INV_FINISH();
417  if (to_pay > 0) {
418  LOG(llevError, "pay_for_amount: Cannot remove enough money -- %" FMT64U " remains\n", to_pay);
419  }
420 
421  fix_object(pl);
422  return 1;
423 }
424 
440 int pay_for_item(object *op, object *pl, uint64_t reduction) {
441  uint64_t to_pay = shop_price_buy(op, pl);
442  assert(to_pay >= reduction);
443  to_pay -= reduction;
444 
445  if (to_pay == 0)
446  return 1;
447  if (to_pay > query_money(pl))
448  return 0;
449 
450  to_pay = pay_from_container(pl, pl, to_pay);
451 
452  FOR_INV_PREPARE(pl, pouch) {
453  if (to_pay <= 0)
454  break;
455  if (pouch->type == CONTAINER && QUERY_FLAG(pouch, FLAG_APPLIED)) {
456  to_pay = pay_from_container(pl, pouch, to_pay);
457  }
458  } FOR_INV_FINISH();
459  if (to_pay > 0) {
460  LOG(llevError, "pay_for_item: Cannot remove enough money -- %" FMT64U " remains\n", to_pay);
461  }
463  SET_FLAG(op, FLAG_WAS_WIZ);
464  fix_object(pl);
465  return 1;
466 }
467 
479 static int64_t remove_value(object *coin_objs[], int64_t remain) {
480  int i;
481 
482  for (i = 0; i < NUM_COINS; i++) {
483  int count;
484  int64_t num_coins;
485 
486  if ((int64_t)coin_objs[i]->nrof * coin_objs[i]->value > remain) {
487  num_coins = remain/coin_objs[i]->value;
488  if ((uint64_t)num_coins*(uint64_t)coin_objs[i]->value < (uint64_t) remain) {
489  num_coins++;
490  }
491  } else {
492  num_coins = coin_objs[i]->nrof;
493  }
494  remain -= (int64_t)num_coins*(int64_t)coin_objs[i]->value;
495  coin_objs[i]->nrof -= num_coins;
496  /* Now start making change. Start at the coin value
497  * below the one we just did, and work down to
498  * the lowest value.
499  */
500  count = i-1;
501  while (remain < 0 && count >= 0) {
502  num_coins = -remain/coin_objs[count]->value;
503  coin_objs[count]->nrof += num_coins;
504  remain += num_coins*coin_objs[count]->value;
505  count--;
506  }
507  }
508 
509  return remain;
510 }
511 
520 static void add_value(object *coin_objs[], int64_t value) {
521  int i;
522 
523  for (i = NUM_COINS-LARGEST_COIN_GIVEN-1; i >= 0; i--) {
524  uint32_t nrof;
525 
526  nrof = (uint32_t)(value/coin_objs[i]->value);
527  value -= nrof*coin_objs[i]->value;
528  coin_objs[i]->nrof += nrof;
529  }
530 }
531 
544 static void insert_objects(object *pl, object *container, object *objects[], int objects_len) {
545  int i, one = 0;
546 
547  for (i = 0; i < objects_len; i++) {
548  if (objects[i]->nrof > 0) {
549  object_insert_in_ob(objects[i], container);
550  one = 1;
551  } else {
553  }
554  }
555  if (one)
556  esrv_update_item(UPD_WEIGHT, pl, container);
557 }
558 
572 static uint64_t pay_from_container(object *pl, object *pouch, uint64_t to_pay) {
573  size_t i;
574  int64_t remain;
575  object *coin_objs[NUM_COINS];
576  object *other_money[16]; /* collects MONEY objects not matching coins[] */
577  size_t other_money_len; /* number of allocated entries in other_money[] */
578  archetype *at;
579 
580  if (pouch->type != PLAYER && pouch->type != CONTAINER)
581  return to_pay;
582 
583  remain = to_pay;
584  for (i = 0; i < NUM_COINS; i++)
585  coin_objs[i] = NULL;
586 
587  /* This hunk should remove all the money objects from the player/container */
588  other_money_len = 0;
589  FOR_INV_PREPARE(pouch, tmp) {
590  if (tmp->type == MONEY) {
591  for (i = 0; i < NUM_COINS; i++) {
592  if (!strcmp(coins[NUM_COINS-1-i], tmp->arch->name)
593  && (tmp->value == tmp->arch->clone.value)) {
594  /* This should not happen, but if it does, just
595  * merge the two.
596  */
597  if (coin_objs[i] != NULL) {
598  LOG(llevError, "%s has two money entries of (%s)\n", pouch->name, coins[NUM_COINS-1-i]);
599  object_remove(tmp);
600  coin_objs[i]->nrof += tmp->nrof;
602  } else {
603  object_remove(tmp);
604  coin_objs[i] = tmp;
605  }
606  break;
607  }
608  }
609  if (i == NUM_COINS) {
610  if (other_money_len >= sizeof(other_money)/sizeof(*other_money)) {
611  LOG(llevError, "pay_for_item: Cannot store non-standard money object %s\n", tmp->arch->name);
612  } else {
613  object_remove(tmp);
614  other_money[other_money_len++] = tmp;
615  }
616  }
617  }
618  } FOR_INV_FINISH();
619 
620  /* Fill in any gaps in the coin_objs array - needed to make change. */
621  /* Note that the coin_objs array goes from least value to greatest value */
622  for (i = 0; i < NUM_COINS; i++)
623  if (coin_objs[i] == NULL) {
624  at = find_archetype(coins[NUM_COINS-1-i]);
625  if (at == NULL) {
626  continue;
627  }
628  coin_objs[i] = object_new();
629  object_copy(&at->clone, coin_objs[i]);
630  coin_objs[i]->nrof = 0;
631  }
632 
633  /* Try to pay from standard coins first. */
634  remain = remove_value(coin_objs, remain);
635 
636  /* Now pay from non-standard coins until all is paid. */
637  for (i = 0; i < other_money_len && remain > 0; i++) {
638  uint32_t nrof;
639  object *coin;
640 
641  coin = other_money[i];
642 
643  /* Find the minimal number of coins to use. This prevents converting
644  * excess non-standard coins to standard money.
645  */
646  nrof = (remain+coin->value-1)/coin->value;
647  if (nrof > coin->nrof) {
648  nrof = coin->nrof;
649  }
650  coin->nrof -= nrof;
651  add_value(coin_objs, nrof*coin->value);
652 
653  remain = remove_value(coin_objs, remain);
654  }
655 
656  /* re-insert remaining coins into player */
657  insert_objects(pl, pouch, coin_objs, NUM_COINS);
658  insert_objects(pl, pouch, other_money, other_money_len);
659 
660  return(remain);
661 }
662 
663 uint64_t add_with_overflow(uint64_t a, uint64_t b) {
664  if (a == UINT64_MAX) {
665  return a;
666  }
667 
668  if (UINT64_MAX - a < b) {
669  // Overflow
670  return UINT64_MAX;
671  } else {
672  return a + b;
673  }
674 }
675 
676 struct unpaid_count {
677  object *pl;
678  int count;
679  uint64_t price;
680 };
681 
689 static void unpaid_iter(object *item, void (*callback)(object *item, void *data), void *data) {
691  if (QUERY_FLAG(item, FLAG_UNPAID)) {
692  callback(item, data);
693  }
694  if (item->inv) {
695  unpaid_iter(item->inv, callback, data);
696  }
698 }
699 
700 static void count_unpaid_callback(object *item, void *data) {
701  struct unpaid_count *args = (struct unpaid_count *)data;
702  args->count++;
703  args->price = add_with_overflow(args->price, shop_price_buy(item, args->pl));
704 }
705 
718 static void count_unpaid(object *pl, object *item, int *unpaid_count, uint64_t *unpaid_price) {
719  struct unpaid_count args = {pl, 0, 0};
720  unpaid_iter(item, count_unpaid_callback, &args);
721  *unpaid_count = args.count;
722  *unpaid_price = args.price;
723 }
724 
732 static void count_coins(object *item, uint32_t *coincount) {
734  /* Merely converting the player's monetary wealth won't do.
735  * If we did that, we could print the wrong numbers for the
736  * coins, so we count the money instead.
737  */
738  for (int i = 0; i < NUM_COINS; i++) {
739  if (!strcmp(coins[i], item->arch->name)) {
740  coincount[i] += item->nrof;
741  break;
742  }
743  }
744  if (item->inv) {
745  count_coins(item->inv, coincount);
746  }
748 }
749 
758 static uint64_t compute_price_variation_with_bargaining(object *pl, uint64_t price, float max_variation) {
760  if (skill && skill->level > 0) {
761  return rndm(0, price * (max_variation * skill->level / settings.max_level));
762  }
763  return 0;
764 }
765 
778 int can_pay(object *pl) {
779  int unpaid_count = 0, i;
780  uint64_t unpaid_price = 0;
781  uint32_t coincount[NUM_COINS];
782 
783  if (!pl || pl->type != PLAYER) {
784  LOG(llevError, "can_pay(): called against something that isn't a player\n");
785  return 0;
786  }
787  uint64_t player_wealth = query_money(pl);
788 
789  for (i = 0; i < NUM_COINS; i++)
790  coincount[i] = 0;
791 
792  count_unpaid(pl, pl->inv, &unpaid_count, &unpaid_price);
793  count_coins(pl->inv, coincount);
794 
795  if (unpaid_price > player_wealth) {
796  char buf[MAX_BUF], coinbuf[MAX_BUF];
797  int denominations = 0;
798  char *value = cost_str(unpaid_price);
799 
800  snprintf(buf, sizeof(buf), "You have %d unpaid items that would cost you %s, ", unpaid_count, value);
801  free(value);
802  for (i = 0; i < NUM_COINS; i++) {
803  if (coincount[i] > 0 && coins[i]) {
804  if (denominations == 0)
805  snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "but you only have");
806  denominations++;
807  archetype *arch = find_archetype(coins[i]);
808  if (arch != NULL)
809  {
810  snprintf(coinbuf, sizeof(coinbuf), " %u %s,", coincount[i], arch->clone.name_pl);
811  snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s", coinbuf);
812  }
813  }
814  }
815  if (denominations == 0)
816  snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "but you don't have any money.");
817  else if (denominations > 1)
821  return 0;
822  } else
823  return 1;
824 }
825 
826 static void shop_pay_unpaid_callback(object *op, void *data) {
827  object *pl = (object *)data;
828  char name_op[MAX_BUF];
829  uint64_t price = shop_price_buy(op, pl);
830  uint64_t reduction = 0;
831  if (!object_value_set(op, "price")) {
832  // only items without a player-set price can be bargained for
834  }
835  if (!pay_for_item(op, pl, reduction)) {
836  uint64_t i = price - query_money(pl);
837  char *missing = cost_str(i);
838 
839  CLEAR_FLAG(op, FLAG_UNPAID);
840  query_name(op, name_op, MAX_BUF);
843  "You lack %s to buy %s.",
844  missing, name_op);
845  free(missing);
846  SET_FLAG(op, FLAG_UNPAID);
847  return;
848  } else {
849  // TODO: Figure out how to pass in the shop owner for player shops.
850  if (events_execute_object_event(op, EVENT_BOUGHT, pl, NULL, NULL, SCRIPT_FIX_ALL) != 0)
851  return;
852 
853  object *tmp;
854  char *value = cost_str(price - reduction);
855 
856  CLEAR_FLAG(op, FLAG_UNPAID);
859  query_name(op, name_op, MAX_BUF);
860 
861  if (reduction > 0) {
862  char *reduction_str = cost_str(reduction);
865  "You paid %s for %s after bargaining a reduction of %s.",
866  value, name_op, reduction_str);
867  change_exp(pl, reduction, "bargaining", SK_EXP_NONE);
868  free(reduction_str);
869  } else {
872  "You paid %s for %s.",
873  value, name_op);
874  }
875  free(value);
876  tmp = object_merge(op, NULL);
877  if (pl->type == PLAYER && !tmp) {
878  /* If item wasn't merged we update it. If merged, object_merge() handled everything for us. */
880  }
881  }
882 }
883 
890 int shop_pay_unpaid(object *pl, object *op) {
891  if (!op) {
892  return 1;
893  }
895  return 1;
896 }
897 
909 void sell_item(object *op, object *pl) {
910  object *tmp;
911  archetype *at;
912  char obj_name[MAX_BUF];
913 
914  query_name(op, obj_name, MAX_BUF);
915 
916  if (pl == NULL || pl->type != PLAYER) {
917  LOG(llevDebug, "Object other than player tried to sell something.\n");
918  return;
919  }
920 
921  if (events_execute_object_event(op, EVENT_SELLING, pl, NULL, NULL, SCRIPT_FIX_ALL) != 0)
922  return;
923 
924  object_set_value(op, CUSTOM_NAME_FIELD, NULL, 0);
925 
926  uint64_t price = shop_price_sell(op, pl);
927  if (price == 0) {
930  "We're not interested in %s.",
931  obj_name);
932  return;
933  }
935 
937  char *value_str = cost_str(price + extra_gain);
938 
939  if (extra_gain > 0) {
940  change_exp(pl, extra_gain, "bargaining", SK_EXP_NONE);
941  char *extra_str = cost_str(extra_gain);
943  "You receive %s for %s, after bargaining for %s more than proposed.", value_str, obj_name, extra_str);
944  free(extra_str);
945  price += extra_gain;
946  } else {
948  "You receive %s for %s.", value_str, obj_name);
949  }
950  free(value_str);
951 
952  for (int count = LARGEST_COIN_GIVEN; coins[count] != NULL; count++) {
953  at = find_archetype(coins[count]);
954  if (at == NULL)
955  LOG(llevError, "Could not find %s archetype\n", coins[count]);
956  else if ((price/at->clone.value) > 0) {
957  FOR_INV_PREPARE(pl, pouch) {
958  // Try to put money in player's active container
959  int n = price/at->clone.value;
960  // pass NULL as PL to avoid sending player messages about things that don't fit
961  if (pouch->type == CONTAINER && sack_can_hold(NULL, pouch, &at->clone, n)) {
962  tmp = object_new();
963  object_copy(&at->clone, tmp);
964  tmp->nrof = n;
965  price -= (uint64_t)tmp->nrof*(uint64_t)tmp->value;
966  tmp = object_insert_in_ob(tmp, pouch);
968  }
969  } FOR_INV_FINISH();
970  // Any left over get put into player's inventory
971  // TODO: This doesn't check the player's weight limit
972  if (price/at->clone.value > 0) {
973  tmp = object_new();
974  object_copy(&at->clone, tmp);
975  tmp->nrof = price/tmp->value;
976  price -= (uint64_t)tmp->nrof*(uint64_t)tmp->value;
977  tmp = object_insert_in_ob(tmp, pl);
979  }
980  }
981  }
982 
983  if (price != 0) {
984  LOG(llevError, "Warning - payment not zero: %" PRIo64 "\n", price);
985  }
986 
987  SET_FLAG(op, FLAG_UNPAID);
988  identify(op);
989 }
990 
1006 static double shop_specialisation_ratio(const object *item, const mapstruct *map) {
1007  shopitems *items = map->shopitems;
1008  double ratio = SPECIALISATION_EFFECT, likedness = 0.001;
1009  int i;
1010 
1011  if (item == NULL) {
1012  LOG(llevError, "shop_specialisation_ratio: passed a NULL item for map %s\n", map->path);
1013  return 0;
1014  }
1015  if (item->type == (uint8_t)-1) {
1016  LOG(llevError, "shop_specialisation_ratio: passed an item with an invalid type\n");
1017  /*
1018  * I'm not really sure what the /right/ thing to do here is,
1019  * these types of item shouldn't exist anyway, but returning
1020  * the ratio is probably the best bet.."
1021  */
1022  return ratio;
1023  }
1024  if (map->shopitems) {
1025  for (i = 0; i < items[0].index; i++)
1026  if (items[i].typenum == item->type || (items[i].typenum == -1 && likedness == 0.001))
1027  likedness = items[i].strength/100.0;
1028  }
1029  if (likedness > 1.0) { /* someone has been rather silly with the map headers. */
1030  LOG(llevDebug, "shop_specialisation ratio: item type %d on map %s is above 100%%\n", item->type, map->path);
1031  likedness = 1.0;
1032  }
1033  if (likedness < -1.0) {
1034  LOG(llevDebug, "shop_specialisation ratio: item type %d on map %s is below -100%%\n", item->type, map->path);
1035  likedness = -1.0;
1036  }
1037  ratio = ratio+(1.0-ratio)*likedness;
1038  if (ratio <= 0.1)
1039  ratio = 0.1; /* if the ratio were much lower than this, we would get silly prices */
1040  return ratio;
1041 }
1042 
1057 static double shop_greed(const mapstruct *map) {
1058  float greed = map->shopgreed;
1059  if (greed == 0) {
1060  greed = 1;
1061  }
1062  return tanh(-greed+2.0)/2 + 0.5;
1063 }
1064 
1065 double shop_approval(const mapstruct *map, const object *player) {
1066  double approval = 1.0;
1067  if (map->shoprace) {
1068  approval = NEUTRAL_RATIO;
1069  if (player->race && !strcmp(player->race, map->shoprace))
1070  approval = 1.0;
1071  }
1072  return approval;
1073 }
1074 
1094 static uint64_t value_limit(uint64_t val, int quantity, const object *who, int isshop) {
1095  uint64_t newval, unit_price;
1096  mapstruct *map;
1097 
1098  unit_price = val/quantity;
1099  if (!isshop || !who) {
1100  if (unit_price > 10000)
1101  newval = 8000+isqrt(unit_price)*20;
1102  else
1103  newval = unit_price;
1104  } else {
1105  if (!who->map) {
1106  LOG(llevError, "value_limit: asked shop price for ob %s on NULL map\n", who->name);
1107  return val;
1108  }
1109  map = who->map;
1110  if (map->shopmin && unit_price < map->shopmin)
1111  return 0;
1112  else if (unit_price > map->shopmax/2)
1113  newval = MIN((map->shopmax/2)+isqrt(unit_price-map->shopmax/2), map->shopmax);
1114  else if (unit_price > 10000)
1115  newval = 8000+isqrt(unit_price)*20;
1116  else
1117  newval = unit_price;
1118  }
1119  newval *= quantity;
1120  return newval;
1121 }
1122 
1128 int shop_describe(const object *op) {
1129  mapstruct *map = op->map;
1130  /*shopitems *items=map->shopitems;*/
1131  int pos = 0, i;
1132  double opinion = 0;
1133  char tmp[MAX_BUF] = "\0", *value;
1134 
1135  if (op->type != PLAYER)
1136  return 0;
1137 
1138  /*check if there is a shop specified for this map */
1139  if (map->shopitems
1140  || map->shopgreed
1141  || map->shoprace
1142  || map->shopmin
1143  || map->shopmax) {
1145  "From looking at the nearby shop you determine that it trades in:");
1146 
1147  if (map->shopitems) {
1148  for (i = 0; i < map->shopitems[0].index; i++) {
1149  if (map->shopitems[i].name && map->shopitems[i].strength > 10) {
1150  snprintf(tmp+pos, sizeof(tmp)-pos, "%s, ", map->shopitems[i].name_pl);
1151  pos += strlen(tmp+pos);
1152  }
1153  }
1154  }
1155  if (!pos)
1156  strcpy(tmp, "a little of everything.");
1157 
1158  /* format the string into a list */
1159  make_list_like(tmp);
1160  draw_ext_info(NDI_UNIQUE, 0, op,
1162 
1163  if (map->shopmax) {
1164  value = cost_str(map->shopmax);
1167  "It won't trade for items above %s.",
1168  value);
1169  free(value);
1170  }
1171 
1172  if (map->shopmin) {
1173  value = cost_str(map->shopmin);
1176  "It won't trade in items worth less than %s.",
1177  value);
1178  free(value);
1179  }
1180 
1181  if (map->shopgreed) {
1182  if (map->shopgreed > 2.0)
1183  draw_ext_info(NDI_UNIQUE, 0, op,
1185  "It tends to overcharge massively.");
1186  else if (map->shopgreed > 1.5)
1187  draw_ext_info(NDI_UNIQUE, 0, op,
1189  "It tends to overcharge substantially.");
1190  else if (map->shopgreed > 1.1)
1191  draw_ext_info(NDI_UNIQUE, 0, op,
1193  "It tends to overcharge slightly.");
1194  else if (map->shopgreed < 0.9)
1195  draw_ext_info(NDI_UNIQUE, 0, op,
1197  "It tends to undercharge.");
1198  }
1199  if (map->shoprace) {
1200  opinion = shop_approval(map, op);
1201  if (opinion > 0.8)
1202  draw_ext_info(NDI_UNIQUE, 0, op,
1204  "You think the shopkeeper likes you.");
1205  else if (opinion > 0.5)
1206  draw_ext_info(NDI_UNIQUE, 0, op,
1208  "The shopkeeper seems unconcerned by you.");
1209  else
1210  draw_ext_info(NDI_UNIQUE, 0, op,
1212  "The shopkeeper seems to have taken a dislike to you.");
1213  }
1215  "There is no shop nearby.");
1216 
1217  return 1;
1218 }
mapstruct::shopgreed
double shopgreed
How much our shopkeeper overcharges.
Definition: map.h:351
object_value_set
bool object_value_set(const object *op, const char *const key)
Determine if an extra value is set.
Definition: object.cpp:4361
object::name_pl
sstring name_pl
The plural name of the object.
Definition: object.h:323
cost_approx_str
char * cost_approx_str(const object *tmp, object *who)
Return a textual cost approximation in a newly-allocated string.
Definition: shop.cpp:312
shopitems::strength
int8_t strength
The degree of specialisation the shop has in this item, as a percentage from -100 to 100.
Definition: map.h:302
shop_approval
double shop_approval(const mapstruct *map, const object *player)
Return the approval ratio for a shop for a given player.
Definition: shop.cpp:1065
PLAYER
@ PLAYER
Definition: object.h:112
EVENT_GBOUGHT
#define EVENT_GBOUGHT
Player bought object in shop, but global.
Definition: events.h:70
global.h
sell_item
void sell_item(object *op, object *pl)
Player is selling an item.
Definition: shop.cpp:909
settings
struct Settings settings
Global settings.
Definition: init.cpp:139
shop_cha_modifier
static float shop_cha_modifier(int charisma)
Calculate the buy price multiplier based on a player's charisma.
Definition: shop.cpp:111
Settings::max_level
int16_t max_level
This is read out of exp_table.
Definition: global.h:303
unpaid_count::price
uint64_t price
Definition: shop.cpp:679
llevError
@ llevError
Error, serious thing.
Definition: logger.h:11
find_skill_by_number
object * find_skill_by_number(object *who, int skillno)
This returns the first skill pointer of the given subtype (the one that accumulates exp,...
Definition: main.cpp:375
unpaid_count::count
int count
Definition: shop.cpp:678
LOG
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:58
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:369
player
One player.
Definition: player.h:107
object::inv
object * inv
Pointer to the first object in the inventory.
Definition: object.h:298
LARGEST_COIN_GIVEN
#define LARGEST_COIN_GIVEN
Never give amber or jade, but accept them.
Definition: shop.cpp:59
MAX_SELL_EXTRA
#define MAX_SELL_EXTRA
Maximum price increase when selling an item with bargaining skill.
Definition: shop.cpp:50
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:371
mapstruct::shopmin
uint64_t shopmin
Minimum price a shop will trade for.
Definition: map.h:352
compute_price_variation_with_bargaining
static uint64_t compute_price_variation_with_bargaining(object *pl, uint64_t price, float max_variation)
Compute a percent of the price which will be used as extra or reduction.
Definition: shop.cpp:758
unpaid_iter
static void unpaid_iter(object *item, void(*callback)(object *item, void *data), void *data)
Search for unpaid items in 'item' and call 'callback' on each item.
Definition: shop.cpp:689
object_merge
object * object_merge(object *op, object *top)
This function goes through all objects below and including top, and merges op to the first matching o...
Definition: object.cpp:2036
cost_str
char * cost_str(uint64_t cost)
Definition: shop.cpp:308
stringbuffer_append_printf
void stringbuffer_append_printf(StringBuffer *sb, const char *format,...)
Append a formatted string to a string buffer instance.
Definition: stringbuffer.cpp:138
find_next_coin
static archetype * find_next_coin(uint64_t c, int *cointype)
Find the coin type that is worth more than 'c'.
Definition: shop.cpp:188
FALSE
#define FALSE
Definition: compat.h:14
add_value
static void add_value(object *coin_objs[], int64_t value)
This function adds a given amount to a list of coins.
Definition: shop.cpp:520
c
static event_registration c
Definition: citylife.cpp:424
object::arch
struct archetype * arch
Pointer to archetype.
Definition: object.h:424
SPECIALISATION_EFFECT
#define SPECIALISATION_EFFECT
This is a measure of how effective store specialisation is.
Definition: shop.cpp:39
stringbuffer_new
StringBuffer * stringbuffer_new(void)
Create a new string buffer.
Definition: stringbuffer.cpp:57
UPD_WEIGHT
#define UPD_WEIGHT
Definition: newclient.h:320
if
if(!(yy_init))
Definition: loader.cpp:36435
EVENT_BOUGHT
#define EVENT_BOUGHT
Object is being bought by player.
Definition: events.h:31
price_approx
uint64_t price_approx(const object *tmp, object *who)
Adjust the value of the given item based on the player's skills.
Definition: shop.cpp:71
insert_objects
static void insert_objects(object *pl, object *container, object *objects[], int objects_len)
Insert a list of objects into a player object.
Definition: shop.cpp:544
object::map
struct mapstruct * map
Pointer to the map in which this object is present.
Definition: object.h:305
typedata::identifyskill
int identifyskill
Skill used to identify this object class.
Definition: define.h:93
shopitems::index
int index
Being the size of the shopitems array.
Definition: map.h:304
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
MIN
#define MIN(x, y)
Definition: compat.h:21
object::count
tag_t count
Unique object number for this object.
Definition: object.h:307
object_copy
void object_copy(const object *src_ob, object *dest_ob)
Copy object first frees everything allocated by the second object, and then copies the contents of th...
Definition: object.cpp:1177
fix_object
void fix_object(object *op)
Updates all abilities given by applied objects in the inventory of the given object.
Definition: living.cpp:1132
shop_pay_unpaid_callback
static void shop_pay_unpaid_callback(object *op, void *data)
Definition: shop.cpp:826
SCRIPT_FIX_ALL
#define SCRIPT_FIX_ALL
Definition: global.h:380
MSG_TYPE_SHOP_PAYMENT
#define MSG_TYPE_SHOP_PAYMENT
Messages about payment, lack of funds.
Definition: newclient.h:514
coins
static const char *const coins[]
Coins to use for shopping.
Definition: shop.cpp:62
rndm
int rndm(int min, int max)
Returns a number between min and max.
Definition: utils.cpp:162
SK_EXP_NONE
#define SK_EXP_NONE
Player gets nothing.
Definition: skills.h:80
NEUTRAL_RATIO
#define NEUTRAL_RATIO
Price a shopkeeper will give someone they neither like nor dislike.
Definition: shop.cpp:45
mapstruct::path
char path[HUGE_BUF]
Filename of the map.
Definition: map.h:358
MSG_TYPE_SHOP_MISC
#define MSG_TYPE_SHOP_MISC
Random messages.
Definition: newclient.h:517
object::level
int16_t level
Level of creature or object.
Definition: object.h:361
events_execute_object_event
int events_execute_object_event(object *op, int eventcode, object *activator, object *third, const char *message, int fix)
Execute an event on the specified object.
Definition: events.cpp:309
buf
StringBuffer * buf
Definition: readable.cpp:1565
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
This function inserts the object op in the linked list inside the object environment.
Definition: object.cpp:2842
pay_for_item
int pay_for_item(object *op, object *pl, uint64_t reduction)
Player attemps to buy an item, if she has enough money then remove coins as needed from active contai...
Definition: shop.cpp:440
NUM_COINS
#define NUM_COINS
Number of coin types.
Definition: shop.cpp:57
real_money_value
static StringBuffer * real_money_value(const object *coin, StringBuffer *buf)
Returns a string representing the money's value, in plain coins.
Definition: shop.cpp:300
draw_ext_info
vs only yadda is in because all tags get reset on the next draw_ext_info In the second since it is all in one draw_ext_info
Definition: media-tags.txt:61
mapstruct::shopitems
struct shopitems * shopitems
List of item-types the map's shop will trade in.
Definition: map.h:349
typedata
Link an object type with skill needed to identify, and general name.
Definition: define.h:89
stringbuffer_finish
char * stringbuffer_finish(StringBuffer *sb)
Deallocate the string buffer instance and return the string.
Definition: stringbuffer.cpp:76
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
Frees everything allocated by an object, removes it from the list of used objects,...
Definition: object.cpp:1545
NROF
static uint32_t NROF(const object *const ob)
Returns ob->nrof, unless it is 0, in which case return 1.
Definition: object.h:625
FLAG_PLAYER_SOLD
#define FLAG_PLAYER_SOLD
Object was sold to a shop by a player.
Definition: define.h:239
get_typedata
const typedata * get_typedata(int itemtype)
Definition: item.cpp:328
FLAG_UNPAID
#define FLAG_UNPAID
Object hasn't been paid for yet.
Definition: define.h:223
shop_price_sell
uint64_t shop_price_sell(const object *tmp, object *who)
Adjust the value of an item to be sold based on the player's bargaining skill and charisma.
Definition: shop.cpp:154
query_name
void query_name(const object *op, char *buf, size_t size)
Describes an item.
Definition: item.cpp:594
shop_pay_unpaid
int shop_pay_unpaid(object *pl, object *op)
Pay as many unpaid items as possible, recursing on op->inv and op->below.
Definition: shop.cpp:890
query_money
uint64_t query_money(const object *op)
Determine the amount of money the given object contains, including what is inside containers.
Definition: shop.cpp:373
shop_specialisation_ratio
static double shop_specialisation_ratio(const object *item, const mapstruct *map)
Returns the ratio of the price that a shop will offer for an item based on the shop's specialisation.
Definition: shop.cpp:1006
sack_can_hold
int sack_can_hold(const object *pl, const object *sack, const object *op, uint32_t nrof)
Check if an item op can be put into a sack.
Definition: container.cpp:66
mapstruct::shoprace
char * shoprace
The preffered race of the local shopkeeper.
Definition: map.h:350
archetype::clone
object clone
An object from which to do object_copy()
Definition: object.h:487
price_base
uint64_t price_base(const object *obj)
Price an item based on its value or archetype value, type, identification/BUC status,...
Definition: item.cpp:1507
FOR_OB_AND_BELOW_FINISH
#define FOR_OB_AND_BELOW_FINISH()
Finishes FOR_OB_AND_BELOW_PREPARE().
Definition: define.h:738
unpaid_count
Definition: shop.cpp:676
CONTAINER
@ CONTAINER
Definition: object.h:236
done
int done
Definition: readable.cpp:1566
object::value
int32_t value
How much money it is worth (or contains)
Definition: object.h:360
isqrt
int isqrt(int n)
Compute the square root.
Definition: utils.cpp:562
cost_string_from_value
char * cost_string_from_value(uint64_t cost, int largest_coin)
Converts a price to number of coins.
Definition: shop.cpp:220
object::type
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:348
MAX_BUY_REDUCTION
#define MAX_BUY_REDUCTION
Maximum price reduction when buying an item with bargaining skill.
Definition: shop.cpp:48
UPD_FLAGS
#define UPD_FLAGS
Definition: newclient.h:319
shopitems::name_pl
const char * name_pl
Plural name.
Definition: map.h:300
FOR_INV_FINISH
#define FOR_INV_FINISH()
Finishes FOR_INV_PREPARE().
Definition: define.h:661
change_exp
void change_exp(object *op, int64_t exp, const char *skill_name, int flag)
Changes experience to a player/monster.
Definition: living.cpp:2179
SK_BARGAINING
@ SK_BARGAINING
Bargaining.
Definition: skills.h:28
archetype
The archetype structure is a set of rules on how to generate and manipulate objects which point to ar...
Definition: object.h:483
add_with_overflow
uint64_t add_with_overflow(uint64_t a, uint64_t b)
Definition: shop.cpp:663
sproto.h
stringbuffer_append_string
void stringbuffer_append_string(StringBuffer *sb, const char *str)
Append a string to a string buffer instance.
Definition: stringbuffer.cpp:95
FOR_OB_AND_BELOW_PREPARE
#define FOR_OB_AND_BELOW_PREPARE(op_)
Constructs a loop iterating over an object and all objects below it in the same pile.
Definition: define.h:734
MSG_TYPE_SHOP
#define MSG_TYPE_SHOP
Definition: newclient.h:407
EVENT_SELLING
#define EVENT_SELLING
Object is being sold by another one.
Definition: events.h:38
MAX_BUF
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
shop_describe
int shop_describe(const object *op)
A player is examining a shop, so describe it.
Definition: shop.cpp:1128
object_new
object * object_new(void)
Grabs an object from the list of unused objects, makes sure it is initialised, and returns it.
Definition: object.cpp:1258
shopitems::typenum
int typenum
Itemtype number we need to match, -1 if it is the default price.
Definition: map.h:301
mapstruct::shopmax
uint64_t shopmax
MMaximum price a shop will offer.
Definition: map.h:353
EVENT_GSOLD
#define EVENT_GSOLD
Player sold object in shop, but global.
Definition: events.h:71
StringBuffer
A buffer that will be expanded as content is added to it.
Definition: stringbuffer.cpp:25
value_limit
static uint64_t value_limit(uint64_t val, int quantity, const object *who, int isshop)
Limit the value of items based on the wealth of the shop.
Definition: shop.cpp:1094
count_coins
static void count_coins(object *item, uint32_t *coincount)
Count the number of coins for each type, for all items below item and in inventory.
Definition: shop.cpp:732
shop_price_buy
uint64_t shop_price_buy(const object *tmp, object *who)
Adjust the value of an item to be bought based on the player's bargaining skill and charisma.
Definition: shop.cpp:128
UINT32_MAX
#define UINT32_MAX
Definition: loader.cpp:84
NDI_UNIQUE
#define NDI_UNIQUE
Print immediately, don't buffer.
Definition: newclient.h:266
object::name
sstring name
The name of the object, obviously...
Definition: object.h:319
MSG_TYPE_SHOP_SELL
#define MSG_TYPE_SHOP_SELL
Messages about selling items.
Definition: newclient.h:516
shopitems::name
const char * name
Name of the item in question, null if it is the default item.
Definition: map.h:299
stringbuffer_delete
void stringbuffer_delete(StringBuffer *sb)
Totally delete a string buffer.
Definition: stringbuffer.cpp:71
remove_value
static int64_t remove_value(object *coin_objs[], int64_t remain)
This function removes a given amount from a list of coins.
Definition: shop.cpp:479
mapstruct
This is a game-map.
Definition: map.h:318
find_archetype
archetype * find_archetype(const char *name)
Definition: assets.cpp:270
shop.h
FLAG_APPLIED
#define FLAG_APPLIED
Object is ready for use by living.
Definition: define.h:222
esrv_update_item
void esrv_update_item(int flags, object *pl, object *op)
Updates object *op for player *pl.
Definition: main.cpp:359
unpaid_count::pl
object * pl
Definition: shop.cpp:677
objects
object * objects
Pointer to the list of used objects.
Definition: object.cpp:294
shopitems
Shop-related information for a map.
Definition: map.h:298
make_list_like
void make_list_like(char *input)
Taking a string as an argument, mutate it into a string that looks like a list.
Definition: utils.cpp:368
object_get_value
sstring object_get_value(const object *op, const char *const key)
Get an extra value by key.
Definition: object.cpp:4331
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:370
shop_efficiency
float shop_efficiency(const object *player)
Return the shop's efficiency (E) for a player, a number greater than (but not equal to) zero and less...
Definition: shop.cpp:122
MSG_TYPE_SHOP_LISTING
#define MSG_TYPE_SHOP_LISTING
Shop listings - inventory, what it deals in.
Definition: newclient.h:512
shop_greed
static double shop_greed(const mapstruct *map)
Gets a shop's greed.
Definition: shop.cpp:1057
typedata::identifyskill2
int identifyskill2
Second skill used to identify this object class.
Definition: define.h:94
data
====Textual A command containing textual data has data fields separated by one ASCII space character. word::A sequence of ASCII characters that does not contain the space or nul character. This is to distinguish it from the _string_, which may contain space characters. Not to be confused with a machine word. int::A _word_ containing the textual representation of an integer. Not to be confused with any of the binary integers in the following section. Otherwise known as the "string value of integer data". Must be parsed, e.g. using `atoi()` to get the actual integer value. string::A sequence of ASCII characters. This must only appear at the end of a command, since spaces are used to separate fields of a textual message.=====Binary All multi-byte integers are transmitted in network byte order(MSB first). int8::1-byte(8-bit) integer int16::2-byte(16-bit) integer int32::4-byte(32-bit) integer lstring::A length-prefixed string, which consists of an `int8` followed by that many bytes of the actual string. This is used to transmit a string(that may contain spaces) in the middle of binary data. l2string::Like _lstring_, but is prefixed with an `int16` to support longer strings Implementation Notes ~~~~~~~~~~~~~~~~~~~~ - Typical implementations read two bytes to determine the length of the subsequent read for the actual message, then read and parse the data from each message according to the commands described below. To send a message, the sender builds the message in a buffer, counts the length of the message, sends the length, and finally sends the actual message. TIP:Incorrectly transmitting or receiving the `length` field can lead to apparent "no response" issues as the client or server blocks to read the entire length of the message. - Since the protocol is highly interactive, it may be useful to set `TCP_NODELAY` on both the client and server. - If you are using a language with a buffered output stream, remember to flush the stream after a complete message. - If the connection is lost(which will also happen if the output buffer overflowing), the player is saved and the server cleans up. This does open up some abuses, but there is no perfect solution here. - The server only reads data from the socket if the player has an action. This isn 't really good, since many of the commands below might not be actual commands for the player. The alternative is to look at the data, and if it is a player command and there isn 't time, store it away to be processed later. But this increases complexity, in that the server must start buffering the commands. Fortunately, for now, there are few such client commands. Commands -------- In the documentation below, `S->C` represents a message to the client from the server, and `C->S` represents a message to the server from the client. Commands are documented in a brief format like:C->S:version< csval >[scval[vinfo]] Fields are enclosed like `< this >`. Optional fields are denoted like `[this]`. Spaces that appear in the command are literal, i.e. the<< _version > > command above uses spaces to separate its fields, but the command below does not:C->S:accountlogin< name >< password > As described in<< _messages > >, if a command contains data, then the command is separated from the data by a literal space. Many of the commands below refer to 'object tags'. Whenever the server creates an object, it creates a unique tag for that object(starting at 1 when the server is first run, and ever increasing.) Tags are unique, but are not consistent between runs. Thus, the client can not store tags when it exits and hope to re-use them when it joins the server at a later time - tags are only valid for the current connection. The protocol commands are broken into various sections which based somewhat on what the commands are for(ie, item related commands, map commands, image commands, etc.) In this way, all the commands related to similar functionality is in the same place. Initialization ~~~~~~~~~~~~~~ version ^^^^^^^ C->S:version< csval >[scval[vinfo]] S->C:version< csval >[scval[vinfo]] Used by the client and server to exchange which version of the Crossfire protocol they understand. Neither send this in response to the other - they should both send this shortly after a connection is established. csval::int, version level of C->S communications scval::int, version level of S->C communications vinfo::string, that is purely for informative that general client/server info(ie, javaclient, x11client, winclient, sinix server, etc). It is purely of interest of server admins who can see what type of clients people are using.=====Version ID If a new command is added to the protocol in the C->S direction, then the version number in csval will get increased. Likewise, the same is true for the scval. The version are currently integers, in the form ABCD. A=1, and will likely for quite a while. This will only really change if needed from rollover of B. B represents major protocol changes - if B mismatches, the clients will be totally unusable. Such an example would be change of map or item sending commands(either new commands or new format.) C represents more minor but still significant changes - clients might still work together, but some features that used to work may now fail due to the mismatch. An example may be a change in the meaning of some field in some command - providing the field is the same size, it still should be decoded properly, but the meaning won 't be processed properly. D represents very minor changes or new commands. Things should work no worse if D does not match, however if they do match, some new features might be included. An example of the would be the C->S mark command to mark items. Server not understanding this just means that the server can not process it, and will ignore it.=====Handling As far as the client is concerned, its _scval_ must be at least equal to the server, and its _csval_ should not be newer than the server. The server does not care about the version command it receives right now - all it currently does is log mismatches. In theory, the server should keep track of what the client has, and adjust the commands it sends respectively in the S->C direction. The server is resilant enough that it won 't crash with a version mismatch(however, client may end up sending commands that the server just ignores). It is really up to the client to enforce versioning and quit if the versions don 't match. NOTE:Since all packets have the length as the first 2 bytes, all that either the client or server needs to be able to do is look at the first string and see if it understands it. If not, it knows how many bytes it can skip. As such, exact version matches should not be necessary for proper operation - however, both the client and server needs to be coded to handle such cases.=====History _scval_ and _vinfo_ were added in version 1020. Before then, there was only one version sent in the version command. NOTE:For the most part, this has been obsoleted by the setup command which always return status and whether it understood the command or not. However there are still some cases where using this versioning is useful - an example it the addition of the requestinfo/replyinfo commands - the client wants to wait for acknowledge of all the replyinfo commands it has issued before sending the addme command. However, if the server doesn 't understand these options, the client will never get a response. With the versioning, the client can look at the version and know if it should wait for a response or if the server will never send back. setup ^^^^^ C->S, S->C:setup< option1 >< value1 >< option2 >< value2 > ... Sent by the client to request protocol option changes. This can be at any point during the life of a connection, but usually sent at least once right after the<< _version > > command. The server responds with a message in the same format confirming what configuration options were set. The server only sends a setup command in response to one from the client. The sc_version should be updated in the server if commands have been obsoleted such that old clients may not be able to play. option::word, name of configuration option value::word, value of configuration option. May need further parsing according to the setup options below=====Setup Options There are really 2 set of setup commands here:. Those that control preferences of the client(how big is the map, what faceset to use, etc). . Those that describe capabilities of the client(client supports this protocol command or that) .Setup Options[options="autowidth,header"]|===========================|Command|Description|beat|Ask the server to enable heartbeat support. When heartbeat is enabled, the client must send the server a command every three seconds. If no commands need to be sent, use the `beat` no-op command. Clients that do not contact the server within the interval are assumed to have a temporary connection failure.|bot(0/1 value)|If set to 1, the client will not be considered a player when updating information to the metaserver. This is to avoid having a server with many bots appear more crowded than others.|darkness(0/1 value)|If set to 1(default), the server will send darkness information in the map protocol commands. If 0, the server will not include darkness, thus saving a minor amount of bandwidth. Since the client is free to ignore the darkness information, this does not allow the client to cheat. In the case of the old 'map' protocol command, turning darkness off will result in the masking faces not getting sent to the client.|extended_stats(0/1 value)|If set to 1, the server will send the CS_STAT_RACE_xxx and CS_STAT_BASE_xxx values too, so the client can display various status related to statistics. Default is 0.|facecache(0/1)|Determines if the client is caching images(1) or wants the images sent to it without caching them(0). Default is 0. This replaces the setfacemode command.|faceset(8 bit)|Faceset the client wishes to use. If the faceset is not valid, the server returns the faceset the client will be using(default 0).|loginmethod(8 bit)|Client sends this to server to note login support. This is basically used as a subset of the csversion/scversion to find out what level of login support the server and client support. Current defined values:0:no advanced support - only legacy login method 1:account based login(described more below) 2:new character creation support This list may grow - for example, advanced character creation could become a feature.|map2cmd:(1)|This indicates client support for the map2 protocol command. See the map2 protocol details above for the main differences. Obsolete:This is the only supported mode now, but many clients use it as a sanity check for protocol versions, so the server still replies. It doesn 't do anything with the data|mapsize(int x) X(int y)|Sets the map size to x X y. Note the spaces here are only for clarity - there should be no spaces when actually sent(it should be 11x11 or 25x25). The default map size unless changed is 11x11. The minimum map size the server will allow is 9x9(no technical reason this could be smaller, but I don 't think the game would be smaller). The maximum map size supported in the current protocol is 63x63. However, each server can have its maximum map size sent to most any value. If the client sends an invalid mapsize command or a mapsize of 0x0, the server will respond with a mapsize that is the maximum size the server supports. Thus, if the client wants to know the maximum map size, it can just do a 'mapsize 0x0' or 'mapsize' and it will get the maximum size back. The server will constrain the provided mapsize x &y to the configured minumum and maximums. For example, if the maximum map size is 25x25, the minimum map size is 9x9, and the client sends a 31x7 mapsize request, the mapsize will be set to 25x9 and the server will send back a mapsize 25x9 setup command. When the values are valid, the server will send back a mapsize XxY setup command. Note that this is from its parsed values, so it may not match stringwise with what the client sent, but will match 0 wise. For example, the client may send a 'mapsize 025X025' command, in which case the server will respond with a 'mapsize 25x25' command - the data is functionally the same. The server will send an updated map view when this command is sent.|notifications(int value)|Value indicating what notifications the client accepts. It is incremental, a value means "all notifications till this level". The following levels are supported:1:quest-related notifications("addquest" and "updquest") 2:knowledge-related notifications("addknowledge") 3:character status flags(overloaded, blind,...)|num_look_objects(int value)|The maximum number of objects shown in the ground view. If more objects are present, fake objects are created for selecting the previous/next group of items. Defaults to 50 if not set. The server may adjust the given value to a suitable one data
Definition: protocol.txt:379
FMT64U
#define FMT64U
Definition: compat.h:17
UPD_NAME
#define UPD_NAME
Definition: newclient.h:322
count_unpaid_callback
static void count_unpaid_callback(object *item, void *data)
Definition: shop.cpp:700
Settings::real_wiz
uint8_t real_wiz
Use mud-like wizards.
Definition: global.h:272
skill
skill
Definition: arch-handbook.txt:585
object_remove
void object_remove(object *op)
This function removes the object op from the linked list of objects which it is currently tied to.
Definition: object.cpp:1818
a
Magical Runes Runes are magical inscriptions on the dungeon which cast a spell or detonate when something steps on them Flying objects don t detonate runes Beware ! Runes are invisible most of the time They are only visible occasionally ! There are several runes which are there are some special runes which may only be called with the invoke and people may apply it to read it Maybe useful for mazes ! This rune will not nor is it ordinarily invisible Partial Visibility of they ll be visible only part of the time They have a(your level/2) chance of being visible in any given round
archetype::name
sstring name
More definite name, like "generate_kobold".
Definition: object.h:484
object::nrof
uint32_t nrof
Number of objects.
Definition: object.h:342
FLAG_WAS_WIZ
#define FLAG_WAS_WIZ
Player was once a wiz.
Definition: define.h:221
object_set_value
int object_set_value(object *op, const char *key, const char *value, int add_key)
Updates the key in op to value.
Definition: object.cpp:4484
can_pay
int can_pay(object *pl)
Checks all unpaid items in op's inventory, adds up all the money they have, and checks that they can ...
Definition: shop.cpp:778
pay_for_amount
int pay_for_amount(uint64_t to_pay, object *pl)
Takes the amount of money from the the player inventory and from it's various pouches using the pay_f...
Definition: shop.cpp:402
CUSTOM_NAME_FIELD
#define CUSTOM_NAME_FIELD
Key in an object for the player-assigned custom name.
Definition: object.h:98
count_unpaid
static void count_unpaid(object *pl, object *item, int *unpaid_count, uint64_t *unpaid_price)
Sum the amount to pay for all unpaid items and find available money.
Definition: shop.cpp:718
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Constructs a loop iterating over the inventory of an object.
Definition: define.h:654
events_execute_global_event
void events_execute_global_event(int eventcode,...)
Execute a global event.
Definition: events.cpp:30
llevDebug
@ llevDebug
Only for debugging purposes.
Definition: logger.h:13
MONEY
@ MONEY
Definition: object.h:142
pay_from_container
static uint64_t pay_from_container(object *pl, object *pouch, uint64_t to_pay)
This pays for the item, and takes the proper amount of money off the specified container (pouch or pl...
Definition: shop.cpp:572
identify
object * identify(object *op)
Identifies an item.
Definition: item.cpp:1446