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,2009 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. |
… | |
… | |
588 | at = archetype::find (coins[NUM_COINS - 1 - i]); |
588 | at = archetype::find (coins[NUM_COINS - 1 - i]); |
589 | |
589 | |
590 | if (at == NULL) |
590 | if (at == NULL) |
591 | 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]); |
592 | |
592 | |
593 | coin_objs[i] = arch_to_object (at); |
593 | coin_objs[i] = at->instance (); |
594 | coin_objs[i]->nrof = 0; |
594 | coin_objs[i]->nrof = 0; |
595 | } |
595 | } |
596 | |
596 | |
597 | for (i = 0; i < NUM_COINS; i++) |
597 | for (i = 0; i < NUM_COINS; i++) |
598 | { |
598 | { |
… | |
… | |
752 | 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)) |
753 | { |
753 | { |
754 | if (pouch->weight_limit && (pouch->weight_limit - pouch->carrying) / w < n) |
754 | if (pouch->weight_limit && (pouch->weight_limit - pouch->carrying) / w < n) |
755 | n = (pouch->weight_limit - pouch->carrying) / w; |
755 | n = (pouch->weight_limit - pouch->carrying) / w; |
756 | |
756 | |
757 | object *tmp = arch_to_object (at); |
757 | object *tmp = at->instance (); |
758 | tmp->nrof = n; |
758 | tmp->nrof = n; |
759 | amount -= tmp->nrof * tmp->value; |
759 | amount -= tmp->nrof * tmp->value; |
760 | pouch->insert (tmp); |
760 | pouch->insert (tmp); |
761 | } |
761 | } |
762 | } |
762 | } |
763 | } |
763 | } |
764 | |
764 | |
765 | if (amount / at->value > 0) |
765 | if (amount / at->value > 0) |
766 | { |
766 | { |
767 | object *tmp = arch_to_object (at); |
767 | object *tmp = at->instance (); |
768 | tmp->nrof = amount / tmp->value; |
768 | tmp->nrof = amount / tmp->value; |
769 | amount -= tmp->nrof * tmp->value; |
769 | amount -= tmp->nrof * tmp->value; |
770 | pl->insert (tmp); |
770 | pl->insert (tmp); |
771 | } |
771 | } |
772 | } |
772 | } |
… | |
… | |
851 | */ |
851 | */ |
852 | static double |
852 | static double |
853 | shop_specialisation_ratio (const object *item, const maptile *map) |
853 | shop_specialisation_ratio (const object *item, const maptile *map) |
854 | { |
854 | { |
855 | shopitems *items = map->shopitems; |
855 | shopitems *items = map->shopitems; |
856 | double likedness = 0.; |
856 | int likedness = 0; |
857 | int i; |
857 | int i; |
858 | |
858 | |
859 | if (item == NULL) |
859 | if (item == NULL) |
860 | { |
860 | { |
861 | 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); |
… | |
… | |
871 | */ |
871 | */ |
872 | return SPECIALISATION_EFFECT; |
872 | return SPECIALISATION_EFFECT; |
873 | } |
873 | } |
874 | |
874 | |
875 | if (map->shopitems) |
875 | if (map->shopitems) |
876 | { |
|
|
877 | for (i = 0; i < items[0].index; i++) |
876 | for (i = 0; i < items[0].index; i++) |
878 | if (items[i].typenum == item->type || (!items[i].typenum && likedness == 0.001)) |
877 | if (items[i].typenum == item->type || (!items[i].typenum && !likedness)) |
879 | likedness = items[i].strength / 100.0; |
878 | likedness = items[i].strength; |
880 | } |
|
|
881 | |
879 | |
882 | if (likedness > 1.0) |
880 | if (likedness > 100) |
883 | { /* someone has been rather silly with the map headers. */ |
881 | { /* someone has been rather silly with the map headers. */ |
884 | 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); |
885 | likedness = 1.0; |
883 | likedness = 100; |
886 | } |
884 | } |
887 | |
885 | |
888 | if (likedness < -1.0) |
886 | if (likedness < -100) |
889 | { |
887 | { |
890 | 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); |
891 | likedness = -1.0; |
889 | likedness = -100; |
892 | } |
890 | } |
893 | |
891 | |
894 | return lerp (likedness, -1., 1., SPECIALISATION_EFFECT, 1.); |
892 | return lerp (double (likedness), -100., 100., SPECIALISATION_EFFECT, 1.); |
895 | } |
893 | } |
896 | |
894 | |
897 | /*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. */ |
898 | static double |
896 | static double |
899 | shop_greed (const maptile *map) |
897 | shop_greed (const maptile *map) |
… | |
… | |
949 | tmpshopmax = map->shopmax ? map->shopmax : 100000; // 20 royalties default |
947 | tmpshopmax = map->shopmax ? map->shopmax : 100000; // 20 royalties default |
950 | |
948 | |
951 | if (map->shopmin && unit_price < map->shopmin) |
949 | if (map->shopmin && unit_price < map->shopmin) |
952 | return 0; |
950 | return 0; |
953 | else if (unit_price > tmpshopmax / 2) |
951 | else if (unit_price > tmpshopmax / 2) |
954 | newval = MIN ((tmpshopmax / 2) + isqrt (unit_price - tmpshopmax / 2), tmpshopmax); |
952 | newval = min ((tmpshopmax / 2) + isqrt (unit_price - tmpshopmax / 2), tmpshopmax); |
955 | else |
953 | else |
956 | newval = unit_price; |
954 | newval = unit_price; |
957 | } |
955 | } |
958 | |
956 | |
959 | newval *= quantity; |
957 | newval *= quantity; |
… | |
… | |
963 | |
961 | |
964 | /* 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. */ |
965 | int |
963 | int |
966 | describe_shop (const object *op) |
964 | describe_shop (const object *op) |
967 | { |
965 | { |
|
|
966 | dynbuf_text buf; |
968 | maptile *map = op->map; |
967 | maptile *map = op->map; |
969 | |
968 | |
970 | /*shopitems *items=map->shopitems; */ |
969 | /*shopitems *items=map->shopitems; */ |
971 | int pos = 0, i; |
970 | int pos = 0, i; |
972 | double opinion = 0; |
971 | double opinion = 0; |
973 | char tmp[MAX_BUF] = "\0"; |
|
|
974 | |
972 | |
975 | if (op->type != PLAYER) |
973 | if (op->type != PLAYER) |
976 | return 0; |
974 | return 0; |
977 | |
975 | |
978 | /*check if there is a shop specified for this map */ |
976 | /*check if there is a shop specified for this map */ |
979 | if (map->shopitems || map->shopgreed || map->shoprace || map->shopmin || map->shopmax) |
977 | if (map->shopitems || map->shopgreed || map->shoprace || map->shopmin || map->shopmax) |
980 | { |
978 | { |
981 | 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; |
982 | |
981 | |
983 | if (map->shopitems) |
982 | if (map->shopitems) |
984 | for (i = 0; i < map->shopitems[0].index; i++) |
983 | for (i = 0; i < map->shopitems[0].index; i++) |
985 | if (map->shopitems[i].name && map->shopitems[i].strength > 10) |
984 | if (map->shopitems[i].name && map->shopitems[i].strength > 10) |
986 | { |
985 | { |
987 | snprintf (tmp + pos, sizeof (tmp) - pos, "%s, ", map->shopitems[i].name_pl); |
986 | buf << map->shopitems[i].name_pl; |
988 | pos += strlen (tmp + pos); |
987 | prevcomma = lastcomma; |
|
|
988 | lastcomma = buf.size (); // remember offset |
|
|
989 | buf << ", "; |
989 | } |
990 | } |
990 | |
991 | |
991 | if (!pos) |
992 | if (lastcomma) |
|
|
993 | { |
|
|
994 | buf.splice (lastcomma, 2); |
|
|
995 | |
|
|
996 | if (prevcomma) |
|
|
997 | buf.splice (prevcomma, 2, " and "); |
|
|
998 | } |
|
|
999 | else |
992 | strcat (tmp, "a little of everything."); |
1000 | buf << "a little of everything."; |
993 | |
1001 | |
994 | /* format the string into a list */ |
1002 | buf << ".\n\n"; |
995 | make_list_like (tmp); |
|
|
996 | new_draw_info_format (NDI_UNIQUE, 0, op, "%s", tmp); |
|
|
997 | |
1003 | |
998 | if (map->shopmax) |
1004 | if (map->shopmax) |
999 | 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"; |
1000 | |
1006 | |
1001 | if (map->shopmin) |
1007 | if (map->shopmin) |
1002 | 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"; |
1003 | |
1009 | |
1004 | if (map->shopgreed) |
1010 | if (map->shopgreed) |
1005 | { |
1011 | { |
1006 | if (map->shopgreed > 2.0) |
1012 | if (map->shopgreed > 2.0) |
1007 | new_draw_info (NDI_UNIQUE, 0, op, "It tends to overcharge massively."); |
1013 | buf << "It tends to overcharge massively.\n\n"; |
1008 | else if (map->shopgreed > 1.5) |
1014 | else if (map->shopgreed > 1.5) |
1009 | new_draw_info (NDI_UNIQUE, 0, op, "It tends to overcharge substantially."); |
1015 | buf << "It tends to overcharge substantially.\n\n"; |
1010 | else if (map->shopgreed > 1.1) |
1016 | else if (map->shopgreed > 1.1) |
1011 | new_draw_info (NDI_UNIQUE, 0, op, "It tends to overcharge slightly."); |
1017 | buf << "It tends to overcharge slightly.\n\n"; |
1012 | else if (map->shopgreed < 0.9) |
1018 | else if (map->shopgreed < 0.9) |
1013 | new_draw_info (NDI_UNIQUE, 0, op, "It tends to undercharge."); |
1019 | buf << "It tends to undercharge.\n\n"; |
1014 | } |
1020 | } |
1015 | |
1021 | |
1016 | if (map->shoprace) |
1022 | if (map->shoprace) |
1017 | { |
1023 | { |
1018 | opinion = shopkeeper_approval (map, op); |
1024 | opinion = shopkeeper_approval (map, op); |
|
|
1025 | |
1019 | if (opinion > 0.8) |
1026 | if (opinion > 0.8) |
1020 | new_draw_info (NDI_UNIQUE, 0, op, "You think the shopkeeper likes you."); |
1027 | buf << "You think the shopkeeper likes you.\n\n"; |
1021 | else if (opinion > 0.5) |
1028 | else if (opinion > 0.5) |
1022 | new_draw_info (NDI_UNIQUE, 0, op, "The shopkeeper seems unconcerned by you."); |
1029 | buf << "The shopkeeper seems unconcerned by you.\n\n"; |
1023 | else |
1030 | else |
1024 | 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"; |
1025 | } |
1032 | } |
1026 | } |
1033 | } |
1027 | else |
1034 | else |
1028 | 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); |
1029 | |
1038 | |
1030 | return 1; |
1039 | return 1; |
1031 | } |
1040 | } |
1032 | |
1041 | |
1033 | struct shopinv |
1042 | struct shopinv |
… | |
… | |
1101 | items[*numitems].item_sort = strdup (query_base_name (tmp, 0)); |
1110 | items[*numitems].item_sort = strdup (query_base_name (tmp, 0)); |
1102 | items[*numitems].item_real = strdup (query_base_name (tmp, 1)); |
1111 | items[*numitems].item_real = strdup (query_base_name (tmp, 1)); |
1103 | (*numitems)++; |
1112 | (*numitems)++; |
1104 | break; |
1113 | break; |
1105 | } |
1114 | } |
|
|
1115 | |
1106 | SET_FLAG (tmp, FLAG_UNPAID); |
1116 | SET_FLAG (tmp, FLAG_UNPAID); |
1107 | } |
1117 | } |
1108 | |
1118 | |
1109 | void |
1119 | void |
1110 | shop_listing (object *sign, object *op) |
1120 | shop_listing (object *sign, object *op) |