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.46 by root, Wed Apr 9 14:36:47 2008 UTC vs.
Revision 1.77 by root, Sun Apr 4 02:51:57 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 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 (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)
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, "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
206 * AND Cha = 30 will get optimal price. 190 * AND Cha = 30 will get optimal price.
207 * Thus charisma will never get useless. 191 * Thus charisma will never get useless.
208 * -b.e. edler@heydernet.de 192 * -b.e. edler@heydernet.de
209 */ 193 */
210 194
211 if (who != NULL && who->type == PLAYER) 195 if (who && who->type == PLAYER)
212 { 196 {
213 int lev_bargain = 0; 197 int lev_bargain = 0;
214 int lev_identify = 0; 198 int lev_identify = 0;
215 int idskill1 = 0;
216 int idskill2 = 0;
217 const typedata *tmptype;
218
219 tmptype = get_typedata (tmp->type);
220 199
221 if (find_skill_by_number (who, SK_BARGAINING)) 200 if (find_skill_by_number (who, SK_BARGAINING))
222 lev_bargain = find_skill_by_number (who, SK_BARGAINING)->level; 201 lev_bargain = find_skill_by_number (who, SK_BARGAINING)->level;
223 202
224 if (tmptype) 203 if (const typedata *tmptype = get_typedata (tmp->type))
225 { 204 {
226 idskill1 = tmptype->identifyskill; 205 if (int idskill1 = tmptype->identifyskill)
227
228 if (idskill1)
229 { 206 {
230 idskill2 = tmptype->identifyskill2; 207 int idskill2 = tmptype->identifyskill2;
231 208
232 if (find_skill_by_number (who, idskill1)) 209 if (find_skill_by_number (who, idskill1))
233 lev_identify = find_skill_by_number (who, idskill1)->level; 210 lev_identify = find_skill_by_number (who, idskill1)->level;
234 211
235 if (idskill2 && find_skill_by_number (who, idskill2)) 212 if (idskill2 && find_skill_by_number (who, idskill2))
236 lev_identify += find_skill_by_number (who, idskill2)->level; 213 lev_identify += find_skill_by_number (who, idskill2)->level;
237 } 214 }
238 } 215 }
239 else
240 LOG (llevError, "Query_cost: item %s hasn't got a valid type\n", tmp->debug_desc ());
241 216
242 /* ratio determines how much of the price modification 217 /* ratio determines how much of the price modification
243 * will come from the basic stat charisma 218 * will come from the basic stat charisma
244 * the rest will come from the level in bargaining skill 219 * the rest will come from the level in bargaining skill
245 */ 220 */
418 393
419 if (coin == NULL) 394 if (coin == NULL)
420 return "nothing"; 395 return "nothing";
421 396
422 num = real_value / coin->value; 397 num = real_value / coin->value;
398
423 if (num == 1) 399 if (num == 1)
424 sprintf (buf, "about one %s", &coin->object::name); 400 sprintf (buf, "about one %s", &coin->object::name);
425 else if (num < 5) 401 else if (num < 5)
426 sprintf (buf, "a few %s", &coin->object::name_pl); 402 sprintf (buf, "a few %s", &coin->object::name_pl);
427 else if (num < 10) 403 else if (num < 10)
432 sprintf (buf, "lots of %s", &coin->object::name_pl); 408 sprintf (buf, "lots of %s", &coin->object::name_pl);
433 else if (num < 1000) 409 else if (num < 1000)
434 sprintf (buf, "a great many %s", &coin->object::name_pl); 410 sprintf (buf, "a great many %s", &coin->object::name_pl);
435 else 411 else
436 sprintf (buf, "a vast quantity of %s", &coin->object::name_pl); 412 sprintf (buf, "a vast quantity of %s", &coin->object::name_pl);
413
437 return buf; 414 return buf;
438 } 415 }
439 } 416 }
440 } 417 }
441 418
472 } 449 }
473 450
474 for (tmp = op->inv; tmp; tmp = tmp->below) 451 for (tmp = op->inv; tmp; tmp = tmp->below)
475 if (tmp->type == MONEY) 452 if (tmp->type == MONEY)
476 total += tmp->nrof * (sint64)tmp->value; 453 total += tmp->nrof * (sint64)tmp->value;
477 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")))
478 total += query_money (tmp); 455 total += query_money (tmp);
479 456
480 return total; 457 return total;
481} 458}
482 459
497 return 0; 474 return 0;
498 475
499 pay_from_container (pl, pl, to_pay); 476 pay_from_container (pl, pl, to_pay);
500 477
501 for (pouch = pl->inv; pouch && to_pay; pouch = pouch->below) 478 for (pouch = pl->inv; pouch && to_pay; pouch = pouch->below)
502 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")))
503 pay_from_container (pl, pouch, to_pay); 480 pay_from_container (pl, pouch, to_pay);
504 481
505 pl->update_stats (); 482 pl->update_stats ();
506 return 1; 483 return 1;
507} 484}
534 change_exp (pl, saved_money, "bargaining", SK_EXP_NONE); 511 change_exp (pl, saved_money, "bargaining", SK_EXP_NONE);
535 512
536 pay_from_container (pl, pl, to_pay); 513 pay_from_container (pl, pl, to_pay);
537 514
538 for (pouch = pl->inv; pouch && to_pay; pouch = pouch->below) 515 for (pouch = pl->inv; pouch && to_pay; pouch = pouch->below)
539 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")))
540 pay_from_container (pl, pouch, to_pay); 517 pay_from_container (pl, pouch, to_pay);
541 518
542 pl->update_stats (); 519 pl->update_stats ();
543 520
544 return 1; 521 return 1;
583 { 560 {
584 // This should not happen, but if it does, just merge the two. 561 // This should not happen, but if it does, just merge the two.
585 if (coin_objs [i]) 562 if (coin_objs [i])
586 { 563 {
587 LOG (llevError, "%s has two money entries of (%s)\n", &pouch->name, coins[NUM_COINS - 1 - i]); 564 LOG (llevError, "%s has two money entries of (%s)\n", &pouch->name, coins[NUM_COINS - 1 - i]);
588 tmp->remove ();
589 coin_objs[i]->nrof += tmp->nrof; 565 coin_objs[i]->nrof += tmp->nrof;
590 esrv_del_item (pl->contr, tmp->count);
591 tmp->destroy (); 566 tmp->destroy ();
592 } 567 }
593 else 568 else
594 { 569 {
595 tmp->remove (); 570 tmp->remove ();
596
597 if (pouch->type == PLAYER)
598 esrv_del_item (pl->contr, tmp->count);
599
600 coin_objs[i] = tmp; 571 coin_objs[i] = tmp;
601 } 572 }
602 573
603 break; 574 break;
604 } 575 }
617 at = archetype::find (coins[NUM_COINS - 1 - i]); 588 at = archetype::find (coins[NUM_COINS - 1 - i]);
618 589
619 if (at == NULL) 590 if (at == NULL)
620 LOG (llevError, "Could not find %s archetype\n", coins[NUM_COINS - 1 - i]); 591 LOG (llevError, "Could not find %s archetype\n", coins[NUM_COINS - 1 - i]);
621 592
622 coin_objs[i] = arch_to_object (at); 593 coin_objs[i] = at->instance ();
623 coin_objs[i]->nrof = 0; 594 coin_objs[i]->nrof = 0;
624 } 595 }
625 596
626 for (i = 0; i < NUM_COINS; i++) 597 for (i = 0; i < NUM_COINS; i++)
627 { 598 {
628 object &coin = *coin_objs[i]; 599 object &coin = *coin_objs[i];
629 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);
630 to_pay -= num_coins * coin.value; 601 to_pay -= num_coins * coin.value;
631 602
632 coin.nrof -= num_coins; 603 coin.nrof -= num_coins;
633 /* Now start making change. Start at the coin value 604 /* Now start making change. Start at the coin value
634 * below the one we just did, and work down to 605 * below the one we just did, and work down to
644 count--; 615 count--;
645 } 616 }
646 } 617 }
647 618
648 for (i = 0; i < NUM_COINS; i++) 619 for (i = 0; i < NUM_COINS; i++)
649 {
650 if (coin_objs[i]->nrof) 620 if (coin_objs[i]->nrof)
651 {
652 object *tmp = insert_ob_in_ob (coin_objs[i], pouch); 621 insert_ob_in_ob (coin_objs [i], pouch);
653
654 esrv_send_item (pl, tmp);
655 esrv_send_item (pl, pouch);
656
657 if (pl != pouch)
658 esrv_update_item (UPD_WEIGHT, pl, pouch);
659
660 if (pl->type != PLAYER)
661 esrv_send_item (pl, pl);
662 }
663 else 622 else
664 coin_objs[i]->destroy (); 623 coin_objs[i]->destroy ();
665 }
666} 624}
667 625
668/* 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
669 * 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.
670 * 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
690 unpaid_price += query_cost (item, pl, F_BUY | F_SHOP); 648 unpaid_price += query_cost (item, pl, F_BUY | F_SHOP);
691 } 649 }
692 650
693 if (unpaid_price > player_wealth) 651 if (unpaid_price > player_wealth)
694 { 652 {
695 char buf[MAX_BUF]; 653 dynbuf_text &buf = msg_dynbuf; buf.clear ();
696 char cost[MAX_BUF];
697 char missing[MAX_BUF];
698 654
699 snprintf (cost, MAX_BUF, "%s", cost_string_from_value (unpaid_price, 0)); 655 buf << "You have " << unpaid_count
700 snprintf (missing, MAX_BUF, "%s", cost_string_from_value (unpaid_price - player_wealth, 0)); 656 << " unpaid item(s) that would cost you " << cost_string_from_value (unpaid_price, 0)
657 << ". You need another " << cost_string_from_value (unpaid_price - player_wealth, 0)
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.>";
701 660
702 snprintf (buf, MAX_BUF, "You have %d unpaid items that would cost you %s. You need another %s to be able to afford that.", 661 pl->failmsg (buf);
703 unpaid_count, cost, missing);
704 new_draw_info (NDI_UNIQUE, 0, pl, buf);
705 662
706 return 0; 663 return 0;
707 } 664 }
708 else 665 else
709 return 1; 666 return 1;
738 SET_FLAG (op, FLAG_UNPAID); 695 SET_FLAG (op, FLAG_UNPAID);
739 return 0; 696 return 0;
740 } 697 }
741 else 698 else
742 { 699 {
743 object *tmp;
744
745 CLEAR_FLAG (op, FLAG_UNPAID); 700 CLEAR_FLAG (op, FLAG_UNPAID);
746 CLEAR_FLAG (op, FLAG_PLAYER_SOLD); 701 CLEAR_FLAG (op, FLAG_PLAYER_SOLD);
747 new_draw_info_format (NDI_UNIQUE, 0, op, "You paid %s for %s.", buf, query_name (op)); 702 new_draw_info_format (NDI_UNIQUE, 0, op, "You paid %s for %s.", buf, query_name (op));
748 tmp = merge_ob (op, NULL);
749 703
750 if (pl->type == PLAYER) 704 if (!merge_ob (op, op->env->inv))
751 {
752 if (tmp)
753 { /* it was merged */
754 esrv_del_item (pl->contr, op->count);
755 op = tmp;
756 }
757
758 esrv_send_item (pl, op); 705 esrv_update_item (UPD_FLAGS, pl, op);
759 }
760 706
761 goto next_item; 707 goto next_item;
762 } 708 }
763 } 709 }
764 } 710 }
793 LOG (llevError, "Could not find %s archetype\n", coins[count]); 739 LOG (llevError, "Could not find %s archetype\n", coins[count]);
794 else if ((amount / at->value) > 0) 740 else if ((amount / at->value) > 0)
795 { 741 {
796 for (pouch = pl->inv; pouch; pouch = pouch->below) 742 for (pouch = pl->inv; pouch; pouch = pouch->below)
797 { 743 {
798 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"))
799 { 745 {
800 int w = at->weight * (100 - pouch->stats.Str) / 100; 746 int w = at->weight * (100 - pouch->stats.Str) / 100;
801 int n = amount / at->value; 747 int n = amount / at->value;
802 748
803 if (w == 0) 749 if (w == 0)
806 if (n > 0 && (!pouch->weight_limit || pouch->carrying + w <= pouch->weight_limit)) 752 if (n > 0 && (!pouch->weight_limit || pouch->carrying + w <= pouch->weight_limit))
807 { 753 {
808 if (pouch->weight_limit && (pouch->weight_limit - pouch->carrying) / w < n) 754 if (pouch->weight_limit && (pouch->weight_limit - pouch->carrying) / w < n)
809 n = (pouch->weight_limit - pouch->carrying) / w; 755 n = (pouch->weight_limit - pouch->carrying) / w;
810 756
811 tmp = arch_to_object (at); 757 object *tmp = at->instance ();
812 tmp->nrof = n; 758 tmp->nrof = n;
813 amount -= tmp->nrof * tmp->value; 759 amount -= tmp->nrof * tmp->value;
814 tmp = insert_ob_in_ob (tmp, pouch); 760 pouch->insert (tmp);
815 esrv_send_item (pl, tmp);
816 esrv_send_item (pl, pouch);
817 esrv_update_item (UPD_WEIGHT, pl, pouch);
818 esrv_send_item (pl, pl);
819 } 761 }
820 } 762 }
821 } 763 }
822 764
823 if (amount / at->value > 0) 765 if (amount / at->value > 0)
824 { 766 {
825 tmp = arch_to_object (at); 767 object *tmp = at->instance ();
826 tmp->nrof = amount / tmp->value; 768 tmp->nrof = amount / tmp->value;
827 amount -= tmp->nrof * tmp->value; 769 amount -= tmp->nrof * tmp->value;
828 tmp = insert_ob_in_ob (tmp, pl); 770 pl->insert (tmp);
829 esrv_send_item (pl, tmp);
830 esrv_send_item (pl, pl);
831 } 771 }
832 } 772 }
833 } 773 }
834 774
835 if (amount != 0) 775 if (amount != 0)
838 778
839/* elmex: this is for the bank plugin :( */ 779/* elmex: this is for the bank plugin :( */
840sint64 780sint64
841pay_player_arch (object *pl, const char *arch, sint64 amount) 781pay_player_arch (object *pl, const char *arch, sint64 amount)
842{ 782{
843 archetype *at = archetype::find (arch);
844 object *tmp = NULL;
845
846 if (at == NULL)
847 return 0;
848
849 if (amount > 0) 783 if (amount)
850 { 784 {
851 tmp = arch_to_object (at); 785 object *ob = archetype::get (arch);
786
787 if (!ob)
788 return 0;
789
852 tmp->nrof = amount; 790 ob->nrof = amount;
853 tmp = insert_ob_in_ob (tmp, pl); 791 pl->insert (ob);
854 esrv_send_item (pl, tmp);
855 esrv_send_item (pl, pl);
856 } 792 }
857 793
858 return 1; 794 return 1;
859} 795}
860 796
863 * buy item. 799 * buy item.
864 * 800 *
865 * Modified to fill available race: gold containers before dumping 801 * Modified to fill available race: gold containers before dumping
866 * remaining coins in character's inventory. 802 * remaining coins in character's inventory.
867 */ 803 */
868void 804bool
869sell_item (object *op, object *pl) 805sell_item (object *op, object *pl)
870{ 806{
871 sint64 amount = query_cost (op, pl, F_SELL | F_SHOP), extra_gain; 807 sint64 amount = query_cost (op, pl, F_SELL | F_SHOP), extra_gain;
872 808
873 if (pl == NULL || pl->type != PLAYER) 809 if (pl == NULL || pl->type != PLAYER)
874 { 810 {
875 LOG (llevDebug, "Object other than player tried to sell something.\n"); 811 LOG (llevDebug, "Object other than player tried to sell something.\n");
876 return; 812 return false;
877 } 813 }
878 814
879 op->custom_name = 0; 815 op->custom_name = 0;
880 816
881 if (!amount) 817 if (!amount)
882 { 818 {
883 new_draw_info_format (NDI_UNIQUE, 0, pl, "We're not interested in %s.", query_name (op)); 819 new_draw_info_format (NDI_UNIQUE, 0, pl, "We're not interested in %s.",
884 820 query_name (op));
885 /* Even if the character doesn't get anything for it, it may still be 821 // elmex: change: the player now gets the item back if the shop is not
886 * worth something. If so, make it unpaid 822 // interested in it.
887 */
888 if (op->value)
889 {
890 SET_FLAG (op, FLAG_UNPAID);
891 SET_FLAG (op, FLAG_PLAYER_SOLD);
892 }
893
894 identify (op);
895 return; 823 return false;
896 } 824 }
897 825
898 /* We compare the price with the one for a player 826 /* We compare the price with the one for a player
899 * without bargaining skill. 827 * without bargaining skill.
900 * This determins the amount of exp (if any) gained for bargaining. 828 * This determins the amount of exp (if any) gained for bargaining.
905 if (extra_gain > 0) 833 if (extra_gain > 0)
906 change_exp (pl, extra_gain / 10, "bargaining", SK_EXP_NONE); 834 change_exp (pl, extra_gain / 10, "bargaining", SK_EXP_NONE);
907 835
908 pay_player (pl, amount); 836 pay_player (pl, amount);
909 837
910 new_draw_info_format (NDI_UNIQUE, 0, pl, "You receive %s for %s.", query_cost_string (op, pl, F_SELL | F_SHOP), query_name (op)); 838 new_draw_info_format (NDI_UNIQUE, 0, pl, "You receive %s for %s.",
839 query_cost_string (op, pl, F_SELL | F_SHOP), query_name (op));
911 pl->play_sound (sound_find ("shop_sell")); 840 pl->play_sound (sound_find ("shop_sell"));
912 841
913 SET_FLAG (op, FLAG_UNPAID); 842 SET_FLAG (op, FLAG_UNPAID);
914 identify (op); 843 identify (op);
844
845 return true;
915} 846}
916 847
917/* returns a double that is the ratio of the price that a shop will offer for 848/* returns a double that is the ratio of the price that a shop will offer for
918 * item based on the shops specialisation. Does not take account of greed, 849 * item based on the shops specialisation. Does not take account of greed,
919 * returned value is between SPECIALISATION_EFFECT and 1. 850 * returned value is between SPECIALISATION_EFFECT and 1.
920 */ 851 */
921static double 852static double
922shop_specialisation_ratio (const object *item, const maptile *map) 853shop_specialisation_ratio (const object *item, const maptile *map)
923{ 854{
924 shopitems *items = map->shopitems; 855 shopitems *items = map->shopitems;
925 double likedness = 0.; 856 int likedness = 0;
926 int i; 857 int i;
927 858
928 if (item == NULL) 859 if (item == NULL)
929 { 860 {
930 LOG (llevError, "shop_specialisation_ratio: passed a NULL item for map %s\n", &map->path); 861 LOG (llevError, "shop_specialisation_ratio: passed a NULL item for map %s\n", &map->path);
940 */ 871 */
941 return SPECIALISATION_EFFECT; 872 return SPECIALISATION_EFFECT;
942 } 873 }
943 874
944 if (map->shopitems) 875 if (map->shopitems)
945 {
946 for (i = 0; i < items[0].index; i++) 876 for (i = 0; i < items[0].index; i++)
947 if (items[i].typenum == item->type || (!items[i].typenum && likedness == 0.001)) 877 if (items[i].typenum == item->type || (!items[i].typenum && !likedness))
948 likedness = items[i].strength / 100.0; 878 likedness = items[i].strength;
949 }
950 879
951 if (likedness > 1.0) 880 if (likedness > 100)
952 { /* someone has been rather silly with the map headers. */ 881 { /* someone has been rather silly with the map headers. */
953 LOG (llevDebug, "shop_specialisation ratio: item type %d on map %s is above 100%%\n", item->type, &map->path); 882 LOG (llevDebug, "shop_specialisation ratio: item type %d on map %s is above 100%%\n", item->type, &map->path);
954 likedness = 1.0; 883 likedness = 100;
955 } 884 }
956 885
957 if (likedness < -1.0) 886 if (likedness < -100)
958 { 887 {
959 LOG (llevDebug, "shop_specialisation ratio: item type %d on map %s is below -100%%\n", item->type, &map->path); 888 LOG (llevDebug, "shop_specialisation ratio: item type %d on map %s is below -100%%\n", item->type, &map->path);
960 likedness = -1.0; 889 likedness = -100;
961 } 890 }
962 891
963 return lerp (likedness, -1., 1., SPECIALISATION_EFFECT, 1.); 892 return lerp (double (likedness), -100., 100., SPECIALISATION_EFFECT, 1.);
964} 893}
965 894
966/*returns the greed of the shop on map, or 1 if it isn't specified. */ 895/*returns the greed of the shop on map, or 1 if it isn't specified. */
967static double 896static double
968shop_greed (const maptile *map) 897shop_greed (const maptile *map)
1018 tmpshopmax = map->shopmax ? map->shopmax : 100000; // 20 royalties default 947 tmpshopmax = map->shopmax ? map->shopmax : 100000; // 20 royalties default
1019 948
1020 if (map->shopmin && unit_price < map->shopmin) 949 if (map->shopmin && unit_price < map->shopmin)
1021 return 0; 950 return 0;
1022 else if (unit_price > tmpshopmax / 2) 951 else if (unit_price > tmpshopmax / 2)
1023 newval = MIN ((tmpshopmax / 2) + isqrt (unit_price - tmpshopmax / 2), tmpshopmax); 952 newval = min ((tmpshopmax / 2) + isqrt (unit_price - tmpshopmax / 2), tmpshopmax);
1024 else 953 else
1025 newval = unit_price; 954 newval = unit_price;
1026 } 955 }
1027 956
1028 newval *= quantity; 957 newval *= quantity;
1032 961
1033/* gives a desciption of the shop on their current map to the player op. */ 962/* gives a desciption of the shop on their current map to the player op. */
1034int 963int
1035describe_shop (const object *op) 964describe_shop (const object *op)
1036{ 965{
966 dynbuf_text buf;
1037 maptile *map = op->map; 967 maptile *map = op->map;
1038 968
1039 /*shopitems *items=map->shopitems; */ 969 /*shopitems *items=map->shopitems; */
1040 int pos = 0, i; 970 int pos = 0, i;
1041 double opinion = 0; 971 double opinion = 0;
1042 char tmp[MAX_BUF] = "\0";
1043 972
1044 if (op->type != PLAYER) 973 if (op->type != PLAYER)
1045 return 0; 974 return 0;
1046 975
1047 /*check if there is a shop specified for this map */ 976 /*check if there is a shop specified for this map */
1048 if (map->shopitems || map->shopgreed || map->shoprace || map->shopmin || map->shopmax) 977 if (map->shopitems || map->shopgreed || map->shoprace || map->shopmin || map->shopmax)
1049 { 978 {
1050 new_draw_info (NDI_UNIQUE, 0, op, "From looking at the nearby shop you determine that it trades in:"); 979 buf << "From looking at the nearby shop you determine that it trades in ";
980 int lastcomma = 0, prevcomma = 0;
1051 981
1052 if (map->shopitems) 982 if (map->shopitems)
1053 for (i = 0; i < map->shopitems[0].index; i++) 983 for (i = 0; i < map->shopitems[0].index; i++)
1054 if (map->shopitems[i].name && map->shopitems[i].strength > 10) 984 if (map->shopitems[i].name && map->shopitems[i].strength > 10)
1055 { 985 {
1056 snprintf (tmp + pos, sizeof (tmp) - pos, "%s, ", map->shopitems[i].name_pl); 986 buf << map->shopitems[i].name_pl;
1057 pos += strlen (tmp + pos); 987 prevcomma = lastcomma;
988 lastcomma = buf.size (); // remember offset
989 buf << ", ";
1058 } 990 }
1059 991
1060 if (!pos) 992 if (lastcomma)
993 {
994 buf.splice (lastcomma, 2);
995
996 if (prevcomma)
997 buf.splice (prevcomma, 2, " and ");
998 }
999 else
1061 strcat (tmp, "a little of everything."); 1000 buf << "a little of everything.";
1062 1001
1063 /* format the string into a list */ 1002 buf << ".\n\n";
1064 make_list_like (tmp);
1065 new_draw_info_format (NDI_UNIQUE, 0, op, "%s", tmp);
1066 1003
1067 if (map->shopmax) 1004 if (map->shopmax)
1068 new_draw_info_format (NDI_UNIQUE, 0, op, "It won't trade for items above %s.", cost_string_from_value (map->shopmax, 0)); 1005 buf << "It won't trade for items above " << cost_string_from_value (map->shopmax, 0) << ".\n\n";
1069 1006
1070 if (map->shopmin) 1007 if (map->shopmin)
1071 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)); 1008 buf << "It won't trade in items worth less than " << cost_string_from_value (map->shopmin, 0) << ".\n\n";
1072 1009
1073 if (map->shopgreed) 1010 if (map->shopgreed)
1074 { 1011 {
1075 if (map->shopgreed > 2.0) 1012 if (map->shopgreed > 2.0)
1076 new_draw_info (NDI_UNIQUE, 0, op, "It tends to overcharge massively."); 1013 buf << "It tends to overcharge massively.\n\n";
1077 else if (map->shopgreed > 1.5) 1014 else if (map->shopgreed > 1.5)
1078 new_draw_info (NDI_UNIQUE, 0, op, "It tends to overcharge substantially."); 1015 buf << "It tends to overcharge substantially.\n\n";
1079 else if (map->shopgreed > 1.1) 1016 else if (map->shopgreed > 1.1)
1080 new_draw_info (NDI_UNIQUE, 0, op, "It tends to overcharge slightly."); 1017 buf << "It tends to overcharge slightly.\n\n";
1081 else if (map->shopgreed < 0.9) 1018 else if (map->shopgreed < 0.9)
1082 new_draw_info (NDI_UNIQUE, 0, op, "It tends to undercharge."); 1019 buf << "It tends to undercharge.\n\n";
1083 } 1020 }
1084 1021
1085 if (map->shoprace) 1022 if (map->shoprace)
1086 { 1023 {
1087 opinion = shopkeeper_approval (map, op); 1024 opinion = shopkeeper_approval (map, op);
1025
1088 if (opinion > 0.8) 1026 if (opinion > 0.8)
1089 new_draw_info (NDI_UNIQUE, 0, op, "You think the shopkeeper likes you."); 1027 buf << "You think the shopkeeper likes you.\n\n";
1090 else if (opinion > 0.5) 1028 else if (opinion > 0.5)
1091 new_draw_info (NDI_UNIQUE, 0, op, "The shopkeeper seems unconcerned by you."); 1029 buf << "The shopkeeper seems unconcerned by you.\n\n";
1092 else 1030 else
1093 new_draw_info (NDI_UNIQUE, 0, op, "The shopkeeper seems to have taken a dislike to you."); 1031 buf << "The shopkeeper seems to have taken a dislike to you.\n\n";
1094 } 1032 }
1095 } 1033 }
1096 else 1034 else
1097 new_draw_info (NDI_UNIQUE, 0, op, "There is no shop nearby."); 1035 buf << "There is no shop nearby.\n\n";
1036
1037 op->contr->infobox (MSG_CHANNEL ("shopinfo"), buf);
1098 1038
1099 return 1; 1039 return 1;
1100} 1040}
1101 1041
1102struct shopinv 1042struct shopinv
1117{ 1057{
1118 shopinv *s1 = (shopinv *) a1, *s2 = (shopinv *) a2; 1058 shopinv *s1 = (shopinv *) a1, *s2 = (shopinv *) a2;
1119 1059
1120 if (s1->type < s2->type) 1060 if (s1->type < s2->type)
1121 return -1; 1061 return -1;
1062
1122 if (s1->type > s2->type) 1063 if (s1->type > s2->type)
1123 return 1; 1064 return 1;
1124 1065
1125 /* the type is the same (what atoi gets), so do a strcasecmp to sort 1066 /* the type is the same (what atoi gets), so do a strcasecmp to sort
1126 * via alphabetical order 1067 * via alphabetical order
1169 items[*numitems].item_sort = strdup (query_base_name (tmp, 0)); 1110 items[*numitems].item_sort = strdup (query_base_name (tmp, 0));
1170 items[*numitems].item_real = strdup (query_base_name (tmp, 1)); 1111 items[*numitems].item_real = strdup (query_base_name (tmp, 1));
1171 (*numitems)++; 1112 (*numitems)++;
1172 break; 1113 break;
1173 } 1114 }
1115
1174 SET_FLAG (tmp, FLAG_UNPAID); 1116 SET_FLAG (tmp, FLAG_UNPAID);
1175} 1117}
1176 1118
1177void 1119void
1178shop_listing (object *sign, object *op) 1120shop_listing (object *sign, object *op)
1179{ 1121{
1180 int i, j, numitems = 0, numallocated = 0, x1, x2, y1, y2; 1122 int i, j, x1, x2, y1, y2;
1181 const char *shop_coords = get_ob_key_value (sign, "shop_coords"); 1123 const char *shop_coords = sign->kv (shstr_shop_coords);
1182 object *stack; 1124 object *stack;
1183 shopinv *items; 1125 shopinv *items;
1184 1126
1185 /* Should never happen, but just in case a monster does apply a sign */ 1127 /* Should never happen, but just in case a monster does apply a sign */
1186 if (op->type != PLAYER) 1128 if (!op->is_player ())
1187 return; 1129 return;
1130
1131 dynbuf_text &buf = msg_dynbuf; buf.clear ();
1188 1132
1189 if (!(shop_coords && sscanf (shop_coords, "%d,%d,%d,%d", &x1, &y1, &x2, &y2))) 1133 if (!(shop_coords && sscanf (shop_coords, "%d,%d,%d,%d", &x1, &y1, &x2, &y2)))
1190 { 1134 {
1191 x1 = 0; 1135 x1 = 0;
1192 y1 = 0; 1136 y1 = 0;
1193 x2 = op->map->width - 1; 1137 x2 = op->map->width - 1;
1194 y2 = op->map->height - 1; 1138 y2 = op->map->height - 1;
1195 } 1139 }
1196 1140
1197 items = (shopinv *) malloc (40 * sizeof (shopinv));
1198 numallocated = 40; 1141 int numallocated = 40;
1142 int numitems = 0;
1143 items = (shopinv *)malloc (sizeof (shopinv) * numallocated);
1199 1144
1200 /* Find all the appropriate items */ 1145 /* Find all the appropriate items */
1201 for (i = x1; i <= x2; i++) 1146 for (i = x1; i <= x2; i++)
1202 {
1203 for (j = y1; j < y2; j++) 1147 for (j = y1; j < y2; j++)
1148 if (op->map->is_in_shop (i, j))
1204 { 1149 {
1205 if (is_in_shop (op->map, i, j)) 1150 stack = GET_MAP_OB (op->map, i, j);
1151
1152 while (stack)
1206 { 1153 {
1207 stack = GET_MAP_OB (op->map, i, j); 1154 if (QUERY_FLAG (stack, FLAG_UNPAID))
1208
1209 while (stack)
1210 { 1155 {
1211 if (QUERY_FLAG (stack, FLAG_UNPAID))
1212 {
1213 if (numitems == numallocated) 1156 if (numitems == numallocated)
1214 {
1215 items = (shopinv *) realloc (items, sizeof (shopinv) * (numallocated + 10)); 1157 items = (shopinv *)realloc (items, sizeof (shopinv) * (numallocated *= 2));
1216 numallocated += 10;
1217 }
1218 1158
1219 add_shop_item (stack, items, &numitems, &numallocated); 1159 add_shop_item (stack, items, &numitems, &numallocated);
1220 }
1221
1222 stack = stack->above;
1223 } 1160 }
1161
1162 stack = stack->above;
1224 } 1163 }
1225 } 1164 }
1226 }
1227 1165
1228 if (numitems == 0) 1166 buf << (numitems ? "T<This shop contains:>\n\n"
1229 { 1167 : "T<This shop is currently empty.>");
1230 new_draw_info (NDI_UNIQUE, 0, op, "The shop is currently empty.\n");
1231 free (items);
1232 return;
1233 }
1234 1168
1235 qsort (items, numitems, sizeof (shopinv), (int (*)(const void *, const void *)) shop_sort); 1169 qsort (items, numitems, sizeof (shopinv), (int (*)(const void *, const void *)) shop_sort);
1236
1237 new_draw_info (NDI_UNIQUE, 0, op, "\nThe shop contains:");
1238 1170
1239 for (i = 0; i < numitems; i++) 1171 for (i = 0; i < numitems; i++)
1240 { 1172 {
1241 /* Collapse items of the same name together */ 1173 /* Collapse items of the same name together */
1242 if ((i + 1) < numitems && !strcmp (items[i].item_real, items[i + 1].item_real)) 1174 if ((i + 1) < numitems && !strcmp (items[i].item_real, items[i + 1].item_real))
1243 {
1244 items[i + 1].nrof += items[i].nrof; 1175 items[i + 1].nrof += items[i].nrof;
1245 free (items[i].item_sort);
1246 free (items[i].item_real);
1247 }
1248 else 1176 else
1249 {
1250 new_draw_info_format (NDI_UNIQUE, 0, op, "%d %s",
1251 items[i].nrof ? items[i].nrof : 1, items[i].nrof == 1 ? items[i].item_sort : items[i].item_real); 1177 buf.printf (" %4d %s\n", items[i].nrof ? items[i].nrof : 1, items[i].nrof == 1 ? items[i].item_sort : items[i].item_real);
1178
1252 free (items[i].item_sort); 1179 free (items[i].item_sort);
1253 free (items[i].item_real); 1180 free (items[i].item_real);
1254 }
1255 } 1181 }
1182
1183 op->contr->infobox (MSG_CHANNEL ("shopitems"), buf);
1256 1184
1257 free (items); 1185 free (items);
1258}
1259
1260/* elmex: this function checks whether the object is in a shop */
1261bool
1262is_in_shop (object *o)
1263{
1264 if (!o->map)
1265 return false;
1266
1267 return is_in_shop (o->map, o->x, o->y);
1268} 1186}
1269 1187
1270/* elmex: this function checks whether we are in a shop or not 1188/* elmex: this function checks whether we are in a shop or not
1271 - change 2007-11-26: enhanced the O(n) case by stopping at the first 1189 - change 2007-11-26: enhanced the O(n) case by stopping at the first
1272 floor tile. this possibly will make map bugs where shopfloors are above 1190 floor tile. this possibly will make map bugs where shopfloors are above
1273 floors more obvious. 1191 floors more obvious.
1274*/ 1192*/
1275
1276bool 1193bool
1277is_in_shop (maptile *map, int x, int y) 1194maptile::is_in_shop (int x, int y) const
1278{ 1195{
1279 for (object *floor = GET_MAP_OB (map, x, y); floor; floor = floor->above) 1196 for (object *floor = at (x, y).bot; floor; floor = floor->above)
1280 if (QUERY_FLAG (floor, FLAG_IS_FLOOR)) 1197 if (floor->flag [FLAG_IS_FLOOR])
1281 return floor->type == SHOP_FLOOR; 1198 return floor->type == SHOP_FLOOR;
1199
1282 return false; 1200 return false;
1283} 1201}
1284 1202

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines