OSDN Git Service

[Refactor] #3286 Removed player-redraw-types.h
[hengbandforosx/hengbandosx.git] / src / cmd-io / cmd-gameoption.cpp
1 #include "cmd-io/cmd-gameoption.h"
2 #include "autopick/autopick.h"
3 #include "cmd-io/cmd-autopick.h"
4 #include "cmd-io/cmd-dump.h"
5 #include "core/asking-player.h"
6 #include "core/show-file.h"
7 #include "core/window-redrawer.h"
8 #include "floor/geometry.h"
9 #include "game-option/game-play-options.h"
10 #include "game-option/keymap-directory-getter.h"
11 #include "game-option/option-flags.h"
12 #include "game-option/option-types-table.h"
13 #include "game-option/special-options.h"
14 #include "io/input-key-acceptor.h"
15 #include "io/write-diary.h"
16 #include "main/sound-of-music.h"
17 #include "system/game-option-types.h"
18 #include "system/player-type-definition.h"
19 #include "system/redrawing-flags-updater.h"
20 #include "term/gameterm.h"
21 #include "term/screen-processor.h"
22 #include "term/term-color-types.h"
23 #include "term/z-form.h"
24 #include "util/bit-flags-calculator.h"
25 #include "util/int-char-converter.h"
26 #include "util/string-processor.h"
27 #include "view/display-messages.h"
28 #include "world/world.h"
29
30 #define OPT_NUM 15
31
32 struct opts {
33     char key;
34     concptr name;
35     int row;
36 };
37
38 static opts option_fields[OPT_NUM] = {
39     { '1', _("    キー入力     オプション", "Input Options"), 3 },
40     { '2', _("   マップ画面    オプション", "Map Screen Options"), 4 },
41     { '3', _("  テキスト表示   オプション", "Text Display Options"), 5 },
42     { '4', _("  ゲームプレイ   オプション", "Game-Play Options"), 6 },
43     { '5', _("  行動中止関係   オプション", "Disturbance Options"), 7 },
44     { '6', _("  簡易自動破壊   オプション", "Easy Auto-Destroyer Options"), 8 },
45     { 'r', _("   プレイ記録    オプション", "Play record Options"), 9 },
46
47     { 'p', _("自動拾いエディタ", "Auto-picker/destroyer editor"), 11 },
48     { 'd', _(" 基本ウェイト量 ", "Base Delay Factor"), 12 },
49     { 'h', _("低ヒットポイント", "Hitpoint Warning"), 13 },
50     { 'm', _("  低魔力色閾値  ", "Mana Color Threshold"), 14 },
51     { 'a', _("   自動セーブ    オプション", "Autosave Options"), 15 },
52     { 'w', _("ウインドウフラグ", "Window Flags"), 16 },
53
54     { 'b', _("      初期       オプション (参照のみ)", "Birth Options (Browse Only)"), 18 },
55     { 'c', _("      詐欺       オプション", "Cheat Options"), 19 },
56 };
57
58 /*!
59  * @brief セーブ頻度ターンの次の値を返す
60  * @param current 現在のセーブ頻度ターン値
61  * @return 次のセーブ頻度ターン値
62  */
63 static int16_t toggle_frequency(int16_t current)
64 {
65     switch (current) {
66     case 0:
67         return 50;
68     case 50:
69         return 100;
70     case 100:
71         return 250;
72     case 250:
73         return 500;
74     case 500:
75         return 1000;
76     case 1000:
77         return 2500;
78     case 2500:
79         return 5000;
80     case 5000:
81         return 10000;
82     case 10000:
83         return 25000;
84     default:
85         return 0;
86     }
87 }
88
89 /*!
90  * @brief 自動セーブオプションを変更するコマンドのメインルーチン
91  * @param info 表示メッセージ
92  */
93 static void do_cmd_options_autosave(PlayerType *player_ptr, concptr info)
94 {
95     char ch;
96     int i, k = 0, n = 2;
97     term_clear();
98     while (true) {
99         prt(format(_("%s ( リターンで次へ, y/n でセット, F で頻度を入力, ESC で決定 ) ", "%s (RET to advance, y/n to set, 'F' for frequency, ESC to accept) "), info), 0, 0);
100         for (i = 0; i < n; i++) {
101             byte a = TERM_WHITE;
102             if (i == k) {
103                 a = TERM_L_BLUE;
104             }
105
106             c_prt(a, format("%-48s: %s (%s)", autosave_info[i].o_desc, (*autosave_info[i].o_var ? _("はい  ", "yes") : _("いいえ", "no ")), autosave_info[i].o_text), i + 2, 0);
107         }
108
109         prt(format(_("自動セーブの頻度: %d ターン毎", "Timed autosave frequency: every %d turns"), autosave_freq), 5, 0);
110         move_cursor(k + 2, 50);
111         ch = inkey();
112         switch (ch) {
113         case ESCAPE: {
114             return;
115         }
116
117         case '-':
118         case '8': {
119             k = (n + k - 1) % n;
120             break;
121         }
122
123         case ' ':
124         case '\n':
125         case '\r':
126         case '2': {
127             k = (k + 1) % n;
128             break;
129         }
130
131         case 'y':
132         case 'Y':
133         case '6': {
134
135             (*autosave_info[k].o_var) = true;
136             k = (k + 1) % n;
137             break;
138         }
139
140         case 'n':
141         case 'N':
142         case '4': {
143             (*autosave_info[k].o_var) = false;
144             k = (k + 1) % n;
145             break;
146         }
147
148         case 'f':
149         case 'F': {
150             autosave_freq = toggle_frequency(autosave_freq);
151             prt(format(_("自動セーブの頻度: %d ターン毎", "Timed autosave frequency: every %d turns"), autosave_freq), 5, 0);
152             break;
153         }
154
155         case '?': {
156             (void)show_file(player_ptr, true, _("joption.txt#Autosave", "option.txt#Autosave"), nullptr, 0, 0);
157             term_clear();
158             break;
159         }
160
161         default: {
162             bell();
163             break;
164         }
165         }
166     }
167 }
168
169 /*!
170  * @brief 指定のサブウィンドウが指定のウィンドウフラグを持つか調べる
171  * @param x ウィンドウ番号
172  * @param y ウィンドウフラグ番号
173  * @return 持つならTRUE、持たないならFALSE
174  */
175 static bool has_window_flag(int x, int y)
176 {
177     auto flag = i2enum<window_redraw_type>(1UL << y);
178     return any_bits(window_flag[x], flag);
179 }
180
181 /*!
182  * @brief 指定のサブウィンドウに指定のウィンドウフラグをセットする
183  * @param x ウィンドウ番号
184  * @param y ウィンドウフラグ番号
185  * @details
186  * 未使用フラグはセットしない。
187  */
188 static void set_window_flag(int x, int y)
189 {
190     auto flag = i2enum<window_redraw_type>(1UL << y);
191     if (any_bits(PW_ALL, flag)) {
192         set_bits(window_flag[x], flag);
193     }
194 }
195
196 /*!
197  * @brief 指定のウィンドウフラグをサブウィンドウからクリアする
198  * @param y ウィンドウフラグ番号
199  */
200 static void clear_window_flag(int x, int y)
201 {
202     window_flag[x] = 0;
203
204     auto flag = i2enum<window_redraw_type>(1UL << y);
205     for (int i = 0; i < 8; i++) {
206         reset_bits(window_flag[i], flag);
207     }
208 }
209
210 /*!
211  * @brief ウィンドウオプションを変更するコマンドのメインルーチン /
212  * Modify the "window" options
213  */
214 static void do_cmd_options_win(PlayerType *player_ptr)
215 {
216     int i, j, d;
217     TERM_LEN y = 0;
218     TERM_LEN x = 0;
219     char ch;
220     bool go = true;
221     uint32_t old_flag[8];
222
223     for (j = 0; j < 8; j++) {
224         old_flag[j] = window_flag[j];
225     }
226
227     term_clear();
228     while (go) {
229         prt(_("ウィンドウ・フラグ (<方向>で移動, 't'でON/OFF,'s'でON(他窓OFF), ESC)", "Window Flags (<dir>, <t>oggle, <s>et, ESC) "), 0, 0);
230         for (j = 0; j < 8; j++) {
231             byte a = TERM_WHITE;
232             concptr s = angband_term_name[j];
233             if (j == x) {
234                 a = TERM_L_BLUE;
235             }
236
237             term_putstr(35 + j * 5 - strlen(s) / 2, 2 + j % 2, -1, a, s);
238         }
239
240         for (i = 0; i < 16; i++) {
241             byte a = TERM_WHITE;
242             concptr str = window_flag_desc[i];
243             if (i == y) {
244                 a = TERM_L_BLUE;
245             }
246
247             if (!str) {
248                 str = _("(未使用)", "(Unused option)");
249             }
250
251             term_putstr(0, i + 5, -1, a, str);
252             for (j = 0; j < 8; j++) {
253                 char c = '.';
254                 a = TERM_WHITE;
255                 if ((i == y) && (j == x)) {
256                     a = TERM_L_BLUE;
257                 }
258
259                 if (window_flag[j] & (1UL << i)) {
260                     c = 'X';
261                 }
262
263                 term_putch(35 + j * 5, i + 5, a, c);
264             }
265         }
266
267         bool has_flag = false;
268         term_gotoxy(35 + x * 5, y + 5);
269         ch = inkey();
270         switch (ch) {
271         case ESCAPE:
272             go = false;
273             break;
274         case ' ':
275         case 't':
276         case 'T':
277             has_flag = has_window_flag(x, y);
278             window_flag[x] = 0;
279             if (x > 0 && !has_flag) {
280                 set_window_flag(x, y);
281             }
282             break;
283         case 's':
284         case 'S':
285             if (x == 0) {
286                 break;
287             }
288             window_flag[x] = 0;
289             clear_window_flag(x, y);
290             set_window_flag(x, y);
291             break;
292         case '?':
293             (void)show_file(player_ptr, true, _("joption.txt#Window", "option.txt#Window"), nullptr, 0, 0);
294             term_clear();
295             break;
296         default:
297             d = get_keymap_dir(ch);
298             x = (x + ddx[d] + 8) % 8;
299             y = (y + ddy[d] + 16) % 16;
300             if (!d) {
301                 bell();
302             }
303             break;
304         }
305     }
306
307     for (auto term_index = 0U; term_index < angband_terms.size(); ++term_index) {
308         term_type *old = game_term;
309         if (!angband_terms[term_index]) {
310             continue;
311         }
312
313         if (window_flag[term_index] == old_flag[term_index]) {
314             continue;
315         }
316
317         term_activate(angband_terms[term_index]);
318         term_clear();
319         term_fresh();
320         term_activate(old);
321     }
322 }
323
324 /*!
325  * @brief チートオプションを変更するコマンドのメインルーチン
326  * Interact with some options for cheating
327  * @param info 表示メッセージ
328  */
329 static void do_cmd_options_cheat(PlayerType *player_ptr, concptr info)
330 {
331     term_clear();
332     auto k = 0U;
333     const auto n = cheat_info.size();
334     while (true) {
335         prt(format(_("%s ( リターンで次へ, y/n でセット, ESC で決定 )", "%s (RET to advance, y/n to set, ESC to accept) "), info), 0, 0);
336
337 #ifdef JP
338         /* 詐欺オプションをうっかりいじってしまう人がいるようなので注意 */
339         prt("                                 <<  注意  >>", 11, 0);
340         prt("      詐欺オプションを一度でも設定すると、スコア記録が残らなくなります!", 12, 0);
341         prt("      後に解除してもダメですので、勝利者を目指す方はここのオプションはい", 13, 0);
342         prt("      じらないようにして下さい。", 14, 0);
343 #endif
344         for (auto i = 0U; i < n; i++) {
345             auto a = TERM_WHITE;
346             if (i == k) {
347                 a = TERM_L_BLUE;
348             }
349
350             c_prt(enum2i(a), format("%-48s: %s (%s)", cheat_info[i].o_desc, (*cheat_info[i].o_var ? _("はい  ", "yes") : _("いいえ", "no ")), cheat_info[i].o_text), i + 2, 0);
351         }
352
353         move_cursor(k + 2, 50);
354         auto ch = inkey();
355         auto dir = get_keymap_dir(ch);
356         if ((dir == 2) || (dir == 4) || (dir == 6) || (dir == 8)) {
357             ch = I2D(dir);
358         }
359
360         switch (ch) {
361         case ESCAPE:
362             return;
363         case '-':
364         case '8':
365             k = (n + k - 1) % n;
366             break;
367         case ' ':
368         case '\n':
369         case '\r':
370         case '2':
371             k = (k + 1) % n;
372             break;
373         case 'y':
374         case 'Y':
375         case '6':
376             if (!w_ptr->noscore) {
377                 exe_write_diary(player_ptr, DIARY_DESCRIPTION, 0,
378                     _("詐欺オプションをONにして、スコアを残せなくなった。", "gave up sending score to use cheating options."));
379             }
380
381             w_ptr->noscore |= cheat_info[k].o_set * 256 + cheat_info[k].o_bit;
382             *cheat_info[k].o_var = true;
383             k = (k + 1) % n;
384             break;
385         case 'n':
386         case 'N':
387         case '4':
388             *cheat_info[k].o_var = false;
389             k = (k + 1) % n;
390             break;
391         case '?':
392             (void)show_file(player_ptr, true, std::string(_("joption.txt#", "option.txt#")).append(cheat_info[k].o_text).data(), nullptr, 0, 0);
393             term_clear();
394             break;
395         default:
396             bell();
397             break;
398         }
399     }
400 }
401
402 /*!
403  * @brief ビットセットからゲームオプションを展開する / Extract option variables from bit sets
404  */
405 void extract_option_vars(void)
406 {
407     for (int i = 0; option_info[i].o_desc; i++) {
408         int os = option_info[i].o_set;
409         int ob = option_info[i].o_bit;
410         if (option_info[i].o_var) {
411             if (option_flag[os] & (1UL << ob)) {
412                 (*option_info[i].o_var) = true;
413             } else {
414                 (*option_info[i].o_var) = false;
415             }
416         }
417     }
418 }
419
420 /*!
421  * @brief 標準オプションを変更するコマンドのメインルーチン /
422  * Set or unset various options.
423  * @details
424  * <pre>
425  * The user must use the "Ctrl-R" command to "adapt" to changes
426  * in any options which control "visual" aspects of the game.
427  * </pre>
428  */
429 void do_cmd_options(PlayerType *player_ptr)
430 {
431     TermCenteredOffsetSetter tcos(MAIN_TERM_MIN_COLS, MAIN_TERM_MIN_ROWS);
432
433     char k;
434     int d, skey;
435     TERM_LEN i, y = 0;
436     screen_save();
437     while (true) {
438         int n = OPT_NUM;
439         if (!w_ptr->noscore && !allow_debug_opts) {
440             n--;
441         }
442
443         term_clear();
444         prt(_("[ オプションの設定 ]", "Game options"), 1, 0);
445         while (true) {
446             for (i = 0; i < n; i++) {
447                 byte a = TERM_WHITE;
448                 if (i == y) {
449                     a = TERM_L_BLUE;
450                 }
451                 term_putstr(5, option_fields[i].row, -1, a, format("(%c) %s", toupper(option_fields[i].key), option_fields[i].name));
452             }
453
454             prt(_("<方向>で移動, Enterで決定, ESCでキャンセル, ?でヘルプ: ", "Move to <dir>, Select to Enter, Cancel to ESC, ? to help: "), 21, 0);
455             skey = inkey_special(true);
456             if (!(skey & SKEY_MASK)) {
457                 k = (char)skey;
458             } else {
459                 k = 0;
460             }
461
462             if (k == ESCAPE) {
463                 break;
464             }
465
466             if (angband_strchr("\n\r ", k)) {
467                 k = option_fields[y].key;
468                 break;
469             }
470
471             for (i = 0; i < n; i++) {
472                 if (tolower(k) == option_fields[i].key) {
473                     break;
474                 }
475             }
476
477             if (i < n) {
478                 break;
479             }
480
481             if (k == '?') {
482                 break;
483             }
484
485             d = 0;
486             if (skey == SKEY_UP) {
487                 d = 8;
488             }
489             if (skey == SKEY_DOWN) {
490                 d = 2;
491             }
492             y = (y + ddy[d] + n) % n;
493             if (!d) {
494                 bell();
495             }
496         }
497
498         if (k == ESCAPE) {
499             break;
500         }
501
502         switch (k) {
503         case '1': {
504             do_cmd_options_aux(player_ptr, OPT_PAGE_INPUT, _("キー入力オプション", "Input Options"));
505             break;
506         }
507         case '2': {
508             do_cmd_options_aux(player_ptr, OPT_PAGE_MAPSCREEN, _("マップ画面オプション", "Map Screen Options"));
509             break;
510         }
511         case '3': {
512             do_cmd_options_aux(player_ptr, OPT_PAGE_TEXT, _("テキスト表示オプション", "Text Display Options"));
513             break;
514         }
515         case '4': {
516             do_cmd_options_aux(player_ptr, OPT_PAGE_GAMEPLAY, _("ゲームプレイ・オプション", "Game-Play Options"));
517             break;
518         }
519         case '5': {
520             do_cmd_options_aux(player_ptr, OPT_PAGE_DISTURBANCE, _("行動中止関係のオプション", "Disturbance Options"));
521             break;
522         }
523         case '6': {
524             do_cmd_options_aux(player_ptr, OPT_PAGE_AUTODESTROY, _("簡易自動破壊オプション", "Easy Auto-Destroyer Options"));
525             break;
526         }
527         case 'R':
528         case 'r': {
529             do_cmd_options_aux(player_ptr, OPT_PAGE_PLAYRECORD, _("プレイ記録オプション", "Play-record Options"));
530             break;
531         }
532         case 'B':
533         case 'b': {
534             do_cmd_options_aux(player_ptr, OPT_PAGE_BIRTH,
535                 (!w_ptr->wizard || !allow_debug_opts) ? _("初期オプション(参照のみ)", "Birth Options(browse only)")
536                                                       : _("初期オプション((*)はスコアに影響)", "Birth Options ((*)) affect score"));
537             break;
538         }
539         case 'C':
540         case 'c': {
541             if (!w_ptr->noscore && !allow_debug_opts) {
542                 bell();
543                 break;
544             }
545
546             do_cmd_options_cheat(player_ptr, _("詐欺師は決して勝利できない!", "Cheaters never win"));
547             break;
548         }
549         case 'a':
550         case 'A': {
551             do_cmd_options_autosave(player_ptr, _("自動セーブ", "Autosave"));
552             break;
553         }
554         case 'W':
555         case 'w': {
556             do_cmd_options_win(player_ptr);
557             player_ptr->window_flags = PW_ALL;
558             break;
559         }
560         case 'P':
561         case 'p': {
562             do_cmd_edit_autopick(player_ptr);
563             break;
564         }
565         case 'D':
566         case 'd': {
567             clear_from(18);
568             prt(format(_("現在ウェイト量(msec): %d", "Current Delay Factor(msec): %d"), delay_factor), 19, 0);
569             (void)get_value(_("コマンド: ウェイト量(msec)", "Command: Delay Factor(msec)"), 0, 1000, &delay_factor);
570             clear_from(18);
571             break;
572         }
573         case 'H':
574         case 'h': {
575             clear_from(18);
576             prt(_("コマンド: 低ヒットポイント警告", "Command: Hitpoint Warning"), 19, 0);
577             while (true) {
578                 prt(format(_("現在の低ヒットポイント警告: %d0%%", "Current hitpoint warning: %d0%%"), hitpoint_warn), 22, 0);
579                 prt(_("低ヒットポイント警告 (0-9) ESCで決定: ", "Hitpoint Warning (0-9 or ESC to accept): "), 20, 0);
580                 k = inkey();
581                 if (k == ESCAPE) {
582                     break;
583                 } else if (k == '?') {
584                     (void)show_file(player_ptr, true, _("joption.txt#Hitpoint", "option.txt#Hitpoint"), nullptr, 0, 0);
585                     term_clear();
586                 } else if (isdigit(k)) {
587                     hitpoint_warn = D2I(k);
588                 } else {
589                     bell();
590                 }
591             }
592
593             break;
594         }
595         case 'M':
596         case 'm': {
597             clear_from(18);
598             prt(_("コマンド: 低魔力色閾値", "Command: Mana Color Threshold"), 19, 0);
599             while (true) {
600                 prt(format(_("現在の低魔力色閾値: %d0%%", "Current mana color threshold: %d0%%"), mana_warn), 22, 0);
601                 prt(_("低魔力閾値 (0-9) ESCで決定: ", "Mana color Threshold (0-9 or ESC to accept): "), 20, 0);
602                 k = inkey();
603                 if (k == ESCAPE) {
604                     break;
605                 } else if (k == '?') {
606                     (void)show_file(player_ptr, true, _("joption.txt#Manapoint", "option.txt#Manapoint"), nullptr, 0, 0);
607                     term_clear();
608                 } else if (isdigit(k)) {
609                     mana_warn = D2I(k);
610                 } else {
611                     bell();
612                 }
613             }
614
615             break;
616         }
617         case '?':
618             (void)show_file(player_ptr, true, _("joption.txt", "option.txt"), nullptr, 0, 0);
619             term_clear();
620             break;
621         default: {
622             bell();
623             break;
624         }
625         }
626
627         msg_erase();
628     }
629
630     screen_load();
631     RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::EQUIPPY);
632 }
633
634 /*!
635  * @brief 標準オプションを変更するコマンドのサブルーチン /
636  * Interact with some options
637  * @param page オプションページ番号
638  * @param info 表示メッセージ
639  */
640 void do_cmd_options_aux(PlayerType *player_ptr, game_option_types page, concptr info)
641 {
642     char ch;
643     int i, k = 0, n = 0, l;
644     int opt[MAIN_TERM_MIN_ROWS];
645     bool browse_only = (page == OPT_PAGE_BIRTH) && w_ptr->character_generated && (!w_ptr->wizard || !allow_debug_opts);
646
647     for (i = 0; i < MAIN_TERM_MIN_ROWS; i++) {
648         opt[i] = 0;
649     }
650
651     for (i = 0; option_info[i].o_desc; i++) {
652         if (option_info[i].o_page == page) {
653             opt[n++] = i;
654         }
655     }
656
657     term_clear();
658     while (true) {
659         DIRECTION dir;
660         constexpr auto command = _("%s (リターン:次, %sESC:終了, ?:ヘルプ) ", "%s (RET:next, %s, ?:help) ");
661         prt(format(command, info, browse_only ? _("", "ESC:exit") : _("y/n:変更, ", "y/n:change, ESC:accept")), 0, 0);
662         if (page == OPT_PAGE_AUTODESTROY) {
663             constexpr auto mes = _("以下のオプションは、簡易自動破壊を使用するときのみ有効", "Following options will protect items from easy auto-destroyer.");
664             c_prt(TERM_YELLOW, mes, 6, _(6, 3));
665         }
666
667         for (i = 0; i < n; i++) {
668             byte a = TERM_WHITE;
669             if (i == k) {
670                 a = TERM_L_BLUE;
671             }
672
673             std::string label = format("%-48s: %s (%.19s)", option_info[opt[i]].o_desc, (*option_info[opt[i]].o_var ? _("はい  ", "yes") : _("いいえ", "no ")),
674                 option_info[opt[i]].o_text);
675             if ((page == OPT_PAGE_AUTODESTROY) && i > 2) {
676                 c_prt(a, label, i + 5, 0);
677             } else {
678                 c_prt(a, label, i + 2, 0);
679             }
680         }
681
682         if ((page == OPT_PAGE_AUTODESTROY) && (k > 2)) {
683             l = 3;
684         } else {
685             l = 0;
686         }
687
688         move_cursor(k + 2 + l, 50);
689         ch = inkey();
690         dir = get_keymap_dir(ch);
691         if ((dir == 2) || (dir == 4) || (dir == 6) || (dir == 8)) {
692             ch = I2D(dir);
693         }
694
695         switch (ch) {
696         case ESCAPE: {
697             return;
698         }
699         case '-':
700         case '8': {
701             k = (n + k - 1) % n;
702             break;
703         }
704         case ' ':
705         case '\n':
706         case '\r':
707         case '2': {
708             k = (k + 1) % n;
709             break;
710         }
711         case 'y':
712         case 'Y':
713         case '6': {
714             if (browse_only) {
715                 break;
716             }
717             (*option_info[opt[k]].o_var) = true;
718             k = (k + 1) % n;
719             break;
720         }
721         case 'n':
722         case 'N':
723         case '4': {
724             if (browse_only) {
725                 break;
726             }
727             (*option_info[opt[k]].o_var) = false;
728             k = (k + 1) % n;
729             break;
730         }
731         case 't':
732         case 'T': {
733             if (!browse_only) {
734                 (*option_info[opt[k]].o_var) = !(*option_info[opt[k]].o_var);
735             }
736             break;
737         }
738         case '?': {
739             (void)show_file(player_ptr, true, std::string(_("joption.txt#", "option.txt#")).append(option_info[opt[k]].o_text).data(), nullptr, 0, 0);
740             term_clear();
741             break;
742         }
743         default: {
744             bell();
745             break;
746         }
747         }
748     }
749 }