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 "locale/japanese.h"
20 #include "main/sound-of-music.h"
21 #include "object-enchant/special-object-flags.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"
39 const owner_type *ot_ptr = nullptr;
40 int16_t old_town_num = 0;
41 int16_t inner_town_num = 0;
43 /* We store the current "store feat" here so everyone can access it */
46 /* Enable "increments" */
47 bool allow_inc = false;
50 * @brief 店舗の最大スロット数を返す
51 * @param store_idx 店舗ID
54 int16_t store_get_stock_max(StoreSaleType sst, bool powerup)
57 case StoreSaleType::HOME:
58 return powerup ? STORE_INVEN_MAX * 10 : STORE_INVEN_MAX;
59 case StoreSaleType::MUSEUM:
60 return STORE_INVEN_MAX * 50;
62 return STORE_INVEN_MAX * 3 / 2;
67 * @brief アイテムが格納可能な数より多いかをチェックする
71 * 1 : Cannot be combined but there are empty spaces.
72 * @details オプション powerup_home が設定されていると我が家が 20 ページまで使える /
73 * Free space is always usable
75 static int check_free_space(StoreSaleType store_num)
77 if ((store_num == StoreSaleType::HOME) && !powerup_home) {
78 if (st_ptr->stock_num < ((st_ptr->stock_size) / 10)) {
81 } 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(ObjectType *o_ptr, StoreSaleType store_num)
106 if ((store_num == StoreSaleType::HOME) || (store_num == StoreSaleType::MUSEUM)) {
107 bool old_stack_force_notes = stack_force_notes;
108 bool old_stack_force_costs = stack_force_costs;
109 if (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)) {
120 if (store_num != StoreSaleType::HOME) {
121 stack_force_notes = old_stack_force_notes;
122 stack_force_costs = old_stack_force_costs;
128 if (store_num != StoreSaleType::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)) {
141 return check_free_space(store_num);
145 * @brief 店舗からアイテムを選択する /
146 * Get the ID of a store item and return its value -RAK-
147 * @param com_val 選択IDを返す参照ポインタ
148 * @param pmt メッセージキャプション
151 * @return 実際に選択したらTRUE、キャンセルしたらFALSE
153 int get_stock(COMMAND_CODE *com_val, concptr pmt, int i, int j, [[maybe_unused]] StoreSaleType store_num)
155 if (repeat_pull(com_val) && (*com_val >= i) && (*com_val <= j)) {
162 char hi = (j > 25) ? toupper(I2A(j - 26)) : I2A(j);
165 (void)sprintf(out_val, "(%s:%c-%c, ESCで中断) %s", (((store_num == StoreSaleType::HOME) || (store_num == StoreSaleType::MUSEUM)) ? "アイテム" : "商品"), lo, hi, pmt);
167 (void)sprintf(out_val, "(Items %c-%c, ESC to exit) %s", lo, hi, pmt);
172 if (!get_com(out_val, &command, false)) {
177 if (islower(command)) {
179 } else if (isupper(command)) {
180 k = A2I(tolower(command)) + 26;
185 if ((k >= i) && (k <= j)) {
194 if (command == ESCAPE) {
198 repeat_push(*com_val);
203 * @brief 店のアイテムを調べるコマンドのメインルーチン /
204 * Examine an item in a store -JDL-
206 void store_examine(PlayerType *player_ptr, StoreSaleType store_num)
208 if (st_ptr->stock_num <= 0) {
209 if (store_num == StoreSaleType::HOME) {
210 msg_print(_("我が家には何も置いてありません。", "Your home is empty."));
211 } else if (store_num == StoreSaleType::MUSEUM) {
212 msg_print(_("博物館には何も置いてありません。", "The Museum is empty."));
214 msg_print(_("現在商品の在庫を切らしています。", "I am currently out of stock."));
219 int i = (st_ptr->stock_num - store_top);
220 if (i > store_bottom) {
225 sprintf(out_val, _("どれを調べますか?", "Which item do you want to examine? "));
228 if (!get_stock(&item, out_val, 0, i - 1, store_num)) {
231 item = item + store_top;
233 o_ptr = &st_ptr->stock[item];
234 if (!o_ptr->is_fully_known()) {
235 msg_print(_("このアイテムについて特に知っていることはない。", "You have no special knowledge about that item."));
239 GAME_TEXT o_name[MAX_NLEN];
240 describe_flavor(player_ptr, o_name, o_ptr, 0);
241 msg_format(_("%sを調べている...", "Examining %s..."), o_name);
242 if (!screen_object(player_ptr, o_ptr, SCROBJ_FORCE_DETAIL)) {
243 msg_print(_("特に変わったところはないようだ。", "You see nothing special."));
248 * @brief 現在の町の店主を交代させる /
249 * Shuffle one of the stores.
250 * @param which 店舗種類のID
251 * @todo init_store()と処理を一部統合&ランダム選択を改善。
253 void store_shuffle(PlayerType *player_ptr, StoreSaleType store_num)
255 auto owner_num = owners.at(store_num).size();
256 if ((store_num == StoreSaleType::HOME) || (store_num == StoreSaleType::MUSEUM) || (owner_num <= (uint16_t)max_towns)) {
260 st_ptr = &town_info[player_ptr->town_num].store[enum2i(store_num)];
261 int j = st_ptr->owner;
263 st_ptr->owner = (byte)randint0(owner_num);
265 if (j == st_ptr->owner) {
270 for (i = 1; i < max_towns; i++) {
271 if (i == player_ptr->town_num) {
275 if (st_ptr->owner == town_info[i].store[enum2i(store_num)].owner) {
280 if (i == max_towns) {
285 ot_ptr = &owners.at(store_num)[st_ptr->owner];
286 st_ptr->insult_cur = 0;
287 st_ptr->store_open = 0;
288 st_ptr->good_buy = 0;
290 for (int i = 0; i < st_ptr->stock_num; i++) {
292 o_ptr = &st_ptr->stock[i];
293 if (o_ptr->is_artifact()) {
297 o_ptr->discount = 50;
298 o_ptr->inscription = quark_add(_("売出中", "on sale"));
303 * @brief 店の品揃えを変化させる /
304 * Maintain the inventory at the stores.
305 * @param player_ptr プレイヤーへの参照ポインタ
306 * @param town_num 町のID
307 * @param store_num 店舗種類のID
308 * @param chance 更新商品数
310 void store_maintenance(PlayerType *player_ptr, int town_num, StoreSaleType store_num, int chance)
312 if ((store_num == StoreSaleType::HOME) || (store_num == StoreSaleType::MUSEUM)) {
316 st_ptr = &town_info[town_num].store[enum2i(store_num)];
317 ot_ptr = &owners.at(store_num)[st_ptr->owner];
318 st_ptr->insult_cur = 0;
319 if (store_num == StoreSaleType::BLACK) {
320 for (INVENTORY_IDX j = st_ptr->stock_num - 1; j >= 0; j--) {
321 auto *o_ptr = &st_ptr->stock[j];
322 if (black_market_crap(player_ptr, o_ptr)) {
323 store_item_increase(j, 0 - o_ptr->number);
324 store_item_optimize(j);
329 INVENTORY_IDX j = st_ptr->stock_num;
330 int remain = STORE_TURNOVER + std::max(0, j - STORE_MAX_KEEP);
332 for (int i = 0; i < chance; i++) {
333 auto n = randint0(remain);
339 if (j > STORE_MAX_KEEP) {
342 if (j < STORE_MIN_KEEP) {
346 while (st_ptr->stock_num > j) {
350 remain = STORE_MAX_KEEP - st_ptr->stock_num;
352 for (int i = 0; i < chance; i++) {
353 auto n = randint0(remain);
358 j = st_ptr->stock_num + turn_over;
359 if (j > STORE_MAX_KEEP) {
362 if (j < STORE_MIN_KEEP) {
365 if (j >= st_ptr->stock_size) {
366 j = st_ptr->stock_size - 1;
369 for (size_t k = 0; k < st_ptr->regular.size(); k++) {
370 store_create(player_ptr, st_ptr->regular[k], black_market_crap, store_will_buy, mass_produce, store_num);
371 if (st_ptr->stock_num >= STORE_MAX_KEEP) {
376 while (st_ptr->stock_num < j) {
377 store_create(player_ptr, 0, black_market_crap, store_will_buy, mass_produce, store_num);
382 * @brief 店舗情報を初期化する /
383 * Initialize the stores
384 * @param town_num 町のID
385 * @param store_num 店舗種類のID
387 void store_init(int town_num, StoreSaleType store_num)
389 auto owner_num = owners.at(store_num).size();
390 st_ptr = &town_info[town_num].store[enum2i(store_num)];
392 st_ptr->owner = (byte)randint0(owner_num);
394 if (owner_num <= (uint16_t)max_towns) {
400 for (i = 1; i < (uint16_t)max_towns; i++) {
404 if (st_ptr->owner == town_info[i].store[enum2i(store_num)].owner) {
409 if (i == max_towns) {
414 ot_ptr = &owners.at(store_num)[st_ptr->owner];
415 st_ptr->store_open = 0;
416 st_ptr->insult_cur = 0;
417 st_ptr->good_buy = 0;
419 st_ptr->stock_num = 0;
420 st_ptr->last_visit = -10L * TURNS_PER_TICK * STORE_TICKS;
421 for (int k = 0; k < st_ptr->stock_size; k++) {
422 (&st_ptr->stock[k])->wipe();