OSDN Git Service

Merge pull request #3569 from sikabane-works/release/3.0.0.88-alpha
[hengbandforosx/hengbandosx.git] / src / store / service-checker.cpp
1 #include "store/service-checker.h"
2 #include "monster-race/monster-race.h"
3 #include "monster-race/race-flags3.h"
4 #include "object-enchant/tr-types.h"
5 #include "object/object-flags.h"
6 #include "object/object-value.h"
7 #include "object/tval-types.h"
8 #include "store/store-util.h"
9 #include "sv-definition/sv-potion-types.h"
10 #include "sv-definition/sv-rod-types.h"
11 #include "sv-definition/sv-scroll-types.h"
12 #include "sv-definition/sv-weapon-types.h"
13 #include "system/item-entity.h"
14 #include "system/monster-race-info.h"
15 #include "system/player-type-definition.h"
16 #include "util/bit-flags-calculator.h"
17 #include "util/string-processor.h"
18
19 /*!
20  * @brief オブジェクトが祝福されているかの判定を返す /
21  * @param item_ptr 判定したいオブジェクト構造体の参照ポインタ
22  * @return アイテムが祝福されたアイテムならばTRUEを返す
23  */
24 static bool is_blessed_item(const ItemEntity *item_ptr)
25 {
26     auto flags = object_flags(item_ptr);
27     return flags.has(TR_BLESSED);
28 }
29
30 static bool check_store_general(const ItemEntity &item)
31 {
32     const auto &bi_key = item.bi_key;
33     switch (bi_key.tval()) {
34     case ItemKindType::ROD:
35         return (bi_key.sval() == SV_ROD_PESTICIDE);
36     case ItemKindType::POTION:
37         return (bi_key.sval() == SV_POTION_WATER);
38     case ItemKindType::WHISTLE:
39     case ItemKindType::FOOD:
40     case ItemKindType::LITE:
41     case ItemKindType::FLASK:
42     case ItemKindType::SPIKE:
43     case ItemKindType::SHOT:
44     case ItemKindType::ARROW:
45     case ItemKindType::BOLT:
46     case ItemKindType::DIGGING:
47     case ItemKindType::CLOAK:
48     case ItemKindType::BOTTLE:
49     case ItemKindType::FIGURINE:
50     case ItemKindType::STATUE:
51     case ItemKindType::CAPTURE:
52     case ItemKindType::CARD:
53         return true;
54     default:
55         return false;
56     }
57 }
58
59 static bool check_store_armoury(const ItemEntity &item)
60 {
61     switch (item.bi_key.tval()) {
62     case ItemKindType::BOOTS:
63     case ItemKindType::GLOVES:
64     case ItemKindType::CROWN:
65     case ItemKindType::HELM:
66     case ItemKindType::SHIELD:
67     case ItemKindType::CLOAK:
68     case ItemKindType::SOFT_ARMOR:
69     case ItemKindType::HARD_ARMOR:
70     case ItemKindType::DRAG_ARMOR:
71         return true;
72     default:
73         return false;
74     }
75 }
76
77 static bool check_store_weapon(const ItemEntity &item)
78 {
79     switch (item.bi_key.tval()) {
80     case ItemKindType::SHOT:
81     case ItemKindType::BOLT:
82     case ItemKindType::ARROW:
83     case ItemKindType::BOW:
84     case ItemKindType::DIGGING:
85     case ItemKindType::POLEARM:
86     case ItemKindType::SWORD:
87     case ItemKindType::HISSATSU_BOOK:
88         return true;
89     case ItemKindType::HAFTED:
90         return item.bi_key.sval() != SV_WIZSTAFF;
91     default:
92         return false;
93     }
94 }
95
96 static bool check_store_temple(const ItemEntity &item)
97 {
98     switch (item.bi_key.tval()) {
99     case ItemKindType::LIFE_BOOK:
100     case ItemKindType::CRUSADE_BOOK:
101     case ItemKindType::SCROLL:
102     case ItemKindType::POTION:
103     case ItemKindType::HAFTED:
104         return true;
105     case ItemKindType::FIGURINE:
106     case ItemKindType::STATUE: {
107         auto *r_ptr = &monraces_info[i2enum<MonsterRaceId>(item.pval)];
108         if (r_ptr->kind_flags.has_not(MonsterKindType::EVIL)) {
109             if ((r_ptr->kind_flags.has(MonsterKindType::GOOD)) || (r_ptr->kind_flags.has(MonsterKindType::ANIMAL)) || (angband_strchr("?!", r_ptr->d_char) != nullptr)) {
110                 return true;
111             }
112         }
113     }
114         [[fallthrough]];
115     case ItemKindType::POLEARM:
116     case ItemKindType::SWORD:
117         if (is_blessed_item(&item)) {
118             return true;
119         }
120
121         [[fallthrough]];
122     default:
123         return false;
124     }
125 }
126
127 static bool check_store_alchemist(const ItemEntity &item)
128 {
129     switch (item.bi_key.tval()) {
130     case ItemKindType::SCROLL:
131     case ItemKindType::POTION:
132         return true;
133     default:
134         return false;
135     }
136 }
137
138 static bool check_store_magic(const ItemEntity &item)
139 {
140     switch (item.bi_key.tval()) {
141     case ItemKindType::SORCERY_BOOK:
142     case ItemKindType::NATURE_BOOK:
143     case ItemKindType::CHAOS_BOOK:
144     case ItemKindType::DEATH_BOOK:
145     case ItemKindType::TRUMP_BOOK:
146     case ItemKindType::ARCANE_BOOK:
147     case ItemKindType::CRAFT_BOOK:
148     case ItemKindType::DEMON_BOOK:
149     case ItemKindType::MUSIC_BOOK:
150     case ItemKindType::HEX_BOOK:
151     case ItemKindType::AMULET:
152     case ItemKindType::RING:
153     case ItemKindType::STAFF:
154     case ItemKindType::WAND:
155     case ItemKindType::ROD:
156     case ItemKindType::SCROLL:
157     case ItemKindType::POTION:
158     case ItemKindType::FIGURINE:
159         return true;
160     case ItemKindType::HAFTED:
161         return item.bi_key.sval() == SV_WIZSTAFF;
162     default:
163         return false;
164     }
165 }
166
167 static bool check_store_book(const ItemEntity &item)
168 {
169     switch (item.bi_key.tval()) {
170     case ItemKindType::SORCERY_BOOK:
171     case ItemKindType::NATURE_BOOK:
172     case ItemKindType::CHAOS_BOOK:
173     case ItemKindType::DEATH_BOOK:
174     case ItemKindType::LIFE_BOOK:
175     case ItemKindType::TRUMP_BOOK:
176     case ItemKindType::ARCANE_BOOK:
177     case ItemKindType::CRAFT_BOOK:
178     case ItemKindType::DEMON_BOOK:
179     case ItemKindType::CRUSADE_BOOK:
180     case ItemKindType::MUSIC_BOOK:
181     case ItemKindType::HEX_BOOK:
182         return true;
183     default:
184         return false;
185     }
186 }
187
188 static bool switch_store_check(const ItemEntity &item, StoreSaleType store_num)
189 {
190     switch (store_num) {
191     case StoreSaleType::GENERAL:
192         return check_store_general(item);
193     case StoreSaleType::ARMOURY:
194         return check_store_armoury(item);
195     case StoreSaleType::WEAPON:
196         return check_store_weapon(item);
197     case StoreSaleType::TEMPLE:
198         return check_store_temple(item);
199     case StoreSaleType::ALCHEMIST:
200         return check_store_alchemist(item);
201     case StoreSaleType::MAGIC:
202         return check_store_magic(item);
203     case StoreSaleType::BOOK:
204         return check_store_book(item);
205     default:
206         return true;
207     }
208 }
209
210 /*!
211  * @brief オブジェクトが所定の店舗で引き取れるかどうかを返す /
212  * Determine if the current store will purchase the given item
213  * @param o_ptr 判定したいオブジェクト構造体の参照ポインタ
214  * @return アイテムが買い取れるならばTRUEを返す
215  * @note
216  * Note that a shop-keeper must refuse to buy "worthless" items
217  */
218
219 bool store_will_buy(PlayerType *, const ItemEntity *o_ptr, StoreSaleType store_num)
220 {
221     if ((store_num == StoreSaleType::HOME) || (store_num == StoreSaleType::MUSEUM)) {
222         return true;
223     }
224
225     if (!switch_store_check(*o_ptr, store_num)) {
226         return false;
227     }
228
229     return o_ptr->get_price() > 0;
230 }
231
232 static int mass_lite_produce(const PRICE cost)
233 {
234     int size = 1;
235     if (cost <= 5L) {
236         size += damroll(3, 5);
237     }
238
239     if (cost <= 20L) {
240         size += damroll(3, 5);
241     }
242
243     if (cost <= 50L) {
244         size += damroll(2, 2);
245     }
246
247     return size;
248 }
249
250 static int mass_scroll_produce(const ItemEntity &item, const PRICE cost)
251 {
252     int size = 1;
253     if (cost <= 60L) {
254         size += damroll(3, 5);
255     }
256
257     if (cost <= 240L) {
258         size += damroll(1, 5);
259     }
260
261     const auto sval = item.bi_key.sval();
262     if (sval == SV_SCROLL_STAR_IDENTIFY) {
263         size += damroll(3, 5);
264     }
265
266     if (sval == SV_SCROLL_STAR_REMOVE_CURSE) {
267         size += damroll(1, 4);
268     }
269
270     return size;
271 }
272
273 static int mass_book_produce(const PRICE cost)
274 {
275     int size = 1;
276     if (cost <= 50L) {
277         size += damroll(2, 3);
278     }
279
280     if (cost <= 500L) {
281         size += damroll(1, 3);
282     }
283
284     return size;
285 }
286
287 static int mass_equipment_produce(const ItemEntity &item, const PRICE cost)
288 {
289     int size = 1;
290     if (item.is_fixed_or_random_artifact() || item.is_ego()) {
291         return size;
292     }
293
294     if (cost <= 10L) {
295         size += damroll(3, 5);
296     }
297
298     if (cost <= 100L) {
299         size += damroll(3, 5);
300     }
301
302     return size;
303 }
304
305 static int mass_arrow_produce(const PRICE cost)
306 {
307     int size = 1;
308     if (cost <= 5L) {
309         size += damroll(5, 5);
310     }
311
312     if (cost <= 50L) {
313         size += damroll(5, 5);
314     }
315
316     if (cost <= 500L) {
317         size += damroll(5, 5);
318     }
319
320     return size;
321 }
322
323 static int mass_figurine_produce(const PRICE cost)
324 {
325     int size = 1;
326     if (cost <= 100L) {
327         size += damroll(2, 2);
328     }
329
330     if (cost <= 1000L) {
331         size += damroll(2, 2);
332     }
333
334     return size;
335 }
336
337 static int mass_magic_produce(const PRICE cost, StoreSaleType store_num)
338 {
339     int size = 1;
340     if ((store_num != StoreSaleType::BLACK) || !one_in_(3)) {
341         return size;
342     }
343
344     if (cost < 1601L) {
345         size += damroll(1, 5);
346     } else if (cost < 3201L) {
347         size += damroll(1, 3);
348     }
349
350     return size;
351 }
352
353 static int switch_mass_production(const ItemEntity &item, const PRICE cost, StoreSaleType store_num)
354 {
355     switch (item.bi_key.tval()) {
356     case ItemKindType::FOOD:
357     case ItemKindType::FLASK:
358     case ItemKindType::LITE:
359         return mass_lite_produce(cost);
360     case ItemKindType::POTION:
361     case ItemKindType::SCROLL:
362         return mass_scroll_produce(item, cost);
363     case ItemKindType::LIFE_BOOK:
364     case ItemKindType::SORCERY_BOOK:
365     case ItemKindType::NATURE_BOOK:
366     case ItemKindType::CHAOS_BOOK:
367     case ItemKindType::DEATH_BOOK:
368     case ItemKindType::TRUMP_BOOK:
369     case ItemKindType::ARCANE_BOOK:
370     case ItemKindType::CRAFT_BOOK:
371     case ItemKindType::DEMON_BOOK:
372     case ItemKindType::CRUSADE_BOOK:
373     case ItemKindType::MUSIC_BOOK:
374     case ItemKindType::HISSATSU_BOOK:
375     case ItemKindType::HEX_BOOK:
376         return mass_book_produce(cost);
377     case ItemKindType::SOFT_ARMOR:
378     case ItemKindType::HARD_ARMOR:
379     case ItemKindType::SHIELD:
380     case ItemKindType::GLOVES:
381     case ItemKindType::BOOTS:
382     case ItemKindType::CLOAK:
383     case ItemKindType::HELM:
384     case ItemKindType::CROWN:
385     case ItemKindType::SWORD:
386     case ItemKindType::POLEARM:
387     case ItemKindType::HAFTED:
388     case ItemKindType::DIGGING:
389     case ItemKindType::BOW:
390         return mass_equipment_produce(item, cost);
391     case ItemKindType::SPIKE:
392     case ItemKindType::SHOT:
393     case ItemKindType::ARROW:
394     case ItemKindType::BOLT:
395         return mass_arrow_produce(cost);
396     case ItemKindType::FIGURINE:
397         return mass_figurine_produce(cost);
398     case ItemKindType::CAPTURE:
399     case ItemKindType::STATUE:
400     case ItemKindType::CARD:
401         return 1;
402     case ItemKindType::ROD:
403     case ItemKindType::WAND:
404     case ItemKindType::STAFF:
405         return mass_magic_produce(cost, store_num);
406     default:
407         return 1;
408     }
409 }
410
411 static byte decide_discount_rate(const PRICE cost)
412 {
413     if (cost < 5) {
414         return 0;
415     }
416
417     if (one_in_(25)) {
418         return 25;
419     }
420
421     if (one_in_(150)) {
422         return 50;
423     }
424
425     if (one_in_(300)) {
426         return 75;
427     }
428
429     if (one_in_(500)) {
430         return 90;
431     }
432
433     return 0;
434 }
435
436 /*!
437  * @brief 安価な消耗品の販売数を増やし、低確率で割引にする /
438  * Certain "cheap" objects should be created in "piles"
439  * @param o_ptr 店舗に並べるオブジェクト構造体の参照ポインタ
440  * @details
441  * <pre>
442  * Some objects can be sold at a "discount" (in small piles)
443  * </pre>
444  */
445 void mass_produce(ItemEntity *o_ptr, StoreSaleType store_num)
446 {
447     const auto cost = o_ptr->get_price();
448     int size = switch_mass_production(*o_ptr, cost, store_num);
449     auto discount = decide_discount_rate(cost);
450     if (o_ptr->is_random_artifact()) {
451         discount = 0;
452     }
453
454     o_ptr->discount = discount;
455     o_ptr->number = size - (size * discount / 100);
456     if (o_ptr->is_wand_rod()) {
457         o_ptr->pval *= (PARAMETER_VALUE)o_ptr->number;
458     }
459 }