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"
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
51 static std::optional<PRICE> prompt_to_buy(PlayerType *player_ptr, ItemEntity *o_ptr, StoreSaleType store_num)
53 auto price_ask = price_item(player_ptr, o_ptr, ot_ptr->inflate, false, store_num);
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)) {
65 * @brief 店舗から購入する際のアイテム選択プロンプト
66 * @param i 店舗インベントリストック数
67 * @return 選択したらtrue、しなかったらfalse
69 static std::optional<short> show_store_select_item(const int i, StoreSaleType store_num)
73 case StoreSaleType::HOME:
74 prompt = _("どのアイテムを取りますか? ", "Which item do you want to take? ");
76 case StoreSaleType::BLACK:
77 prompt = _("どれ? ", "Which item, huh? ");
80 prompt = _("どの品物が欲しいんだい? ", "Which item are you interested in? ");
84 return input_stock(prompt, 0, i - 1, store_num);
89 * @param player_ptr プレイヤー情報の参照ポインタ
90 * @param o_ptr 取得元オブジェクト
91 * @param j_ptr 取得先オブジェクト(指定数量分)
92 * @param item_new 取得先インベントリ番号(アドレス渡し)
94 * @param i お店のストック数(アドレス渡し)
97 static void take_item_from_home(PlayerType *player_ptr, ItemEntity *o_ptr, ItemEntity *j_ptr, const COMMAND_CODE item)
99 const int amt = j_ptr->number;
100 distribute_charges(o_ptr, j_ptr, amt);
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));
107 auto i = st_ptr->stock_num;
108 store_item_increase(item, -amt);
109 store_item_optimize(item);
111 auto combined_or_reordered = combine_and_reorder_home(player_ptr, StoreSaleType::HOME);
113 if (i == st_ptr->stock_num) {
114 if (combined_or_reordered) {
115 display_store_inventory(player_ptr, StoreSaleType::HOME);
117 display_entry(player_ptr, item, StoreSaleType::HOME);
122 if (st_ptr->stock_num == 0) {
124 } else if (store_top >= st_ptr->stock_num) {
125 store_top -= store_bottom;
128 display_store_inventory(player_ptr, StoreSaleType::HOME);
129 chg_virtue(player_ptr, Virtue::SACRIFICE, 1);
132 static void shuffle_store(PlayerType *player_ptr, StoreSaleType store_num)
134 if (!one_in_(STORE_SHUFFLE)) {
135 msg_print(_("店主は新たな在庫を取り出した。", "The shopkeeper brings out some new stock."));
139 msg_print(_("店主は引退した。", "The shopkeeper retires."));
140 store_shuffle(player_ptr, store_num);
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);
147 static void switch_store_stock(PlayerType *player_ptr, const int i, const COMMAND_CODE item, StoreSaleType store_num)
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);
154 display_store_inventory(player_ptr, store_num);
158 if (st_ptr->stock_num != i) {
159 if (store_top >= st_ptr->stock_num) {
160 store_top -= store_bottom;
163 display_store_inventory(player_ptr, store_num);
167 display_entry(player_ptr, item, store_num);
171 * @brief 店からの購入処理のメインルーチン /
172 * Buy an item from a store -RAK-
173 * @param player_ptr プレイヤーへの参照ポインタ
175 void store_purchase(PlayerType *player_ptr, StoreSaleType store_num)
177 if (store_num == StoreSaleType::MUSEUM) {
178 msg_print(_("博物館から取り出すことはできません。", "Items cannot be taken out of the Museum."));
182 if (st_ptr->stock_num <= 0) {
183 if (store_num == StoreSaleType::HOME) {
184 msg_print(_("我が家には何も置いてありません。", "Your home is empty."));
186 msg_print(_("現在商品の在庫を切らしています。", "I am currently out of stock."));
191 int i = (st_ptr->stock_num - store_top);
192 if (i > store_bottom) {
196 auto item_num_opt = show_store_select_item(i, store_num);
201 const short item_num = *item_num_opt + store_top;
202 auto *o_ptr = &st_ptr->stock[item_num];
206 auto *j_ptr = &forge;
207 j_ptr->copy_from(o_ptr);
210 * If a rod or wand, allocate total maximum timeouts or charges
211 * between those purchased and left on the shelf.
213 reduce_charges(j_ptr, o_ptr->number - amt);
215 if (!check_store_item_to_inventory(player_ptr, j_ptr)) {
216 msg_print(_("そんなにアイテムを持てない。", "You cannot carry that many different items."));
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);
226 amt = input_quantity(o_ptr->number);
233 j_ptr->copy_from(o_ptr);
236 * If a rod or wand, allocate total maximum timeouts or charges
237 * between those purchased and left on the shelf.
239 reduce_charges(j_ptr, o_ptr->number - amt);
241 if (!check_store_item_to_inventory(player_ptr, j_ptr)) {
242 msg_print(_("ザックにそのアイテムを入れる隙間がない。", "You cannot carry that many items."));
246 if (store_num == StoreSaleType::HOME) {
247 take_item_from_home(player_ptr, o_ptr, j_ptr, item_num);
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));
256 auto res = prompt_to_buy(player_ptr, j_ptr, store_num);
257 if (st_ptr->store_open >= w_ptr->game_turn) {
264 const auto price = *res;
266 if (player_ptr->au < price) {
267 msg_print(_("お金が足りません。", "You do not have enough gold."));
271 store_owner_says_comment(player_ptr, store_num);
272 if (store_num == StoreSaleType::BLACK) {
273 chg_virtue(player_ptr, Virtue::JUSTICE, -1);
275 if ((o_ptr->bi_key.tval() == ItemKindType::BOTTLE) && (store_num != StoreSaleType::HOME)) {
276 chg_virtue(player_ptr, Virtue::NATURE, -1);
280 player_ptr->au -= price;
281 store_prt_gold(player_ptr);
282 object_aware(player_ptr, j_ptr);
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;
289 exe_write_diary(floor, DiaryKind::BUY, 0, purchased_item_name);
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);
297 j_ptr->inscription.reset();
298 j_ptr->feeling = FEEL_NONE;
299 j_ptr->ident &= ~(IDENT_STORE);
301 const auto idx = find_autopick_list(player_ptr, j_ptr);
302 auto_inscribe_item(j_ptr, idx);
304 item_new = store_item_to_inventory(player_ptr, j_ptr);
305 handle_stuff(player_ptr);
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));
310 if (o_ptr->is_wand_rod()) {
311 o_ptr->pval -= j_ptr->pval;
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);