OSDN Git Service

[Refactor] #3312 StatusRedrawingFlag をStatusRecalculatingFlag に改名した
[hengbandforosx/hengbandosx.git] / src / wizard / wizard-item-modifier.cpp
1 #include "wizard/wizard-item-modifier.h"
2 #include "artifact/fixed-art-generator.h"
3 #include "artifact/fixed-art-types.h"
4 #include "artifact/random-art-effects.h"
5 #include "artifact/random-art-generator.h"
6 #include "core/asking-player.h"
7 #include "core/show-file.h"
8 #include "core/stuff-handler.h"
9 #include "core/window-redrawer.h"
10 #include "flavor/flavor-describer.h"
11 #include "flavor/object-flavor-types.h"
12 #include "floor/floor-object.h"
13 #include "game-option/cheat-options.h"
14 #include "inventory/inventory-slot-types.h"
15 #include "io/input-key-acceptor.h"
16 #include "io/input-key-requester.h"
17 #include "object-enchant/item-apply-magic.h"
18 #include "object-enchant/item-magic-applier.h"
19 #include "object-enchant/object-ego.h"
20 #include "object-enchant/special-object-flags.h"
21 #include "object-enchant/tr-types.h"
22 #include "object/item-use-flags.h"
23 #include "object/object-flags.h"
24 #include "object/object-info.h"
25 #include "object/object-kind-hook.h"
26 #include "object/object-mark-types.h"
27 #include "object/object-value.h"
28 #include "spell-kind/spells-perception.h"
29 #include "spell/spells-object.h"
30 #include "system/alloc-entries.h"
31 #include "system/artifact-type-definition.h"
32 #include "system/baseitem-info.h"
33 #include "system/floor-type-definition.h"
34 #include "system/item-entity.h"
35 #include "system/player-type-definition.h"
36 #include "system/redrawing-flags-updater.h"
37 #include "system/system-variables.h"
38 #include "term/screen-processor.h"
39 #include "term/term-color-types.h"
40 #include "term/z-form.h"
41 #include "util/bit-flags-calculator.h"
42 #include "util/int-char-converter.h"
43 #include "util/string-processor.h"
44 #include "view/display-messages.h"
45 #include "wizard/wizard-special-process.h"
46 #include "world/world.h"
47 #include <algorithm>
48 #include <limits>
49 #include <sstream>
50 #include <tuple>
51 #include <vector>
52
53 constexpr auto BASEITEM_MAX_DEPTH = 110; /*!< アイテムの階層毎生成率を表示する最大階 */
54
55 namespace {
56 /*!
57  * @brief アイテム設定コマンド一覧表
58  */
59 constexpr std::array wizard_sub_menu_table = {
60     std::make_tuple('a', _("アーティファクト出現フラグリセット", "Restore aware flag of fixed artifact")),
61     std::make_tuple('A', _("アーティファクトを出現済みにする", "Make a fixed artifact awared")),
62     std::make_tuple('e', _("高級品獲得ドロップ", "Drop excellent object")),
63     std::make_tuple('f', _("*鑑定*", "*Idenfity*")),
64     std::make_tuple('i', _("鑑定", "Idenfity")),
65     std::make_tuple('I', _("インベントリ全*鑑定*", "Idenfity all objects fully in inventory")),
66     std::make_tuple('l', _("指定アイテム番号まで一括鑑定", "Make objects awared to target object id")),
67     std::make_tuple('g', _("上質なアイテムドロップ", "Drop good object")),
68     std::make_tuple('s', _("特別品獲得ドロップ", "Drop special object")),
69     std::make_tuple('w', _("願い", "Wishing")),
70     std::make_tuple('U', _("発動を変更する", "Modify item activation")),
71 };
72
73 /*!
74  * @brief ゲーム設定コマンドの一覧を表示する
75  */
76 void display_wizard_sub_menu()
77 {
78     for (auto y = 1U; y <= wizard_sub_menu_table.size(); y++) {
79         term_erase(14, y, 64);
80     }
81
82     int r = 1;
83     int c = 15;
84     for (const auto &[symbol, desc] : wizard_sub_menu_table) {
85         std::stringstream ss;
86         ss << symbol << ") " << desc;
87         put_str(ss.str(), r++, c);
88     }
89 }
90 }
91
92 /*!
93  * @brief キャスト先の型の最小値、最大値でclampする。
94  */
95 template <typename T>
96 T clamp_cast(int val)
97 {
98     return static_cast<T>(std::clamp(val,
99         static_cast<int>(std::numeric_limits<T>::min()),
100         static_cast<int>(std::numeric_limits<T>::max())));
101 }
102
103 void wiz_restore_aware_flag_of_fixed_arfifact(FixedArtifactId reset_artifact_idx, bool aware = false);
104 void wiz_modify_item_activation(PlayerType *player_ptr);
105 void wiz_identify_full_inventory(PlayerType *player_ptr);
106
107 /*!
108  * @brief ゲーム設定コマンドの入力を受け付ける
109  * @param player_ptr プレイヤーの情報へのポインタ
110  */
111 void wizard_item_modifier(PlayerType *player_ptr)
112 {
113     screen_save();
114     display_wizard_sub_menu();
115
116     char cmd;
117     get_com("Player Command: ", &cmd, false);
118     screen_load();
119
120     switch (cmd) {
121     case ESCAPE:
122     case ' ':
123     case '\n':
124     case '\r':
125         break;
126     case 'a':
127         wiz_restore_aware_flag_of_fixed_arfifact(i2enum<FixedArtifactId>(command_arg));
128         break;
129     case 'A':
130         wiz_restore_aware_flag_of_fixed_arfifact(i2enum<FixedArtifactId>(command_arg), true);
131         break;
132     case 'e':
133         if (command_arg <= 0) {
134             command_arg = 1;
135         }
136
137         acquirement(player_ptr, player_ptr->y, player_ptr->x, command_arg, true, false, true);
138         break;
139     case 'f':
140         identify_fully(player_ptr, false);
141         break;
142     case 'g':
143         if (command_arg <= 0) {
144             command_arg = 1;
145         }
146
147         acquirement(player_ptr, player_ptr->y, player_ptr->x, command_arg, false, false, true);
148         break;
149     case 'i':
150         (void)ident_spell(player_ptr, false);
151         break;
152     case 'I':
153         wiz_identify_full_inventory(player_ptr);
154         break;
155     case 'l':
156         wiz_learn_items_all(player_ptr);
157         break;
158     case 's':
159         if (command_arg <= 0) {
160             command_arg = 1;
161         }
162
163         acquirement(player_ptr, player_ptr->y, player_ptr->x, command_arg, true, true, true);
164         break;
165     case 'U':
166         wiz_modify_item_activation(player_ptr);
167         break;
168     case 'w':
169         do_cmd_wishing(player_ptr, -1, true, true, true);
170         break;
171     }
172 }
173
174 /*!
175  * @brief 固定アーティファクトの出現フラグをリセットする
176  * @param a_idx 指定したアーティファクトID
177  * @details 外からはenum class を受け取るが、この関数内では数値の直指定処理なので数値型にキャストする.
178  */
179 void wiz_restore_aware_flag_of_fixed_arfifact(FixedArtifactId reset_artifact_idx, bool aware)
180 {
181     auto max_a_idx = enum2i(artifacts_info.rbegin()->first);
182     int int_a_idx = enum2i(reset_artifact_idx);
183     if (int_a_idx <= 0) {
184         if (!get_value("Artifact ID", 1, max_a_idx, &int_a_idx)) {
185             return;
186         }
187     }
188
189     ArtifactsInfo::get_instance().get_artifact(i2enum<FixedArtifactId>(int_a_idx)).is_generated = aware;
190     msg_print(aware ? "Modified." : "Restored.");
191 }
192
193 /*!
194  * @brief オブジェクトに発動を追加する/変更する
195  * @param catser_ptr プレイヤー情報への参照ポインタ
196  */
197 void wiz_modify_item_activation(PlayerType *player_ptr)
198 {
199     constexpr auto q = _("どのアイテムの発動を変更しますか? ", "Which object? ");
200     constexpr auto s = _("発動を変更するアイテムがない。", "Nothing to do with.");
201     short item;
202     auto *o_ptr = choose_object(player_ptr, &item, q, s, USE_EQUIP | USE_INVEN | USE_FLOOR | IGNORE_BOTHHAND_SLOT);
203     if (!o_ptr) {
204         return;
205     }
206
207     int val;
208     if (!get_value("Activation ID", enum2i(RandomArtActType::NONE), enum2i(RandomArtActType::MAX) - 1, &val)) {
209         return;
210     }
211
212     auto act_idx = i2enum<RandomArtActType>(val);
213     o_ptr->art_flags.set(TR_ACTIVATE);
214     o_ptr->activation_id = act_idx;
215 }
216
217 /*!
218  * @brief インベントリ内のアイテムを全て*鑑定*済みにする
219  * @param catser_ptr プレイヤー情報への参照ポインタ
220  */
221 void wiz_identify_full_inventory(PlayerType *player_ptr)
222 {
223     for (int i = 0; i < INVEN_TOTAL; i++) {
224         auto *o_ptr = &player_ptr->inventory_list[i];
225         if (!o_ptr->is_valid()) {
226             continue;
227         }
228
229         auto &baseitem = o_ptr->get_baseitem();
230         baseitem.aware = true; //!< @note 記録には残さないためTRUEを立てるのみ
231         set_bits(o_ptr->ident, IDENT_KNOWN | IDENT_FULL_KNOWN);
232         o_ptr->marked.set(OmType::TOUCHED);
233     }
234
235     auto &rfu = RedrawingFlagsUpdater::get_instance();
236     static constexpr auto flags_srf = {
237         StatusRecalculatingFlag::COMBINATION,
238         StatusRecalculatingFlag::REORDER,
239     };
240     rfu.set_flags(flags_srf);
241     static constexpr auto flags_swrf = {
242         SubWindowRedrawingFlag::INVENTORY,
243         SubWindowRedrawingFlag::EQUIPMENT,
244     };
245     rfu.set_flags(flags_swrf);
246 }
247
248 /*!
249  * @brief アイテムの階層毎生成率を表示する / Output a rarity graph for a type of object.
250  * @param tval ベースアイテムの大項目ID
251  * @param sval ベースアイテムの小項目ID
252  * @param row 表示列
253  * @param col 表示行
254  */
255 static void prt_alloc(const BaseitemKey &bi_key, TERM_LEN row, TERM_LEN col)
256 {
257     uint32_t rarity[BASEITEM_MAX_DEPTH]{};
258     uint32_t total[BASEITEM_MAX_DEPTH]{};
259     int32_t display[22]{};
260
261     auto home = 0;
262     for (auto i = 0; i < BASEITEM_MAX_DEPTH; i++) {
263         auto total_frac = 0;
264         constexpr auto magnificant = CHANCE_BASEITEM_LEVEL_BOOST * BASEITEM_MAX_DEPTH;
265         for (const auto &entry : alloc_kind_table) {
266             auto prob = 0;
267             if (entry.level <= i) {
268                 prob = entry.prob1 * magnificant;
269             } else if (entry.level - 1 > 0) {
270                 prob = entry.prob1 * i * BASEITEM_MAX_DEPTH / (entry.level - 1);
271             }
272
273             const auto &baseitem = entry.get_baseitem();
274             total[i] += prob / magnificant;
275             total_frac += prob % magnificant;
276
277             if (baseitem.bi_key == bi_key) {
278                 home = baseitem.level;
279                 rarity[i] += prob / magnificant;
280             }
281         }
282
283         total[i] += total_frac / magnificant;
284     }
285
286     for (auto i = 0; i < 22; i++) {
287         auto possibility = 0;
288         for (int j = i * BASEITEM_MAX_DEPTH / 22; j < (i + 1) * BASEITEM_MAX_DEPTH / 22; j++) {
289             possibility += rarity[j] * 100000 / total[j];
290         }
291
292         display[i] = possibility / 5;
293     }
294
295     for (auto i = 0; i < 22; i++) {
296         term_putch(col, row + i + 1, TERM_WHITE, '|');
297         prt(format("%2dF", (i * 5)), row + i + 1, col);
298         if ((i * BASEITEM_MAX_DEPTH / 22 <= home) && (home < (i + 1) * BASEITEM_MAX_DEPTH / 22)) {
299             c_prt(TERM_RED, format("%3d.%04d%%", display[i] / 1000, display[i] % 1000), row + i + 1, col + 3);
300         } else {
301             c_prt(TERM_WHITE, format("%3d.%04d%%", display[i] / 1000, display[i] % 1000), row + i + 1, col + 3);
302         }
303     }
304
305     concptr r = "+---Rate---+";
306     prt(r, row, col);
307 }
308
309 /*!
310  * @brief 32ビット変数のビット配列を並べて描画する / Output a long int in binary format.
311  */
312 static void prt_binary(BIT_FLAGS flags, const int row, int col)
313 {
314     uint32_t bitmask;
315     for (int i = bitmask = 1; i <= 32; i++, bitmask *= 2) {
316         if (flags & bitmask) {
317             term_putch(col++, row, TERM_BLUE, '*');
318         } else {
319             term_putch(col++, row, TERM_WHITE, '-');
320         }
321     }
322 }
323
324 /*!
325  * @brief アイテムの詳細ステータスを表示する /
326  * Change various "permanent" player variables.
327  * @param player_ptr プレイヤーへの参照ポインタ
328  * @param o_ptr 詳細を表示するアイテム情報の参照ポインタ
329  */
330 static void wiz_display_item(PlayerType *player_ptr, ItemEntity *o_ptr)
331 {
332     auto flags = object_flags(o_ptr);
333     auto get_seq_32bits = [](const TrFlags &flags, uint start) {
334         BIT_FLAGS result = 0U;
335         for (auto i = 0U; i < 32 && start + i < flags.size(); i++) {
336             if (flags.has(i2enum<tr_type>(start + i))) {
337                 result |= 1U << i;
338             }
339         }
340         return result;
341     };
342     int j = 13;
343     for (int i = 1; i <= 23; i++) {
344         prt("", i, j - 2);
345     }
346
347     prt_alloc(o_ptr->bi_key, 1, 0);
348     const auto item_name = describe_flavor(player_ptr, o_ptr, OD_STORE);
349     prt(item_name, 2, j);
350
351     auto line = 4;
352     const auto &bi_key = o_ptr->bi_key;
353     const auto item_level = o_ptr->get_baseitem().level;
354     prt(format("kind = %-5d  level = %-4d  tval = %-5d  sval = %-5d", o_ptr->bi_id, item_level, enum2i(bi_key.tval()), bi_key.sval().value()), line, j);
355     prt(format("number = %-3d  wgt = %-6d  ac = %-5d    damage = %dd%d", o_ptr->number, o_ptr->weight, o_ptr->ac, o_ptr->dd, o_ptr->ds), ++line, j);
356     prt(format("pval = %-5d  toac = %-5d  tohit = %-4d  todam = %-4d", o_ptr->pval, o_ptr->to_a, o_ptr->to_h, o_ptr->to_d), ++line, j);
357     prt(format("fixed_artifact_idx = %-4d  ego_idx = %-4d  cost = %d", enum2i(o_ptr->fixed_artifact_idx), enum2i(o_ptr->ego_idx), object_value_real(o_ptr)), ++line, j);
358     prt(format("ident = %04x  activation_id = %-4d  timeout = %-d", o_ptr->ident, enum2i(o_ptr->activation_id), o_ptr->timeout), ++line, j);
359     prt(format("chest_level = %-4d  fuel = %-d", o_ptr->chest_level, o_ptr->fuel), ++line, j);
360     prt(format("smith_hit = %-4d  smith_damage = %-4d", o_ptr->smith_hit, o_ptr->smith_damage), ++line, j);
361     prt(format("cursed  = %-4lX  captured_monster_speed = %-4d", o_ptr->curse_flags.to_ulong(), o_ptr->captured_monster_speed), ++line, j);
362     prt(format("captured_monster_max_hp = %-4d  captured_monster_max_hp = %-4d", o_ptr->captured_monster_current_hp, o_ptr->captured_monster_max_hp), ++line, j);
363
364     prt("+------------FLAGS1------------+", ++line, j);
365     prt("AFFECT........SLAY........BRAND.", ++line, j);
366     prt("      mf      cvae      xsqpaefc", ++line, j);
367     prt("siwdccsossidsahanvudotgddhuoclio", ++line, j);
368     prt("tnieohtctrnipttmiinmrrnrrraiierl", ++line, j);
369     prt("rtsxnarelcfgdkcpmldncltggpksdced", ++line, j);
370     prt_binary(get_seq_32bits(flags, 32 * 0), ++line, j);
371
372     prt("+------------FLAGS2------------+", ++line, j);
373     prt("SUST....IMMUN.RESIST............", ++line, j);
374     prt("      reaefctrpsaefcpfldbc sn   ", ++line, j);
375     prt("siwdcciaclioheatcliooeialoshtncd", ++line, j);
376     prt("tnieohdsierlrfraierliatrnnnrhehi", ++line, j);
377     prt("rtsxnaeydcedwlatdcedsrekdfddrxss", ++line, j);
378     prt_binary(get_seq_32bits(flags, 32 * 1), ++line, j);
379
380     line = 13;
381     prt("+------------FLAGS3------------+", line, j + 32);
382     prt("fe cnn t      stdrmsiiii d ab   ", ++line, j + 32);
383     prt("aa aoomywhs lleeieihgggg rtgl   ", ++line, j + 32);
384     prt("uu utmacaih eielgggonnnnaaere   ", ++line, j + 32);
385     prt("rr reanurdo vtieeehtrrrrcilas   ", ++line, j + 32);
386     prt("aa algarnew ienpsntsaefctnevs   ", ++line, j + 32);
387     prt_binary(get_seq_32bits(flags, 32 * 2), ++line, j + 32);
388
389     prt("+------------FLAGS4------------+", ++line, j + 32);
390     prt("KILL....ESP.........            ", ++line, j + 32);
391     prt("aeud tghaud tgdhegnu            ", ++line, j + 32);
392     prt("nvneoriunneoriruvoon            ", ++line, j + 32);
393     prt("iidmroamidmroagmionq            ", ++line, j + 32);
394     prt("mlenclnmmenclnnnldlu            ", ++line, j + 32);
395     prt_binary(get_seq_32bits(flags, 32 * 3), ++line, j + 32);
396 }
397
398 /*!
399  * @brief 検査対象のアイテムを基準とした生成テストを行う /
400  * Try to create an item again. Output some statistics.    -Bernd-
401  * @param player_ptr プレイヤーへの参照ポインタ
402  * @param o_ptr 生成テストの基準となるアイテム情報の参照ポインタ
403  * The statistics are correct now.  We acquire a clean grid, and then
404  * repeatedly place an object in this grid, copying it into an item
405  * holder, and then deleting the object.  We fiddle with the artifact
406  * counter flags to prevent weirdness.  We use the items to collect
407  * statistics on item creation relative to the initial item.
408  */
409 static void wiz_statistics(PlayerType *player_ptr, ItemEntity *o_ptr)
410 {
411     concptr q = "Rolls: %ld  Correct: %ld  Matches: %ld  Better: %ld  Worse: %ld  Other: %ld";
412     concptr p = "Enter number of items to roll: ";
413     char tmp_val[80];
414
415     if (o_ptr->is_fixed_artifact()) {
416         o_ptr->get_fixed_artifact().is_generated = false;
417     }
418
419     uint32_t i, matches, better, worse, other, correct;
420     uint32_t test_roll = 1000000;
421     char ch;
422     concptr quality;
423     BIT_FLAGS mode;
424     while (true) {
425         concptr pmt = "Roll for [n]ormal, [g]ood, or [e]xcellent treasure? ";
426         wiz_display_item(player_ptr, o_ptr);
427         if (!get_com(pmt, &ch, false)) {
428             break;
429         }
430
431         if (ch == 'n' || ch == 'N') {
432             mode = 0L;
433             quality = "normal";
434         } else if (ch == 'g' || ch == 'G') {
435             mode = AM_GOOD;
436             quality = "good";
437         } else if (ch == 'e' || ch == 'E') {
438             mode = AM_GOOD | AM_GREAT;
439             quality = "excellent";
440         } else {
441             break;
442         }
443
444         strnfmt(tmp_val, sizeof(tmp_val), "%ld", (long int)test_roll);
445         if (get_string(p, tmp_val, 10)) {
446             test_roll = atol(tmp_val);
447         }
448         test_roll = std::max<uint>(1, test_roll);
449         msg_format("Creating a lot of %s items. Base level = %d.", quality, player_ptr->current_floor_ptr->dun_level);
450         msg_print(nullptr);
451
452         correct = matches = better = worse = other = 0;
453         for (i = 0; i <= test_roll; i++) {
454             if ((i < 100) || (i % 100 == 0)) {
455                 inkey_scan = true;
456                 if (inkey()) {
457                     flush();
458                     break; // stop rolling
459                 }
460
461                 prt(format(q, i, correct, matches, better, worse, other), 0, 0);
462                 term_fresh();
463             }
464
465             ItemEntity forge;
466             auto *q_ptr = &forge;
467             q_ptr->wipe();
468             make_object(player_ptr, q_ptr, mode);
469             if (q_ptr->is_fixed_artifact()) {
470                 q_ptr->get_fixed_artifact().is_generated = false;
471             }
472
473             if (o_ptr->bi_key != q_ptr->bi_key) {
474                 continue;
475             }
476
477             correct++;
478             const auto is_same_fixed_artifact_idx = o_ptr->is_specific_artifact(q_ptr->fixed_artifact_idx);
479             if ((q_ptr->pval == o_ptr->pval) && (q_ptr->to_a == o_ptr->to_a) && (q_ptr->to_h == o_ptr->to_h) && (q_ptr->to_d == o_ptr->to_d) && is_same_fixed_artifact_idx) {
480                 matches++;
481             } else if ((q_ptr->pval >= o_ptr->pval) && (q_ptr->to_a >= o_ptr->to_a) && (q_ptr->to_h >= o_ptr->to_h) && (q_ptr->to_d >= o_ptr->to_d)) {
482                 better++;
483             } else if ((q_ptr->pval <= o_ptr->pval) && (q_ptr->to_a <= o_ptr->to_a) && (q_ptr->to_h <= o_ptr->to_h) && (q_ptr->to_d <= o_ptr->to_d)) {
484                 worse++;
485             } else {
486                 other++;
487             }
488         }
489
490         msg_format(q, i, correct, matches, better, worse, other);
491         msg_print(nullptr);
492     }
493
494     if (o_ptr->is_fixed_artifact()) {
495         o_ptr->get_fixed_artifact().is_generated = true;
496     }
497 }
498
499 /*!
500  * @brief アイテムの質を選択して再生成する /
501  * Apply magic to an item or turn it into an artifact. -Bernd-
502  * @param o_ptr 再生成の対象となるアイテム情報の参照ポインタ
503  */
504 static void wiz_reroll_item(PlayerType *player_ptr, ItemEntity *o_ptr)
505 {
506     if (o_ptr->is_fixed_or_random_artifact()) {
507         return;
508     }
509
510     ItemEntity forge;
511     ItemEntity *q_ptr;
512     q_ptr = &forge;
513     q_ptr->copy_from(o_ptr);
514
515     char ch;
516     bool changed = false;
517     while (true) {
518         wiz_display_item(player_ptr, q_ptr);
519         if (!get_com("[a]ccept, [w]orthless, [c]ursed, [n]ormal, [g]ood, [e]xcellent, [s]pecial? ", &ch, false)) {
520             if (q_ptr->is_fixed_artifact()) {
521                 q_ptr->get_fixed_artifact().is_generated = false;
522                 q_ptr->fixed_artifact_idx = FixedArtifactId::NONE;
523             }
524
525             changed = false;
526             break;
527         }
528
529         if (ch == 'A' || ch == 'a') {
530             changed = true;
531             break;
532         }
533
534         if (q_ptr->is_fixed_artifact()) {
535             q_ptr->get_fixed_artifact().is_generated = false;
536             q_ptr->fixed_artifact_idx = FixedArtifactId::NONE;
537         }
538
539         switch (tolower(ch)) {
540         /* Apply bad magic, but first clear object */
541         case 'w':
542             q_ptr->prep(o_ptr->bi_id);
543             ItemMagicApplier(player_ptr, q_ptr, player_ptr->current_floor_ptr->dun_level, AM_NO_FIXED_ART | AM_GOOD | AM_GREAT | AM_CURSED).execute();
544             break;
545         /* Apply bad magic, but first clear object */
546         case 'c':
547             q_ptr->prep(o_ptr->bi_id);
548             ItemMagicApplier(player_ptr, q_ptr, player_ptr->current_floor_ptr->dun_level, AM_NO_FIXED_ART | AM_GOOD | AM_CURSED).execute();
549             break;
550         /* Apply normal magic, but first clear object */
551         case 'n':
552             q_ptr->prep(o_ptr->bi_id);
553             ItemMagicApplier(player_ptr, q_ptr, player_ptr->current_floor_ptr->dun_level, AM_NO_FIXED_ART).execute();
554             break;
555         /* Apply good magic, but first clear object */
556         case 'g':
557             q_ptr->prep(o_ptr->bi_id);
558             ItemMagicApplier(player_ptr, q_ptr, player_ptr->current_floor_ptr->dun_level, AM_NO_FIXED_ART | AM_GOOD).execute();
559             break;
560         /* Apply great magic, but first clear object */
561         case 'e':
562             q_ptr->prep(o_ptr->bi_id);
563             ItemMagicApplier(player_ptr, q_ptr, player_ptr->current_floor_ptr->dun_level, AM_NO_FIXED_ART | AM_GOOD | AM_GREAT).execute();
564             break;
565         /* Apply special magic, but first clear object */
566         case 's':
567             q_ptr->prep(o_ptr->bi_id);
568             ItemMagicApplier(player_ptr, q_ptr, player_ptr->current_floor_ptr->dun_level, AM_GOOD | AM_GREAT | AM_SPECIAL).execute();
569             if (!q_ptr->is_fixed_or_random_artifact()) {
570                 become_random_artifact(player_ptr, q_ptr, false);
571             }
572
573             break;
574         default:
575             break;
576         }
577
578         q_ptr->iy = o_ptr->iy;
579         q_ptr->ix = o_ptr->ix;
580         q_ptr->marked = o_ptr->marked;
581     }
582
583     if (!changed) {
584         return;
585     }
586
587     o_ptr->copy_from(q_ptr);
588     auto &rfu = RedrawingFlagsUpdater::get_instance();
589     static constexpr auto flags_srf = {
590         StatusRecalculatingFlag::BONUS,
591         StatusRecalculatingFlag::COMBINATION,
592         StatusRecalculatingFlag::REORDER,
593     };
594     rfu.set_flags(flags_srf);
595     static constexpr auto flags_swrf = {
596         SubWindowRedrawingFlag::INVENTORY,
597         SubWindowRedrawingFlag::EQUIPMENT,
598         SubWindowRedrawingFlag::SPELL,
599         SubWindowRedrawingFlag::PLAYER,
600         SubWindowRedrawingFlag::FLOOR_ITEMS,
601         SubWindowRedrawingFlag::FOUND_ITEMS,
602     };
603     rfu.set_flags(flags_swrf);
604 }
605
606 /*!
607  * @briefアイテムの基礎能力値を調整する / Tweak an item
608  * @param player_ptr プレイヤーへの参照ポインタ
609  * @param o_ptr 調整するアイテムの参照ポインタ
610  */
611 static void wiz_tweak_item(PlayerType *player_ptr, ItemEntity *o_ptr)
612 {
613     if (o_ptr->is_fixed_or_random_artifact()) {
614         return;
615     }
616
617     concptr p = "Enter new 'pval' setting: ";
618     char tmp_val[80];
619     strnfmt(tmp_val, sizeof(tmp_val), "%d", o_ptr->pval);
620     if (!get_string(p, tmp_val, 5)) {
621         return;
622     }
623
624     o_ptr->pval = clamp_cast<int16_t>(atoi(tmp_val));
625     wiz_display_item(player_ptr, o_ptr);
626     p = "Enter new 'to_a' setting: ";
627     strnfmt(tmp_val, sizeof(tmp_val), "%d", o_ptr->to_a);
628     if (!get_string(p, tmp_val, 5)) {
629         return;
630     }
631
632     o_ptr->to_a = clamp_cast<int16_t>(atoi(tmp_val));
633     wiz_display_item(player_ptr, o_ptr);
634     p = "Enter new 'to_h' setting: ";
635     strnfmt(tmp_val, sizeof(tmp_val), "%d", o_ptr->to_h);
636     if (!get_string(p, tmp_val, 5)) {
637         return;
638     }
639
640     o_ptr->to_h = clamp_cast<int16_t>(atoi(tmp_val));
641     wiz_display_item(player_ptr, o_ptr);
642     p = "Enter new 'to_d' setting: ";
643     strnfmt(tmp_val, sizeof(tmp_val), "%d", (int)o_ptr->to_d);
644     if (!get_string(p, tmp_val, 5)) {
645         return;
646     }
647
648     o_ptr->to_d = clamp_cast<int16_t>(atoi(tmp_val));
649     wiz_display_item(player_ptr, o_ptr);
650 }
651
652 /*!
653  * @brief 検査対象のアイテムの数を変更する /
654  * Change the quantity of a the item
655  * @param player_ptr プレイヤーへの参照ポインタ
656  * @param o_ptr 変更するアイテム情報構造体の参照ポインタ
657  */
658 static void wiz_quantity_item(ItemEntity *o_ptr)
659 {
660     if (o_ptr->is_fixed_or_random_artifact()) {
661         return;
662     }
663
664     int tmp_qnt = o_ptr->number;
665     char tmp_val[100];
666     strnfmt(tmp_val, sizeof(tmp_val), "%d", (int)o_ptr->number);
667     if (get_string("Quantity: ", tmp_val, 2)) {
668         int tmp_int = atoi(tmp_val);
669         if (tmp_int < 1) {
670             tmp_int = 1;
671         }
672
673         if (tmp_int > 99) {
674             tmp_int = 99;
675         }
676
677         o_ptr->number = (byte)tmp_int;
678     }
679
680     if (o_ptr->bi_key.tval() == ItemKindType::ROD) {
681         o_ptr->pval = o_ptr->pval * o_ptr->number / tmp_qnt;
682     }
683 }
684
685 /*!
686  * @brief アイテムを弄るデバッグコマンド
687  * Play with an item. Options include:
688  * @details
689  *   - Output statistics (via wiz_roll_item)<br>
690  *   - Reroll item (via wiz_reroll_item)<br>
691  *   - Change properties (via wiz_tweak_item)<br>
692  *   - Change the number of items (via wiz_quantity_item)<br>
693  */
694 void wiz_modify_item(PlayerType *player_ptr)
695 {
696     constexpr auto q = "Play with which object? ";
697     constexpr auto s = "You have nothing to play with.";
698     short item;
699     auto *o_ptr = choose_object(player_ptr, &item, q, s, USE_EQUIP | USE_INVEN | USE_FLOOR | IGNORE_BOTHHAND_SLOT);
700     if (!o_ptr) {
701         return;
702     }
703
704     screen_save();
705
706     ItemEntity forge;
707     ItemEntity *q_ptr;
708     q_ptr = &forge;
709     q_ptr->copy_from(o_ptr);
710     char ch;
711     bool changed = false;
712     while (true) {
713         wiz_display_item(player_ptr, q_ptr);
714         if (!get_com("[a]ccept [s]tatistics [r]eroll [t]weak [q]uantity? ", &ch, false)) {
715             changed = false;
716             break;
717         }
718
719         if (ch == 'A' || ch == 'a') {
720             changed = true;
721             break;
722         }
723
724         if (ch == 's' || ch == 'S') {
725             wiz_statistics(player_ptr, q_ptr);
726         }
727
728         if (ch == 'r' || ch == 'R') {
729             wiz_reroll_item(player_ptr, q_ptr);
730         }
731
732         if (ch == 't' || ch == 'T') {
733             wiz_tweak_item(player_ptr, q_ptr);
734         }
735
736         if (ch == 'q' || ch == 'Q') {
737             wiz_quantity_item(q_ptr);
738         }
739     }
740
741     screen_load();
742     if (changed) {
743         msg_print("Changes accepted.");
744
745         o_ptr->copy_from(q_ptr);
746         auto &rfu = RedrawingFlagsUpdater::get_instance();
747         static constexpr auto flags_srf = {
748             StatusRecalculatingFlag::BONUS,
749             StatusRecalculatingFlag::COMBINATION,
750             StatusRecalculatingFlag::REORDER,
751         };
752         rfu.set_flags(flags_srf);
753         static constexpr auto flags_swrf = {
754             SubWindowRedrawingFlag::INVENTORY,
755             SubWindowRedrawingFlag::EQUIPMENT,
756             SubWindowRedrawingFlag::SPELL,
757             SubWindowRedrawingFlag::PLAYER,
758             SubWindowRedrawingFlag::FLOOR_ITEMS,
759             SubWindowRedrawingFlag::FOUND_ITEMS,
760         };
761         rfu.set_flags(flags_swrf);
762     } else {
763         msg_print("Changes ignored.");
764     }
765 }
766
767 /*!
768  * @brief オブジェクトの装備スロットがエゴが有効なスロットかどうか判定
769  */
770 static int is_slot_able_to_be_ego(PlayerType *player_ptr, ItemEntity *o_ptr)
771 {
772     int slot = wield_slot(player_ptr, o_ptr);
773
774     if (slot > -1) {
775         return slot;
776     }
777
778     if (o_ptr->is_ammo()) {
779         return INVEN_AMMO;
780     }
781
782     return -1;
783 }
784
785 /*!
786  * @brief 願ったが消えてしまった場合のメッセージ
787  */
788 static void wishing_puff_of_smoke(void)
789 {
790     msg_print(_("何かが足下に転がってきたが、煙のように消えてしまった。",
791         "You feel something roll beneath your feet, but it disappears in a puff of smoke!"));
792 }
793
794 /*!
795  * @brief 願ったが消えてしまった場合のメッセージ
796  * @param player_ptr 願ったプレイヤー情報への参照ポインタ
797  * @param prob ★などを願った場合の生成確率
798  * @param art_ok アーティファクトの生成を許すならTRUE
799  * @param ego_ok エゴの生成を許すならTRUE
800  * @param confirm 願わない場合に確認するかどうか
801  * @return 願った結果
802  */
803 WishResultType do_cmd_wishing(PlayerType *player_ptr, int prob, bool allow_art, bool allow_ego, bool confirm)
804 {
805     concptr fixed_str[] = {
806 #ifdef JP
807         "燃えない",
808         "錆びない",
809         "腐食しない",
810         "安定した",
811 #else
812         "rotproof",
813         "fireproof",
814         "rustproof",
815         "erodeproof",
816         "corrodeproof",
817         "fixed",
818 #endif
819         nullptr,
820     };
821
822     char buf[MAX_NLEN] = "\0";
823     char *str = buf;
824     ItemEntity forge;
825     auto *o_ptr = &forge;
826     bool wish_art = false;
827     bool wish_randart = false;
828     bool wish_ego = false;
829     bool exam_base = true;
830     bool ok_art = randint0(100) < prob;
831     bool ok_ego = randint0(100) < 50 + prob;
832     bool must = prob < 0;
833     bool blessed = false;
834     bool fixed = true;
835
836     while (1) {
837         if (get_string(_("何をお望み? ", "For what do you wish?"), buf, (MAX_NLEN - 1))) {
838             break;
839         }
840         if (confirm) {
841             if (!get_check(_("何も願いません。本当によろしいですか?", "Do you wish nothing, really? "))) {
842                 continue;
843             }
844         }
845         return WishResultType::NOTHING;
846     }
847
848 #ifndef JP
849     str_tolower(str);
850
851     /* remove 'a' */
852     if (!strncmp(buf, "a ", 2)) {
853         str = ltrim(str + 1);
854     } else if (!strncmp(buf, "an ", 3)) {
855         str = ltrim(str + 2);
856     }
857 #endif // !JP
858
859     str = rtrim(str);
860
861     if (!strncmp(str, _("祝福された", "blessed"), _(10, 7))) {
862         str = ltrim(str + _(10, 7));
863         blessed = true;
864     }
865
866     for (int i = 0; fixed_str[i] != nullptr; i++) {
867         int len = strlen(fixed_str[i]);
868         if (!strncmp(str, fixed_str[i], len)) {
869             str = ltrim(str + len);
870             fixed = true;
871             break;
872         }
873     }
874
875 #ifdef JP
876     if (!strncmp(str, "★", 2)) {
877         str = ltrim(str + 2);
878         wish_art = true;
879         exam_base = false;
880     } else
881 #endif
882
883         if (!strncmp(str, _("☆", "The "), _(2, 4))) {
884         str = ltrim(str + _(2, 4));
885         wish_art = true;
886         wish_randart = true;
887     }
888
889     /* wishing random ego ? */
890     else if (!strncmp(str, _("高級な", "excellent "), _(6, 9))) {
891         str = ltrim(str + _(6, 9));
892         wish_ego = true;
893     }
894
895     if (strlen(str) < 1) {
896         msg_print(_("名前がない!", "What?"));
897         return WishResultType::NOTHING;
898     }
899
900     if (!allow_art && wish_art) {
901         msg_print(_("アーティファクトは願えない!", "You can not wish artifacts!"));
902         return WishResultType::NOTHING;
903     }
904
905     if (cheat_xtra) {
906         msg_format("Wishing %s....", buf);
907     }
908
909     std::vector<short> k_ids;
910     std::vector<EgoType> e_ids;
911     if (exam_base) {
912         int max_len = 0;
913         for (const auto &baseitem : baseitems_info) {
914             if (baseitem.idx == 0 || baseitem.name.empty()) {
915                 continue;
916             }
917
918             o_ptr->prep(baseitem.idx);
919 #ifdef JP
920             const auto item_name = describe_flavor(player_ptr, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY | OD_STORE));
921 #else
922             auto item_name = describe_flavor(player_ptr, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY | OD_STORE));
923             str_tolower(item_name.data());
924 #endif
925             if (cheat_xtra) {
926                 msg_format("Matching object No.%d %s", baseitem.idx, item_name.data());
927             }
928
929             const int len = item_name.length();
930             if (std::string(str).find(item_name) != std::string::npos) {
931                 if (len > max_len) {
932                     k_ids.push_back(baseitem.idx);
933                     max_len = len;
934                 }
935             }
936         }
937
938         if (allow_ego && k_ids.size() == 1) {
939             short bi_id = k_ids.back();
940             o_ptr->prep(bi_id);
941
942             for (const auto &[e_idx, ego] : egos_info) {
943                 if (ego.idx == EgoType::NONE || ego.name.empty()) {
944                     continue;
945                 }
946
947                 std::string item_name(ego.name);
948 #ifdef JP
949 #else
950                 str_tolower(item_name.data());
951 #endif
952                 if (cheat_xtra) {
953                     msg_format("matching ego no.%d %s...", enum2i(ego.idx), item_name.data());
954                 }
955
956                 if (std::string(str).find(item_name) != std::string::npos) {
957                     if (is_slot_able_to_be_ego(player_ptr, o_ptr) != ego.slot) {
958                         continue;
959                     }
960
961                     e_ids.push_back(ego.idx);
962                 }
963             }
964         }
965     }
966
967     std::vector<FixedArtifactId> a_ids;
968
969     if (allow_art) {
970         char a_desc[MAX_NLEN] = "\0";
971         char *a_str = a_desc;
972
973         int len;
974         int mlen = 0;
975         for (const auto &[a_idx, artifact] : artifacts_info) {
976             if (a_idx == FixedArtifactId::NONE || artifact.name.empty()) {
977                 continue;
978             }
979
980             const auto bi_id = lookup_baseitem_id(artifact.bi_key);
981             if (bi_id == 0) {
982                 continue;
983             }
984
985             o_ptr->prep(bi_id);
986             o_ptr->fixed_artifact_idx = a_idx;
987
988 #ifdef JP
989             const auto item_name = describe_flavor(player_ptr, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY | OD_STORE));
990 #else
991             auto item_name = describe_flavor(player_ptr, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY | OD_STORE));
992             str_tolower(item_name.data());
993 #endif
994             a_str = a_desc;
995             strcpy(a_desc, artifact.name.data());
996
997             if (*a_str == '$') {
998                 a_str++;
999             }
1000 #ifdef JP
1001             /* remove quotes */
1002             if (!strncmp(a_str, "『", 2)) {
1003                 a_str += 2;
1004                 char *s = strstr(a_str, "』");
1005                 *s = '\0';
1006             }
1007             /* remove 'of' */
1008             else {
1009                 int l = strlen(a_str);
1010                 if (!strrncmp(a_str, "の", 2)) {
1011                     a_str[l - 2] = '\0';
1012                 }
1013             }
1014 #else
1015             /* remove quotes */
1016             if (a_str[0] == '\'') {
1017                 a_str += 1;
1018                 char *s = strchr(a_desc, '\'');
1019                 *s = '\0';
1020             }
1021             /* remove 'of ' */
1022             else if (!strncmp(a_str, (const char *)"of ", 3)) {
1023                 a_str += 3;
1024             }
1025
1026             str_tolower(a_str);
1027 #endif
1028             const auto match_name = _(item_name.data() + 2, item_name.data());
1029             if (cheat_xtra) {
1030                 msg_format("Matching artifact No.%d %s(%s)", enum2i(a_idx), a_desc, match_name);
1031             }
1032
1033             std::vector<const char *> l = { a_str, artifact.name.data(), match_name };
1034             for (size_t c = 0; c < l.size(); c++) {
1035                 if (!strcmp(str, l.at(c))) {
1036                     len = strlen(l.at(c));
1037                     if (len > mlen) {
1038                         a_ids.push_back(a_idx);
1039                         mlen = len;
1040                     }
1041                 }
1042             }
1043         }
1044     }
1045
1046     if (w_ptr->wizard && (a_ids.size() > 1 || e_ids.size() > 1)) {
1047         msg_print(_("候補が多すぎる!", "Too many matches!"));
1048         return WishResultType::FAIL;
1049     }
1050
1051     if (a_ids.size() == 1) {
1052         const auto a_idx = a_ids.back();
1053         const auto &artifact = ArtifactsInfo::get_instance().get_artifact(a_idx);
1054         if (must || (ok_art && !artifact.is_generated)) {
1055             (void)create_named_art(player_ptr, a_idx, player_ptr->y, player_ptr->x);
1056         } else {
1057             wishing_puff_of_smoke();
1058         }
1059
1060         return WishResultType::ARTIFACT;
1061     }
1062
1063     if (!allow_ego && (wish_ego || e_ids.size() > 0)) {
1064         msg_print(_("エゴアイテムは願えない!", "Can not wish ego item."));
1065         return WishResultType::NOTHING;
1066     }
1067
1068     if (k_ids.size() == 1) {
1069         const auto bi_id = k_ids.back();
1070         const auto &baseitem = baseitems_info[bi_id];
1071         auto a_idx = FixedArtifactId::NONE;
1072         if (baseitem.gen_flags.has(ItemGenerationTraitType::INSTA_ART)) {
1073             for (const auto &[a_idx_loop, artifact_loop] : artifacts_info) {
1074                 if (a_idx_loop == FixedArtifactId::NONE || artifact_loop.bi_key != baseitem.bi_key) {
1075                     continue;
1076                 }
1077
1078                 a_idx = a_idx_loop;
1079                 break;
1080             }
1081         }
1082
1083         if (a_idx != FixedArtifactId::NONE) {
1084             const auto &artifact = ArtifactsInfo::get_instance().get_artifact(a_idx);
1085             if (must || (ok_art && !artifact.is_generated)) {
1086                 (void)create_named_art(player_ptr, a_idx, player_ptr->y, player_ptr->x);
1087             } else {
1088                 wishing_puff_of_smoke();
1089             }
1090
1091             return WishResultType::ARTIFACT;
1092         }
1093
1094         if (wish_randart) {
1095             if (must || ok_art) {
1096                 do {
1097                     o_ptr->prep(bi_id);
1098                     ItemMagicApplier(player_ptr, o_ptr, baseitem.level, AM_SPECIAL | AM_NO_FIXED_ART).execute();
1099                 } while (!o_ptr->is_random_artifact() || o_ptr->is_ego() || o_ptr->is_cursed());
1100
1101                 if (o_ptr->is_random_artifact()) {
1102                     drop_near(player_ptr, o_ptr, -1, player_ptr->y, player_ptr->x);
1103                 }
1104             } else {
1105                 wishing_puff_of_smoke();
1106             }
1107             return WishResultType::ARTIFACT;
1108         }
1109
1110         WishResultType res = WishResultType::NOTHING;
1111         if (allow_ego && (wish_ego || e_ids.size() > 0)) {
1112             if (must || ok_ego) {
1113                 if (e_ids.size() > 0) {
1114                     o_ptr->prep(bi_id);
1115                     o_ptr->ego_idx = e_ids[0];
1116                     apply_ego(o_ptr, player_ptr->current_floor_ptr->base_level);
1117                 } else {
1118                     int max_roll = 1000;
1119                     int i = 0;
1120                     for (i = 0; i < max_roll; i++) {
1121                         o_ptr->prep(bi_id);
1122                         ItemMagicApplier(player_ptr, o_ptr, baseitem.level, AM_GREAT | AM_NO_FIXED_ART).execute();
1123                         if (o_ptr->is_random_artifact()) {
1124                             continue;
1125                         }
1126
1127                         if (wish_ego) {
1128                             break;
1129                         }
1130
1131                         EgoType e_idx = EgoType::NONE;
1132                         for (auto e : e_ids) {
1133                             if (o_ptr->ego_idx == e) {
1134                                 e_idx = e;
1135                                 break;
1136                             }
1137                         }
1138
1139                         if (e_idx != EgoType::NONE) {
1140                             break;
1141                         }
1142                     }
1143
1144                     if (i == max_roll) {
1145                         msg_print(_("失敗!もう一度願ってみてください。", "Failed! Try again."));
1146                         return WishResultType::FAIL;
1147                     }
1148                 }
1149             } else {
1150                 wishing_puff_of_smoke();
1151             }
1152
1153             res = WishResultType::EGO;
1154         } else {
1155             for (int i = 0; i < 100; i++) {
1156                 o_ptr->prep(bi_id);
1157                 ItemMagicApplier(player_ptr, o_ptr, 0, AM_NO_FIXED_ART).execute();
1158                 if (!o_ptr->is_cursed()) {
1159                     break;
1160                 }
1161             }
1162             res = WishResultType::NORMAL;
1163         }
1164
1165         if (blessed && wield_slot(player_ptr, o_ptr) != -1) {
1166             o_ptr->art_flags.set(TR_BLESSED);
1167         }
1168
1169         if (fixed && wield_slot(player_ptr, o_ptr) != -1) {
1170             o_ptr->art_flags.set(TR_IGNORE_ACID);
1171             o_ptr->art_flags.set(TR_IGNORE_FIRE);
1172         }
1173
1174         (void)drop_near(player_ptr, o_ptr, -1, player_ptr->y, player_ptr->x);
1175
1176         return res;
1177     }
1178
1179     msg_print(_("うーん、そんなものは存在しないようだ。", "Ummmm, that is not existing..."));
1180     return WishResultType::FAIL;
1181 }