2 * @brief 店の処理 / Store commands
7 #include "store/store.h"
8 #include "core/asking-player.h"
9 #include "flavor/flavor-describer.h"
10 #include "floor/floor-town.h"
11 #include "game-option/birth-options.h"
12 #include "game-option/game-play-options.h"
13 #include "io/command-repeater.h"
14 #include "locale/japanese.h"
15 #include "main/sound-of-music.h"
16 #include "object-enchant/item-apply-magic.h"
17 #include "object-enchant/item-magic-applier.h"
18 #include "object-enchant/special-object-flags.h"
19 #include "object/object-stack.h"
20 #include "object/object-value.h"
21 #include "object/tval-types.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 "sv-definition/sv-lite-types.h"
29 #include "sv-definition/sv-scroll-types.h"
30 #include "system/item-entity.h"
31 #include "system/player-type-definition.h"
32 #include "term/screen-processor.h"
33 #include "util/int-char-converter.h"
34 #include "util/quarks.h"
35 #include "view/display-messages.h"
36 #include "world/world-object.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(StoreSaleType store_num)
79 if ((store_num == StoreSaleType::HOME) && !powerup_home) {
80 if (st_ptr->stock_num < ((st_ptr->stock_size) / 10)) {
83 } else if (st_ptr->stock_num < st_ptr->stock_size) {
91 * @brief 店舗に品を置くスペースがあるかどうかの判定を返す /
92 * Check to see if the shop will be carrying too many objects -RAK-
93 * @param o_ptr 店舗に置きたいオブジェクト構造体の参照ポインタ
94 * @return 置き場がないなら0、重ね合わせできるアイテムがあるなら-1、スペースがあるなら1を返す。
97 * Note that the shop, just like a player, will not accept things
98 * it cannot hold. Before, one could "nuke" potions this way.
99 * Return value is now int:
101 * -1 : Can be combined to existing slot.
102 * 1 : Cannot be combined but there are empty spaces.
105 int store_check_num(ItemEntity *o_ptr, StoreSaleType store_num)
108 if ((store_num == StoreSaleType::HOME) || (store_num == StoreSaleType::MUSEUM)) {
109 bool old_stack_force_notes = stack_force_notes;
110 bool old_stack_force_costs = stack_force_costs;
111 if (store_num != StoreSaleType::HOME) {
112 stack_force_notes = false;
113 stack_force_costs = false;
116 for (int i = 0; i < st_ptr->stock_num; i++) {
117 j_ptr = &st_ptr->stock[i];
118 if (!object_similar(j_ptr, o_ptr)) {
122 if (store_num != StoreSaleType::HOME) {
123 stack_force_notes = old_stack_force_notes;
124 stack_force_costs = old_stack_force_costs;
130 if (store_num != StoreSaleType::HOME) {
131 stack_force_notes = old_stack_force_notes;
132 stack_force_costs = old_stack_force_costs;
135 for (int i = 0; i < st_ptr->stock_num; i++) {
136 j_ptr = &st_ptr->stock[i];
137 if (store_object_similar(j_ptr, o_ptr)) {
143 return check_free_space(store_num);
147 * @brief 店舗からアイテムを選択する /
148 * Get the ID of a store item and return its value -RAK-
149 * @param com_val 選択IDを返す参照ポインタ
150 * @param pmt メッセージキャプション
153 * @return 実際に選択したらTRUE、キャンセルしたらFALSE
155 int get_stock(COMMAND_CODE *com_val, concptr pmt, int i, int j, [[maybe_unused]] StoreSaleType store_num)
157 if (repeat_pull(com_val) && (*com_val >= i) && (*com_val <= j)) {
164 char hi = (j > 25) ? toupper(I2A(j - 26)) : I2A(j);
167 (void)sprintf(out_val, "(%s:%c-%c, ESCで中断) %s", (((store_num == StoreSaleType::HOME) || (store_num == StoreSaleType::MUSEUM)) ? "アイテム" : "商品"), lo, hi, pmt);
169 (void)sprintf(out_val, "(Items %c-%c, ESC to exit) %s", lo, hi, pmt);
174 if (!get_com(out_val, &command, false)) {
179 if (islower(command)) {
181 } else if (isupper(command)) {
182 k = A2I(tolower(command)) + 26;
187 if ((k >= i) && (k <= j)) {
196 if (command == ESCAPE) {
200 repeat_push(*com_val);
205 * @brief 店のアイテムを調べるコマンドのメインルーチン /
206 * Examine an item in a store -JDL-
208 void store_examine(PlayerType *player_ptr, StoreSaleType store_num)
210 if (st_ptr->stock_num <= 0) {
211 if (store_num == StoreSaleType::HOME) {
212 msg_print(_("我が家には何も置いてありません。", "Your home is empty."));
213 } else if (store_num == StoreSaleType::MUSEUM) {
214 msg_print(_("博物館には何も置いてありません。", "The Museum is empty."));
216 msg_print(_("現在商品の在庫を切らしています。", "I am currently out of stock."));
221 int i = (st_ptr->stock_num - store_top);
222 if (i > store_bottom) {
227 sprintf(out_val, _("どれを調べますか?", "Which item do you want to examine? "));
230 if (!get_stock(&item, out_val, 0, i - 1, store_num)) {
233 item = item + store_top;
235 o_ptr = &st_ptr->stock[item];
236 if (!o_ptr->is_fully_known()) {
237 msg_print(_("このアイテムについて特に知っていることはない。", "You have no special knowledge about that item."));
241 GAME_TEXT o_name[MAX_NLEN];
242 describe_flavor(player_ptr, o_name, o_ptr, 0);
243 msg_format(_("%sを調べている...", "Examining %s..."), o_name);
244 if (!screen_object(player_ptr, o_ptr, SCROBJ_FORCE_DETAIL)) {
245 msg_print(_("特に変わったところはないようだ。", "You see nothing special."));
250 * @brief 現在の町の店主を交代させる /
251 * Shuffle one of the stores.
252 * @param which 店舗種類のID
253 * @todo init_store()と処理を一部統合&ランダム選択を改善。
255 void store_shuffle(PlayerType *player_ptr, StoreSaleType store_num)
257 auto owner_num = owners.at(store_num).size();
258 if ((store_num == StoreSaleType::HOME) || (store_num == StoreSaleType::MUSEUM) || (owner_num <= (uint16_t)max_towns)) {
262 st_ptr = &town_info[player_ptr->town_num].store[enum2i(store_num)];
263 int j = st_ptr->owner;
265 st_ptr->owner = (byte)randint0(owner_num);
267 if (j == st_ptr->owner) {
272 for (i = 1; i < max_towns; i++) {
273 if (i == player_ptr->town_num) {
277 if (st_ptr->owner == town_info[i].store[enum2i(store_num)].owner) {
282 if (i == max_towns) {
287 ot_ptr = &owners.at(store_num)[st_ptr->owner];
288 st_ptr->insult_cur = 0;
289 st_ptr->store_open = 0;
290 st_ptr->good_buy = 0;
292 for (int i = 0; i < st_ptr->stock_num; i++) {
294 o_ptr = &st_ptr->stock[i];
295 if (o_ptr->is_artifact()) {
299 o_ptr->discount = 50;
300 o_ptr->inscription = quark_add(_("売出中", "on sale"));
305 * @brief 店舗の品揃え変化のためにアイテムを追加する /
306 * Creates a random item and gives it to a store
307 * @param player_ptr プレイヤーへの参照ポインタ
310 * This algorithm needs to be rethought. A lot.
311 * Currently, "normal" stores use a pre-built array.
312 * Note -- the "level" given to "obj_get_num()" is a "favored"
313 * level, that is, there is a much higher chance of getting
314 * items with a level approaching that of the given level...
315 * Should we check for "permission" to have the given item?
318 static void store_create(PlayerType *player_ptr, short fix_k_idx, StoreSaleType store_num)
320 if (st_ptr->stock_num >= st_ptr->stock_size) {
324 const owner_type *ow_ptr = &owners.at(store_num)[st_ptr->owner];
326 for (int tries = 0; tries < 4; tries++) {
329 if (store_num == StoreSaleType::BLACK) {
330 level = 25 + randint0(25);
331 bi_id = get_obj_index(player_ptr, level, 0x00000000);
335 } else if (fix_k_idx > 0) {
337 level = rand_range(1, ow_ptr->level);
339 bi_id = st_ptr->table[randint0(st_ptr->table.size())];
340 level = rand_range(1, ow_ptr->level);
347 ItemMagicApplier(player_ptr, q_ptr, level, AM_NO_FIXED_ART).execute();
348 if (!store_will_buy(player_ptr, q_ptr, store_num)) {
352 auto pvals = store_same_magic_device_pvals(q_ptr);
353 if (pvals.size() >= 2) {
354 auto pval = pvals.at(randint0(pvals.size()));
358 const auto tval = q_ptr->bi_key.tval();
359 const auto sval = q_ptr->bi_key.sval();
360 if (tval == ItemKindType::LITE) {
361 if (sval == SV_LITE_TORCH) {
362 q_ptr->fuel = FUEL_TORCH / 2;
365 if (sval == SV_LITE_LANTERN) {
366 q_ptr->fuel = FUEL_LAMP / 2;
371 q_ptr->ident |= IDENT_STORE;
372 if (tval == ItemKindType::CHEST) {
376 if (store_num == StoreSaleType::BLACK) {
377 if (black_market_crap(player_ptr, q_ptr) || (q_ptr->get_price() < 10)) {
381 if (q_ptr->get_price() <= 0) {
386 mass_produce(q_ptr, store_num);
387 (void)store_carry(q_ptr);
393 * @brief 店の品揃えを変化させる /
394 * Maintain the inventory at the stores.
395 * @param player_ptr プレイヤーへの参照ポインタ
396 * @param town_num 町のID
397 * @param store_num 店舗種類のID
398 * @param chance 更新商品数
400 void store_maintenance(PlayerType *player_ptr, int town_num, StoreSaleType store_num, int chance)
402 if ((store_num == StoreSaleType::HOME) || (store_num == StoreSaleType::MUSEUM)) {
406 st_ptr = &town_info[town_num].store[enum2i(store_num)];
407 ot_ptr = &owners.at(store_num)[st_ptr->owner];
408 st_ptr->insult_cur = 0;
409 if (store_num == StoreSaleType::BLACK) {
410 for (INVENTORY_IDX j = st_ptr->stock_num - 1; j >= 0; j--) {
411 auto *o_ptr = &st_ptr->stock[j];
412 if (black_market_crap(player_ptr, o_ptr)) {
413 store_item_increase(j, 0 - o_ptr->number);
414 store_item_optimize(j);
419 INVENTORY_IDX j = st_ptr->stock_num;
420 int remain = STORE_TURNOVER + std::max(0, j - STORE_MAX_KEEP);
422 for (int i = 0; i < chance; i++) {
423 auto n = randint0(remain);
429 if (j > STORE_MAX_KEEP) {
432 if (j < STORE_MIN_KEEP) {
436 while (st_ptr->stock_num > j) {
440 remain = STORE_MAX_KEEP - st_ptr->stock_num;
442 for (int i = 0; i < chance; i++) {
443 auto n = randint0(remain);
448 j = st_ptr->stock_num + turn_over;
449 if (j > STORE_MAX_KEEP) {
452 if (j < STORE_MIN_KEEP) {
455 if (j >= st_ptr->stock_size) {
456 j = st_ptr->stock_size - 1;
459 for (size_t k = 0; k < st_ptr->regular.size(); k++) {
460 store_create(player_ptr, st_ptr->regular[k], store_num);
461 if (st_ptr->stock_num >= STORE_MAX_KEEP) {
466 while (st_ptr->stock_num < j) {
467 store_create(player_ptr, 0, store_num);
472 * @brief 店舗情報を初期化する /
473 * Initialize the stores
474 * @param town_num 町のID
475 * @param store_num 店舗種類のID
477 void store_init(int town_num, StoreSaleType store_num)
479 auto owner_num = owners.at(store_num).size();
480 st_ptr = &town_info[town_num].store[enum2i(store_num)];
482 st_ptr->owner = (byte)randint0(owner_num);
484 if (owner_num <= (uint16_t)max_towns) {
490 for (i = 1; i < (uint16_t)max_towns; i++) {
494 if (st_ptr->owner == town_info[i].store[enum2i(store_num)].owner) {
499 if (i == max_towns) {
504 ot_ptr = &owners.at(store_num)[st_ptr->owner];
505 st_ptr->store_open = 0;
506 st_ptr->insult_cur = 0;
507 st_ptr->good_buy = 0;
509 st_ptr->stock_num = 0;
510 st_ptr->last_visit = -10L * TURNS_PER_TICK * STORE_TICKS;
511 for (int k = 0; k < st_ptr->stock_size; k++) {
512 (&st_ptr->stock[k])->wipe();