Atrinik Server  4.0
shop.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
30 #include <global.h>
31 #include <toolkit/string.h>
32 #include <arch.h>
33 
43 int64_t shop_get_cost(object *op, int mode)
44 {
45  HARD_ASSERT(op != NULL);
46 
47  SOFT_ASSERT_RC(op->arch != NULL, 0, "Object has no archetype: %s",
48  object_get_str(op));
49 
50  uint32_t nrof = MAX(1, op->nrof);
51  /* Money is always identified */
52  if (op->type == MONEY) {
53  return op->value * nrof;
54  }
55 
56  int64_t val;
57 
58  if (QUERY_FLAG(op, FLAG_IDENTIFIED) || !need_identify(op)) {
59  /* Handle identified items */
60  if (QUERY_FLAG(op, FLAG_CURSED) || QUERY_FLAG(op, FLAG_DAMNED)) {
61  /* Cursed or damned items have no value at all. */
62  return 0;
63  } else {
64  val = op->value * nrof;
65  }
66  } else {
67  /* This area deals with objects that are not identified, but can be. */
68  if (mode == COST_BUY) {
69  log_error("Asking for buy value of unidentified object: %s",
70  object_get_str(op));
71  val = op->arch->clone.value * nrof;
72  } else {
73  /* Trying to sell something, or get true value. */
74  if (op->type == GEM || op->type == JEWEL || op->type == NUGGET ||
75  op->type == PEARL) {
76  val = 3 * nrof;
77  } else if (op->type == POTION) {
78  val = 50 * nrof;
79  } else {
80  val = op->arch->clone.value * nrof;
81  }
82  }
83  }
84 
85  /* Handle spell tool items. */
86  if (OBJECT_IS_SPELL_TOOL(op)) {
87  int spell = SP_NO_SPELL;
88  if (op->stats.sp > SP_NO_SPELL && op->stats.sp < NROFREALSPELLS) {
89  spell = op->stats.sp;
90  }
91 
92  /* If we have a valid spell, increase the value using the value
93  * multiplier of the spell. */
94  if (spell != SP_NO_SPELL) {
95  val *= spells[spell].value_mul;
96  }
97 
98  int level = op->level;
99  if (op->type == BOOK_SPELL) {
100  /* Spell books don't have a level, so we need to use the base
101  * level of the spell instead. */
102  level = spells[spell].at->clone.level;
103  }
104 
105  int level_value = val * level;
106  if (op->type == WAND) {
107  /* Wands have increased value for each charge they hold. */
108  level_value *= op->stats.food;
109  }
110 
111  val += level_value;
112 
113  /* For spell books, add the base value of the spell as well. */
114  if (op->type == BOOK_SPELL && spell != SP_NO_SPELL) {
115  val += spells[spell].at->clone.value;
116  }
117  }
118 
119  /* We are done if we only want get the real value. */
120  if (mode == COST_TRUE) {
121  return val;
122  }
123 
124  double diff;
125  /* Now adjust for sell or buy multiplier. */
126  if (mode == COST_BUY) {
127  diff = 1.0;
128  } else {
129  diff = 0.20;
130  }
131 
132  val = (int64_t) (val * diff);
133  if (val < 1 && op->value > 0) {
134  val = 1;
135  }
136 
137  return val;
138 }
139 
150 static archetype_t *shop_get_next_coin(int64_t cost, int *cointype)
151 {
152  archetype_t *coin;
153 
154  do {
155  if (coins[*cointype] == NULL) {
156  return NULL;
157  }
158 
159  coin = arch_find(coins[*cointype]);
160  if (coin == NULL) {
161  return NULL;
162  }
163 
164  *cointype += 1;
165  } while (coin->clone.value > cost);
166 
167  return coin;
168 }
169 
178 const char *shop_get_cost_string(int64_t cost)
179 {
180  static char buf[MAX_BUF];
181 
182  int cointype = 0;
183  archetype_t *coin = shop_get_next_coin(cost, &cointype);
184  if (coin == NULL) {
185  return "nothing";
186  }
187 
188  int64_t num = cost / coin->clone.value;
189  cost -= num * coin->clone.value;
190 
191  snprintf(VS(buf), "%" PRId64 " %s%s%s", num,
193  coin->clone.name, num == 1 ? "" : "s");
194 
195  archetype_t *next_coin = shop_get_next_coin(cost, &cointype);
196  if (next_coin == NULL) {
197  return buf;
198  }
199 
200  do {
201  coin = next_coin;
202  num = cost / coin->clone.value;
203  cost -= num * coin->clone.value;
204 
205  if (cost == 0) {
206  next_coin = NULL;
207  } else {
208  next_coin = shop_get_next_coin(cost, &cointype);
209  }
210 
211  if (next_coin != NULL) {
212  /* There will be at least one more string to add to the list,
213  * use a comma. */
214  snprintfcat(VS(buf), ", ");
215  } else {
216  snprintfcat(VS(buf), " and ");
217  }
218 
219  snprintfcat(VS(buf), "%" PRId64 " %s%s%s", num,
221  coin->clone.name, num == 1 ? "" : "s");
222  } while (next_coin != NULL);
223 
224  return buf;
225 }
226 
238 const char *shop_get_cost_string_item(object *op, int mode)
239 {
240  return shop_get_cost_string(shop_get_cost(op, mode));
241 }
242 
251 int64_t shop_get_money(object *op)
252 {
253  HARD_ASSERT(op != NULL);
254 
255  SOFT_ASSERT_RC(op->type == PLAYER || op->type == CONTAINER, 0,
256  "Called with invalid object type: %s", object_get_str(op));
257 
258  int64_t total = 0;
259  FOR_INV_PREPARE(op, tmp) {
260  if (tmp->type == MONEY) {
261  total += tmp->nrof * tmp->value;
262  } else if (tmp->type == CONTAINER && (QUERY_FLAG(tmp, FLAG_APPLIED) ||
263  tmp->race == NULL || strstr(tmp->race, "gold") != NULL)) {
264  total += shop_get_money(tmp);
265  } else if (tmp->arch->name == shstr_cons.player_info &&
266  tmp->name == shstr_cons.BANK_GENERAL) {
267  total += tmp->value;
268  }
269  } FOR_INV_FINISH();
270 
271  return total;
272 }
273 
284 static int64_t shop_pay_inventory(object *obj, int64_t to_pay)
285 {
286  int64_t remain = to_pay;
287 
288  object *coins_objects[NUM_COINS];
289  for (int i = 0; i < NUM_COINS; i++) {
290  coins_objects[i] = NULL;
291  }
292 
293  /* Remove all the money objects from the container, and store them in
294  * the coin_objs pointers array. */
295  FOR_INV_PREPARE(obj, tmp) {
296  if (tmp->type != MONEY) {
297  continue;
298  }
299 
300  bool found = false;
301  for (int i = 0; i < NUM_COINS; i++) {
302  if (strcmp(coins[NUM_COINS - 1 - i], tmp->arch->name) != 0 ||
303  tmp->value != tmp->arch->clone.value) {
304  continue;
305  }
306 
307  /* This should not happen, but if it does, just merge them. */
308  if (coins_objects[i] != NULL) {
309  log_error("Two money entries of %s in object: %s",
310  coins[NUM_COINS - 1 - i],
311  object_get_str(obj));
312  coins_objects[i]->nrof += tmp->nrof;
313  object_remove(tmp, 0);
314  object_destroy(tmp);
315  } else {
316  object_remove(tmp, 0);
317  coins_objects[i] = tmp;
318  }
319 
320  found = true;
321  break;
322  }
323 
324  if (!found) {
325  log_error("Did not find coin match for %s", tmp->arch->name);
326  }
327  } FOR_INV_FINISH();
328 
329  for (int i = 0; i < NUM_COINS; i++) {
330  if (coins_objects[i] == NULL) {
331  continue;
332  }
333 
334  int64_t num_coins;
335  if ((int64_t) coins_objects[i]->nrof * coins_objects[i]->value >
336  remain) {
337  num_coins = remain / coins_objects[i]->value;
338 
339  if (num_coins * coins_objects[i]->value < remain) {
340  num_coins++;
341  }
342  } else {
343  num_coins = coins_objects[i]->nrof;
344  }
345 
346  if (num_coins > UINT32_MAX) {
347  LOG(ERROR, "Money overflow value->nrof: number of coins > "
348  "UINT32_MAX (type coin %d)", i);
349  num_coins = UINT32_MAX;
350  }
351 
352  remain -= num_coins * coins_objects[i]->value;
353  coins_objects[i]->nrof -= (uint32_t) num_coins;
354 
355  /* Now start making change. Start at the coin value
356  * below the one we just did, and work down to
357  * the lowest value. */
358  int count = i - 1;
359  while (remain < 0 && count >= 0) {
360  if (coins_objects[count] == NULL) {
361  coins_objects[count] = arch_get(coins[NUM_COINS - 1 - count]);
362  coins_objects[count]->nrof = 0;
363  }
364 
365  num_coins = -remain / coins_objects[count]->value;
366  coins_objects[count]->nrof += (uint32_t) num_coins;
367  remain += num_coins * coins_objects[count]->value;
368  count--;
369  }
370  }
371 
372  for (int i = 0; i < NUM_COINS; i++) {
373  if (coins_objects[i] == NULL) {
374  continue;
375  }
376 
377  if (coins_objects[i]->nrof == 0) {
378  object_destroy(coins_objects[i]);
379  continue;
380  }
381 
382  object_insert_into(coins_objects[i], obj, 0);
383  }
384 
385  return remain;
386 }
387 
397 static int64_t shop_pay_amount(object *op, int64_t to_pay)
398 {
399  to_pay = shop_pay_inventory(op, to_pay);
400  FOR_INV_PREPARE(op, tmp) {
401  if (to_pay <= 0) {
402  break;
403  }
404 
405  if (tmp->type != CONTAINER || tmp->inv == NULL) {
406  continue;
407  }
408 
409  if (QUERY_FLAG(tmp, FLAG_APPLIED) || tmp->race == NULL ||
410  strstr(tmp->race, "gold") != NULL) {
411  to_pay = shop_pay_amount(tmp, to_pay);
412  }
413  } FOR_INV_FINISH();
414 
415  return to_pay;
416 }
417 
428 bool shop_pay(object *op, int64_t to_pay)
429 {
430  if (to_pay == 0) {
431  return true;
432  }
433 
434  if (to_pay > shop_get_money(op)) {
435  return false;
436  }
437 
438  to_pay = shop_pay_amount(op, to_pay);
439  if (to_pay != 0) {
440  object *bank = bank_find_info(op);
441  if (bank != NULL) {
442  SOFT_ASSERT_RC(bank->value >= to_pay, true, "Bank object value is "
443  "%" PRId64 ", but object needs %" PRId64 " to finish "
444  "payment, op: %s", bank->value, to_pay, object_get_str(op));
445  bank->value -= to_pay;
446  to_pay = 0;
447  }
448  }
449 
450  SOFT_ASSERT_RC(to_pay == 0, true, "Still %" PRId64 " left to pay, op: %s",
451  to_pay, object_get_str(op));
452 
453  return true;
454 }
455 
465 bool shop_pay_item(object *op, object *item)
466 {
467  return shop_pay(op, shop_get_cost(item, COST_BUY));
468 }
469 
479 static bool shop_pay_items_rec(object *op, object *where)
480 {
481  FOR_INV_PREPARE(where, tmp) {
482  if (tmp->inv != NULL) {
483  if (!shop_pay_items_rec(op, tmp)) {
484  return false;
485  }
486  }
487 
488  if (!QUERY_FLAG(tmp, FLAG_UNPAID)) {
489  continue;
490  }
491 
492  if (!shop_pay_item(op, tmp)) {
493  CLEAR_FLAG(tmp, FLAG_UNPAID);
494  int64_t need = shop_get_cost(tmp, COST_BUY) - shop_get_money(op);
495  char *name = object_get_name_s(tmp, op);
496  draw_info_format(COLOR_WHITE, op, "You lack %s to buy %s.",
497  shop_get_cost_string(need), name);
498  efree(name);
499  SET_FLAG(tmp, FLAG_UNPAID);
500  return false;
501  } else {
502  CLEAR_FLAG(tmp, FLAG_UNPAID);
504  char *name = object_get_name_s(tmp, op);
505  draw_info_format(COLOR_WHITE, op, "You paid %s for %s.",
506  shop_get_cost_string_item(tmp, COST_BUY), name);
507 
508  if (QUERY_FLAG(tmp, FLAG_SOULBOUND)) {
509  bool ret = object_set_value(tmp,
510  "soulbound_name",
511  op->name,
512  true);
513  if (ret) {
514  draw_info_format(COLOR_WHITE, op,
515  "%s becomes soulbound to you.", name);
516  } else {
518  LOG(ERROR, "Failed to soulbind %s to %s",
519  object_get_str(tmp), object_get_str(op));
520  }
521  }
522 
523  efree(name);
524 
525  /* If the object wasn't merged, send flags update. */
526  if (object_merge(tmp) == tmp) {
527  esrv_update_item(UPD_FLAGS, tmp);
528  }
529  }
530  } FOR_INV_FINISH();
531 
532  return true;
533 }
534 
542 bool shop_pay_items(object *op)
543 {
544  return shop_pay_items_rec(op, op);
545 }
546 
554 void shop_sell_item(object *op, object *item)
555 {
556  HARD_ASSERT(op != NULL);
557 
558  if (item->custom_name) {
560  }
561 
562  int64_t value = shop_get_cost(item, COST_SELL);
563  char *name = object_get_name_s(item, op);
564  if (value == 0) {
565  draw_info_format(COLOR_WHITE, op, "We're not interested in %s.", name);
566  }
567 
568  shop_insert_coins(op, value);
569  draw_info_format(COLOR_WHITE, op, "You receive %s for %s.",
570  shop_get_cost_string(value), name);
571 
572  SET_FLAG(item, FLAG_UNPAID);
573  /* Identify the item. Makes any unidentified item sold to unique shop appear
574  * identified. */
575  identify(item);
576  efree(name);
577 }
578 
587 void shop_insert_coins(object *op, int64_t value)
588 {
589  for (int i = 0; coins[i] != NULL; i++) {
590  archetype_t *at = arch_find(coins[i]);
591  if (at == NULL) {
592  log_error("Could not find archetype: %s", coins[i]);
593  continue;
594  }
595 
596  if (value / at->clone.value <= 0) {
597  continue;
598  }
599 
600  FOR_INV_PREPARE(op, tmp) {
601  if (tmp->type != CONTAINER) {
602  continue;
603  }
604 
605  if (!QUERY_FLAG(tmp, FLAG_APPLIED)) {
606  continue;
607  }
608 
609  if (tmp->race == NULL || strstr(tmp->race, "gold") == NULL) {
610  continue;
611  }
612 
613  uint32_t nrof = (uint32_t) (value / at->clone.value);
614  if (nrof == 0) {
615  continue;
616  }
617 
618  double weight = at->clone.weight * tmp->weapon_speed;
619  if (tmp->weight_limit != 0 && tmp->carrying + weight >
620  tmp->weight_limit) {
621  continue;
622  }
623 
624  if (weight > 0.0 && tmp->weight_limit != 0 &&
625  (tmp->weight_limit - tmp->carrying) / weight < nrof) {
626  nrof = (tmp->weight_limit - tmp->carrying) / weight;
627  }
628 
629  object *coin = object_get();
630  object_copy(coin, &at->clone, false);
631  coin->nrof = nrof;
632  value -= coin->nrof * coin->value;
633  object_insert_into(coin, tmp, 0);
634  } FOR_INV_FINISH();
635 
636  if (value / at->clone.value > 0) {
637  uint32_t nrof = (uint32_t) (value / at->clone.value);
638  uint32_t weight_max = weight_limit[MIN(op->stats.Str, MAX_STAT)];
639 
640  if (nrof > 0 && op->carrying + at->clone.weight <= weight_max) {
641  if ((weight_max - op->carrying) / at->clone.weight < nrof) {
642  nrof = (weight_max - op->carrying) / at->clone.weight;
643  }
644 
645  object *coin = object_get();
646  object_copy(coin, &at->clone, false);
647  coin->nrof = nrof;
648  value -= coin->nrof * coin->value;
649  object_insert_into(coin, op, 0);
650  }
651  }
652 
653  if (value / at->clone.value > 0) {
654  object *coin = object_get();
655  object_copy(coin, &at->clone, false);
656  coin->nrof = (uint32_t) (value / at->clone.value);
657  value -= coin->nrof * coin->value;
658  coin->x = op->x;
659  coin->y = op->y;
660  object_insert_map(coin, op->map, NULL, 0);
661  }
662  }
663 
664  SOFT_ASSERT(value == 0, "Value is not zero: %" PRId64 ", object: %s",
665  value, object_get_str(op));
666 }
Definition: object.h:94
int64_t shop_get_cost(object *op, int mode)
Definition: shop.c:43
void object_destroy(object *ob)
Definition: object.c:1441
const char * shop_get_cost_string(int64_t cost)
Definition: shop.c:178
const char * shop_get_cost_string_item(object *op, int mode)
Definition: shop.c:238
bool shop_pay(object *op, int64_t to_pay)
Definition: shop.c:428
bool object_set_value(object *op, const char *key, const char *value, bool add_key)
Definition: object.c:2635
#define FLAG_CURSED
Definition: define.h:1154
void identify(object *op)
Definition: item.c:1364
#define JEWEL
Definition: define.h:473
char name[MAX_BUF]
Definition: material.h:115
object * object_get(void)
Definition: object.c:993
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:1691
#define FOR_INV_FINISH()
Definition: define.h:1698
#define NROFREALSPELLS
Definition: define.h:664
#define WAND
Definition: define.h:445
bool shop_pay_items(object *op)
Definition: shop.c:542
void object_copy(object *op, const object *src, bool no_speed)
Definition: object.c:886
int16_t material_real
Definition: object.h:307
object * bank_find_info(object *op)
Definition: bank.c:227
#define FLAG_SOULBOUND
Definition: define.h:1044
object * arch_get(const char *name)
Definition: arch.c:430
#define MONEY
Definition: define.h:226
#define PLAYER
Definition: define.h:122
static archetype_t * shop_get_next_coin(int64_t cost, int *cointype)
Definition: shop.c:150
int16_t sp
Definition: living.h:78
#define QUERY_FLAG(xyz, p)
Definition: define.h:761
struct archetype * arch
Definition: object.h:225
static int64_t shop_pay_inventory(object *obj, int64_t to_pay)
Definition: shop.c:284
void object_remove(object *op, int flags)
Definition: object.c:1623
#define NUM_COINS
Definition: treasure.h:37
#define FLAG_STARTEQUIP
Definition: define.h:1004
char * object_get_name_s(const object *op, const object *caller)
Definition: item.c:398
material_real_t materials_real[NUM_MATERIALS_REAL]
Definition: material.c:56
#define FLAG_DAMNED
Definition: define.h:1158
int16_t y
Definition: object.h:276
#define COST_BUY
Definition: define.h:1370
#define COST_TRUE
Definition: define.h:1374
int64_t shop_get_money(object *op)
Definition: shop.c:251
const char * object_get_str(const object *op)
Definition: object.c:3151
Definition: arch.h:40
uint32_t weight
Definition: object.h:246
static bool shop_pay_items_rec(object *op, object *where)
Definition: shop.c:479
object * object_insert_into(object *op, object *where, int flag)
Definition: object.c:2158
struct mapdef * map
Definition: object.h:139
#define FLAG_UNPAID
Definition: define.h:1251
#define SP_NO_SPELL
Definition: spells.h:202
uint32_t carrying
Definition: object.h:157
const char * name
Definition: object.h:168
#define SET_FLAG(xyz, p)
Definition: define.h:741
uint32_t weight_limit[MAX_STAT+1]
Definition: living.c:129
struct archetype * at
Definition: spells.h:198
#define CONTAINER
Definition: define.h:493
void shop_sell_item(object *op, object *item)
Definition: shop.c:554
uint32_t nrof
Definition: object.h:264
#define BOOK_SPELL
Definition: define.h:373
bool need_identify(const object *op)
Definition: item.c:1315
#define COST_SELL
Definition: define.h:1372
#define GEM
Definition: define.h:296
uint64_t num
Number of successful updates.
Definition: metaserver.c:43
#define POTION
Definition: define.h:138
object * object_merge(object *op)
Definition: object.c:479
#define NUGGET
Definition: define.h:477
const char *const coins[NUM_COINS+1]
Definition: treasure.c:38
int16_t x
Definition: object.h:273
#define PEARL
Definition: define.h:292
object * object_insert_map(object *op, mapstruct *m, object *originator, int flag)
Definition: object.c:1741
spell_struct spells[NROFREALSPELLS]
Definition: spellist.h:34
void shop_insert_coins(object *op, int64_t value)
Definition: shop.c:587
#define FLAG_APPLIED
Definition: define.h:1182
shstr_constants shstr_cons
Definition: init.c:58
living stats
Definition: object.h:481
void esrv_update_item(int flags, object *op)
Definition: item.c:639
uint8_t type
Definition: object.h:360
#define CLEAR_FLAG(xyz, p)
Definition: define.h:751
bool shop_pay_item(object *op, object *item)
Definition: shop.c:465
#define FREE_AND_CLEAR_HASH(_nv_)
Definition: global.h:130
int8_t Str
Definition: living.h:103
float value_mul
Definition: spells.h:156
shstr * custom_name
Definition: object.h:190
#define OBJECT_IS_SPELL_TOOL(ob)
Definition: object.h:840
static int64_t shop_pay_amount(object *op, int64_t to_pay)
Definition: shop.c:397
int8_t level
Definition: object.h:347
int64_t value
Definition: object.h:240
object clone
An object from which to do object_copy().
Definition: arch.h:47
archetype_t * arch_find(const char *name)
Definition: arch.c:407
#define MAX_STAT
Definition: define.h:47
#define FLAG_IDENTIFIED
Definition: define.h:980
int16_t food
Definition: living.h:84