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-hook/hook-enchant.h"
22 #include "object/object-stack.h"
23 #include "perception/identification.h"
24 #include "perception/object-perception.h"
25 #include "store/black-market.h"
26 #include "store/service-checker.h"
27 #include "store/store-owners.h"
28 #include "store/store-util.h"
29 #include "system/object-type-definition.h"
30 #include "system/player-type-definition.h"
31 #include "term/screen-processor.h"
32 #include "util/int-char-converter.h"
33 #include "util/quarks.h"
34 #include "view/display-messages.h"
36 #include "locale/japanese.h"
42 const owner_type *ot_ptr = NULL;
43 s16b old_town_num = 0;
44 s16b inner_town_num = 0;
46 /* We store the current "store feat" here so everyone can access it */
49 /* Enable "increments" */
50 bool allow_inc = FALSE;
53 * @brief 店舗の最大スロット数を返す
54 * @param store_idx 店舗ID
57 s16b store_get_stock_max(STORE_TYPE_IDX store_idx, bool powerup)
61 return powerup ? STORE_INVEN_MAX * 10 : STORE_INVEN_MAX;
63 return STORE_INVEN_MAX * 50;
65 return STORE_INVEN_MAX * 3 / 2;
70 * @brief アイテムが格納可能な数より多いかをチェックする
74 * 1 : Cannot be combined but there are empty spaces.
75 * @details オプション powerup_home が設定されていると我が家が 20 ページまで使える /
76 * Free space is always usable
78 static int check_free_space(void)
80 if ((cur_store_num == STORE_HOME) && !powerup_home) {
81 if (st_ptr->stock_num < ((st_ptr->stock_size) / 10))
83 } else if (st_ptr->stock_num < st_ptr->stock_size)
90 * @brief 店舗に品を置くスペースがあるかどうかの判定を返す /
91 * Check to see if the shop will be carrying too many objects -RAK-
92 * @param o_ptr 店舗に置きたいオブジェクト構造体の参照ポインタ
93 * @return 置き場がないなら0、重ね合わせできるアイテムがあるなら-1、スペースがあるなら1を返す。
96 * Note that the shop, just like a player, will not accept things
97 * it cannot hold. Before, one could "nuke" potions this way.
98 * Return value is now int:
100 * -1 : Can be combined to existing slot.
101 * 1 : Cannot be combined but there are empty spaces.
104 int store_check_num(object_type *o_ptr)
107 if ((cur_store_num == STORE_HOME) || (cur_store_num == STORE_MUSEUM)) {
108 bool old_stack_force_notes = stack_force_notes;
109 bool old_stack_force_costs = stack_force_costs;
110 if (cur_store_num != STORE_HOME) {
111 stack_force_notes = FALSE;
112 stack_force_costs = FALSE;
115 for (int i = 0; i < st_ptr->stock_num; i++) {
116 j_ptr = &st_ptr->stock[i];
117 if (!object_similar(j_ptr, o_ptr))
120 if (cur_store_num != STORE_HOME) {
121 stack_force_notes = old_stack_force_notes;
122 stack_force_costs = old_stack_force_costs;
128 if (cur_store_num != STORE_HOME) {
129 stack_force_notes = old_stack_force_notes;
130 stack_force_costs = old_stack_force_costs;
133 for (int i = 0; i < st_ptr->stock_num; i++) {
134 j_ptr = &st_ptr->stock[i];
135 if (store_object_similar(j_ptr, o_ptr))
140 return check_free_space();
144 * @brief 店舗からアイテムを選択する /
145 * Get the ID of a store item and return its value -RAK-
146 * @param com_val 選択IDを返す参照ポインタ
147 * @param pmt メッセージキャプション
150 * @return 実際に選択したらTRUE、キャンセルしたらFALSE
152 int get_stock(COMMAND_CODE *com_val, concptr pmt, int i, int j)
154 if (repeat_pull(com_val) && (*com_val >= i) && (*com_val <= j))
160 char hi = (j > 25) ? toupper(I2A(j - 26)) : I2A(j);
163 (void)sprintf(out_val, "(%s:%c-%c, ESCで中断) %s", (((cur_store_num == STORE_HOME) || (cur_store_num == STORE_MUSEUM)) ? "アイテム" : "商品"), lo, hi, pmt);
165 (void)sprintf(out_val, "(Items %c-%c, ESC to exit) %s", lo, hi, pmt);
170 if (!get_com(out_val, &command, FALSE))
174 if (islower(command))
176 else if (isupper(command))
177 k = A2I(tolower(command)) + 26;
181 if ((k >= i) && (k <= j)) {
190 if (command == ESCAPE)
193 repeat_push(*com_val);
198 * @brief 店のアイテムを調べるコマンドのメインルーチン /
199 * Examine an item in a store -JDL-
201 void store_examine(player_type *player_ptr)
203 if (st_ptr->stock_num <= 0) {
204 if (cur_store_num == STORE_HOME)
205 msg_print(_("我が家には何も置いてありません。", "Your home is empty."));
206 else if (cur_store_num == STORE_MUSEUM)
207 msg_print(_("博物館には何も置いてありません。", "The Museum is empty."));
209 msg_print(_("現在商品の在庫を切らしています。", "I am currently out of stock."));
213 int i = (st_ptr->stock_num - store_top);
214 if (i > store_bottom)
218 sprintf(out_val, _("どれを調べますか?", "Which item do you want to examine? "));
221 if (!get_stock(&item, out_val, 0, i - 1))
223 item = item + store_top;
225 o_ptr = &st_ptr->stock[item];
226 if (!object_is_fully_known(o_ptr)) {
227 msg_print(_("このアイテムについて特に知っていることはない。", "You have no special knowledge about that item."));
231 GAME_TEXT o_name[MAX_NLEN];
232 describe_flavor(player_ptr, o_name, o_ptr, 0);
233 msg_format(_("%sを調べている...", "Examining %s..."), o_name);
234 if (!screen_object(player_ptr, o_ptr, SCROBJ_FORCE_DETAIL))
235 msg_print(_("特に変わったところはないようだ。", "You see nothing special."));
239 * @brief 現在の町の店主を交代させる /
240 * Shuffle one of the stores.
241 * @param which 店舗種類のID
243 void store_shuffle(player_type *player_ptr, int which)
245 if ((which == STORE_HOME) || (which == STORE_MUSEUM))
248 cur_store_num = which;
249 st_ptr = &town_info[player_ptr->town_num].store[cur_store_num];
250 int j = st_ptr->owner;
252 st_ptr->owner = (byte)randint0(MAX_OWNERS);
253 if (j == st_ptr->owner)
257 for (i = 1; i < max_towns; i++) {
258 if (i == player_ptr->town_num)
261 if (st_ptr->owner == town_info[i].store[cur_store_num].owner)
269 ot_ptr = &owners[cur_store_num][st_ptr->owner];
270 st_ptr->insult_cur = 0;
271 st_ptr->store_open = 0;
272 st_ptr->good_buy = 0;
274 for (int i = 0; i < st_ptr->stock_num; i++) {
276 o_ptr = &st_ptr->stock[i];
277 if (object_is_artifact(o_ptr))
280 o_ptr->discount = 50;
281 o_ptr->ident &= ~(IDENT_FIXED);
282 o_ptr->inscription = quark_add(_("売出中", "on sale"));
287 * @brief 店の品揃えを変化させる /
288 * Maintain the inventory at the stores.
289 * @param player_ptr プレーヤーへの参照ポインタ
290 * @param town_num 町のID
291 * @param store_num 店舗種類のID
292 * @param chance 更新商品数
294 void store_maintenance(player_type *player_ptr, int town_num, int store_num, int chance)
296 cur_store_num = store_num;
297 if ((store_num == STORE_HOME) || (store_num == STORE_MUSEUM))
300 st_ptr = &town_info[town_num].store[store_num];
301 ot_ptr = &owners[store_num][st_ptr->owner];
302 st_ptr->insult_cur = 0;
303 if (store_num == STORE_BLACK) {
304 for (INVENTORY_IDX j = st_ptr->stock_num - 1; j >= 0; j--) {
305 object_type *o_ptr = &st_ptr->stock[j];
306 if (black_market_crap(player_ptr, o_ptr)) {
307 store_item_increase(j, 0 - o_ptr->number);
308 store_item_optimize(j);
313 INVENTORY_IDX j = st_ptr->stock_num;
314 int remain = STORE_TURNOVER + MAX(0, j - STORE_MAX_KEEP);
316 for (int i = 0; i < chance; i++) {
317 auto n = randint0(remain);
323 if (j > STORE_MAX_KEEP)
325 if (j < STORE_MIN_KEEP)
328 while (st_ptr->stock_num > j)
331 remain = STORE_MAX_KEEP - st_ptr->stock_num;
333 for (int i = 0; i < chance; i++) {
334 auto n = randint0(remain);
339 j = st_ptr->stock_num + turn_over;
340 if (j > STORE_MAX_KEEP)
342 if (j < STORE_MIN_KEEP)
344 if (j >= st_ptr->stock_size)
345 j = st_ptr->stock_size - 1;
347 for (size_t k = 0; k < st_ptr->regular.size(); k++) {
348 store_create(player_ptr, st_ptr->regular[k], black_market_crap, store_will_buy, mass_produce);
349 if (st_ptr->stock_num >= STORE_MAX_KEEP)
353 while (st_ptr->stock_num < j)
354 store_create(player_ptr, 0, black_market_crap, store_will_buy, mass_produce);
358 * @brief 店舗情報を初期化する /
359 * Initialize the stores
360 * @param town_num 町のID
361 * @param store_num 店舗種類のID
363 void store_init(int town_num, int store_num)
365 cur_store_num = store_num;
366 st_ptr = &town_info[town_num].store[store_num];
368 st_ptr->owner = (byte)randint0(MAX_OWNERS);
370 for (i = 1; i < max_towns; i++) {
373 if (st_ptr->owner == town_info[i].store[store_num].owner)
381 ot_ptr = &owners[store_num][st_ptr->owner];
382 st_ptr->store_open = 0;
383 st_ptr->insult_cur = 0;
384 st_ptr->good_buy = 0;
386 st_ptr->stock_num = 0;
387 st_ptr->last_visit = -10L * TURNS_PER_TICK * STORE_TICKS;
388 for (int k = 0; k < st_ptr->stock_size; k++)
389 (&st_ptr->stock[k])->object_wipe();