OSDN Git Service

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