OSDN Git Service

[Refactor] グローバル変数 cur_store_num を削除する
[hengbandforosx/hengbandosx.git] / src / store / purchase-order.cpp
1 #include "store/purchase-order.h"
2 #include "autopick/autopick-finder.h"
3 #include "autopick/autopick-util.h"
4 #include "autopick/autopick.h"
5 #include "avatar/avatar.h"
6 #include "core/asking-player.h"
7 #include "core/stuff-handler.h"
8 #include "flavor/flavor-describer.h"
9 #include "flavor/object-flavor-types.h"
10 #include "game-option/birth-options.h"
11 #include "game-option/play-record-options.h"
12 #include "grid/feature.h"
13 #include "inventory/inventory-object.h"
14 #include "io/write-diary.h"
15 #include "main/sound-definitions-table.h"
16 #include "main/sound-of-music.h"
17 #include "object-enchant/item-feeling.h"
18 #include "object-enchant/special-object-flags.h"
19 #include "object/object-info.h"
20 #include "object/object-stack.h"
21 #include "object/object-value.h"
22 #include "perception/object-perception.h"
23 #include "player/race-info-table.h"
24 #include "store/home.h"
25 #include "store/pricing.h"
26 #include "store/say-comments.h"
27 #include "store/store-owners.h"
28 #include "store/store-util.h"
29 #include "store/store.h"
30 #include "system/object-type-definition.h"
31 #include "system/player-type-definition.h"
32 #include "term/screen-processor.h"
33 #include "util/enum-converter.h"
34 #include "util/int-char-converter.h"
35 #include "view/display-messages.h"
36 #include "view/display-store.h"
37 #include "world/world.h"
38 #include <optional>
39
40 /*!
41  * @brief プレイヤーが購入する時の値切り処理メインルーチン /
42  * Haggling routine                             -RAK-
43  * @param player_ptr プレイヤーへの参照ポインタ
44  * @param o_ptr オブジェクトの構造体参照ポインタ
45  * @param price 最終価格を返す参照ポインタ
46  * @return プレイヤーの価格に対して店主が不服ならばTRUEを返す /
47  * Return TRUE if purchase is NOT successful
48  */
49 static std::optional<PRICE> prompt_to_buy(PlayerType *player_ptr, ObjectType *o_ptr, StoreSaleType store_num)
50 {
51     auto price_ask = price_item(player_ptr, o_ptr, ot_ptr->inflate, false, store_num);
52
53     price_ask *= o_ptr->number;
54     concptr s = format(_("買値 $%ld で買いますか?", "Do you buy for $%ld? "), static_cast<long>(price_ask));
55     if (get_check_strict(player_ptr, s, CHECK_DEFAULT_Y)) {
56         return price_ask;
57     }
58
59     return std::nullopt;
60 }
61
62 /*!
63  * @brief 店舗から購入する際のアイテム選択プロンプト
64  * @param item 店舗インベントリ番号(アドレス渡し)
65  * @param i 店舗インベントリストック数
66  * @return 選択したらtrue、しなかったらfalse
67  * @details
68  * 選択したインベントリ番号はitemに返る。
69  * ブラックマーケットの時は別のメッセージ。
70  */
71 static bool show_store_select_item(COMMAND_CODE *item, const int i, StoreSaleType store_num)
72 {
73     char out_val[160];
74
75     switch (store_num) {
76     case StoreSaleType::HOME:
77         sprintf(out_val, _("どのアイテムを取りますか? ", "Which item do you want to take? "));
78         break;
79     case StoreSaleType::BLACK:
80         sprintf(out_val, _("どれ? ", "Which item, huh? "));
81         break;
82     default:
83         sprintf(out_val, _("どの品物が欲しいんだい? ", "Which item are you interested in? "));
84         break;
85     }
86
87     return get_stock(item, out_val, 0, i - 1, store_num) != 0;
88 }
89
90 /*!
91  * @brief 家のアイテムを取得する
92  * @param player_ptr プレイヤー情報の参照ポインタ
93  * @param o_ptr 取得元オブジェクト
94  * @param j_ptr 取得先オブジェクト(指定数量分)
95  * @param item_new 取得先インベントリ番号(アドレス渡し)
96  * @param amt 数量
97  * @param i お店のストック数(アドレス渡し)
98  * @param 取得元インベントリ番号
99  */
100 static void take_item_from_home(PlayerType *player_ptr, ObjectType *o_ptr, ObjectType *j_ptr, const COMMAND_CODE item)
101 {
102     const int amt = j_ptr->number;
103     distribute_charges(o_ptr, j_ptr, amt);
104
105     GAME_TEXT o_name[MAX_NLEN];
106     auto item_new = store_item_to_inventory(player_ptr, j_ptr);
107     describe_flavor(player_ptr, o_name, &player_ptr->inventory_list[item_new], 0);
108     handle_stuff(player_ptr);
109     msg_format(_("%s(%c)を取った。", "You have %s (%c)."), o_name, index_to_label(item_new));
110
111     auto i = st_ptr->stock_num;
112     store_item_increase(item, -amt);
113     store_item_optimize(item);
114
115     auto combined_or_reordered = combine_and_reorder_home(player_ptr, StoreSaleType::HOME);
116
117     if (i == st_ptr->stock_num) {
118         if (combined_or_reordered) {
119             display_store_inventory(player_ptr, StoreSaleType::HOME);
120         } else {
121             display_entry(player_ptr, item, StoreSaleType::HOME);
122         }
123         return;
124     }
125
126     if (st_ptr->stock_num == 0) {
127         store_top = 0;
128     } else if (store_top >= st_ptr->stock_num) {
129         store_top -= store_bottom;
130     }
131
132     display_store_inventory(player_ptr, StoreSaleType::HOME);
133     chg_virtue(player_ptr, V_SACRIFICE, 1);
134 }
135
136 static void shuffle_store(PlayerType *player_ptr, StoreSaleType store_num)
137 {
138     if (!one_in_(STORE_SHUFFLE)) {
139         msg_print(_("店主は新たな在庫を取り出した。", "The shopkeeper brings out some new stock."));
140         return;
141     }
142
143     char buf[80];
144
145     msg_print(_("店主は引退した。", "The shopkeeper retires."));
146     store_shuffle(player_ptr, store_num);
147     prt("", 3, 0);
148     sprintf(buf, "%s (%s)", ot_ptr->owner_name, race_info[enum2i(ot_ptr->owner_race)].title);
149     put_str(buf, 3, 10);
150     sprintf(buf, "%s (%ld)", f_info[cur_store_feat].name.c_str(), (long)(ot_ptr->max_cost));
151     prt(buf, 3, 50);
152 }
153
154 static void switch_store_stock(PlayerType *player_ptr, const int i, const COMMAND_CODE item, StoreSaleType store_num)
155 {
156     if (st_ptr->stock_num == 0) {
157         shuffle_store(player_ptr, store_num);
158         store_maintenance(player_ptr, player_ptr->town_num, store_num, 10);
159
160         store_top = 0;
161         display_store_inventory(player_ptr, store_num);
162         return;
163     }
164
165     if (st_ptr->stock_num != i) {
166         if (store_top >= st_ptr->stock_num) {
167             store_top -= store_bottom;
168         }
169
170         display_store_inventory(player_ptr, store_num);
171         return;
172     }
173
174     display_entry(player_ptr, item, store_num);
175 }
176
177 /*!
178  * @brief 店からの購入処理のメインルーチン /
179  * Buy an item from a store                     -RAK-
180  * @param player_ptr プレイヤーへの参照ポインタ
181  */
182 void store_purchase(PlayerType *player_ptr, StoreSaleType store_num)
183 {
184     if (store_num == StoreSaleType::MUSEUM) {
185         msg_print(_("博物館から取り出すことはできません。", "Items cannot be taken out of the Museum."));
186         return;
187     }
188
189     if (st_ptr->stock_num <= 0) {
190         if (store_num == StoreSaleType::HOME) {
191             msg_print(_("我が家には何も置いてありません。", "Your home is empty."));
192         } else {
193             msg_print(_("現在商品の在庫を切らしています。", "I am currently out of stock."));
194         }
195         return;
196     }
197
198     int i = (st_ptr->stock_num - store_top);
199     if (i > store_bottom) {
200         i = store_bottom;
201     }
202
203     COMMAND_CODE item;
204     if (!show_store_select_item(&item, i, store_num)) {
205         return;
206     }
207
208     item = item + store_top;
209     ObjectType *o_ptr;
210     o_ptr = &st_ptr->stock[item];
211
212     ITEM_NUMBER amt = 1;
213     ObjectType forge;
214     auto *j_ptr = &forge;
215     j_ptr->copy_from(o_ptr);
216
217     /*
218      * If a rod or wand, allocate total maximum timeouts or charges
219      * between those purchased and left on the shelf.
220      */
221     reduce_charges(j_ptr, o_ptr->number - amt);
222     j_ptr->number = amt;
223     if (!check_store_item_to_inventory(player_ptr, j_ptr)) {
224         msg_print(_("そんなにアイテムを持てない。", "You cannot carry that many different items."));
225         return;
226     }
227
228     PRICE best = price_item(player_ptr, j_ptr, ot_ptr->inflate, false, store_num);
229     if (o_ptr->number > 1) {
230         if (store_num != StoreSaleType::HOME) {
231             msg_format(_("一つにつき $%ldです。", "That costs %ld gold per item."), (long)(best));
232         }
233
234         amt = get_quantity(nullptr, o_ptr->number);
235         if (amt <= 0) {
236             return;
237         }
238     }
239
240     j_ptr = &forge;
241     j_ptr->copy_from(o_ptr);
242
243     /*
244      * If a rod or wand, allocate total maximum timeouts or charges
245      * between those purchased and left on the shelf.
246      */
247     reduce_charges(j_ptr, o_ptr->number - amt);
248     j_ptr->number = amt;
249     if (!check_store_item_to_inventory(player_ptr, j_ptr)) {
250         msg_print(_("ザックにそのアイテムを入れる隙間がない。", "You cannot carry that many items."));
251         return;
252     }
253
254     if (store_num == StoreSaleType::HOME) {
255         take_item_from_home(player_ptr, o_ptr, j_ptr, item);
256         return;
257     }
258
259     COMMAND_CODE item_new;
260     PRICE price;
261     GAME_TEXT o_name[MAX_NLEN];
262     describe_flavor(player_ptr, o_name, j_ptr, 0);
263     msg_format(_("%s(%c)を購入する。", "Buying %s (%c)."), o_name, I2A(item));
264     msg_print(nullptr);
265
266     auto res = prompt_to_buy(player_ptr, j_ptr, store_num);
267     if (st_ptr->store_open >= w_ptr->game_turn) {
268         return;
269     }
270     if (!res) {
271         return;
272     }
273
274     price = res.value();
275
276     if (player_ptr->au < price) {
277         msg_print(_("お金が足りません。", "You do not have enough gold."));
278         return;
279     }
280
281     store_owner_says_comment(player_ptr, store_num);
282     if (store_num == StoreSaleType::BLACK) {
283         chg_virtue(player_ptr, V_JUSTICE, -1);
284     }
285     if ((o_ptr->tval == ItemKindType::BOTTLE) && (store_num != StoreSaleType::HOME)) {
286         chg_virtue(player_ptr, V_NATURE, -1);
287     }
288
289     sound(SOUND_BUY);
290     player_ptr->au -= price;
291     store_prt_gold(player_ptr);
292     object_aware(player_ptr, j_ptr);
293
294     describe_flavor(player_ptr, o_name, j_ptr, 0);
295     msg_format(_("%sを $%ldで購入しました。", "You bought %s for %ld gold."), o_name, (long)price);
296
297     strcpy(record_o_name, o_name);
298     record_turn = w_ptr->game_turn;
299
300     if (record_buy) {
301         exe_write_diary(player_ptr, DIARY_BUY, 0, o_name);
302     }
303
304     describe_flavor(player_ptr, o_name, o_ptr, OD_NAME_ONLY);
305     if (record_rand_art && o_ptr->art_name) {
306         exe_write_diary(player_ptr, DIARY_ART, 0, o_name);
307     }
308
309     j_ptr->inscription = 0;
310     j_ptr->feeling = FEEL_NONE;
311     j_ptr->ident &= ~(IDENT_STORE);
312
313     const auto idx = find_autopick_list(player_ptr, j_ptr);
314     auto_inscribe_item(player_ptr, j_ptr, idx);
315
316     item_new = store_item_to_inventory(player_ptr, j_ptr);
317     handle_stuff(player_ptr);
318
319     describe_flavor(player_ptr, o_name, &player_ptr->inventory_list[item_new], 0);
320     msg_format(_("%s(%c)を手に入れた。", "You have %s (%c)."), o_name, index_to_label(item_new));
321
322     if ((o_ptr->tval == ItemKindType::ROD) || (o_ptr->tval == ItemKindType::WAND)) {
323         o_ptr->pval -= j_ptr->pval;
324     }
325
326     i = st_ptr->stock_num;
327     store_item_increase(item, -amt);
328     store_item_optimize(item);
329     switch_store_stock(player_ptr, i, item, store_num);
330 }