OSDN Git Service

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