OSDN Git Service

Replace sprintf() simply. Does part of the work to resolve https://github.com/hengba...
[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/player-update-types.h"
8 #include "core/show-file.h"
9 #include "core/stuff-handler.h"
10 #include "core/window-redrawer.h"
11 #include "flavor/flavor-describer.h"
12 #include "flavor/object-flavor-types.h"
13 #include "floor/floor-object.h"
14 #include "game-option/cheat-options.h"
15 #include "inventory/inventory-slot-types.h"
16 #include "io/input-key-acceptor.h"
17 #include "io/input-key-requester.h"
18 #include "object-enchant/item-apply-magic.h"
19 #include "object-enchant/item-magic-applier.h"
20 #include "object-enchant/object-ego.h"
21 #include "object-enchant/special-object-flags.h"
22 #include "object-enchant/tr-types.h"
23 #include "object/item-use-flags.h"
24 #include "object/object-flags.h"
25 #include "object/object-info.h"
26 #include "object/object-kind-hook.h"
27 #include "object/object-mark-types.h"
28 #include "object/object-value.h"
29 #include "spell-kind/spells-perception.h"
30 #include "spell/spells-object.h"
31 #include "system/alloc-entries.h"
32 #include "system/artifact-type-definition.h"
33 #include "system/baseitem-info.h"
34 #include "system/floor-type-definition.h"
35 #include "system/item-entity.h"
36 #include "system/player-type-definition.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().data(), 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     artifacts_info.at(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     auto q = _("どのアイテムの発動を変更しますか? ", "Which object? ");
200     auto s = _("発動を変更するアイテムがない。", "Nothing to do with.");
201     OBJECT_IDX 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->bi_id) {
226             continue;
227         }
228
229         auto &baseitem = baseitems_info[o_ptr->bi_id];
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     /* Refrect item informaiton onto subwindows without updating inventory */
236     reset_bits(player_ptr->update, PU_COMBINE | PU_REORDER);
237     handle_stuff(player_ptr);
238     set_bits(player_ptr->update, PU_COMBINE | PU_REORDER);
239     set_bits(player_ptr->window_flags, PW_INVEN | PW_EQUIP);
240 }
241
242 /*!
243  * @brief アイテムの階層毎生成率を表示する / Output a rarity graph for a type of object.
244  * @param tval ベースアイテムの大項目ID
245  * @param sval ベースアイテムの小項目ID
246  * @param row 表示列
247  * @param col 表示行
248  */
249 static void prt_alloc(const BaseitemKey &bi_key, TERM_LEN row, TERM_LEN col)
250 {
251     uint32_t rarity[BASEITEM_MAX_DEPTH]{};
252     uint32_t total[BASEITEM_MAX_DEPTH]{};
253     int32_t display[22]{};
254
255     auto home = 0;
256     for (auto i = 0; i < BASEITEM_MAX_DEPTH; i++) {
257         auto total_frac = 0;
258         constexpr auto magnificant = CHANCE_BASEITEM_LEVEL_BOOST * BASEITEM_MAX_DEPTH;
259         for (const auto &entry : alloc_kind_table) {
260             auto prob = 0;
261             if (entry.level <= i) {
262                 prob = entry.prob1 * magnificant;
263             } else if (entry.level - 1 > 0) {
264                 prob = entry.prob1 * i * BASEITEM_MAX_DEPTH / (entry.level - 1);
265             }
266
267             const auto &baseitem = baseitems_info[entry.index];
268             total[i] += prob / magnificant;
269             total_frac += prob % magnificant;
270
271             if (baseitem.bi_key == bi_key) {
272                 home = baseitem.level;
273                 rarity[i] += prob / magnificant;
274             }
275         }
276
277         total[i] += total_frac / magnificant;
278     }
279
280     for (auto i = 0; i < 22; i++) {
281         auto possibility = 0;
282         for (int j = i * BASEITEM_MAX_DEPTH / 22; j < (i + 1) * BASEITEM_MAX_DEPTH / 22; j++) {
283             possibility += rarity[j] * 100000 / total[j];
284         }
285
286         display[i] = possibility / 5;
287     }
288
289     for (auto i = 0; i < 22; i++) {
290         term_putch(col, row + i + 1, TERM_WHITE, '|');
291         prt(format("%2dF", (i * 5)), row + i + 1, col);
292         if ((i * BASEITEM_MAX_DEPTH / 22 <= home) && (home < (i + 1) * BASEITEM_MAX_DEPTH / 22)) {
293             c_prt(TERM_RED, format("%3d.%04d%%", display[i] / 1000, display[i] % 1000), row + i + 1, col + 3);
294         } else {
295             c_prt(TERM_WHITE, format("%3d.%04d%%", display[i] / 1000, display[i] % 1000), row + i + 1, col + 3);
296         }
297     }
298
299     concptr r = "+---Rate---+";
300     prt(r, row, col);
301 }
302
303 /*!
304  * @brief 32ビット変数のビット配列を並べて描画する / Output a long int in binary format.
305  */
306 static void prt_binary(BIT_FLAGS flags, const int row, int col)
307 {
308     uint32_t bitmask;
309     for (int i = bitmask = 1; i <= 32; i++, bitmask *= 2) {
310         if (flags & bitmask) {
311             term_putch(col++, row, TERM_BLUE, '*');
312         } else {
313             term_putch(col++, row, TERM_WHITE, '-');
314         }
315     }
316 }
317
318 /*!
319  * @brief アイテムの詳細ステータスを表示する /
320  * Change various "permanent" player variables.
321  * @param player_ptr プレイヤーへの参照ポインタ
322  * @param o_ptr 詳細を表示するアイテム情報の参照ポインタ
323  */
324 static void wiz_display_item(PlayerType *player_ptr, ItemEntity *o_ptr)
325 {
326     auto flgs = object_flags(o_ptr);
327     auto get_seq_32bits = [](const TrFlags &flgs, uint start) {
328         BIT_FLAGS result = 0U;
329         for (auto i = 0U; i < 32 && start + i < flgs.size(); i++) {
330             if (flgs.has(i2enum<tr_type>(start + i))) {
331                 result |= 1U << i;
332             }
333         }
334         return result;
335     };
336     int j = 13;
337     for (int i = 1; i <= 23; i++) {
338         prt("", i, j - 2);
339     }
340
341     prt_alloc(o_ptr->bi_key, 1, 0);
342     char buf[256];
343     describe_flavor(player_ptr, buf, o_ptr, OD_STORE);
344     prt(buf, 2, j);
345
346     auto line = 4;
347     const auto &bi_key = o_ptr->bi_key;
348     const auto item_level = baseitems_info[o_ptr->bi_id].level;
349     prt(format("kind = %-5d  level = %-4d  tval = %-5d  sval = %-5d", o_ptr->bi_id, item_level, bi_key.tval(), bi_key.sval().value()), line, j);
350     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);
351     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);
352     prt(format("fixed_artifact_idx = %-4d  ego_idx = %-4d  cost = %ld", o_ptr->fixed_artifact_idx, o_ptr->ego_idx, object_value_real(o_ptr)), ++line, j);
353     prt(format("ident = %04x  activation_id = %-4d  timeout = %-d", o_ptr->ident, o_ptr->activation_id, o_ptr->timeout), ++line, j);
354     prt(format("chest_level = %-4d  fuel = %-d", o_ptr->chest_level, o_ptr->fuel), ++line, j);
355     prt(format("smith_hit = %-4d  smith_damage = %-4d", o_ptr->smith_hit, o_ptr->smith_damage), ++line, j);
356     prt(format("cursed  = %-d  captured_monster_speed = %-4d", o_ptr->curse_flags, o_ptr->captured_monster_speed), ++line, j);
357     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);
358
359     prt("+------------FLAGS1------------+", ++line, j);
360     prt("AFFECT........SLAY........BRAND.", ++line, j);
361     prt("      mf      cvae      xsqpaefc", ++line, j);
362     prt("siwdccsossidsahanvudotgddhuoclio", ++line, j);
363     prt("tnieohtctrnipttmiinmrrnrrraiierl", ++line, j);
364     prt("rtsxnarelcfgdkcpmldncltggpksdced", ++line, j);
365     prt_binary(get_seq_32bits(flgs, 32 * 0), ++line, j);
366
367     prt("+------------FLAGS2------------+", ++line, j);
368     prt("SUST....IMMUN.RESIST............", ++line, j);
369     prt("      reaefctrpsaefcpfldbc sn   ", ++line, j);
370     prt("siwdcciaclioheatcliooeialoshtncd", ++line, j);
371     prt("tnieohdsierlrfraierliatrnnnrhehi", ++line, j);
372     prt("rtsxnaeydcedwlatdcedsrekdfddrxss", ++line, j);
373     prt_binary(get_seq_32bits(flgs, 32 * 1), ++line, j);
374
375     line = 13;
376     prt("+------------FLAGS3------------+", line, j + 32);
377     prt("fe cnn t      stdrmsiiii d ab   ", ++line, j + 32);
378     prt("aa aoomywhs lleeieihgggg rtgl   ", ++line, j + 32);
379     prt("uu utmacaih eielgggonnnnaaere   ", ++line, j + 32);
380     prt("rr reanurdo vtieeehtrrrrcilas   ", ++line, j + 32);
381     prt("aa algarnew ienpsntsaefctnevs   ", ++line, j + 32);
382     prt_binary(get_seq_32bits(flgs, 32 * 2), ++line, j + 32);
383
384     prt("+------------FLAGS4------------+", ++line, j + 32);
385     prt("KILL....ESP.........            ", ++line, j + 32);
386     prt("aeud tghaud tgdhegnu            ", ++line, j + 32);
387     prt("nvneoriunneoriruvoon            ", ++line, j + 32);
388     prt("iidmroamidmroagmionq            ", ++line, j + 32);
389     prt("mlenclnmmenclnnnldlu            ", ++line, j + 32);
390     prt_binary(get_seq_32bits(flgs, 32 * 3), ++line, j + 32);
391 }
392
393 /*!
394  * @brief 検査対象のアイテムを基準とした生成テストを行う /
395  * Try to create an item again. Output some statistics.    -Bernd-
396  * @param player_ptr プレイヤーへの参照ポインタ
397  * @param o_ptr 生成テストの基準となるアイテム情報の参照ポインタ
398  * The statistics are correct now.  We acquire a clean grid, and then
399  * repeatedly place an object in this grid, copying it into an item
400  * holder, and then deleting the object.  We fiddle with the artifact
401  * counter flags to prevent weirdness.  We use the items to collect
402  * statistics on item creation relative to the initial item.
403  */
404 static void wiz_statistics(PlayerType *player_ptr, ItemEntity *o_ptr)
405 {
406     concptr q = "Rolls: %ld  Correct: %ld  Matches: %ld  Better: %ld  Worse: %ld  Other: %ld";
407     concptr p = "Enter number of items to roll: ";
408     char tmp_val[80];
409
410     if (o_ptr->is_fixed_artifact()) {
411         artifacts_info.at(o_ptr->fixed_artifact_idx).is_generated = false;
412     }
413
414     uint32_t i, matches, better, worse, other, correct;
415     uint32_t test_roll = 1000000;
416     char ch;
417     concptr quality;
418     BIT_FLAGS mode;
419     while (true) {
420         concptr pmt = "Roll for [n]ormal, [g]ood, or [e]xcellent treasure? ";
421         wiz_display_item(player_ptr, o_ptr);
422         if (!get_com(pmt, &ch, false)) {
423             break;
424         }
425
426         if (ch == 'n' || ch == 'N') {
427             mode = 0L;
428             quality = "normal";
429         } else if (ch == 'g' || ch == 'G') {
430             mode = AM_GOOD;
431             quality = "good";
432         } else if (ch == 'e' || ch == 'E') {
433             mode = AM_GOOD | AM_GREAT;
434             quality = "excellent";
435         } else {
436             break;
437         }
438
439         strnfmt(tmp_val, sizeof(tmp_val), "%ld", (long int)test_roll);
440         if (get_string(p, tmp_val, 10)) {
441             test_roll = atol(tmp_val);
442         }
443         test_roll = std::max<uint>(1, test_roll);
444         msg_format("Creating a lot of %s items. Base level = %d.", quality, player_ptr->current_floor_ptr->dun_level);
445         msg_print(nullptr);
446
447         correct = matches = better = worse = other = 0;
448         for (i = 0; i <= test_roll; i++) {
449             if ((i < 100) || (i % 100 == 0)) {
450                 inkey_scan = true;
451                 if (inkey()) {
452                     flush();
453                     break; // stop rolling
454                 }
455
456                 prt(format(q, i, correct, matches, better, worse, other), 0, 0);
457                 term_fresh();
458             }
459
460             ItemEntity forge;
461             auto *q_ptr = &forge;
462             q_ptr->wipe();
463             make_object(player_ptr, q_ptr, mode);
464             if (q_ptr->is_fixed_artifact()) {
465                 artifacts_info.at(q_ptr->fixed_artifact_idx).is_generated = false;
466             }
467
468             if (o_ptr->bi_key != q_ptr->bi_key) {
469                 continue;
470             }
471
472             correct++;
473             const auto is_same_fixed_artifact_idx = o_ptr->is_specific_artifact(q_ptr->fixed_artifact_idx);
474             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) {
475                 matches++;
476             } 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)) {
477                 better++;
478             } 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)) {
479                 worse++;
480             } else {
481                 other++;
482             }
483         }
484
485         msg_format(q, i, correct, matches, better, worse, other);
486         msg_print(nullptr);
487     }
488
489     if (o_ptr->is_fixed_artifact()) {
490         artifacts_info.at(o_ptr->fixed_artifact_idx).is_generated = true;
491     }
492 }
493
494 /*!
495  * @brief アイテムの質を選択して再生成する /
496  * Apply magic to an item or turn it into an artifact. -Bernd-
497  * @param o_ptr 再生成の対象となるアイテム情報の参照ポインタ
498  */
499 static void wiz_reroll_item(PlayerType *player_ptr, ItemEntity *o_ptr)
500 {
501     if (o_ptr->is_artifact()) {
502         return;
503     }
504
505     ItemEntity forge;
506     ItemEntity *q_ptr;
507     q_ptr = &forge;
508     q_ptr->copy_from(o_ptr);
509
510     char ch;
511     bool changed = false;
512     while (true) {
513         wiz_display_item(player_ptr, q_ptr);
514         if (!get_com("[a]ccept, [w]orthless, [c]ursed, [n]ormal, [g]ood, [e]xcellent, [s]pecial? ", &ch, false)) {
515             if (q_ptr->is_fixed_artifact()) {
516                 artifacts_info.at(q_ptr->fixed_artifact_idx).is_generated = false;
517                 q_ptr->fixed_artifact_idx = FixedArtifactId::NONE;
518             }
519
520             changed = false;
521             break;
522         }
523
524         if (ch == 'A' || ch == 'a') {
525             changed = true;
526             break;
527         }
528
529         if (q_ptr->is_fixed_artifact()) {
530             artifacts_info.at(q_ptr->fixed_artifact_idx).is_generated = false;
531             q_ptr->fixed_artifact_idx = FixedArtifactId::NONE;
532         }
533
534         switch (tolower(ch)) {
535         /* Apply bad magic, but first clear object */
536         case 'w':
537             q_ptr->prep(o_ptr->bi_id);
538             ItemMagicApplier(player_ptr, q_ptr, player_ptr->current_floor_ptr->dun_level, AM_NO_FIXED_ART | AM_GOOD | AM_GREAT | AM_CURSED).execute();
539             break;
540         /* Apply bad magic, but first clear object */
541         case 'c':
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_CURSED).execute();
544             break;
545         /* Apply normal magic, but first clear object */
546         case 'n':
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).execute();
549             break;
550         /* Apply good magic, but first clear object */
551         case 'g':
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 | AM_GOOD).execute();
554             break;
555         /* Apply great magic, but first clear object */
556         case 'e':
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 | AM_GREAT).execute();
559             break;
560         /* Apply special magic, but first clear object */
561         case 's':
562             q_ptr->prep(o_ptr->bi_id);
563             ItemMagicApplier(player_ptr, q_ptr, player_ptr->current_floor_ptr->dun_level, AM_GOOD | AM_GREAT | AM_SPECIAL).execute();
564             if (!q_ptr->is_artifact()) {
565                 become_random_artifact(player_ptr, q_ptr, false);
566             }
567
568             break;
569         default:
570             break;
571         }
572
573         q_ptr->iy = o_ptr->iy;
574         q_ptr->ix = o_ptr->ix;
575         q_ptr->marked = o_ptr->marked;
576     }
577
578     if (!changed) {
579         return;
580     }
581
582     o_ptr->copy_from(q_ptr);
583     set_bits(player_ptr->update, PU_BONUS | PU_COMBINE | PU_REORDER);
584     set_bits(player_ptr->window_flags, PW_INVEN | PW_EQUIP | PW_SPELL | PW_PLAYER | PW_FLOOR_ITEM_LIST | PW_FOUND_ITEM_LIST);
585 }
586
587 /*!
588  * @briefアイテムの基礎能力値を調整する / Tweak an item
589  * @param player_ptr プレイヤーへの参照ポインタ
590  * @param o_ptr 調整するアイテムの参照ポインタ
591  */
592 static void wiz_tweak_item(PlayerType *player_ptr, ItemEntity *o_ptr)
593 {
594     if (o_ptr->is_artifact()) {
595         return;
596     }
597
598     concptr p = "Enter new 'pval' setting: ";
599     char tmp_val[80];
600     strnfmt(tmp_val, sizeof(tmp_val), "%d", o_ptr->pval);
601     if (!get_string(p, tmp_val, 5)) {
602         return;
603     }
604
605     o_ptr->pval = clamp_cast<int16_t>(atoi(tmp_val));
606     wiz_display_item(player_ptr, o_ptr);
607     p = "Enter new 'to_a' setting: ";
608     strnfmt(tmp_val, sizeof(tmp_val), "%d", o_ptr->to_a);
609     if (!get_string(p, tmp_val, 5)) {
610         return;
611     }
612
613     o_ptr->to_a = clamp_cast<int16_t>(atoi(tmp_val));
614     wiz_display_item(player_ptr, o_ptr);
615     p = "Enter new 'to_h' setting: ";
616     strnfmt(tmp_val, sizeof(tmp_val), "%d", o_ptr->to_h);
617     if (!get_string(p, tmp_val, 5)) {
618         return;
619     }
620
621     o_ptr->to_h = clamp_cast<int16_t>(atoi(tmp_val));
622     wiz_display_item(player_ptr, o_ptr);
623     p = "Enter new 'to_d' setting: ";
624     strnfmt(tmp_val, sizeof(tmp_val), "%d", (int)o_ptr->to_d);
625     if (!get_string(p, tmp_val, 5)) {
626         return;
627     }
628
629     o_ptr->to_d = clamp_cast<int16_t>(atoi(tmp_val));
630     wiz_display_item(player_ptr, o_ptr);
631 }
632
633 /*!
634  * @brief 検査対象のアイテムの数を変更する /
635  * Change the quantity of a the item
636  * @param player_ptr プレイヤーへの参照ポインタ
637  * @param o_ptr 変更するアイテム情報構造体の参照ポインタ
638  */
639 static void wiz_quantity_item(ItemEntity *o_ptr)
640 {
641     if (o_ptr->is_artifact()) {
642         return;
643     }
644
645     int tmp_qnt = o_ptr->number;
646     char tmp_val[100];
647     strnfmt(tmp_val, sizeof(tmp_val), "%d", (int)o_ptr->number);
648     if (get_string("Quantity: ", tmp_val, 2)) {
649         int tmp_int = atoi(tmp_val);
650         if (tmp_int < 1) {
651             tmp_int = 1;
652         }
653
654         if (tmp_int > 99) {
655             tmp_int = 99;
656         }
657
658         o_ptr->number = (byte)tmp_int;
659     }
660
661     if (o_ptr->bi_key.tval() == ItemKindType::ROD) {
662         o_ptr->pval = o_ptr->pval * o_ptr->number / tmp_qnt;
663     }
664 }
665
666 /*!
667  * @brief アイテムを弄るデバッグコマンド
668  * Play with an item. Options include:
669  * @details
670  *   - Output statistics (via wiz_roll_item)<br>
671  *   - Reroll item (via wiz_reroll_item)<br>
672  *   - Change properties (via wiz_tweak_item)<br>
673  *   - Change the number of items (via wiz_quantity_item)<br>
674  */
675 void wiz_modify_item(PlayerType *player_ptr)
676 {
677     concptr q = "Play with which object? ";
678     concptr s = "You have nothing to play with.";
679     OBJECT_IDX item;
680     ItemEntity *o_ptr;
681     o_ptr = choose_object(player_ptr, &item, q, s, USE_EQUIP | USE_INVEN | USE_FLOOR | IGNORE_BOTHHAND_SLOT);
682     if (!o_ptr) {
683         return;
684     }
685
686     screen_save();
687
688     ItemEntity forge;
689     ItemEntity *q_ptr;
690     q_ptr = &forge;
691     q_ptr->copy_from(o_ptr);
692     char ch;
693     bool changed = false;
694     while (true) {
695         wiz_display_item(player_ptr, q_ptr);
696         if (!get_com("[a]ccept [s]tatistics [r]eroll [t]weak [q]uantity? ", &ch, false)) {
697             changed = false;
698             break;
699         }
700
701         if (ch == 'A' || ch == 'a') {
702             changed = true;
703             break;
704         }
705
706         if (ch == 's' || ch == 'S') {
707             wiz_statistics(player_ptr, q_ptr);
708         }
709
710         if (ch == 'r' || ch == 'R') {
711             wiz_reroll_item(player_ptr, q_ptr);
712         }
713
714         if (ch == 't' || ch == 'T') {
715             wiz_tweak_item(player_ptr, q_ptr);
716         }
717
718         if (ch == 'q' || ch == 'Q') {
719             wiz_quantity_item(q_ptr);
720         }
721     }
722
723     screen_load();
724     if (changed) {
725         msg_print("Changes accepted.");
726
727         o_ptr->copy_from(q_ptr);
728         set_bits(player_ptr->update, PU_BONUS | PU_COMBINE | PU_REORDER);
729         set_bits(player_ptr->window_flags, PW_INVEN | PW_EQUIP | PW_SPELL | PW_PLAYER | PW_FLOOR_ITEM_LIST | PW_FOUND_ITEM_LIST);
730     } else {
731         msg_print("Changes ignored.");
732     }
733 }
734
735 /*!
736  * @brief オブジェクトの装備スロットがエゴが有効なスロットかどうか判定
737  */
738 static int is_slot_able_to_be_ego(PlayerType *player_ptr, ItemEntity *o_ptr)
739 {
740     int slot = wield_slot(player_ptr, o_ptr);
741
742     if (slot > -1) {
743         return slot;
744     }
745
746     if (o_ptr->is_ammo()) {
747         return INVEN_AMMO;
748     }
749
750     return -1;
751 }
752
753 /*!
754  * @brief 願ったが消えてしまった場合のメッセージ
755  */
756 static void wishing_puff_of_smoke(void)
757 {
758     msg_print(_("何かが足下に転がってきたが、煙のように消えてしまった。",
759         "You feel something roll beneath your feet, but it disappears in a puff of smoke!"));
760 }
761
762 /*!
763  * @brief 願ったが消えてしまった場合のメッセージ
764  * @param player_ptr 願ったプレイヤー情報への参照ポインタ
765  * @param prob ★などを願った場合の生成確率
766  * @param art_ok アーティファクトの生成を許すならTRUE
767  * @param ego_ok エゴの生成を許すならTRUE
768  * @param confirm 願わない場合に確認するかどうか
769  * @return 願った結果
770  */
771 WishResultType do_cmd_wishing(PlayerType *player_ptr, int prob, bool allow_art, bool allow_ego, bool confirm)
772 {
773     concptr fixed_str[] = {
774 #ifdef JP
775         "燃えない",
776         "錆びない",
777         "腐食しない",
778         "安定した",
779 #else
780         "rotproof",
781         "fireproof",
782         "rustproof",
783         "erodeproof",
784         "corrodeproof",
785         "fixed",
786 #endif
787         nullptr,
788     };
789
790     char buf[MAX_NLEN] = "\0";
791     char *str = buf;
792     ItemEntity forge;
793     auto *o_ptr = &forge;
794     char o_name[MAX_NLEN];
795
796     bool wish_art = false;
797     bool wish_randart = false;
798     bool wish_ego = false;
799     bool exam_base = true;
800     bool ok_art = randint0(100) < prob;
801     bool ok_ego = randint0(100) < 50 + prob;
802     bool must = prob < 0;
803     bool blessed = false;
804     bool fixed = true;
805
806     while (1) {
807         if (get_string(_("何をお望み? ", "For what do you wish?"), buf, (MAX_NLEN - 1))) {
808             break;
809         }
810         if (confirm) {
811             if (!get_check(_("何も願いません。本当によろしいですか?", "Do you wish nothing, really? "))) {
812                 continue;
813             }
814         }
815         return WishResultType::NOTHING;
816     }
817
818 #ifndef JP
819     str_tolower(str);
820
821     /* remove 'a' */
822     if (!strncmp(buf, "a ", 2)) {
823         str = ltrim(str + 1);
824     } else if (!strncmp(buf, "an ", 3)) {
825         str = ltrim(str + 2);
826     }
827 #endif // !JP
828
829     str = rtrim(str);
830
831     if (!strncmp(str, _("祝福された", "blessed"), _(10, 7))) {
832         str = ltrim(str + _(10, 7));
833         blessed = true;
834     }
835
836     for (int i = 0; fixed_str[i] != nullptr; i++) {
837         int len = strlen(fixed_str[i]);
838         if (!strncmp(str, fixed_str[i], len)) {
839             str = ltrim(str + len);
840             fixed = true;
841             break;
842         }
843     }
844
845 #ifdef JP
846     if (!strncmp(str, "★", 2)) {
847         str = ltrim(str + 2);
848         wish_art = true;
849         exam_base = false;
850     } else
851 #endif
852
853         if (!strncmp(str, _("☆", "The "), _(2, 4))) {
854         str = ltrim(str + _(2, 4));
855         wish_art = true;
856         wish_randart = true;
857     }
858
859     /* wishing random ego ? */
860     else if (!strncmp(str, _("高級な", "excellent "), _(6, 9))) {
861         str = ltrim(str + _(6, 9));
862         wish_ego = true;
863     }
864
865     if (strlen(str) < 1) {
866         msg_print(_("名前がない!", "What?"));
867         return WishResultType::NOTHING;
868     }
869
870     if (!allow_art && wish_art) {
871         msg_print(_("アーティファクトは願えない!", "You can not wish artifacts!"));
872         return WishResultType::NOTHING;
873     }
874
875     if (cheat_xtra) {
876         msg_format("Wishing %s....", buf);
877     }
878
879     std::vector<short> k_ids;
880     std::vector<EgoType> e_ids;
881     if (exam_base) {
882         int len;
883         int max_len = 0;
884         for (const auto &baseitem : baseitems_info) {
885             if (baseitem.idx == 0 || baseitem.name.empty()) {
886                 continue;
887             }
888
889             o_ptr->prep(baseitem.idx);
890             describe_flavor(player_ptr, o_name, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY | OD_STORE));
891 #ifndef JP
892             str_tolower(o_name);
893 #endif
894             if (cheat_xtra) {
895                 msg_format("Matching object No.%d %s", baseitem.idx, o_name);
896             }
897
898             len = strlen(o_name);
899
900             if (_(!strrncmp(str, o_name, len), !strncmp(str, o_name, len))) {
901                 if (len > max_len) {
902                     k_ids.push_back(baseitem.idx);
903                     max_len = len;
904                 }
905             }
906         }
907
908         if (allow_ego && k_ids.size() == 1) {
909             short bi_id = k_ids.back();
910             o_ptr->prep(bi_id);
911
912             for (const auto &[e_idx, e_ref] : egos_info) {
913                 if (e_ref.idx == EgoType::NONE || e_ref.name.empty()) {
914                     continue;
915                 }
916
917                 strcpy(o_name, e_ref.name.data());
918 #ifndef JP
919                 str_tolower(o_name);
920 #endif
921                 if (cheat_xtra) {
922                     msg_format("matching ego no.%d %s...", e_ref.idx, o_name);
923                 }
924
925                 if (_(!strncmp(str, o_name, strlen(o_name)), !strrncmp(str, o_name, strlen(o_name)))) {
926                     if (is_slot_able_to_be_ego(player_ptr, o_ptr) != e_ref.slot) {
927                         continue;
928                     }
929
930                     e_ids.push_back(e_ref.idx);
931                 }
932             }
933         }
934     }
935
936     std::vector<FixedArtifactId> a_ids;
937
938     if (allow_art) {
939         char a_desc[MAX_NLEN] = "\0";
940         char *a_str = a_desc;
941
942         int len;
943         int mlen = 0;
944         for (const auto &[a_idx, a_ref] : artifacts_info) {
945             if (a_idx == FixedArtifactId::NONE || a_ref.name.empty()) {
946                 continue;
947             }
948
949             const auto bi_id = lookup_baseitem_id(a_ref.bi_key);
950             if (bi_id == 0) {
951                 continue;
952             }
953
954             o_ptr->prep(bi_id);
955             o_ptr->fixed_artifact_idx = a_idx;
956
957             describe_flavor(player_ptr, o_name, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY | OD_STORE));
958 #ifndef JP
959             str_tolower(o_name);
960 #endif
961             a_str = a_desc;
962             strcpy(a_desc, a_ref.name.data());
963
964             if (*a_str == '$') {
965                 a_str++;
966             }
967 #ifdef JP
968             /* remove quotes */
969             if (!strncmp(a_str, "『", 2)) {
970                 a_str += 2;
971                 char *s = strstr(a_str, "』");
972                 *s = '\0';
973             }
974             /* remove 'of' */
975             else {
976                 int l = strlen(a_str);
977                 if (!strrncmp(a_str, "の", 2)) {
978                     a_str[l - 2] = '\0';
979                 }
980             }
981 #else
982             /* remove quotes */
983             if (a_str[0] == '\'') {
984                 a_str += 1;
985                 char *s = strchr(a_desc, '\'');
986                 *s = '\0';
987             }
988             /* remove 'of ' */
989             else if (!strncmp(a_str, (const char *)"of ", 3)) {
990                 a_str += 3;
991             }
992
993             str_tolower(a_str);
994 #endif
995
996             if (cheat_xtra) {
997                 msg_format("Matching artifact No.%d %s(%s)", a_idx, a_desc, _(&o_name[2], o_name));
998             }
999
1000             std::vector<const char *> l = { a_str, a_ref.name.data(), _(&o_name[2], o_name) };
1001             for (size_t c = 0; c < l.size(); c++) {
1002                 if (!strcmp(str, l.at(c))) {
1003                     len = strlen(l.at(c));
1004                     if (len > mlen) {
1005                         a_ids.push_back(a_idx);
1006                         mlen = len;
1007                     }
1008                 }
1009             }
1010         }
1011     }
1012
1013     if (w_ptr->wizard && (a_ids.size() > 1 || e_ids.size() > 1)) {
1014         msg_print(_("候補が多すぎる!", "Too many matches!"));
1015         return WishResultType::FAIL;
1016     }
1017
1018     if (a_ids.size() == 1) {
1019         const auto a_idx = a_ids.back();
1020         auto &a_ref = artifacts_info.at(a_idx);
1021         if (must || (ok_art && !a_ref.is_generated)) {
1022             (void)create_named_art(player_ptr, a_idx, player_ptr->y, player_ptr->x);
1023         } else {
1024             wishing_puff_of_smoke();
1025         }
1026
1027         return WishResultType::ARTIFACT;
1028     }
1029
1030     if (!allow_ego && (wish_ego || e_ids.size() > 0)) {
1031         msg_print(_("エゴアイテムは願えない!", "Can not wish ego item."));
1032         return WishResultType::NOTHING;
1033     }
1034
1035     if (k_ids.size() == 1) {
1036         const auto bi_id = k_ids.back();
1037         const auto &baseitem = baseitems_info[bi_id];
1038         auto a_idx = FixedArtifactId::NONE;
1039         if (baseitem.gen_flags.has(ItemGenerationTraitType::INSTA_ART)) {
1040             for (const auto &[a_idx_loop, a_ref_loop] : artifacts_info) {
1041                 if (a_idx_loop == FixedArtifactId::NONE || a_ref_loop.bi_key != baseitem.bi_key) {
1042                     continue;
1043                 }
1044
1045                 a_idx = a_idx_loop;
1046                 break;
1047             }
1048         }
1049
1050         if (a_idx != FixedArtifactId::NONE) {
1051             const auto &a_ref = artifacts_info.at(a_idx);
1052             if (must || (ok_art && !a_ref.is_generated)) {
1053                 (void)create_named_art(player_ptr, a_idx, player_ptr->y, player_ptr->x);
1054             } else {
1055                 wishing_puff_of_smoke();
1056             }
1057
1058             return WishResultType::ARTIFACT;
1059         }
1060
1061         if (wish_randart) {
1062             if (must || ok_art) {
1063                 do {
1064                     o_ptr->prep(bi_id);
1065                     ItemMagicApplier(player_ptr, o_ptr, baseitem.level, AM_SPECIAL | AM_NO_FIXED_ART).execute();
1066                 } while (!o_ptr->art_name || o_ptr->is_ego() || o_ptr->is_cursed());
1067
1068                 if (o_ptr->art_name) {
1069                     drop_near(player_ptr, o_ptr, -1, player_ptr->y, player_ptr->x);
1070                 }
1071             } else {
1072                 wishing_puff_of_smoke();
1073             }
1074             return WishResultType::ARTIFACT;
1075         }
1076
1077         WishResultType res = WishResultType::NOTHING;
1078         if (allow_ego && (wish_ego || e_ids.size() > 0)) {
1079             if (must || ok_ego) {
1080                 if (e_ids.size() > 0) {
1081                     o_ptr->prep(bi_id);
1082                     o_ptr->ego_idx = e_ids[0];
1083                     apply_ego(o_ptr, player_ptr->current_floor_ptr->base_level);
1084                 } else {
1085                     int max_roll = 1000;
1086                     int i = 0;
1087                     for (i = 0; i < max_roll; i++) {
1088                         o_ptr->prep(bi_id);
1089                         ItemMagicApplier(player_ptr, o_ptr, baseitem.level, AM_GREAT | AM_NO_FIXED_ART).execute();
1090                         if (o_ptr->art_name) {
1091                             continue;
1092                         }
1093
1094                         if (wish_ego) {
1095                             break;
1096                         }
1097
1098                         EgoType e_idx = EgoType::NONE;
1099                         for (auto e : e_ids) {
1100                             if (o_ptr->ego_idx == e) {
1101                                 e_idx = e;
1102                                 break;
1103                             }
1104                         }
1105
1106                         if (e_idx != EgoType::NONE) {
1107                             break;
1108                         }
1109                     }
1110
1111                     if (i == max_roll) {
1112                         msg_print(_("失敗!もう一度願ってみてください。", "Failed! Try again."));
1113                         return WishResultType::FAIL;
1114                     }
1115                 }
1116             } else {
1117                 wishing_puff_of_smoke();
1118             }
1119
1120             res = WishResultType::EGO;
1121         } else {
1122             for (int i = 0; i < 100; i++) {
1123                 o_ptr->prep(bi_id);
1124                 ItemMagicApplier(player_ptr, o_ptr, 0, AM_NO_FIXED_ART).execute();
1125                 if (!o_ptr->is_cursed()) {
1126                     break;
1127                 }
1128             }
1129             res = WishResultType::NORMAL;
1130         }
1131
1132         if (blessed && wield_slot(player_ptr, o_ptr) != -1) {
1133             o_ptr->art_flags.set(TR_BLESSED);
1134         }
1135
1136         if (fixed && wield_slot(player_ptr, o_ptr) != -1) {
1137             o_ptr->art_flags.set(TR_IGNORE_ACID);
1138             o_ptr->art_flags.set(TR_IGNORE_FIRE);
1139         }
1140
1141         (void)drop_near(player_ptr, o_ptr, -1, player_ptr->y, player_ptr->x);
1142
1143         return res;
1144     }
1145
1146     msg_print(_("うーん、そんなものは存在しないようだ。", "Ummmm, that is not existing..."));
1147     return WishResultType::FAIL;
1148 }