OSDN Git Service

d221d832618e2f54f1d9c3411922656ba619fc0d
[hengbandforosx/hengbandosx.git] / src / store / store.cpp
1 /*!
2  * @brief 店の処理 / Store commands
3  * @date 2022/03/26
4  * @author Hourier
5  */
6
7 #include "store/store.h"
8 #include "core/asking-player.h"
9 #include "flavor/flavor-describer.h"
10 #include "floor/floor-town.h"
11 #include "game-option/birth-options.h"
12 #include "game-option/game-play-options.h"
13 #include "io/command-repeater.h"
14 #include "locale/japanese.h"
15 #include "main/sound-of-music.h"
16 #include "object-enchant/item-apply-magic.h"
17 #include "object-enchant/item-magic-applier.h"
18 #include "object-enchant/special-object-flags.h"
19 #include "object/object-stack.h"
20 #include "object/object-value.h"
21 #include "object/tval-types.h"
22 #include "perception/identification.h"
23 #include "perception/object-perception.h"
24 #include "store/black-market.h"
25 #include "store/service-checker.h"
26 #include "store/store-owners.h"
27 #include "store/store-util.h"
28 #include "sv-definition/sv-lite-types.h"
29 #include "sv-definition/sv-scroll-types.h"
30 #include "system/item-entity.h"
31 #include "system/player-type-definition.h"
32 #include "term/screen-processor.h"
33 #include "util/int-char-converter.h"
34 #include "util/quarks.h"
35 #include "view/display-messages.h"
36 #include "world/world-object.h"
37
38 int store_top = 0;
39 int store_bottom = 0;
40 int xtra_stock = 0;
41 const owner_type *ot_ptr = nullptr;
42 int16_t old_town_num = 0;
43 int16_t inner_town_num = 0;
44
45 /* We store the current "store feat" here so everyone can access it */
46 int cur_store_feat;
47
48 /* Enable "increments" */
49 bool allow_inc = false;
50
51 /*!
52  * @brief 店舗の最大スロット数を返す
53  * @param store_idx 店舗ID
54  * @return 店舗の最大スロット数
55  */
56 int16_t store_get_stock_max(StoreSaleType sst, bool powerup)
57 {
58     switch (sst) {
59     case StoreSaleType::HOME:
60         return powerup ? STORE_INVEN_MAX * 10 : STORE_INVEN_MAX;
61     case StoreSaleType::MUSEUM:
62         return STORE_INVEN_MAX * 50;
63     default:
64         return STORE_INVEN_MAX * 3 / 2;
65     }
66 }
67
68 /*!
69  * @brief アイテムが格納可能な数より多いかをチェックする
70  * @param なし
71  * @return
72  * 0 : No space
73  * 1 : Cannot be combined but there are empty spaces.
74  * @details オプション powerup_home が設定されていると我が家が 20 ページまで使える /
75  * Free space is always usable
76  */
77 static int check_free_space(StoreSaleType store_num)
78 {
79     if ((store_num == StoreSaleType::HOME) && !powerup_home) {
80         if (st_ptr->stock_num < ((st_ptr->stock_size) / 10)) {
81             return 1;
82         }
83     } else if (st_ptr->stock_num < st_ptr->stock_size) {
84         return 1;
85     }
86
87     return 0;
88 }
89
90 /*!
91  * @brief 店舗に品を置くスペースがあるかどうかの判定を返す /
92  * Check to see if the shop will be carrying too many objects   -RAK-
93  * @param o_ptr 店舗に置きたいオブジェクト構造体の参照ポインタ
94  * @return 置き場がないなら0、重ね合わせできるアイテムがあるなら-1、スペースがあるなら1を返す。
95  * @details
96  * <pre>
97  * Note that the shop, just like a player, will not accept things
98  * it cannot hold.      Before, one could "nuke" potions this way.
99  * Return value is now int:
100  *  0 : No space
101  * -1 : Can be combined to existing slot.
102  *  1 : Cannot be combined but there are empty spaces.
103  * </pre>
104  */
105 int store_check_num(ItemEntity *o_ptr, StoreSaleType store_num)
106 {
107     ItemEntity *j_ptr;
108     if ((store_num == StoreSaleType::HOME) || (store_num == StoreSaleType::MUSEUM)) {
109         bool old_stack_force_notes = stack_force_notes;
110         bool old_stack_force_costs = stack_force_costs;
111         if (store_num != StoreSaleType::HOME) {
112             stack_force_notes = false;
113             stack_force_costs = false;
114         }
115
116         for (int i = 0; i < st_ptr->stock_num; i++) {
117             j_ptr = &st_ptr->stock[i];
118             if (!object_similar(j_ptr, o_ptr)) {
119                 continue;
120             }
121
122             if (store_num != StoreSaleType::HOME) {
123                 stack_force_notes = old_stack_force_notes;
124                 stack_force_costs = old_stack_force_costs;
125             }
126
127             return -1;
128         }
129
130         if (store_num != StoreSaleType::HOME) {
131             stack_force_notes = old_stack_force_notes;
132             stack_force_costs = old_stack_force_costs;
133         }
134     } else {
135         for (int i = 0; i < st_ptr->stock_num; i++) {
136             j_ptr = &st_ptr->stock[i];
137             if (store_object_similar(j_ptr, o_ptr)) {
138                 return -1;
139             }
140         }
141     }
142
143     return check_free_space(store_num);
144 }
145
146 /*!
147  * @brief 店舗からアイテムを選択する /
148  * Get the ID of a store item and return its value      -RAK-
149  * @param com_val 選択IDを返す参照ポインタ
150  * @param pmt メッセージキャプション
151  * @param i 選択範囲の最小値
152  * @param j 選択範囲の最大値
153  * @return 実際に選択したらTRUE、キャンセルしたらFALSE
154  */
155 int get_stock(COMMAND_CODE *com_val, concptr pmt, int i, int j, [[maybe_unused]] StoreSaleType store_num)
156 {
157     if (repeat_pull(com_val) && (*com_val >= i) && (*com_val <= j)) {
158         return true;
159     }
160
161     msg_print(nullptr);
162     *com_val = (-1);
163     char lo = I2A(i);
164     char hi = (j > 25) ? toupper(I2A(j - 26)) : I2A(j);
165     char out_val[160];
166 #ifdef JP
167     (void)sprintf(out_val, "(%s:%c-%c, ESCで中断) %s", (((store_num == StoreSaleType::HOME) || (store_num == StoreSaleType::MUSEUM)) ? "アイテム" : "商品"), lo, hi, pmt);
168 #else
169     (void)sprintf(out_val, "(Items %c-%c, ESC to exit) %s", lo, hi, pmt);
170 #endif
171
172     char command;
173     while (true) {
174         if (!get_com(out_val, &command, false)) {
175             break;
176         }
177
178         COMMAND_CODE k;
179         if (islower(command)) {
180             k = A2I(command);
181         } else if (isupper(command)) {
182             k = A2I(tolower(command)) + 26;
183         } else {
184             k = -1;
185         }
186
187         if ((k >= i) && (k <= j)) {
188             *com_val = k;
189             break;
190         }
191
192         bell();
193     }
194
195     prt("", 0, 0);
196     if (command == ESCAPE) {
197         return false;
198     }
199
200     repeat_push(*com_val);
201     return true;
202 }
203
204 /*!
205  * @brief 店のアイテムを調べるコマンドのメインルーチン /
206  * Examine an item in a store                      -JDL-
207  */
208 void store_examine(PlayerType *player_ptr, StoreSaleType store_num)
209 {
210     if (st_ptr->stock_num <= 0) {
211         if (store_num == StoreSaleType::HOME) {
212             msg_print(_("我が家には何も置いてありません。", "Your home is empty."));
213         } else if (store_num == StoreSaleType::MUSEUM) {
214             msg_print(_("博物館には何も置いてありません。", "The Museum is empty."));
215         } else {
216             msg_print(_("現在商品の在庫を切らしています。", "I am currently out of stock."));
217         }
218         return;
219     }
220
221     int i = (st_ptr->stock_num - store_top);
222     if (i > store_bottom) {
223         i = store_bottom;
224     }
225
226     char out_val[160];
227     sprintf(out_val, _("どれを調べますか?", "Which item do you want to examine? "));
228
229     COMMAND_CODE item;
230     if (!get_stock(&item, out_val, 0, i - 1, store_num)) {
231         return;
232     }
233     item = item + store_top;
234     ItemEntity *o_ptr;
235     o_ptr = &st_ptr->stock[item];
236     if (!o_ptr->is_fully_known()) {
237         msg_print(_("このアイテムについて特に知っていることはない。", "You have no special knowledge about that item."));
238         return;
239     }
240
241     GAME_TEXT o_name[MAX_NLEN];
242     describe_flavor(player_ptr, o_name, o_ptr, 0);
243     msg_format(_("%sを調べている...", "Examining %s..."), o_name);
244     if (!screen_object(player_ptr, o_ptr, SCROBJ_FORCE_DETAIL)) {
245         msg_print(_("特に変わったところはないようだ。", "You see nothing special."));
246     }
247 }
248
249 /*!
250  * @brief 現在の町の店主を交代させる /
251  * Shuffle one of the stores.
252  * @param which 店舗種類のID
253  * @todo init_store()と処理を一部統合&ランダム選択を改善。
254  */
255 void store_shuffle(PlayerType *player_ptr, StoreSaleType store_num)
256 {
257     auto owner_num = owners.at(store_num).size();
258     if ((store_num == StoreSaleType::HOME) || (store_num == StoreSaleType::MUSEUM) || (owner_num <= (uint16_t)max_towns)) {
259         return;
260     }
261
262     st_ptr = &town_info[player_ptr->town_num].store[enum2i(store_num)];
263     int j = st_ptr->owner;
264     while (true) {
265         st_ptr->owner = (byte)randint0(owner_num);
266
267         if (j == st_ptr->owner) {
268             continue;
269         }
270
271         int i;
272         for (i = 1; i < max_towns; i++) {
273             if (i == player_ptr->town_num) {
274                 continue;
275             }
276
277             if (st_ptr->owner == town_info[i].store[enum2i(store_num)].owner) {
278                 break;
279             }
280         }
281
282         if (i == max_towns) {
283             break;
284         }
285     }
286
287     ot_ptr = &owners.at(store_num)[st_ptr->owner];
288     st_ptr->insult_cur = 0;
289     st_ptr->store_open = 0;
290     st_ptr->good_buy = 0;
291     st_ptr->bad_buy = 0;
292     for (int i = 0; i < st_ptr->stock_num; i++) {
293         ItemEntity *o_ptr;
294         o_ptr = &st_ptr->stock[i];
295         if (o_ptr->is_artifact()) {
296             continue;
297         }
298
299         o_ptr->discount = 50;
300         o_ptr->inscription = quark_add(_("売出中", "on sale"));
301     }
302 }
303
304 /*!
305  * @brief 店舗の品揃え変化のためにアイテムを追加する /
306  * Creates a random item and gives it to a store
307  * @param player_ptr プレイヤーへの参照ポインタ
308  * @details
309  * <pre>
310  * This algorithm needs to be rethought.  A lot.
311  * Currently, "normal" stores use a pre-built array.
312  * Note -- the "level" given to "obj_get_num()" is a "favored"
313  * level, that is, there is a much higher chance of getting
314  * items with a level approaching that of the given level...
315  * Should we check for "permission" to have the given item?
316  * </pre>
317  */
318 static void store_create(PlayerType *player_ptr, short fix_k_idx, StoreSaleType store_num)
319 {
320     if (st_ptr->stock_num >= st_ptr->stock_size) {
321         return;
322     }
323
324     const owner_type *ow_ptr = &owners.at(store_num)[st_ptr->owner];
325
326     for (int tries = 0; tries < 4; tries++) {
327         short bi_id;
328         DEPTH level;
329         if (store_num == StoreSaleType::BLACK) {
330             level = 25 + randint0(25);
331             bi_id = get_obj_index(player_ptr, level, 0x00000000);
332             if (bi_id == 0) {
333                 continue;
334             }
335         } else if (fix_k_idx > 0) {
336             bi_id = fix_k_idx;
337             level = rand_range(1, ow_ptr->level);
338         } else {
339             bi_id = st_ptr->table[randint0(st_ptr->table.size())];
340             level = rand_range(1, ow_ptr->level);
341         }
342
343         ItemEntity forge;
344         ItemEntity *q_ptr;
345         q_ptr = &forge;
346         q_ptr->prep(bi_id);
347         ItemMagicApplier(player_ptr, q_ptr, level, AM_NO_FIXED_ART).execute();
348         if (!store_will_buy(player_ptr, q_ptr, store_num)) {
349             continue;
350         }
351
352         auto pvals = store_same_magic_device_pvals(q_ptr);
353         if (pvals.size() >= 2) {
354             auto pval = pvals.at(randint0(pvals.size()));
355             q_ptr->pval = pval;
356         }
357
358         const auto tval = q_ptr->bi_key.tval();
359         const auto sval = q_ptr->bi_key.sval();
360         if (tval == ItemKindType::LITE) {
361             if (sval == SV_LITE_TORCH) {
362                 q_ptr->fuel = FUEL_TORCH / 2;
363             }
364
365             if (sval == SV_LITE_LANTERN) {
366                 q_ptr->fuel = FUEL_LAMP / 2;
367             }
368         }
369
370         object_known(q_ptr);
371         q_ptr->ident |= IDENT_STORE;
372         if (tval == ItemKindType::CHEST) {
373             continue;
374         }
375
376         if (store_num == StoreSaleType::BLACK) {
377             if (black_market_crap(player_ptr, q_ptr) || (q_ptr->get_price() < 10)) {
378                 continue;
379             }
380         } else {
381             if (q_ptr->get_price() <= 0) {
382                 continue;
383             }
384         }
385
386         mass_produce(q_ptr, store_num);
387         (void)store_carry(q_ptr);
388         break;
389     }
390 }
391
392 /*!
393  * @brief 店の品揃えを変化させる /
394  * Maintain the inventory at the stores.
395  * @param player_ptr プレイヤーへの参照ポインタ
396  * @param town_num 町のID
397  * @param store_num 店舗種類のID
398  * @param chance 更新商品数
399  */
400 void store_maintenance(PlayerType *player_ptr, int town_num, StoreSaleType store_num, int chance)
401 {
402     if ((store_num == StoreSaleType::HOME) || (store_num == StoreSaleType::MUSEUM)) {
403         return;
404     }
405
406     st_ptr = &town_info[town_num].store[enum2i(store_num)];
407     ot_ptr = &owners.at(store_num)[st_ptr->owner];
408     st_ptr->insult_cur = 0;
409     if (store_num == StoreSaleType::BLACK) {
410         for (INVENTORY_IDX j = st_ptr->stock_num - 1; j >= 0; j--) {
411             auto *o_ptr = &st_ptr->stock[j];
412             if (black_market_crap(player_ptr, o_ptr)) {
413                 store_item_increase(j, 0 - o_ptr->number);
414                 store_item_optimize(j);
415             }
416         }
417     }
418
419     INVENTORY_IDX j = st_ptr->stock_num;
420     int remain = STORE_TURNOVER + std::max(0, j - STORE_MAX_KEEP);
421     int turn_over = 1;
422     for (int i = 0; i < chance; i++) {
423         auto n = randint0(remain);
424         turn_over += n;
425         remain -= n;
426     }
427
428     j = j - turn_over;
429     if (j > STORE_MAX_KEEP) {
430         j = STORE_MAX_KEEP;
431     }
432     if (j < STORE_MIN_KEEP) {
433         j = STORE_MIN_KEEP;
434     }
435
436     while (st_ptr->stock_num > j) {
437         store_delete();
438     }
439
440     remain = STORE_MAX_KEEP - st_ptr->stock_num;
441     turn_over = 1;
442     for (int i = 0; i < chance; i++) {
443         auto n = randint0(remain);
444         turn_over += n;
445         remain -= n;
446     }
447
448     j = st_ptr->stock_num + turn_over;
449     if (j > STORE_MAX_KEEP) {
450         j = STORE_MAX_KEEP;
451     }
452     if (j < STORE_MIN_KEEP) {
453         j = STORE_MIN_KEEP;
454     }
455     if (j >= st_ptr->stock_size) {
456         j = st_ptr->stock_size - 1;
457     }
458
459     for (size_t k = 0; k < st_ptr->regular.size(); k++) {
460         store_create(player_ptr, st_ptr->regular[k], store_num);
461         if (st_ptr->stock_num >= STORE_MAX_KEEP) {
462             break;
463         }
464     }
465
466     while (st_ptr->stock_num < j) {
467         store_create(player_ptr, 0, store_num);
468     }
469 }
470
471 /*!
472  * @brief 店舗情報を初期化する /
473  * Initialize the stores
474  * @param town_num 町のID
475  * @param store_num 店舗種類のID
476  */
477 void store_init(int town_num, StoreSaleType store_num)
478 {
479     auto owner_num = owners.at(store_num).size();
480     st_ptr = &town_info[town_num].store[enum2i(store_num)];
481     while (true) {
482         st_ptr->owner = (byte)randint0(owner_num);
483
484         if (owner_num <= (uint16_t)max_towns) {
485             break;
486         }
487
488         int i;
489
490         for (i = 1; i < (uint16_t)max_towns; i++) {
491             if (i == town_num) {
492                 continue;
493             }
494             if (st_ptr->owner == town_info[i].store[enum2i(store_num)].owner) {
495                 break;
496             }
497         }
498
499         if (i == max_towns) {
500             break;
501         }
502     }
503
504     ot_ptr = &owners.at(store_num)[st_ptr->owner];
505     st_ptr->store_open = 0;
506     st_ptr->insult_cur = 0;
507     st_ptr->good_buy = 0;
508     st_ptr->bad_buy = 0;
509     st_ptr->stock_num = 0;
510     st_ptr->last_visit = -10L * TURNS_PER_TICK * STORE_TICKS;
511     for (int k = 0; k < st_ptr->stock_size; k++) {
512         (&st_ptr->stock[k])->wipe();
513     }
514 }