OSDN Git Service

[Refactor] 店舗の種類を enum class StoreSaleType 型に変更しリファクタリング。
[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 "main/sound-of-music.h"
20 #include "object-enchant/special-object-flags.h"
21 #include "object/object-stack.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 "system/object-type-definition.h"
29 #include "system/player-type-definition.h"
30 #include "term/screen-processor.h"
31 #include "util/int-char-converter.h"
32 #include "util/quarks.h"
33 #include "view/display-messages.h"
34 #ifdef JP
35 #include "locale/japanese.h"
36 #endif
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(void)
78 {
79     if ((cur_store_num == StoreSaleType::HOME) && !powerup_home) {
80         if (st_ptr->stock_num < ((st_ptr->stock_size) / 10))
81             return 1;
82     } else if (st_ptr->stock_num < st_ptr->stock_size)
83         return 1;
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(object_type *o_ptr)
104 {
105     object_type *j_ptr;
106     if ((cur_store_num == StoreSaleType::HOME) || (cur_store_num == StoreSaleType::MUSEUM)) {
107         bool old_stack_force_notes = stack_force_notes;
108         bool old_stack_force_costs = stack_force_costs;
109         if (cur_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             if (cur_store_num != StoreSaleType::HOME) {
120                 stack_force_notes = old_stack_force_notes;
121                 stack_force_costs = old_stack_force_costs;
122             }
123
124             return -1;
125         }
126
127         if (cur_store_num != StoreSaleType::HOME) {
128             stack_force_notes = old_stack_force_notes;
129             stack_force_costs = old_stack_force_costs;
130         }
131     } else {
132         for (int i = 0; i < st_ptr->stock_num; i++) {
133             j_ptr = &st_ptr->stock[i];
134             if (store_object_similar(j_ptr, o_ptr))
135                 return -1;
136         }
137     }
138
139     return check_free_space();
140 }
141
142 /*!
143  * @brief 店舗からアイテムを選択する /
144  * Get the ID of a store item and return its value      -RAK-
145  * @param com_val 選択IDを返す参照ポインタ
146  * @param pmt メッセージキャプション
147  * @param i 選択範囲の最小値
148  * @param j 選択範囲の最大値
149  * @return 実際に選択したらTRUE、キャンセルしたらFALSE
150  */
151 int get_stock(COMMAND_CODE *com_val, concptr pmt, int i, int j)
152 {
153     if (repeat_pull(com_val) && (*com_val >= i) && (*com_val <= j))
154         return true;
155
156     msg_print(nullptr);
157     *com_val = (-1);
158     char lo = I2A(i);
159     char hi = (j > 25) ? toupper(I2A(j - 26)) : I2A(j);
160     char out_val[160];
161 #ifdef JP
162     (void)sprintf(out_val, "(%s:%c-%c, ESCで中断) %s", (((cur_store_num == StoreSaleType::HOME) || (cur_store_num == StoreSaleType::MUSEUM)) ? "アイテム" : "商品"), lo, hi, pmt);
163 #else
164     (void)sprintf(out_val, "(Items %c-%c, ESC to exit) %s", lo, hi, pmt);
165 #endif
166
167     char command;
168     while (true) {
169         if (!get_com(out_val, &command, false))
170             break;
171
172         COMMAND_CODE k;
173         if (islower(command))
174             k = A2I(command);
175         else if (isupper(command))
176             k = A2I(tolower(command)) + 26;
177         else
178             k = -1;
179
180         if ((k >= i) && (k <= j)) {
181             *com_val = k;
182             break;
183         }
184
185         bell();
186     }
187
188     prt("", 0, 0);
189     if (command == ESCAPE)
190         return false;
191
192     repeat_push(*com_val);
193     return true;
194 }
195
196 /*!
197  * @brief 店のアイテムを調べるコマンドのメインルーチン /
198  * Examine an item in a store                      -JDL-
199  */
200 void store_examine(player_type *player_ptr)
201 {
202     if (st_ptr->stock_num <= 0) {
203         if (cur_store_num == StoreSaleType::HOME)
204             msg_print(_("我が家には何も置いてありません。", "Your home is empty."));
205         else if (cur_store_num == StoreSaleType::MUSEUM)
206             msg_print(_("博物館には何も置いてありません。", "The Museum is empty."));
207         else
208             msg_print(_("現在商品の在庫を切らしています。", "I am currently out of stock."));
209         return;
210     }
211
212     int i = (st_ptr->stock_num - store_top);
213     if (i > store_bottom)
214         i = store_bottom;
215
216     char out_val[160];
217     sprintf(out_val, _("どれを調べますか?", "Which item do you want to examine? "));
218
219     COMMAND_CODE item;
220     if (!get_stock(&item, out_val, 0, i - 1))
221         return;
222     item = item + store_top;
223     object_type *o_ptr;
224     o_ptr = &st_ptr->stock[item];
225     if (!o_ptr->is_fully_known()) {
226         msg_print(_("このアイテムについて特に知っていることはない。", "You have no special knowledge about that item."));
227         return;
228     }
229
230     GAME_TEXT o_name[MAX_NLEN];
231     describe_flavor(player_ptr, o_name, o_ptr, 0);
232     msg_format(_("%sを調べている...", "Examining %s..."), o_name);
233     if (!screen_object(player_ptr, o_ptr, SCROBJ_FORCE_DETAIL))
234         msg_print(_("特に変わったところはないようだ。", "You see nothing special."));
235 }
236
237 /*!
238  * @brief 現在の町の店主を交代させる /
239  * Shuffle one of the stores.
240  * @param which 店舗種類のID
241  */
242 void store_shuffle(player_type *player_ptr, StoreSaleType which)
243 {
244     if ((which == StoreSaleType::HOME) || (which == StoreSaleType::MUSEUM))
245         return;
246
247     cur_store_num = which;
248     st_ptr = &town_info[player_ptr->town_num].store[enum2i(cur_store_num)];
249     int j = st_ptr->owner;
250     while (true) {
251         st_ptr->owner = (byte)randint0(MAX_OWNERS);
252         if (j == st_ptr->owner)
253             continue;
254
255         int i;
256         for (i = 1; i < max_towns; i++) {
257             if (i == player_ptr->town_num)
258                 continue;
259
260             if (st_ptr->owner == town_info[i].store[enum2i(cur_store_num)].owner)
261                 break;
262         }
263
264         if (i == max_towns)
265             break;
266     }
267
268     ot_ptr = &owners[enum2i(cur_store_num)][st_ptr->owner];
269     st_ptr->insult_cur = 0;
270     st_ptr->store_open = 0;
271     st_ptr->good_buy = 0;
272     st_ptr->bad_buy = 0;
273     for (int i = 0; i < st_ptr->stock_num; i++) {
274         object_type *o_ptr;
275         o_ptr = &st_ptr->stock[i];
276         if (o_ptr->is_artifact())
277             continue;
278
279         o_ptr->discount = 50;
280         o_ptr->inscription = quark_add(_("売出中", "on sale"));
281     }
282 }
283
284 /*!
285  * @brief 店の品揃えを変化させる /
286  * Maintain the inventory at the stores.
287  * @param player_ptr プレイヤーへの参照ポインタ
288  * @param town_num 町のID
289  * @param store_num 店舗種類のID
290  * @param chance 更新商品数
291  */
292 void store_maintenance(player_type *player_ptr, int town_num, StoreSaleType store_num, int chance)
293 {
294     cur_store_num = store_num;
295     if ((store_num == StoreSaleType::HOME) || (store_num == StoreSaleType::MUSEUM))
296         return;
297
298     st_ptr = &town_info[town_num].store[enum2i(store_num)];
299     ot_ptr = &owners[enum2i(store_num)][st_ptr->owner];
300     st_ptr->insult_cur = 0;
301     if (store_num == StoreSaleType::BLACK) {
302         for (INVENTORY_IDX j = st_ptr->stock_num - 1; j >= 0; j--) {
303             object_type *o_ptr = &st_ptr->stock[j];
304             if (black_market_crap(player_ptr, o_ptr)) {
305                 store_item_increase(j, 0 - o_ptr->number);
306                 store_item_optimize(j);
307             }
308         }
309     }
310
311     INVENTORY_IDX j = st_ptr->stock_num;
312     int remain = STORE_TURNOVER + MAX(0, j - STORE_MAX_KEEP);
313     int turn_over = 1;
314     for (int i = 0; i < chance; i++) {
315         auto n = randint0(remain);
316         turn_over += n;
317         remain -= n;
318     }
319
320     j = j - turn_over;
321     if (j > STORE_MAX_KEEP)
322         j = STORE_MAX_KEEP;
323     if (j < STORE_MIN_KEEP)
324         j = STORE_MIN_KEEP;
325
326     while (st_ptr->stock_num > j)
327         store_delete();
328
329     remain = STORE_MAX_KEEP - st_ptr->stock_num;
330     turn_over = 1;
331     for (int i = 0; i < chance; i++) {
332         auto n = randint0(remain);
333         turn_over += n;
334         remain -= n;
335     }
336
337     j = st_ptr->stock_num + turn_over;
338     if (j > STORE_MAX_KEEP)
339         j = STORE_MAX_KEEP;
340     if (j < STORE_MIN_KEEP)
341         j = STORE_MIN_KEEP;
342     if (j >= st_ptr->stock_size)
343         j = st_ptr->stock_size - 1;
344
345     for (size_t k = 0; k < st_ptr->regular.size(); k++) {
346         store_create(player_ptr, st_ptr->regular[k], black_market_crap, store_will_buy, mass_produce);
347         if (st_ptr->stock_num >= STORE_MAX_KEEP)
348             break;
349     }
350
351     while (st_ptr->stock_num < j)
352         store_create(player_ptr, 0, black_market_crap, store_will_buy, mass_produce);
353 }
354
355 /*!
356  * @brief 店舗情報を初期化する /
357  * Initialize the stores
358  * @param town_num 町のID
359  * @param store_num 店舗種類のID
360  */
361 void store_init(int town_num, StoreSaleType store_num)
362 {
363     cur_store_num = store_num;
364     st_ptr = &town_info[town_num].store[enum2i(store_num)];
365     while (true) {
366         st_ptr->owner = (byte)randint0(MAX_OWNERS);
367         int i;
368         for (i = 1; i < max_towns; i++) {
369             if (i == town_num)
370                 continue;
371             if (st_ptr->owner == town_info[i].store[enum2i(store_num)].owner)
372                 break;
373         }
374
375         if (i == max_towns)
376             break;
377     }
378
379     ot_ptr = &owners[enum2i(store_num)][st_ptr->owner];
380     st_ptr->store_open = 0;
381     st_ptr->insult_cur = 0;
382     st_ptr->good_buy = 0;
383     st_ptr->bad_buy = 0;
384     st_ptr->stock_num = 0;
385     st_ptr->last_visit = -10L * TURNS_PER_TICK * STORE_TICKS;
386     for (int k = 0; k < st_ptr->stock_size; k++)
387         (&st_ptr->stock[k])->wipe();
388 }
389