OSDN Git Service

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