1 | /* |
1 | /* |
2 | * This file is part of Deliantra, the Roguelike Realtime MMORPG. |
2 | * This file is part of Deliantra, the Roguelike Realtime MMORPG. |
3 | * |
3 | * |
4 | * Copyright (©) 2005,2006,2007,2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team |
4 | * Copyright (©) 2005,2006,2007,2008,2009 Marc Alexander Lehmann / Robin Redeker / the Deliantra team |
5 | * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team |
5 | * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team |
6 | * Copyright (©) 1992,2007 Frank Tore Johansen |
6 | * Copyright (©) 1992,2007 Frank Tore Johansen |
7 | * |
7 | * |
8 | * Deliantra is free software: you can redistribute it and/or modify |
8 | * Deliantra is free software: you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by |
9 | * it under the terms of the GNU General Public License as published by |
… | |
… | |
74 | |
74 | |
75 | sint64 |
75 | sint64 |
76 | query_cost (const object *tmp, object *who, int flag) |
76 | query_cost (const object *tmp, object *who, int flag) |
77 | { |
77 | { |
78 | double val; |
78 | double val; |
79 | int number; /* used to better calculate value */ |
|
|
80 | int no_bargain; |
79 | int no_bargain; |
81 | int identified; |
80 | int identified; |
82 | int not_cursed; |
81 | int not_cursed; |
83 | int approximate; |
82 | int approximate; |
84 | int shop; |
83 | int shop; |
… | |
… | |
109 | |
108 | |
110 | LOG (llevError, "Query_cost: Gem type with unknown flag %d: %s\n", flag, tmp->debug_desc ()); |
109 | LOG (llevError, "Query_cost: Gem type with unknown flag %d: %s\n", flag, tmp->debug_desc ()); |
111 | return 0; |
110 | return 0; |
112 | } |
111 | } |
113 | |
112 | |
114 | number = tmp->nrof; |
113 | int number = tmp->number_of (); |
115 | if (number == 0) |
114 | |
116 | number = 1; |
|
|
117 | if (QUERY_FLAG (tmp, FLAG_IDENTIFIED) || !need_identify (tmp) || identified) |
115 | if (QUERY_FLAG (tmp, FLAG_IDENTIFIED) || !need_identify (tmp) || identified) |
118 | { |
116 | { |
119 | if (!not_cursed && (QUERY_FLAG (tmp, FLAG_CURSED) || QUERY_FLAG (tmp, FLAG_DAMNED))) |
117 | if (!not_cursed && (QUERY_FLAG (tmp, FLAG_CURSED) || QUERY_FLAG (tmp, FLAG_DAMNED))) |
120 | return 0; |
118 | return 0; |
121 | else |
119 | else |
122 | val = tmp->value * number; |
120 | val = tmp->value * number; |
123 | } |
121 | } |
124 | /* This area deals with objects that are not identified, but can be */ |
122 | /* This area deals with objects that are not identified, but can be */ |
125 | else |
123 | else |
126 | { |
124 | { |
127 | if (tmp->arch != NULL) |
125 | if (flag == F_BUY) |
128 | { |
126 | { |
129 | if (flag == F_BUY) |
127 | LOG (llevError | logBacktrace, "Asking for buy-value of unidentified object: %s\n", tmp->debug_desc ()); |
|
|
128 | val = tmp->arch->value * 50 * number; |
|
|
129 | } |
|
|
130 | else |
|
|
131 | { /* Trying to sell something, or get true value */ |
|
|
132 | if (tmp->type == POTION) |
|
|
133 | val = number * 40; /* Don't want to give anything away */ |
|
|
134 | else |
130 | { |
135 | { |
131 | LOG (llevError, "Asking for buy-value of unidentified object.\n"); |
136 | /* Get 2/3'rd value for applied objects, 1/3'rd for totally |
132 | val = tmp->arch->value * 50 * number; |
137 | * unknown objects |
|
|
138 | */ |
|
|
139 | if (QUERY_FLAG (tmp, FLAG_BEEN_APPLIED)) |
|
|
140 | val = number * tmp->arch->value * 2 / 3; |
|
|
141 | else |
|
|
142 | val = number * tmp->arch->value / 3; |
133 | } |
143 | } |
134 | else |
|
|
135 | { /* Trying to sell something, or get true value */ |
|
|
136 | if (tmp->type == POTION) |
|
|
137 | val = number * 40; /* Don't want to give anything away */ |
|
|
138 | else |
|
|
139 | { |
|
|
140 | /* Get 2/3'rd value for applied objects, 1/3'rd for totally |
|
|
141 | * unknown objects |
|
|
142 | */ |
|
|
143 | if (QUERY_FLAG (tmp, FLAG_BEEN_APPLIED)) |
|
|
144 | val = number * tmp->arch->value * 2 / 3; |
|
|
145 | else |
|
|
146 | val = number * tmp->arch->value / 3; |
|
|
147 | } |
|
|
148 | } |
|
|
149 | } |
|
|
150 | else |
|
|
151 | { /* No archetype with this object */ |
|
|
152 | LOG (llevDebug, "In sell item: Have object with no archetype: %s\n", &tmp->name); |
|
|
153 | if (flag == F_BUY) |
|
|
154 | { |
|
|
155 | LOG (llevError, "Asking for buy-value of unidentified object without arch.\n"); |
|
|
156 | val = number * tmp->value * 10; |
|
|
157 | } |
|
|
158 | else |
|
|
159 | val = number * tmp->value / 5; |
|
|
160 | } |
144 | } |
161 | } |
145 | } |
162 | |
146 | |
163 | /* If the item has been applied or identifed or does not need to be |
147 | /* If the item has been applied or identifed or does not need to be |
164 | * identified, AND the object is magical and the archetype is non |
148 | * identified, AND the object is magical and the archetype is non |
… | |
… | |
465 | } |
449 | } |
466 | |
450 | |
467 | for (tmp = op->inv; tmp; tmp = tmp->below) |
451 | for (tmp = op->inv; tmp; tmp = tmp->below) |
468 | if (tmp->type == MONEY) |
452 | if (tmp->type == MONEY) |
469 | total += tmp->nrof * (sint64)tmp->value; |
453 | total += tmp->nrof * (sint64)tmp->value; |
470 | else if (tmp->type == CONTAINER && QUERY_FLAG (tmp, FLAG_APPLIED) && (tmp->race == NULL || strstr (tmp->race, "gold"))) |
454 | else if (tmp->type == CONTAINER && QUERY_FLAG (tmp, FLAG_APPLIED) && (!tmp->race || tmp->race.contains ("gold"))) |
471 | total += query_money (tmp); |
455 | total += query_money (tmp); |
472 | |
456 | |
473 | return total; |
457 | return total; |
474 | } |
458 | } |
475 | |
459 | |
… | |
… | |
490 | return 0; |
474 | return 0; |
491 | |
475 | |
492 | pay_from_container (pl, pl, to_pay); |
476 | pay_from_container (pl, pl, to_pay); |
493 | |
477 | |
494 | for (pouch = pl->inv; pouch && to_pay; pouch = pouch->below) |
478 | for (pouch = pl->inv; pouch && to_pay; pouch = pouch->below) |
495 | if (pouch->type == CONTAINER && QUERY_FLAG (pouch, FLAG_APPLIED) && (pouch->race == NULL || strstr (pouch->race, "gold"))) |
479 | if (pouch->type == CONTAINER && QUERY_FLAG (pouch, FLAG_APPLIED) && (!pouch->race || pouch->race.contains ("gold"))) |
496 | pay_from_container (pl, pouch, to_pay); |
480 | pay_from_container (pl, pouch, to_pay); |
497 | |
481 | |
498 | pl->update_stats (); |
482 | pl->update_stats (); |
499 | return 1; |
483 | return 1; |
500 | } |
484 | } |
… | |
… | |
527 | change_exp (pl, saved_money, "bargaining", SK_EXP_NONE); |
511 | change_exp (pl, saved_money, "bargaining", SK_EXP_NONE); |
528 | |
512 | |
529 | pay_from_container (pl, pl, to_pay); |
513 | pay_from_container (pl, pl, to_pay); |
530 | |
514 | |
531 | for (pouch = pl->inv; pouch && to_pay; pouch = pouch->below) |
515 | for (pouch = pl->inv; pouch && to_pay; pouch = pouch->below) |
532 | if (pouch->type == CONTAINER && QUERY_FLAG (pouch, FLAG_APPLIED) && (pouch->race == NULL || strstr (pouch->race, "gold"))) |
516 | if (pouch->type == CONTAINER && QUERY_FLAG (pouch, FLAG_APPLIED) && (!pouch->race || pouch->race.contains ("gold"))) |
533 | pay_from_container (pl, pouch, to_pay); |
517 | pay_from_container (pl, pouch, to_pay); |
534 | |
518 | |
535 | pl->update_stats (); |
519 | pl->update_stats (); |
536 | |
520 | |
537 | return 1; |
521 | return 1; |
… | |
… | |
611 | } |
595 | } |
612 | |
596 | |
613 | for (i = 0; i < NUM_COINS; i++) |
597 | for (i = 0; i < NUM_COINS; i++) |
614 | { |
598 | { |
615 | object &coin = *coin_objs[i]; |
599 | object &coin = *coin_objs[i]; |
616 | sint64 num_coins = min ((to_pay + coin.value - 1) / coin.value, coin.nrof); |
600 | sint64 num_coins = min ((to_pay + coin.value - 1) / coin.value, (sint64) coin.nrof); |
617 | to_pay -= num_coins * coin.value; |
601 | to_pay -= num_coins * coin.value; |
618 | |
602 | |
619 | coin.nrof -= num_coins; |
603 | coin.nrof -= num_coins; |
620 | /* Now start making change. Start at the coin value |
604 | /* Now start making change. Start at the coin value |
621 | * below the one we just did, and work down to |
605 | * below the one we just did, and work down to |
… | |
… | |
631 | count--; |
615 | count--; |
632 | } |
616 | } |
633 | } |
617 | } |
634 | |
618 | |
635 | for (i = 0; i < NUM_COINS; i++) |
619 | for (i = 0; i < NUM_COINS; i++) |
636 | { |
|
|
637 | if (coin_objs[i]->nrof) |
620 | if (coin_objs[i]->nrof) |
638 | insert_ob_in_ob (coin_objs [i], pouch); |
621 | insert_ob_in_ob (coin_objs [i], pouch); |
639 | else |
622 | else |
640 | coin_objs[i]->destroy (); |
623 | coin_objs[i]->destroy (); |
641 | } |
|
|
642 | } |
624 | } |
643 | |
625 | |
644 | /* Checks all unpaid items in op's inventory, adds up all the money they |
626 | /* Checks all unpaid items in op's inventory, adds up all the money they |
645 | * have, and checks that they can actually afford what they want to buy. |
627 | * have, and checks that they can actually afford what they want to buy. |
646 | * Returns 1 if they can, and 0 if they can't. also prints an appropriate message |
628 | * Returns 1 if they can, and 0 if they can't. also prints an appropriate message |
… | |
… | |
666 | unpaid_price += query_cost (item, pl, F_BUY | F_SHOP); |
648 | unpaid_price += query_cost (item, pl, F_BUY | F_SHOP); |
667 | } |
649 | } |
668 | |
650 | |
669 | if (unpaid_price > player_wealth) |
651 | if (unpaid_price > player_wealth) |
670 | { |
652 | { |
671 | dynbuf_text buf; |
653 | dynbuf_text &buf = msg_dynbuf; buf.clear (); |
672 | |
654 | |
673 | buf << "You have " << unpaid_count |
655 | buf << "You have " << unpaid_count |
674 | << " unpaid item(s) that would cost you " << cost_string_from_value (unpaid_price, 0) |
656 | << " unpaid item(s) that would cost you " << cost_string_from_value (unpaid_price, 0) |
675 | << ". You need another " << cost_string_from_value (unpaid_price - player_wealth, 0) |
657 | << ". You need another " << cost_string_from_value (unpaid_price - player_wealth, 0) |
676 | << " to be able to afford that."; |
658 | << " to be able to afford that. " |
|
|
659 | "H<You cannot leave a shop without paying - drop unpaid items first to be able to leave.>"; |
677 | |
660 | |
678 | pl->failmsg (buf); |
661 | pl->failmsg (buf); |
679 | |
662 | |
680 | return 0; |
663 | return 0; |
681 | } |
664 | } |
… | |
… | |
756 | LOG (llevError, "Could not find %s archetype\n", coins[count]); |
739 | LOG (llevError, "Could not find %s archetype\n", coins[count]); |
757 | else if ((amount / at->value) > 0) |
740 | else if ((amount / at->value) > 0) |
758 | { |
741 | { |
759 | for (pouch = pl->inv; pouch; pouch = pouch->below) |
742 | for (pouch = pl->inv; pouch; pouch = pouch->below) |
760 | { |
743 | { |
761 | if (pouch->type == CONTAINER && QUERY_FLAG (pouch, FLAG_APPLIED) && pouch->race && strstr (pouch->race, "gold")) |
744 | if (pouch->type == CONTAINER && QUERY_FLAG (pouch, FLAG_APPLIED) && pouch->race.contains ("gold")) |
762 | { |
745 | { |
763 | int w = at->weight * (100 - pouch->stats.Str) / 100; |
746 | int w = at->weight * (100 - pouch->stats.Str) / 100; |
764 | int n = amount / at->value; |
747 | int n = amount / at->value; |
765 | |
748 | |
766 | if (w == 0) |
749 | if (w == 0) |
… | |
… | |
1124 | } |
1107 | } |
1125 | |
1108 | |
1126 | void |
1109 | void |
1127 | shop_listing (object *sign, object *op) |
1110 | shop_listing (object *sign, object *op) |
1128 | { |
1111 | { |
1129 | int i, j, numitems = 0, numallocated = 0, x1, x2, y1, y2; |
1112 | int i, j, x1, x2, y1, y2; |
1130 | const char *shop_coords = sign->kv (shstr_shop_coords); |
1113 | const char *shop_coords = sign->kv (shstr_shop_coords); |
1131 | object *stack; |
1114 | object *stack; |
1132 | shopinv *items; |
1115 | shopinv *items; |
1133 | |
1116 | |
1134 | /* Should never happen, but just in case a monster does apply a sign */ |
1117 | /* Should never happen, but just in case a monster does apply a sign */ |
1135 | if (op->type != PLAYER) |
1118 | if (!op->is_player ()) |
1136 | return; |
1119 | return; |
|
|
1120 | |
|
|
1121 | dynbuf_text &buf = msg_dynbuf; buf.clear (); |
1137 | |
1122 | |
1138 | if (!(shop_coords && sscanf (shop_coords, "%d,%d,%d,%d", &x1, &y1, &x2, &y2))) |
1123 | if (!(shop_coords && sscanf (shop_coords, "%d,%d,%d,%d", &x1, &y1, &x2, &y2))) |
1139 | { |
1124 | { |
1140 | x1 = 0; |
1125 | x1 = 0; |
1141 | y1 = 0; |
1126 | y1 = 0; |
1142 | x2 = op->map->width - 1; |
1127 | x2 = op->map->width - 1; |
1143 | y2 = op->map->height - 1; |
1128 | y2 = op->map->height - 1; |
1144 | } |
1129 | } |
1145 | |
1130 | |
1146 | items = (shopinv *) malloc (40 * sizeof (shopinv)); |
|
|
1147 | numallocated = 40; |
1131 | int numallocated = 40; |
|
|
1132 | int numitems = 0; |
|
|
1133 | items = (shopinv *)malloc (sizeof (shopinv) * numallocated); |
1148 | |
1134 | |
1149 | /* Find all the appropriate items */ |
1135 | /* Find all the appropriate items */ |
1150 | for (i = x1; i <= x2; i++) |
1136 | for (i = x1; i <= x2; i++) |
1151 | { |
|
|
1152 | for (j = y1; j < y2; j++) |
1137 | for (j = y1; j < y2; j++) |
|
|
1138 | if (is_in_shop (op->map, i, j)) |
1153 | { |
1139 | { |
1154 | if (is_in_shop (op->map, i, j)) |
1140 | stack = GET_MAP_OB (op->map, i, j); |
|
|
1141 | |
|
|
1142 | while (stack) |
1155 | { |
1143 | { |
1156 | stack = GET_MAP_OB (op->map, i, j); |
1144 | if (QUERY_FLAG (stack, FLAG_UNPAID)) |
1157 | |
|
|
1158 | while (stack) |
|
|
1159 | { |
1145 | { |
1160 | if (QUERY_FLAG (stack, FLAG_UNPAID)) |
|
|
1161 | { |
|
|
1162 | if (numitems == numallocated) |
1146 | if (numitems == numallocated) |
1163 | { |
|
|
1164 | items = (shopinv *) realloc (items, sizeof (shopinv) * (numallocated + 10)); |
1147 | items = (shopinv *)realloc (items, sizeof (shopinv) * (numallocated *= 2)); |
1165 | numallocated += 10; |
|
|
1166 | } |
|
|
1167 | |
1148 | |
1168 | add_shop_item (stack, items, &numitems, &numallocated); |
1149 | add_shop_item (stack, items, &numitems, &numallocated); |
1169 | } |
|
|
1170 | |
|
|
1171 | stack = stack->above; |
|
|
1172 | } |
1150 | } |
|
|
1151 | |
|
|
1152 | stack = stack->above; |
1173 | } |
1153 | } |
1174 | } |
1154 | } |
1175 | } |
|
|
1176 | |
1155 | |
1177 | if (numitems == 0) |
1156 | buf << (numitems ? "T<This shop contains:>\n\n" |
1178 | { |
1157 | : "T<This shop is currently empty.>"); |
1179 | new_draw_info (NDI_UNIQUE, 0, op, "The shop is currently empty.\n"); |
|
|
1180 | free (items); |
|
|
1181 | return; |
|
|
1182 | } |
|
|
1183 | |
1158 | |
1184 | qsort (items, numitems, sizeof (shopinv), (int (*)(const void *, const void *)) shop_sort); |
1159 | qsort (items, numitems, sizeof (shopinv), (int (*)(const void *, const void *)) shop_sort); |
1185 | |
|
|
1186 | new_draw_info (NDI_UNIQUE, 0, op, "\nThe shop contains:"); |
|
|
1187 | |
1160 | |
1188 | for (i = 0; i < numitems; i++) |
1161 | for (i = 0; i < numitems; i++) |
1189 | { |
1162 | { |
1190 | /* Collapse items of the same name together */ |
1163 | /* Collapse items of the same name together */ |
1191 | if ((i + 1) < numitems && !strcmp (items[i].item_real, items[i + 1].item_real)) |
1164 | if ((i + 1) < numitems && !strcmp (items[i].item_real, items[i + 1].item_real)) |
1192 | { |
|
|
1193 | items[i + 1].nrof += items[i].nrof; |
1165 | items[i + 1].nrof += items[i].nrof; |
1194 | free (items[i].item_sort); |
|
|
1195 | free (items[i].item_real); |
|
|
1196 | } |
|
|
1197 | else |
1166 | else |
1198 | { |
|
|
1199 | new_draw_info_format (NDI_UNIQUE, 0, op, "%d %s", |
|
|
1200 | items[i].nrof ? items[i].nrof : 1, items[i].nrof == 1 ? items[i].item_sort : items[i].item_real); |
1167 | buf.printf (" %4d %s\n", items[i].nrof ? items[i].nrof : 1, items[i].nrof == 1 ? items[i].item_sort : items[i].item_real); |
|
|
1168 | |
1201 | free (items[i].item_sort); |
1169 | free (items[i].item_sort); |
1202 | free (items[i].item_real); |
1170 | free (items[i].item_real); |
1203 | } |
|
|
1204 | } |
1171 | } |
|
|
1172 | |
|
|
1173 | op->contr->infobox (MSG_CHANNEL ("shopitems"), buf); |
1205 | |
1174 | |
1206 | free (items); |
1175 | free (items); |
1207 | } |
1176 | } |
1208 | |
1177 | |
1209 | /* elmex: this function checks whether the object is in a shop */ |
1178 | /* elmex: this function checks whether the object is in a shop */ |