OSDN Git Service

90b42936792099f61cedd66a6b478ac0f905c808
[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-hook/hook-enchant.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 #ifdef JP
36 #include "locale/japanese.h"
37 #endif
38
39 int store_top = 0;
40 int store_bottom = 0;
41 int xtra_stock = 0;
42 const owner_type *ot_ptr = NULL;
43 s16b old_town_num = 0;
44 s16b inner_town_num = 0;
45
46 /* We store the current "store feat" here so everyone can access it */
47 int cur_store_feat;
48
49 /* Enable "increments" */
50 bool allow_inc = FALSE;
51
52 /*!
53  * @brief 店舗の最大スロット数を返す
54  * @param store_idx 店舗ID
55  * @return 店舗の最大スロット数
56  */
57 s16b store_get_stock_max(STORE_TYPE_IDX store_idx, bool powerup)
58 {
59     switch (store_idx) {
60     case STORE_HOME:
61         return powerup ? STORE_INVEN_MAX * 10 : STORE_INVEN_MAX;
62     case STORE_MUSEUM:
63         return STORE_INVEN_MAX * 50;
64     default:
65         return STORE_INVEN_MAX * 3 / 2;
66     }
67 }
68
69 /*!
70  * @brief アイテムが格納可能な数より多いかをチェックする
71  * @param なし
72  * @return 
73  * 0 : No space
74  * 1 : Cannot be combined but there are empty spaces.
75  * @details オプション powerup_home が設定されていると我が家が 20 ページまで使える /
76  * Free space is always usable
77  */
78 static int check_free_space(void)
79 {
80     if ((cur_store_num == STORE_HOME) && !powerup_home) {
81         if (st_ptr->stock_num < ((st_ptr->stock_size) / 10))
82             return 1;
83     } else if (st_ptr->stock_num < st_ptr->stock_size)
84         return 1;
85
86     return 0;
87 }
88
89 /*!
90  * @brief 店舗に品を置くスペースがあるかどうかの判定を返す /
91  * Check to see if the shop will be carrying too many objects   -RAK-
92  * @param o_ptr 店舗に置きたいオブジェクト構造体の参照ポインタ
93  * @return 置き場がないなら0、重ね合わせできるアイテムがあるなら-1、スペースがあるなら1を返す。
94  * @details
95  * <pre>
96  * Note that the shop, just like a player, will not accept things
97  * it cannot hold.      Before, one could "nuke" potions this way.
98  * Return value is now int:
99  *  0 : No space
100  * -1 : Can be combined to existing slot.
101  *  1 : Cannot be combined but there are empty spaces.
102  * </pre>
103  */
104 int store_check_num(object_type *o_ptr)
105 {
106     object_type *j_ptr;
107     if ((cur_store_num == STORE_HOME) || (cur_store_num == STORE_MUSEUM)) {
108         bool old_stack_force_notes = stack_force_notes;
109         bool old_stack_force_costs = stack_force_costs;
110         if (cur_store_num != STORE_HOME) {
111             stack_force_notes = FALSE;
112             stack_force_costs = FALSE;
113         }
114
115         for (int i = 0; i < st_ptr->stock_num; i++) {
116             j_ptr = &st_ptr->stock[i];
117             if (!object_similar(j_ptr, o_ptr))
118                 continue;
119
120             if (cur_store_num != STORE_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 (cur_store_num != STORE_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     return check_free_space();
141 }
142
143 /*!
144  * @brief 店舗からアイテムを選択する /
145  * Get the ID of a store item and return its value      -RAK-
146  * @param com_val 選択IDを返す参照ポインタ
147  * @param pmt メッセージキャプション
148  * @param i 選択範囲の最小値
149  * @param j 選択範囲の最大値
150  * @return 実際に選択したらTRUE、キャンセルしたらFALSE
151  */
152 int get_stock(COMMAND_CODE *com_val, concptr pmt, int i, int j)
153 {
154     if (repeat_pull(com_val) && (*com_val >= i) && (*com_val <= j))
155         return TRUE;
156
157     msg_print(NULL);
158     *com_val = (-1);
159     char lo = I2A(i);
160     char hi = (j > 25) ? toupper(I2A(j - 26)) : I2A(j);
161     char out_val[160];
162 #ifdef JP
163     (void)sprintf(out_val, "(%s:%c-%c, ESCで中断) %s", (((cur_store_num == STORE_HOME) || (cur_store_num == STORE_MUSEUM)) ? "アイテム" : "商品"), lo, hi, pmt);
164 #else
165     (void)sprintf(out_val, "(Items %c-%c, ESC to exit) %s", lo, hi, pmt);
166 #endif
167
168     char command;
169     while (TRUE) {
170         if (!get_com(out_val, &command, FALSE))
171             break;
172
173         COMMAND_CODE k;
174         if (islower(command))
175             k = A2I(command);
176         else if (isupper(command))
177             k = A2I(tolower(command)) + 26;
178         else
179             k = -1;
180
181         if ((k >= i) && (k <= j)) {
182             *com_val = k;
183             break;
184         }
185
186         bell();
187     }
188
189     prt("", 0, 0);
190     if (command == ESCAPE)
191         return FALSE;
192
193     repeat_push(*com_val);
194     return TRUE;
195 }
196
197 /*!
198  * @brief 店のアイテムを調べるコマンドのメインルーチン /
199  * Examine an item in a store                      -JDL-
200  */
201 void store_examine(player_type *player_ptr)
202 {
203     if (st_ptr->stock_num <= 0) {
204         if (cur_store_num == STORE_HOME)
205             msg_print(_("我が家には何も置いてありません。", "Your home is empty."));
206         else if (cur_store_num == STORE_MUSEUM)
207             msg_print(_("博物館には何も置いてありません。", "The Museum is empty."));
208         else
209             msg_print(_("現在商品の在庫を切らしています。", "I am currently out of stock."));
210         return;
211     }
212
213     int i = (st_ptr->stock_num - store_top);
214     if (i > store_bottom)
215         i = store_bottom;
216
217     char out_val[160];
218     sprintf(out_val, _("どれを調べますか?", "Which item do you want to examine? "));
219
220     COMMAND_CODE item;
221     if (!get_stock(&item, out_val, 0, i - 1))
222         return;
223     item = item + store_top;
224     object_type *o_ptr;
225     o_ptr = &st_ptr->stock[item];
226     if (!object_is_fully_known(o_ptr)) {
227         msg_print(_("このアイテムについて特に知っていることはない。", "You have no special knowledge about that item."));
228         return;
229     }
230
231     GAME_TEXT o_name[MAX_NLEN];
232     describe_flavor(player_ptr, o_name, o_ptr, 0);
233     msg_format(_("%sを調べている...", "Examining %s..."), o_name);
234     if (!screen_object(player_ptr, o_ptr, SCROBJ_FORCE_DETAIL))
235         msg_print(_("特に変わったところはないようだ。", "You see nothing special."));
236 }
237
238 /*!
239  * @brief 現在の町の店主を交代させる /
240  * Shuffle one of the stores.
241  * @param which 店舗種類のID
242  */
243 void store_shuffle(player_type *player_ptr, int which)
244 {
245     if ((which == STORE_HOME) || (which == STORE_MUSEUM))
246         return;
247
248     cur_store_num = which;
249     st_ptr = &town_info[player_ptr->town_num].store[cur_store_num];
250     int j = st_ptr->owner;
251     while (TRUE) {
252         st_ptr->owner = (byte)randint0(MAX_OWNERS);
253         if (j == st_ptr->owner)
254             continue;
255
256         int i;
257         for (i = 1; i < max_towns; i++) {
258             if (i == player_ptr->town_num)
259                 continue;
260
261             if (st_ptr->owner == town_info[i].store[cur_store_num].owner)
262                 break;
263         }
264
265         if (i == max_towns)
266             break;
267     }
268
269     ot_ptr = &owners[cur_store_num][st_ptr->owner];
270     st_ptr->insult_cur = 0;
271     st_ptr->store_open = 0;
272     st_ptr->good_buy = 0;
273     st_ptr->bad_buy = 0;
274     for (int i = 0; i < st_ptr->stock_num; i++) {
275         object_type *o_ptr;
276         o_ptr = &st_ptr->stock[i];
277         if (object_is_artifact(o_ptr))
278             continue;
279
280         o_ptr->discount = 50;
281         o_ptr->ident &= ~(IDENT_FIXED);
282         o_ptr->inscription = quark_add(_("売出中", "on sale"));
283     }
284 }
285
286 /*!
287  * @brief 店の品揃えを変化させる /
288  * Maintain the inventory at the stores.
289  * @param player_ptr プレーヤーへの参照ポインタ
290  * @param town_num 町のID
291  * @param store_num 店舗種類のID
292  * @param chance 更新商品数
293  */
294 void store_maintenance(player_type *player_ptr, int town_num, int store_num, int chance)
295 {
296     cur_store_num = store_num;
297     if ((store_num == STORE_HOME) || (store_num == STORE_MUSEUM))
298         return;
299
300     st_ptr = &town_info[town_num].store[store_num];
301     ot_ptr = &owners[store_num][st_ptr->owner];
302     st_ptr->insult_cur = 0;
303     if (store_num == STORE_BLACK) {
304         for (INVENTORY_IDX j = st_ptr->stock_num - 1; j >= 0; j--) {
305             object_type *o_ptr = &st_ptr->stock[j];
306             if (black_market_crap(player_ptr, o_ptr)) {
307                 store_item_increase(j, 0 - o_ptr->number);
308                 store_item_optimize(j);
309             }
310         }
311     }
312
313     INVENTORY_IDX j = st_ptr->stock_num;
314     int remain = STORE_TURNOVER + MAX(0, j - STORE_MAX_KEEP);
315     int turn_over = 1;
316     for (int i = 0; i < chance; i++) {
317         auto n = randint0(remain);
318         turn_over += n;
319         remain -= n;
320     }
321
322     j = j - turn_over;
323     if (j > STORE_MAX_KEEP)
324         j = STORE_MAX_KEEP;
325     if (j < STORE_MIN_KEEP)
326         j = STORE_MIN_KEEP;
327
328     while (st_ptr->stock_num > j)
329         store_delete();
330
331     remain = STORE_MAX_KEEP - st_ptr->stock_num;
332     turn_over = 1;
333     for (int i = 0; i < chance; i++) {
334         auto n = randint0(remain);
335         turn_over += n;
336         remain -= n;
337     }
338
339     j = st_ptr->stock_num + turn_over;
340     if (j > STORE_MAX_KEEP)
341         j = STORE_MAX_KEEP;
342     if (j < STORE_MIN_KEEP)
343         j = STORE_MIN_KEEP;
344     if (j >= st_ptr->stock_size)
345         j = st_ptr->stock_size - 1;
346
347     for (size_t k = 0; k < st_ptr->regular.size(); k++) {
348         store_create(player_ptr, st_ptr->regular[k], black_market_crap, store_will_buy, mass_produce);
349         if (st_ptr->stock_num >= STORE_MAX_KEEP)
350             break;
351     }
352
353     while (st_ptr->stock_num < j)
354         store_create(player_ptr, 0, black_market_crap, store_will_buy, mass_produce);
355 }
356
357 /*!
358  * @brief 店舗情報を初期化する /
359  * Initialize the stores
360  * @param town_num 町のID
361  * @param store_num 店舗種類のID
362  */
363 void store_init(int town_num, int store_num)
364 {
365     cur_store_num = store_num;
366     st_ptr = &town_info[town_num].store[store_num];
367     while (TRUE) {
368         st_ptr->owner = (byte)randint0(MAX_OWNERS);
369         int i;
370         for (i = 1; i < max_towns; i++) {
371             if (i == town_num)
372                 continue;
373             if (st_ptr->owner == town_info[i].store[store_num].owner)
374                 break;
375         }
376
377         if (i == max_towns)
378             break;
379     }
380
381     ot_ptr = &owners[store_num][st_ptr->owner];
382     st_ptr->store_open = 0;
383     st_ptr->insult_cur = 0;
384     st_ptr->good_buy = 0;
385     st_ptr->bad_buy = 0;
386     st_ptr->stock_num = 0;
387     st_ptr->last_visit = -10L * TURNS_PER_TICK * STORE_TICKS;
388     for (int k = 0; k < st_ptr->stock_size; k++)
389         (&st_ptr->stock[k])->object_wipe();
390 }