2 * @brief 店の処理 / Store commands
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.
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/object-stack.h"
22 #include "perception/identification.h"
23 #include "perception/object-perception.h"
24 #include "store/black-market.h"
25 #include "store/service-checker.h"
26 #include "store/store-owners.h"
27 #include "store/store-util.h"
28 #include "system/object-type-definition.h"
29 #include "system/player-type-definition.h"
30 #include "term/screen-processor.h"
31 #include "util/int-char-converter.h"
32 #include "util/quarks.h"
33 #include "view/display-messages.h"
35 #include "locale/japanese.h"
41 const owner_type *ot_ptr = nullptr;
42 int16_t old_town_num = 0;
43 int16_t inner_town_num = 0;
45 /* We store the current "store feat" here so everyone can access it */
48 /* Enable "increments" */
49 bool allow_inc = false;
52 * @brief 店舗の最大スロット数を返す
53 * @param store_idx 店舗ID
56 int16_t store_get_stock_max(StoreSaleType sst, bool powerup)
59 case StoreSaleType::HOME:
60 return powerup ? STORE_INVEN_MAX * 10 : STORE_INVEN_MAX;
61 case StoreSaleType::MUSEUM:
62 return STORE_INVEN_MAX * 50;
64 return STORE_INVEN_MAX * 3 / 2;
69 * @brief アイテムが格納可能な数より多いかをチェックする
73 * 1 : Cannot be combined but there are empty spaces.
74 * @details オプション powerup_home が設定されていると我が家が 20 ページまで使える /
75 * Free space is always usable
77 static int check_free_space(void)
79 if ((cur_store_num == StoreSaleType::HOME) && !powerup_home) {
80 if (st_ptr->stock_num < ((st_ptr->stock_size) / 10))
82 } else if (st_ptr->stock_num < st_ptr->stock_size)
89 * @brief 店舗に品を置くスペースがあるかどうかの判定を返す /
90 * Check to see if the shop will be carrying too many objects -RAK-
91 * @param o_ptr 店舗に置きたいオブジェクト構造体の参照ポインタ
92 * @return 置き場がないなら0、重ね合わせできるアイテムがあるなら-1、スペースがあるなら1を返す。
95 * Note that the shop, just like a player, will not accept things
96 * it cannot hold. Before, one could "nuke" potions this way.
97 * Return value is now int:
99 * -1 : Can be combined to existing slot.
100 * 1 : Cannot be combined but there are empty spaces.
103 int store_check_num(object_type *o_ptr)
106 if ((cur_store_num == StoreSaleType::HOME) || (cur_store_num == StoreSaleType::MUSEUM)) {
107 bool old_stack_force_notes = stack_force_notes;
108 bool old_stack_force_costs = stack_force_costs;
109 if (cur_store_num != StoreSaleType::HOME) {
110 stack_force_notes = false;
111 stack_force_costs = false;
114 for (int i = 0; i < st_ptr->stock_num; i++) {
115 j_ptr = &st_ptr->stock[i];
116 if (!object_similar(j_ptr, o_ptr))
119 if (cur_store_num != StoreSaleType::HOME) {
120 stack_force_notes = old_stack_force_notes;
121 stack_force_costs = old_stack_force_costs;
127 if (cur_store_num != StoreSaleType::HOME) {
128 stack_force_notes = old_stack_force_notes;
129 stack_force_costs = old_stack_force_costs;
132 for (int i = 0; i < st_ptr->stock_num; i++) {
133 j_ptr = &st_ptr->stock[i];
134 if (store_object_similar(j_ptr, o_ptr))
139 return check_free_space();
143 * @brief 店舗からアイテムを選択する /
144 * Get the ID of a store item and return its value -RAK-
145 * @param com_val 選択IDを返す参照ポインタ
146 * @param pmt メッセージキャプション
149 * @return 実際に選択したらTRUE、キャンセルしたらFALSE
151 int get_stock(COMMAND_CODE *com_val, concptr pmt, int i, int j)
153 if (repeat_pull(com_val) && (*com_val >= i) && (*com_val <= j))
159 char hi = (j > 25) ? toupper(I2A(j - 26)) : I2A(j);
162 (void)sprintf(out_val, "(%s:%c-%c, ESCで中断) %s", (((cur_store_num == StoreSaleType::HOME) || (cur_store_num == StoreSaleType::MUSEUM)) ? "アイテム" : "商品"), lo, hi, pmt);
164 (void)sprintf(out_val, "(Items %c-%c, ESC to exit) %s", lo, hi, pmt);
169 if (!get_com(out_val, &command, false))
173 if (islower(command))
175 else if (isupper(command))
176 k = A2I(tolower(command)) + 26;
180 if ((k >= i) && (k <= j)) {
189 if (command == ESCAPE)
192 repeat_push(*com_val);
197 * @brief 店のアイテムを調べるコマンドのメインルーチン /
198 * Examine an item in a store -JDL-
200 void store_examine(player_type *player_ptr)
202 if (st_ptr->stock_num <= 0) {
203 if (cur_store_num == StoreSaleType::HOME)
204 msg_print(_("我が家には何も置いてありません。", "Your home is empty."));
205 else if (cur_store_num == StoreSaleType::MUSEUM)
206 msg_print(_("博物館には何も置いてありません。", "The Museum is empty."));
208 msg_print(_("現在商品の在庫を切らしています。", "I am currently out of stock."));
212 int i = (st_ptr->stock_num - store_top);
213 if (i > store_bottom)
217 sprintf(out_val, _("どれを調べますか?", "Which item do you want to examine? "));
220 if (!get_stock(&item, out_val, 0, i - 1))
222 item = item + store_top;
224 o_ptr = &st_ptr->stock[item];
225 if (!o_ptr->is_fully_known()) {
226 msg_print(_("このアイテムについて特に知っていることはない。", "You have no special knowledge about that item."));
230 GAME_TEXT o_name[MAX_NLEN];
231 describe_flavor(player_ptr, o_name, o_ptr, 0);
232 msg_format(_("%sを調べている...", "Examining %s..."), o_name);
233 if (!screen_object(player_ptr, o_ptr, SCROBJ_FORCE_DETAIL))
234 msg_print(_("特に変わったところはないようだ。", "You see nothing special."));
238 * @brief 現在の町の店主を交代させる /
239 * Shuffle one of the stores.
240 * @param which 店舗種類のID
242 void store_shuffle(player_type *player_ptr, StoreSaleType which)
244 if ((which == StoreSaleType::HOME) || (which == StoreSaleType::MUSEUM))
247 cur_store_num = which;
248 st_ptr = &town_info[player_ptr->town_num].store[enum2i(cur_store_num)];
249 int j = st_ptr->owner;
251 st_ptr->owner = (byte)randint0(MAX_OWNERS);
252 if (j == st_ptr->owner)
256 for (i = 1; i < max_towns; i++) {
257 if (i == player_ptr->town_num)
260 if (st_ptr->owner == town_info[i].store[enum2i(cur_store_num)].owner)
268 ot_ptr = &owners[enum2i(cur_store_num)][st_ptr->owner];
269 st_ptr->insult_cur = 0;
270 st_ptr->store_open = 0;
271 st_ptr->good_buy = 0;
273 for (int i = 0; i < st_ptr->stock_num; i++) {
275 o_ptr = &st_ptr->stock[i];
276 if (o_ptr->is_artifact())
279 o_ptr->discount = 50;
280 o_ptr->inscription = quark_add(_("売出中", "on sale"));
285 * @brief 店の品揃えを変化させる /
286 * Maintain the inventory at the stores.
287 * @param player_ptr プレイヤーへの参照ポインタ
288 * @param town_num 町のID
289 * @param store_num 店舗種類のID
290 * @param chance 更新商品数
292 void store_maintenance(player_type *player_ptr, int town_num, StoreSaleType store_num, int chance)
294 cur_store_num = store_num;
295 if ((store_num == StoreSaleType::HOME) || (store_num == StoreSaleType::MUSEUM))
298 st_ptr = &town_info[town_num].store[enum2i(store_num)];
299 ot_ptr = &owners[enum2i(store_num)][st_ptr->owner];
300 st_ptr->insult_cur = 0;
301 if (store_num == StoreSaleType::BLACK) {
302 for (INVENTORY_IDX j = st_ptr->stock_num - 1; j >= 0; j--) {
303 object_type *o_ptr = &st_ptr->stock[j];
304 if (black_market_crap(player_ptr, o_ptr)) {
305 store_item_increase(j, 0 - o_ptr->number);
306 store_item_optimize(j);
311 INVENTORY_IDX j = st_ptr->stock_num;
312 int remain = STORE_TURNOVER + MAX(0, j - STORE_MAX_KEEP);
314 for (int i = 0; i < chance; i++) {
315 auto n = randint0(remain);
321 if (j > STORE_MAX_KEEP)
323 if (j < STORE_MIN_KEEP)
326 while (st_ptr->stock_num > j)
329 remain = STORE_MAX_KEEP - st_ptr->stock_num;
331 for (int i = 0; i < chance; i++) {
332 auto n = randint0(remain);
337 j = st_ptr->stock_num + turn_over;
338 if (j > STORE_MAX_KEEP)
340 if (j < STORE_MIN_KEEP)
342 if (j >= st_ptr->stock_size)
343 j = st_ptr->stock_size - 1;
345 for (size_t k = 0; k < st_ptr->regular.size(); k++) {
346 store_create(player_ptr, st_ptr->regular[k], black_market_crap, store_will_buy, mass_produce);
347 if (st_ptr->stock_num >= STORE_MAX_KEEP)
351 while (st_ptr->stock_num < j)
352 store_create(player_ptr, 0, black_market_crap, store_will_buy, mass_produce);
356 * @brief 店舗情報を初期化する /
357 * Initialize the stores
358 * @param town_num 町のID
359 * @param store_num 店舗種類のID
361 void store_init(int town_num, StoreSaleType store_num)
363 cur_store_num = store_num;
364 st_ptr = &town_info[town_num].store[enum2i(store_num)];
366 st_ptr->owner = (byte)randint0(MAX_OWNERS);
368 for (i = 1; i < max_towns; i++) {
371 if (st_ptr->owner == town_info[i].store[enum2i(store_num)].owner)
379 ot_ptr = &owners[enum2i(store_num)][st_ptr->owner];
380 st_ptr->store_open = 0;
381 st_ptr->insult_cur = 0;
382 st_ptr->good_buy = 0;
384 st_ptr->stock_num = 0;
385 st_ptr->last_visit = -10L * TURNS_PER_TICK * STORE_TICKS;
386 for (int k = 0; k < st_ptr->stock_size; k++)
387 (&st_ptr->stock[k])->wipe();