ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/shop.C
(Generate patch)

Comparing deliantra/server/server/shop.C (file contents):
Revision 1.56 by root, Mon Sep 29 10:20:49 2008 UTC vs.
Revision 1.79 by root, Thu Apr 15 02:51:40 2010 UTC

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,2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team 5 * Copyright (©) 2002 Mark Wedel & Crossfire Development Team
6 * Copyright (©) 1992,2007 Frank Tore Johansen 6 * Copyright (©) 1992 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 it under
9 * it under the terms of the GNU General Public License as published by 9 * the terms of the Affero GNU General Public License as published by the
10 * the Free Software Foundation, either version 3 of the License, or 10 * Free Software Foundation, either version 3 of the License, or (at your
11 * (at your option) any later version. 11 * option) any later version.
12 * 12 *
13 * This program is distributed in the hope that it will be useful, 13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details. 16 * GNU General Public License for more details.
17 * 17 *
18 * You should have received a copy of the GNU General Public License 18 * You should have received a copy of the Affero GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>. 19 * and the GNU General Public License along with this program. If not, see
20 * <http://www.gnu.org/licenses/>.
20 * 21 *
21 * The authors can be reached via e-mail to <support@deliantra.net> 22 * The authors can be reached via e-mail to <support@deliantra.net>
22 */ 23 */
23 24
24#include <global.h> 25#include <global.h>
25#include <spells.h> 26#include <spells.h>
26#include <skills.h> 27#include <skills.h>
27#include <living.h> 28#include <living.h>
28#include <sproto.h> 29#include <sproto.h>
29#include <math.h>
30 30
31/* this is a measure of how effective store specialisation is. A general store 31/* this is a measure of how effective store specialisation is. A general store
32 * will offer this proportion of the 'maximum' price, a specialised store will 32 * will offer this proportion of the 'maximum' price, a specialised store will
33 * offer a range of prices around it such that the maximum price is always one 33 * offer a range of prices around it such that the maximum price is always one
34 * therefore making this number higher, makes specialisation less effective. 34 * therefore making this number higher, makes specialisation less effective.
74 74
75sint64 75sint64
76query_cost (const object *tmp, object *who, int flag) 76query_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 (tmp->flag [FLAG_IDENTIFIED] || !tmp->need_identify () || identified)
118 { 116 {
119 if (!not_cursed && (QUERY_FLAG (tmp, FLAG_CURSED) || QUERY_FLAG (tmp, FLAG_DAMNED))) 117 if (!not_cursed && (tmp->flag [FLAG_CURSED] || tmp->flag [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)
128 {
129 if (flag == F_BUY) 125 if (flag == F_BUY)
126 {
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 | logBacktrace, "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 (tmp->flag [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 | logBacktrace, "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
167 * tmp->arch->magic for any magic. The check for archetype 151 * tmp->arch->magic for any magic. The check for archetype
168 * magic is to not give extra money for archetypes that are by 152 * magic is to not give extra money for archetypes that are by
169 * default magical. This is because the archetype value should have 153 * default magical. This is because the archetype value should have
170 * already figured in that value. 154 * already figured in that value.
171 */ 155 */
172 if ((QUERY_FLAG (tmp, FLAG_IDENTIFIED) || !need_identify (tmp) || identified || 156 if ((tmp->flag [FLAG_IDENTIFIED] || !tmp->need_identify () || identified || tmp->flag [FLAG_BEEN_APPLIED])
173 QUERY_FLAG (tmp, FLAG_BEEN_APPLIED)) && tmp->magic && (tmp->arch == NULL || !tmp->arch->magic)) 157 && tmp->magic && (!tmp->arch || !tmp->arch->magic))
174 { 158 {
175 if (tmp->magic > 0) 159 if (tmp->magic > 0)
176 val *= (3 * tmp->magic * tmp->magic * tmp->magic); 160 val *= (3 * tmp->magic * tmp->magic * tmp->magic);
177 else 161 else
178 /* Note that tmp->magic is negative, so that this 162 /* Note that tmp->magic is negative, so that this
185 { 169 {
186 /* Value of the wand is multiplied by the number of 170 /* Value of the wand is multiplied by the number of
187 * charges. the treasure code already sets up the value 171 * charges. the treasure code already sets up the value
188 * 50 charges is used as the baseline. 172 * 50 charges is used as the baseline.
189 */ 173 */
190 if (QUERY_FLAG (tmp, FLAG_IDENTIFIED) || !need_identify (tmp) || identified) 174 if (tmp->flag [FLAG_IDENTIFIED] || !tmp->need_identify () || identified)
191 val = (val * tmp->stats.food) / 50; 175 val = (val * tmp->stats.food) / 50;
192 else /* if not identified, presume one charge */ 176 else /* if not identified, presume one charge */
193 val /= 50; 177 val /= 50;
194 } 178 }
195 179
258 */ 242 */
259 if ((sint64) val < 0) 243 if ((sint64) val < 0)
260 val = 0; 244 val = 0;
261 245
262 /* Unidentified stuff won't sell for more than 60gp */ 246 /* Unidentified stuff won't sell for more than 60gp */
263 if (flag == F_SELL && !QUERY_FLAG (tmp, FLAG_IDENTIFIED) && need_identify (tmp) && !identified) 247 if (flag == F_SELL && !tmp->flag [FLAG_IDENTIFIED] && tmp->need_identify () && !identified)
264 { 248 min_it (val, 600);
265 val = (val > 600) ? 600 : val;
266 }
267 249
268 /* if we are in a shop, check how the type of shop should affect the price */ 250 /* if we are in a shop, check how the type of shop should affect the price */
269 if (shop && who) 251 if (shop && who)
270 { 252 {
271 if (flag == F_SELL) 253 if (flag == F_SELL)
283 * be sold for (otherwise players could camp map resets to make money). 265 * be sold for (otherwise players could camp map resets to make money).
284 * In game terms, a non-specialist shop, might not recognise the true 266 * In game terms, a non-specialist shop, might not recognise the true
285 * value of the items they sell (much like how people sometimes find 267 * value of the items they sell (much like how people sometimes find
286 * antiques in a junk shop in real life). 268 * antiques in a junk shop in real life).
287 */ 269 */
288 if (QUERY_FLAG (tmp, FLAG_PLAYER_SOLD)) 270 if (tmp->flag [FLAG_PLAYER_SOLD])
289 val = (val * shop_greed (who->map) * shop_specialisation_ratio (tmp, who->map) / shopkeeper_approval (who->map, who)); 271 val = (val * shop_greed (who->map) * shop_specialisation_ratio (tmp, who->map) / shopkeeper_approval (who->map, who));
290 else 272 else
291 val = (val * shop_greed (who->map) / (shop_specialisation_ratio (tmp, who->map) * shopkeeper_approval (who->map, who))); 273 val = (val * shop_greed (who->map) / (shop_specialisation_ratio (tmp, who->map) * shopkeeper_approval (who->map, who)));
292 } 274 }
293 275
465 } 447 }
466 448
467 for (tmp = op->inv; tmp; tmp = tmp->below) 449 for (tmp = op->inv; tmp; tmp = tmp->below)
468 if (tmp->type == MONEY) 450 if (tmp->type == MONEY)
469 total += tmp->nrof * (sint64)tmp->value; 451 total += tmp->nrof * (sint64)tmp->value;
470 else if (tmp->type == CONTAINER && QUERY_FLAG (tmp, FLAG_APPLIED) && (tmp->race == NULL || strstr (tmp->race, "gold"))) 452 else if (tmp->type == CONTAINER && tmp->flag [FLAG_APPLIED] && (!tmp->race || tmp->race.contains ("gold")))
471 total += query_money (tmp); 453 total += query_money (tmp);
472 454
473 return total; 455 return total;
474} 456}
475 457
490 return 0; 472 return 0;
491 473
492 pay_from_container (pl, pl, to_pay); 474 pay_from_container (pl, pl, to_pay);
493 475
494 for (pouch = pl->inv; pouch && to_pay; pouch = pouch->below) 476 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"))) 477 if (pouch->type == CONTAINER && pouch->flag [FLAG_APPLIED] && (!pouch->race || pouch->race.contains ("gold")))
496 pay_from_container (pl, pouch, to_pay); 478 pay_from_container (pl, pouch, to_pay);
497 479
498 pl->update_stats (); 480 pl->update_stats ();
499 return 1; 481 return 1;
500} 482}
527 change_exp (pl, saved_money, "bargaining", SK_EXP_NONE); 509 change_exp (pl, saved_money, "bargaining", SK_EXP_NONE);
528 510
529 pay_from_container (pl, pl, to_pay); 511 pay_from_container (pl, pl, to_pay);
530 512
531 for (pouch = pl->inv; pouch && to_pay; pouch = pouch->below) 513 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"))) 514 if (pouch->type == CONTAINER && pouch->flag [FLAG_APPLIED] && (!pouch->race || pouch->race.contains ("gold")))
533 pay_from_container (pl, pouch, to_pay); 515 pay_from_container (pl, pouch, to_pay);
534 516
535 pl->update_stats (); 517 pl->update_stats ();
536 518
537 return 1; 519 return 1;
577 // This should not happen, but if it does, just merge the two. 559 // This should not happen, but if it does, just merge the two.
578 if (coin_objs [i]) 560 if (coin_objs [i])
579 { 561 {
580 LOG (llevError, "%s has two money entries of (%s)\n", &pouch->name, coins[NUM_COINS - 1 - i]); 562 LOG (llevError, "%s has two money entries of (%s)\n", &pouch->name, coins[NUM_COINS - 1 - i]);
581 coin_objs[i]->nrof += tmp->nrof; 563 coin_objs[i]->nrof += tmp->nrof;
582 tmp->destroy (true); 564 tmp->destroy ();
583 } 565 }
584 else 566 else
585 { 567 {
586 tmp->remove (); 568 tmp->remove ();
587 coin_objs[i] = tmp; 569 coin_objs[i] = tmp;
604 at = archetype::find (coins[NUM_COINS - 1 - i]); 586 at = archetype::find (coins[NUM_COINS - 1 - i]);
605 587
606 if (at == NULL) 588 if (at == NULL)
607 LOG (llevError, "Could not find %s archetype\n", coins[NUM_COINS - 1 - i]); 589 LOG (llevError, "Could not find %s archetype\n", coins[NUM_COINS - 1 - i]);
608 590
609 coin_objs[i] = arch_to_object (at); 591 coin_objs[i] = at->instance ();
610 coin_objs[i]->nrof = 0; 592 coin_objs[i]->nrof = 0;
611 } 593 }
612 594
613 for (i = 0; i < NUM_COINS; i++) 595 for (i = 0; i < NUM_COINS; i++)
614 { 596 {
615 object &coin = *coin_objs[i]; 597 object &coin = *coin_objs[i];
616 sint64 num_coins = min ((to_pay + coin.value - 1) / coin.value, coin.nrof); 598 sint64 num_coins = min ((to_pay + coin.value - 1) / coin.value, (sint64) coin.nrof);
617 to_pay -= num_coins * coin.value; 599 to_pay -= num_coins * coin.value;
618 600
619 coin.nrof -= num_coins; 601 coin.nrof -= num_coins;
620 /* Now start making change. Start at the coin value 602 /* Now start making change. Start at the coin value
621 * below the one we just did, and work down to 603 * below the one we just did, and work down to
634 616
635 for (i = 0; i < NUM_COINS; i++) 617 for (i = 0; i < NUM_COINS; i++)
636 if (coin_objs[i]->nrof) 618 if (coin_objs[i]->nrof)
637 insert_ob_in_ob (coin_objs [i], pouch); 619 insert_ob_in_ob (coin_objs [i], pouch);
638 else 620 else
639 coin_objs[i]->destroy (true); 621 coin_objs[i]->destroy ();
640} 622}
641 623
642/* Checks all unpaid items in op's inventory, adds up all the money they 624/* Checks all unpaid items in op's inventory, adds up all the money they
643 * have, and checks that they can actually afford what they want to buy. 625 * have, and checks that they can actually afford what they want to buy.
644 * Returns 1 if they can, and 0 if they can't. also prints an appropriate message 626 * Returns 1 if they can, and 0 if they can't. also prints an appropriate message
656 LOG (llevError, "can_pay(): called against something that isn't a player\n"); 638 LOG (llevError, "can_pay(): called against something that isn't a player\n");
657 return 0; 639 return 0;
658 } 640 }
659 641
660 for (object::depth_iterator item = pl->begin (); item != pl->end (); ++item) 642 for (object::depth_iterator item = pl->begin (); item != pl->end (); ++item)
661 if (QUERY_FLAG (item, FLAG_UNPAID)) 643 if (item->flag [FLAG_UNPAID])
662 { 644 {
663 unpaid_count++; 645 unpaid_count++;
664 unpaid_price += query_cost (item, pl, F_BUY | F_SHOP); 646 unpaid_price += query_cost (item, pl, F_BUY | F_SHOP);
665 } 647 }
666 648
667 if (unpaid_price > player_wealth) 649 if (unpaid_price > player_wealth)
668 { 650 {
669 dynbuf_text buf; 651 dynbuf_text &buf = msg_dynbuf; buf.clear ();
670 652
671 buf << "You have " << unpaid_count 653 buf << "You have " << unpaid_count
672 << " unpaid item(s) that would cost you " << cost_string_from_value (unpaid_price, 0) 654 << " unpaid item(s) that would cost you " << cost_string_from_value (unpaid_price, 0)
673 << ". You need another " << cost_string_from_value (unpaid_price - player_wealth, 0) 655 << ". You need another " << cost_string_from_value (unpaid_price - player_wealth, 0)
674 << " to be able to afford that."; 656 << " to be able to afford that. "
657 "H<You cannot leave a shop without paying - drop unpaid items first to be able to leave.>";
675 658
676 pl->failmsg (buf); 659 pl->failmsg (buf);
677 660
678 return 0; 661 return 0;
679 } 662 }
694 { 677 {
695 next_item: 678 next_item:
696 679
697 for (object::depth_iterator op = pl->begin (); op != pl->end (); ++op) 680 for (object::depth_iterator op = pl->begin (); op != pl->end (); ++op)
698 { 681 {
699 if (QUERY_FLAG (op, FLAG_UNPAID)) 682 if (op->flag [FLAG_UNPAID])
700 { 683 {
701 char buf[MAX_BUF]; 684 char buf[MAX_BUF];
702 snprintf (buf, MAX_BUF, "%s", query_cost_string (op, pl, F_BUY | F_SHOP)); 685 snprintf (buf, MAX_BUF, "%s", query_cost_string (op, pl, F_BUY | F_SHOP));
703 686
704 if (!pay_for_item (op, pl)) 687 if (!pay_for_item (op, pl))
705 { 688 {
706 sint64 i = query_cost (op, pl, F_BUY | F_SHOP) - query_money (pl); 689 sint64 i = query_cost (op, pl, F_BUY | F_SHOP) - query_money (pl);
707 690
708 CLEAR_FLAG (op, FLAG_UNPAID); 691 op->clr_flag (FLAG_UNPAID);
709 new_draw_info_format (NDI_UNIQUE, 0, pl, "You lack %s to buy %s.", cost_string_from_value (i, 0), query_name (op)); 692 new_draw_info_format (NDI_UNIQUE, 0, pl, "You lack %s to buy %s.", cost_string_from_value (i, 0), query_name (op));
710 SET_FLAG (op, FLAG_UNPAID); 693 op->set_flag (FLAG_UNPAID);
711 return 0; 694 return 0;
712 } 695 }
713 else 696 else
714 { 697 {
715 CLEAR_FLAG (op, FLAG_UNPAID); 698 op->clr_flag (FLAG_UNPAID);
716 CLEAR_FLAG (op, FLAG_PLAYER_SOLD); 699 op->clr_flag (FLAG_PLAYER_SOLD);
717 new_draw_info_format (NDI_UNIQUE, 0, op, "You paid %s for %s.", buf, query_name (op)); 700 new_draw_info_format (NDI_UNIQUE, 0, op, "You paid %s for %s.", buf, query_name (op));
718 701
719 if (!merge_ob (op, op->env->inv)) 702 if (!merge_ob (op, op->env->inv))
720 esrv_update_item (UPD_FLAGS, pl, op); 703 esrv_update_item (UPD_FLAGS, pl, op);
721 704
754 LOG (llevError, "Could not find %s archetype\n", coins[count]); 737 LOG (llevError, "Could not find %s archetype\n", coins[count]);
755 else if ((amount / at->value) > 0) 738 else if ((amount / at->value) > 0)
756 { 739 {
757 for (pouch = pl->inv; pouch; pouch = pouch->below) 740 for (pouch = pl->inv; pouch; pouch = pouch->below)
758 { 741 {
759 if (pouch->type == CONTAINER && QUERY_FLAG (pouch, FLAG_APPLIED) && pouch->race && strstr (pouch->race, "gold")) 742 if (pouch->type == CONTAINER && pouch->flag [FLAG_APPLIED] && pouch->race.contains ("gold"))
760 { 743 {
761 int w = at->weight * (100 - pouch->stats.Str) / 100; 744 int w = at->weight * (100 - pouch->stats.Str) / 100;
762 int n = amount / at->value; 745 int n = amount / at->value;
763 746
764 if (w == 0) 747 if (w == 0)
767 if (n > 0 && (!pouch->weight_limit || pouch->carrying + w <= pouch->weight_limit)) 750 if (n > 0 && (!pouch->weight_limit || pouch->carrying + w <= pouch->weight_limit))
768 { 751 {
769 if (pouch->weight_limit && (pouch->weight_limit - pouch->carrying) / w < n) 752 if (pouch->weight_limit && (pouch->weight_limit - pouch->carrying) / w < n)
770 n = (pouch->weight_limit - pouch->carrying) / w; 753 n = (pouch->weight_limit - pouch->carrying) / w;
771 754
772 object *tmp = arch_to_object (at); 755 object *tmp = at->instance ();
773 tmp->nrof = n; 756 tmp->nrof = n;
774 amount -= tmp->nrof * tmp->value; 757 amount -= tmp->nrof * tmp->value;
775 pouch->insert (tmp); 758 pouch->insert (tmp);
776 } 759 }
777 } 760 }
778 } 761 }
779 762
780 if (amount / at->value > 0) 763 if (amount / at->value > 0)
781 { 764 {
782 object *tmp = arch_to_object (at); 765 object *tmp = at->instance ();
783 tmp->nrof = amount / tmp->value; 766 tmp->nrof = amount / tmp->value;
784 amount -= tmp->nrof * tmp->value; 767 amount -= tmp->nrof * tmp->value;
785 pl->insert (tmp); 768 pl->insert (tmp);
786 } 769 }
787 } 770 }
852 835
853 new_draw_info_format (NDI_UNIQUE, 0, pl, "You receive %s for %s.", 836 new_draw_info_format (NDI_UNIQUE, 0, pl, "You receive %s for %s.",
854 query_cost_string (op, pl, F_SELL | F_SHOP), query_name (op)); 837 query_cost_string (op, pl, F_SELL | F_SHOP), query_name (op));
855 pl->play_sound (sound_find ("shop_sell")); 838 pl->play_sound (sound_find ("shop_sell"));
856 839
857 SET_FLAG (op, FLAG_UNPAID); 840 op->set_flag (FLAG_UNPAID);
858 identify (op); 841 identify (op);
859 842
860 return true; 843 return true;
861} 844}
862 845
866 */ 849 */
867static double 850static double
868shop_specialisation_ratio (const object *item, const maptile *map) 851shop_specialisation_ratio (const object *item, const maptile *map)
869{ 852{
870 shopitems *items = map->shopitems; 853 shopitems *items = map->shopitems;
871 double likedness = 0.; 854 int likedness = 0;
872 int i; 855 int i;
873 856
874 if (item == NULL) 857 if (item == NULL)
875 { 858 {
876 LOG (llevError, "shop_specialisation_ratio: passed a NULL item for map %s\n", &map->path); 859 LOG (llevError, "shop_specialisation_ratio: passed a NULL item for map %s\n", &map->path);
886 */ 869 */
887 return SPECIALISATION_EFFECT; 870 return SPECIALISATION_EFFECT;
888 } 871 }
889 872
890 if (map->shopitems) 873 if (map->shopitems)
891 {
892 for (i = 0; i < items[0].index; i++) 874 for (i = 0; i < items[0].index; i++)
893 if (items[i].typenum == item->type || (!items[i].typenum && likedness == 0.001)) 875 if (items[i].typenum == item->type || (!items[i].typenum && !likedness))
894 likedness = items[i].strength / 100.0; 876 likedness = items[i].strength;
895 }
896 877
897 if (likedness > 1.0) 878 if (likedness > 100)
898 { /* someone has been rather silly with the map headers. */ 879 { /* someone has been rather silly with the map headers. */
899 LOG (llevDebug, "shop_specialisation ratio: item type %d on map %s is above 100%%\n", item->type, &map->path); 880 LOG (llevDebug, "shop_specialisation ratio: item type %d on map %s is above 100%%\n", item->type, &map->path);
900 likedness = 1.0; 881 likedness = 100;
901 } 882 }
902 883
903 if (likedness < -1.0) 884 if (likedness < -100)
904 { 885 {
905 LOG (llevDebug, "shop_specialisation ratio: item type %d on map %s is below -100%%\n", item->type, &map->path); 886 LOG (llevDebug, "shop_specialisation ratio: item type %d on map %s is below -100%%\n", item->type, &map->path);
906 likedness = -1.0; 887 likedness = -100;
907 } 888 }
908 889
909 return lerp (likedness, -1., 1., SPECIALISATION_EFFECT, 1.); 890 return lerp (double (likedness), -100., 100., SPECIALISATION_EFFECT, 1.);
910} 891}
911 892
912/*returns the greed of the shop on map, or 1 if it isn't specified. */ 893/*returns the greed of the shop on map, or 1 if it isn't specified. */
913static double 894static double
914shop_greed (const maptile *map) 895shop_greed (const maptile *map)
964 tmpshopmax = map->shopmax ? map->shopmax : 100000; // 20 royalties default 945 tmpshopmax = map->shopmax ? map->shopmax : 100000; // 20 royalties default
965 946
966 if (map->shopmin && unit_price < map->shopmin) 947 if (map->shopmin && unit_price < map->shopmin)
967 return 0; 948 return 0;
968 else if (unit_price > tmpshopmax / 2) 949 else if (unit_price > tmpshopmax / 2)
969 newval = MIN ((tmpshopmax / 2) + isqrt (unit_price - tmpshopmax / 2), tmpshopmax); 950 newval = min ((tmpshopmax / 2) + isqrt (unit_price - tmpshopmax / 2), tmpshopmax);
970 else 951 else
971 newval = unit_price; 952 newval = unit_price;
972 } 953 }
973 954
974 newval *= quantity; 955 newval *= quantity;
978 959
979/* gives a desciption of the shop on their current map to the player op. */ 960/* gives a desciption of the shop on their current map to the player op. */
980int 961int
981describe_shop (const object *op) 962describe_shop (const object *op)
982{ 963{
964 dynbuf_text buf;
983 maptile *map = op->map; 965 maptile *map = op->map;
984 966
985 /*shopitems *items=map->shopitems; */ 967 /*shopitems *items=map->shopitems; */
986 int pos = 0, i; 968 int pos = 0, i;
987 double opinion = 0; 969 double opinion = 0;
988 char tmp[MAX_BUF] = "\0";
989 970
990 if (op->type != PLAYER) 971 if (op->type != PLAYER)
991 return 0; 972 return 0;
992 973
993 /*check if there is a shop specified for this map */ 974 /*check if there is a shop specified for this map */
994 if (map->shopitems || map->shopgreed || map->shoprace || map->shopmin || map->shopmax) 975 if (map->shopitems || map->shopgreed || map->shoprace || map->shopmin || map->shopmax)
995 { 976 {
996 new_draw_info (NDI_UNIQUE, 0, op, "From looking at the nearby shop you determine that it trades in:"); 977 buf << "From looking at the nearby shop you determine that it trades in ";
978 int lastcomma = 0, prevcomma = 0;
997 979
998 if (map->shopitems) 980 if (map->shopitems)
999 for (i = 0; i < map->shopitems[0].index; i++) 981 for (i = 0; i < map->shopitems[0].index; i++)
1000 if (map->shopitems[i].name && map->shopitems[i].strength > 10) 982 if (map->shopitems[i].name && map->shopitems[i].strength > 10)
1001 { 983 {
1002 snprintf (tmp + pos, sizeof (tmp) - pos, "%s, ", map->shopitems[i].name_pl); 984 buf << map->shopitems[i].name_pl;
1003 pos += strlen (tmp + pos); 985 prevcomma = lastcomma;
986 lastcomma = buf.size (); // remember offset
987 buf << ", ";
1004 } 988 }
1005 989
1006 if (!pos) 990 if (lastcomma)
991 {
992 buf.splice (lastcomma, 2);
993
994 if (prevcomma)
995 buf.splice (prevcomma, 2, " and ");
996 }
997 else
1007 strcat (tmp, "a little of everything."); 998 buf << "a little of everything.";
1008 999
1009 /* format the string into a list */ 1000 buf << ".\n\n";
1010 make_list_like (tmp);
1011 new_draw_info_format (NDI_UNIQUE, 0, op, "%s", tmp);
1012 1001
1013 if (map->shopmax) 1002 if (map->shopmax)
1014 new_draw_info_format (NDI_UNIQUE, 0, op, "It won't trade for items above %s.", cost_string_from_value (map->shopmax, 0)); 1003 buf << "It won't trade for items above " << cost_string_from_value (map->shopmax, 0) << ".\n\n";
1015 1004
1016 if (map->shopmin) 1005 if (map->shopmin)
1017 new_draw_info_format (NDI_UNIQUE, 0, op, "It won't trade in items worth less than %s.", cost_string_from_value (map->shopmin, 0)); 1006 buf << "It won't trade in items worth less than " << cost_string_from_value (map->shopmin, 0) << ".\n\n";
1018 1007
1019 if (map->shopgreed) 1008 if (map->shopgreed)
1020 { 1009 {
1021 if (map->shopgreed > 2.0) 1010 if (map->shopgreed > 2.0)
1022 new_draw_info (NDI_UNIQUE, 0, op, "It tends to overcharge massively."); 1011 buf << "It tends to overcharge massively.\n\n";
1023 else if (map->shopgreed > 1.5) 1012 else if (map->shopgreed > 1.5)
1024 new_draw_info (NDI_UNIQUE, 0, op, "It tends to overcharge substantially."); 1013 buf << "It tends to overcharge substantially.\n\n";
1025 else if (map->shopgreed > 1.1) 1014 else if (map->shopgreed > 1.1)
1026 new_draw_info (NDI_UNIQUE, 0, op, "It tends to overcharge slightly."); 1015 buf << "It tends to overcharge slightly.\n\n";
1027 else if (map->shopgreed < 0.9) 1016 else if (map->shopgreed < 0.9)
1028 new_draw_info (NDI_UNIQUE, 0, op, "It tends to undercharge."); 1017 buf << "It tends to undercharge.\n\n";
1029 } 1018 }
1030 1019
1031 if (map->shoprace) 1020 if (map->shoprace)
1032 { 1021 {
1033 opinion = shopkeeper_approval (map, op); 1022 opinion = shopkeeper_approval (map, op);
1023
1034 if (opinion > 0.8) 1024 if (opinion > 0.8)
1035 new_draw_info (NDI_UNIQUE, 0, op, "You think the shopkeeper likes you."); 1025 buf << "You think the shopkeeper likes you.\n\n";
1036 else if (opinion > 0.5) 1026 else if (opinion > 0.5)
1037 new_draw_info (NDI_UNIQUE, 0, op, "The shopkeeper seems unconcerned by you."); 1027 buf << "The shopkeeper seems unconcerned by you.\n\n";
1038 else 1028 else
1039 new_draw_info (NDI_UNIQUE, 0, op, "The shopkeeper seems to have taken a dislike to you."); 1029 buf << "The shopkeeper seems to have taken a dislike to you.\n\n";
1040 } 1030 }
1041 } 1031 }
1042 else 1032 else
1043 new_draw_info (NDI_UNIQUE, 0, op, "There is no shop nearby."); 1033 buf << "There is no shop nearby.\n\n";
1034
1035 op->contr->infobox (MSG_CHANNEL ("shopinfo"), buf);
1044 1036
1045 return 1; 1037 return 1;
1046} 1038}
1047 1039
1048struct shopinv 1040struct shopinv
1084 /* clear unpaid flag so that doesn't come up in query 1076 /* clear unpaid flag so that doesn't come up in query
1085 * string. We clear nrof so that we can better sort 1077 * string. We clear nrof so that we can better sort
1086 * the object names. 1078 * the object names.
1087 */ 1079 */
1088 1080
1089 CLEAR_FLAG (tmp, FLAG_UNPAID); 1081 tmp->clr_flag (FLAG_UNPAID);
1090 items[*numitems].nrof = tmp->nrof; 1082 items[*numitems].nrof = tmp->nrof;
1091 /* Non mergable items have nrof of 0, but count them as one 1083 /* Non mergable items have nrof of 0, but count them as one
1092 * so the display is properly. 1084 * so the display is properly.
1093 */ 1085 */
1094 if (tmp->nrof == 0) 1086 if (tmp->nrof == 0)
1116 items[*numitems].item_sort = strdup (query_base_name (tmp, 0)); 1108 items[*numitems].item_sort = strdup (query_base_name (tmp, 0));
1117 items[*numitems].item_real = strdup (query_base_name (tmp, 1)); 1109 items[*numitems].item_real = strdup (query_base_name (tmp, 1));
1118 (*numitems)++; 1110 (*numitems)++;
1119 break; 1111 break;
1120 } 1112 }
1121 SET_FLAG (tmp, FLAG_UNPAID); 1113
1114 tmp->set_flag (FLAG_UNPAID);
1122} 1115}
1123 1116
1124void 1117void
1125shop_listing (object *sign, object *op) 1118shop_listing (object *sign, object *op)
1126{ 1119{
1127 int i, j, numitems = 0, numallocated = 0, x1, x2, y1, y2; 1120 int i, j, x1, x2, y1, y2;
1128 const char *shop_coords = sign->kv (shstr_shop_coords); 1121 const char *shop_coords = sign->kv (shstr_shop_coords);
1129 object *stack; 1122 object *stack;
1130 shopinv *items; 1123 shopinv *items;
1131 1124
1132 /* Should never happen, but just in case a monster does apply a sign */ 1125 /* Should never happen, but just in case a monster does apply a sign */
1133 if (op->type != PLAYER) 1126 if (!op->is_player ())
1134 return; 1127 return;
1128
1129 dynbuf_text &buf = msg_dynbuf; buf.clear ();
1135 1130
1136 if (!(shop_coords && sscanf (shop_coords, "%d,%d,%d,%d", &x1, &y1, &x2, &y2))) 1131 if (!(shop_coords && sscanf (shop_coords, "%d,%d,%d,%d", &x1, &y1, &x2, &y2)))
1137 { 1132 {
1138 x1 = 0; 1133 x1 = 0;
1139 y1 = 0; 1134 y1 = 0;
1140 x2 = op->map->width - 1; 1135 x2 = op->map->width - 1;
1141 y2 = op->map->height - 1; 1136 y2 = op->map->height - 1;
1142 } 1137 }
1143 1138
1144 items = (shopinv *) malloc (40 * sizeof (shopinv));
1145 numallocated = 40; 1139 int numallocated = 40;
1140 int numitems = 0;
1141 items = (shopinv *)malloc (sizeof (shopinv) * numallocated);
1146 1142
1147 /* Find all the appropriate items */ 1143 /* Find all the appropriate items */
1148 for (i = x1; i <= x2; i++) 1144 for (i = x1; i <= x2; i++)
1149 {
1150 for (j = y1; j < y2; j++) 1145 for (j = y1; j < y2; j++)
1146 if (op->map->is_in_shop (i, j))
1151 { 1147 {
1152 if (is_in_shop (op->map, i, j)) 1148 stack = GET_MAP_OB (op->map, i, j);
1149
1150 while (stack)
1153 { 1151 {
1154 stack = GET_MAP_OB (op->map, i, j); 1152 if (stack->flag [FLAG_UNPAID])
1155
1156 while (stack)
1157 { 1153 {
1158 if (QUERY_FLAG (stack, FLAG_UNPAID))
1159 {
1160 if (numitems == numallocated) 1154 if (numitems == numallocated)
1161 {
1162 items = (shopinv *) realloc (items, sizeof (shopinv) * (numallocated + 10)); 1155 items = (shopinv *)realloc (items, sizeof (shopinv) * (numallocated *= 2));
1163 numallocated += 10;
1164 }
1165 1156
1166 add_shop_item (stack, items, &numitems, &numallocated); 1157 add_shop_item (stack, items, &numitems, &numallocated);
1167 }
1168
1169 stack = stack->above;
1170 } 1158 }
1159
1160 stack = stack->above;
1171 } 1161 }
1172 } 1162 }
1173 }
1174 1163
1175 if (numitems == 0) 1164 buf << (numitems ? "T<This shop contains:>\n\n"
1176 { 1165 : "T<This shop is currently empty.>");
1177 new_draw_info (NDI_UNIQUE, 0, op, "The shop is currently empty.\n");
1178 free (items);
1179 return;
1180 }
1181 1166
1182 qsort (items, numitems, sizeof (shopinv), (int (*)(const void *, const void *)) shop_sort); 1167 qsort (items, numitems, sizeof (shopinv), (int (*)(const void *, const void *)) shop_sort);
1183
1184 new_draw_info (NDI_UNIQUE, 0, op, "\nThe shop contains:");
1185 1168
1186 for (i = 0; i < numitems; i++) 1169 for (i = 0; i < numitems; i++)
1187 { 1170 {
1188 /* Collapse items of the same name together */ 1171 /* Collapse items of the same name together */
1189 if ((i + 1) < numitems && !strcmp (items[i].item_real, items[i + 1].item_real)) 1172 if ((i + 1) < numitems && !strcmp (items[i].item_real, items[i + 1].item_real))
1190 {
1191 items[i + 1].nrof += items[i].nrof; 1173 items[i + 1].nrof += items[i].nrof;
1192 free (items[i].item_sort);
1193 free (items[i].item_real);
1194 }
1195 else 1174 else
1196 {
1197 new_draw_info_format (NDI_UNIQUE, 0, op, "%d %s",
1198 items[i].nrof ? items[i].nrof : 1, items[i].nrof == 1 ? items[i].item_sort : items[i].item_real); 1175 buf.printf (" %4d %s\n", items[i].nrof ? items[i].nrof : 1, items[i].nrof == 1 ? items[i].item_sort : items[i].item_real);
1176
1199 free (items[i].item_sort); 1177 free (items[i].item_sort);
1200 free (items[i].item_real); 1178 free (items[i].item_real);
1201 }
1202 } 1179 }
1180
1181 op->contr->infobox (MSG_CHANNEL ("shopitems"), buf);
1203 1182
1204 free (items); 1183 free (items);
1205}
1206
1207/* elmex: this function checks whether the object is in a shop */
1208bool
1209is_in_shop (object *o)
1210{
1211 if (!o->is_on_map ())
1212 return false;
1213
1214 return is_in_shop (o->map, o->x, o->y);
1215} 1184}
1216 1185
1217/* elmex: this function checks whether we are in a shop or not 1186/* elmex: this function checks whether we are in a shop or not
1218 - change 2007-11-26: enhanced the O(n) case by stopping at the first 1187 - change 2007-11-26: enhanced the O(n) case by stopping at the first
1219 floor tile. this possibly will make map bugs where shopfloors are above 1188 floor tile. this possibly will make map bugs where shopfloors are above
1220 floors more obvious. 1189 floors more obvious.
1221*/ 1190*/
1222
1223bool 1191bool
1224is_in_shop (maptile *map, int x, int y) 1192maptile::is_in_shop (int x, int y) const
1225{ 1193{
1226 for (object *floor = GET_MAP_OB (map, x, y); floor; floor = floor->above) 1194 for (object *floor = at (x, y).bot; floor; floor = floor->above)
1227 if (QUERY_FLAG (floor, FLAG_IS_FLOOR)) 1195 if (floor->flag [FLAG_IS_FLOOR])
1228 return floor->type == SHOP_FLOOR; 1196 return floor->type == SHOP_FLOOR;
1229 1197
1230 return false; 1198 return false;
1231} 1199}
1232 1200

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines