OSDN Git Service

Merge branch 'develop' into macos-develop
[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"), 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     if (x == 0) {
178         return false;
179     }
180
181     auto flag = i2enum<SubWindowRedrawingFlag>(y);
182     return g_window_flags[x].has(flag);
183 }
184
185 /*!
186  * @brief 指定のサブウィンドウに指定のウィンドウフラグをセットする
187  * @param x ウィンドウ番号
188  * @param y ウィンドウフラグ番号
189  * @details
190  * 未使用フラグはセットしない。
191  */
192 static void set_window_flag(int x, int y)
193 {
194     if (x == 0) {
195         return;
196     }
197
198     auto flag = i2enum<SubWindowRedrawingFlag>(y);
199     g_window_flags[x].set(flag);
200 }
201
202 /*!
203  * @brief 指定のウィンドウフラグをサブウィンドウからクリアする
204  * @param y ウィンドウフラグ番号
205  */
206 static void clear_window_flag(int x, int y)
207 {
208     g_window_flags[x].clear();
209     if (x == 0) {
210         return;
211     }
212
213     auto flag = i2enum<SubWindowRedrawingFlag>(y);
214     for (auto &window_flag : g_window_flags) {
215         window_flag.reset(flag);
216     }
217 }
218
219 /*!
220  * @brief ウィンドウオプションを変更するコマンドのメインルーチン /
221  * Modify the "window" options
222  */
223 static void do_cmd_options_win(PlayerType *player_ptr)
224 {
225     int i, j, d;
226     TERM_LEN y = 0;
227     TERM_LEN x = 0;
228     char ch;
229     bool go = true;
230     const auto old_flags = g_window_flags;
231     term_clear();
232     while (go) {
233         prt(_("ウィンドウ・フラグ (<方向>で移動, 't'でON/OFF,'s'でON(他窓OFF), ESC)", "Window Flags (<dir>, <t>oggle, <s>et, ESC) "), 0, 0);
234         for (j = 0; j < 8; j++) {
235             byte a = TERM_WHITE;
236             auto s = angband_term_name[j];
237             if (j == x) {
238                 a = TERM_L_BLUE;
239             }
240
241             term_putstr(35 + j * 5 - strlen(s) / 2, 2 + j % 2, -1, a, s);
242         }
243
244         for (i = 0; i < 16; i++) {
245             byte a = TERM_WHITE;
246             concptr str = window_flag_desc[i];
247             if (i == y) {
248                 a = TERM_L_BLUE;
249             }
250
251             if (!str) {
252                 str = _("(未使用)", "(Unused option)");
253             }
254
255             term_putstr(0, i + 5, -1, a, str);
256             for (j = 0; j < 8; j++) {
257                 char c = '.';
258                 a = TERM_WHITE;
259                 if ((i == y) && (j == x)) {
260                     a = TERM_L_BLUE;
261                 }
262
263                 if (g_window_flags[j].has(i2enum<SubWindowRedrawingFlag>(i))) {
264                     c = 'X';
265                 }
266
267                 term_putch(35 + j * 5, i + 5, a, c);
268             }
269         }
270
271         bool has_flag = false;
272         term_gotoxy(35 + x * 5, y + 5);
273         ch = inkey();
274         switch (ch) {
275         case ESCAPE:
276             go = false;
277             break;
278         case ' ':
279         case 't':
280         case 'T':
281             has_flag = has_window_flag(x, y);
282             g_window_flags[x].clear();
283             if (x > 0 && !has_flag) {
284                 set_window_flag(x, y);
285             }
286             break;
287         case 's':
288         case 'S':
289             if (x == 0) {
290                 break;
291             }
292             g_window_flags[x].clear();
293             clear_window_flag(x, y);
294             set_window_flag(x, y);
295             break;
296         case '?':
297             (void)show_file(player_ptr, true, _("joption.txt#Window", "option.txt#Window"), 0, 0);
298             term_clear();
299             break;
300         default:
301             d = get_keymap_dir(ch);
302             x = (x + ddx[d] + 8) % 8;
303             y = (y + ddy[d] + 16) % 16;
304             if (!d) {
305                 bell();
306             }
307             break;
308         }
309     }
310
311     for (auto term_index = 0U; term_index < angband_terms.size(); ++term_index) {
312         term_type *old = game_term;
313         if (!angband_terms[term_index]) {
314             continue;
315         }
316
317         if (g_window_flags[term_index] == old_flags[term_index]) {
318             continue;
319         }
320
321         term_activate(angband_terms[term_index]);
322         term_clear();
323         term_fresh();
324         term_activate(old);
325     }
326 }
327
328 /*!
329  * @brief チートオプションを変更するコマンドのメインルーチン
330  * Interact with some options for cheating
331  * @param info 表示メッセージ
332  */
333 static void do_cmd_options_cheat(PlayerType *player_ptr, concptr info)
334 {
335     term_clear();
336     auto k = 0U;
337     const auto n = cheat_info.size();
338     while (true) {
339         prt(format(_("%s ( リターンで次へ, y/n でセット, ESC で決定 )", "%s (RET to advance, y/n to set, ESC to accept) "), info), 0, 0);
340
341 #ifdef JP
342         /* 詐欺オプションをうっかりいじってしまう人がいるようなので注意 */
343         prt("                                 <<  注意  >>", 11, 0);
344         prt("      詐欺オプションを一度でも設定すると、スコア記録が残らなくなります!", 12, 0);
345         prt("      後に解除してもダメですので、勝利者を目指す方はここのオプションはい", 13, 0);
346         prt("      じらないようにして下さい。", 14, 0);
347 #endif
348         for (auto i = 0U; i < n; i++) {
349             auto a = TERM_WHITE;
350             if (i == k) {
351                 a = TERM_L_BLUE;
352             }
353
354             const auto yesno = *cheat_info[i].o_var ? _("はい  ", "yes") : _("いいえ", "no ");
355             c_prt(enum2i(a), format("%-48s: %s (%s)", cheat_info[i].o_desc, yesno, cheat_info[i].o_text), i + 2, 0);
356         }
357
358         move_cursor(k + 2, 50);
359         auto ch = inkey();
360         auto dir = get_keymap_dir(ch);
361         if ((dir == 2) || (dir == 4) || (dir == 6) || (dir == 8)) {
362             ch = I2D(dir);
363         }
364
365         switch (ch) {
366         case ESCAPE:
367             return;
368         case '-':
369         case '8':
370             k = (n + k - 1) % n;
371             break;
372         case ' ':
373         case '\n':
374         case '\r':
375         case '2':
376             k = (k + 1) % n;
377             break;
378         case 'y':
379         case 'Y':
380         case '6':
381             if (!w_ptr->noscore) {
382                 exe_write_diary(player_ptr, DiaryKind::DESCRIPTION, 0,
383                     _("詐欺オプションをONにして、スコアを残せなくなった。", "gave up sending score to use cheating options."));
384             }
385
386             w_ptr->noscore |= cheat_info[k].o_set * 256 + cheat_info[k].o_bit;
387             *cheat_info[k].o_var = true;
388             k = (k + 1) % n;
389             break;
390         case 'n':
391         case 'N':
392         case '4':
393             *cheat_info[k].o_var = false;
394             k = (k + 1) % n;
395             break;
396         case '?':
397             (void)show_file(player_ptr, true, std::string(_("joption.txt#", "option.txt#")).append(cheat_info[k].o_text), 0, 0);
398             term_clear();
399             break;
400         default:
401             bell();
402             break;
403         }
404     }
405 }
406
407 /*!
408  * @brief ビットセットからゲームオプションを展開する / Extract option variables from bit sets
409  */
410 void extract_option_vars(void)
411 {
412     for (int i = 0; option_info[i].o_desc; i++) {
413         int os = option_info[i].o_set;
414         int ob = option_info[i].o_bit;
415         if (option_info[i].o_var) {
416             if (g_option_flags[os] & (1UL << ob)) {
417                 (*option_info[i].o_var) = true;
418             } else {
419                 (*option_info[i].o_var) = false;
420             }
421         }
422     }
423 }
424
425 /*!
426  * @brief 標準オプションを変更するコマンドのメインルーチン /
427  * Set or unset various options.
428  * @details
429  * <pre>
430  * The user must use the "Ctrl-R" command to "adapt" to changes
431  * in any options which control "visual" aspects of the game.
432  * </pre>
433  */
434 void do_cmd_options(PlayerType *player_ptr)
435 {
436     TermCenteredOffsetSetter tcos(MAIN_TERM_MIN_COLS, MAIN_TERM_MIN_ROWS);
437
438     char k;
439     int d, skey;
440     TERM_LEN i, y = 0;
441     screen_save();
442     while (true) {
443         int n = OPT_NUM;
444         if (!w_ptr->noscore && !allow_debug_opts) {
445             n--;
446         }
447
448         term_clear();
449         prt(_("[ オプションの設定 ]", "Game options"), 1, 0);
450         while (true) {
451             for (i = 0; i < n; i++) {
452                 byte a = TERM_WHITE;
453                 if (i == y) {
454                     a = TERM_L_BLUE;
455                 }
456                 term_putstr(5, option_fields[i].row, -1, a, format("(%c) %s", toupper(option_fields[i].key), option_fields[i].name));
457             }
458
459             prt(_("<方向>で移動, Enterで決定, ESCでキャンセル, ?でヘルプ: ", "Move to <dir>, Select to Enter, Cancel to ESC, ? to help: "), 21, 0);
460             skey = inkey_special(true);
461             if (!(skey & SKEY_MASK)) {
462                 k = (char)skey;
463             } else {
464                 k = 0;
465             }
466
467             if (k == ESCAPE) {
468                 break;
469             }
470
471             if (angband_strchr("\n\r ", k)) {
472                 k = option_fields[y].key;
473                 break;
474             }
475
476             for (i = 0; i < n; i++) {
477                 if (tolower(k) == option_fields[i].key) {
478                     break;
479                 }
480             }
481
482             if (i < n) {
483                 break;
484             }
485
486             if (k == '?') {
487                 break;
488             }
489
490             d = 0;
491             if (skey == SKEY_UP) {
492                 d = 8;
493             }
494             if (skey == SKEY_DOWN) {
495                 d = 2;
496             }
497             y = (y + ddy[d] + n) % n;
498             if (!d) {
499                 bell();
500             }
501         }
502
503         if (k == ESCAPE) {
504             break;
505         }
506
507         switch (k) {
508         case '1': {
509             do_cmd_options_aux(player_ptr, OPT_PAGE_INPUT, _("キー入力オプション", "Input Options"));
510             break;
511         }
512         case '2': {
513             do_cmd_options_aux(player_ptr, OPT_PAGE_MAPSCREEN, _("マップ画面オプション", "Map Screen Options"));
514             break;
515         }
516         case '3': {
517             do_cmd_options_aux(player_ptr, OPT_PAGE_TEXT, _("テキスト表示オプション", "Text Display Options"));
518             break;
519         }
520         case '4': {
521             do_cmd_options_aux(player_ptr, OPT_PAGE_GAMEPLAY, _("ゲームプレイ・オプション", "Game-Play Options"));
522             break;
523         }
524         case '5': {
525             do_cmd_options_aux(player_ptr, OPT_PAGE_DISTURBANCE, _("行動中止関係のオプション", "Disturbance Options"));
526             break;
527         }
528         case '6': {
529             do_cmd_options_aux(player_ptr, OPT_PAGE_AUTODESTROY, _("簡易自動破壊オプション", "Easy Auto-Destroyer Options"));
530             break;
531         }
532         case 'R':
533         case 'r': {
534             do_cmd_options_aux(player_ptr, OPT_PAGE_PLAYRECORD, _("プレイ記録オプション", "Play-record Options"));
535             break;
536         }
537         case 'B':
538         case 'b': {
539             do_cmd_options_aux(player_ptr, OPT_PAGE_BIRTH,
540                 (!w_ptr->wizard || !allow_debug_opts) ? _("初期オプション(参照のみ)", "Birth Options(browse only)")
541                                                       : _("初期オプション((*)はスコアに影響)", "Birth Options ((*)) affect score"));
542             break;
543         }
544         case 'C':
545         case 'c': {
546             if (!w_ptr->noscore && !allow_debug_opts) {
547                 bell();
548                 break;
549             }
550
551             do_cmd_options_cheat(player_ptr, _("詐欺師は決して勝利できない!", "Cheaters never win"));
552             break;
553         }
554         case 'a':
555         case 'A': {
556             do_cmd_options_autosave(player_ptr, _("自動セーブ", "Autosave"));
557             break;
558         }
559         case 'W':
560         case 'w': {
561             do_cmd_options_win(player_ptr);
562             RedrawingFlagsUpdater::get_instance().fill_up_sub_flags();
563             break;
564         }
565         case 'P':
566         case 'p': {
567             do_cmd_edit_autopick(player_ptr);
568             break;
569         }
570         case 'D':
571         case 'd': {
572             clear_from(18);
573             prt(format(_("現在ウェイト量(msec): %d", "Current Delay Factor(msec): %d"), delay_factor), 19, 0);
574             constexpr auto prompt = _("コマンド: ウェイト量(msec)", "Command: Delay Factor(msec)");
575             const auto new_delay_factor = input_integer(prompt, 0, 1000, delay_factor);
576             if (new_delay_factor) {
577                 delay_factor = *new_delay_factor;
578             }
579
580             clear_from(18);
581             break;
582         }
583         case 'H':
584         case 'h': {
585             clear_from(18);
586             prt(_("コマンド: 低ヒットポイント警告", "Command: Hitpoint Warning"), 19, 0);
587             while (true) {
588                 prt(format(_("現在の低ヒットポイント警告: %d0%%", "Current hitpoint warning: %d0%%"), hitpoint_warn), 22, 0);
589                 prt(_("低ヒットポイント警告 (0-9) ESCで決定: ", "Hitpoint Warning (0-9 or ESC to accept): "), 20, 0);
590                 k = inkey();
591                 if (k == ESCAPE) {
592                     break;
593                 } else if (k == '?') {
594                     (void)show_file(player_ptr, true, _("joption.txt#Hitpoint", "option.txt#Hitpoint"), 0, 0);
595                     term_clear();
596                 } else if (isdigit(k)) {
597                     hitpoint_warn = D2I(k);
598                 } else {
599                     bell();
600                 }
601             }
602
603             break;
604         }
605         case 'M':
606         case 'm': {
607             clear_from(18);
608             prt(_("コマンド: 低魔力色閾値", "Command: Mana Color Threshold"), 19, 0);
609             while (true) {
610                 prt(format(_("現在の低魔力色閾値: %d0%%", "Current mana color threshold: %d0%%"), mana_warn), 22, 0);
611                 prt(_("低魔力閾値 (0-9) ESCで決定: ", "Mana color Threshold (0-9 or ESC to accept): "), 20, 0);
612                 k = inkey();
613                 if (k == ESCAPE) {
614                     break;
615                 } else if (k == '?') {
616                     (void)show_file(player_ptr, true, _("joption.txt#Manapoint", "option.txt#Manapoint"), 0, 0);
617                     term_clear();
618                 } else if (isdigit(k)) {
619                     mana_warn = D2I(k);
620                 } else {
621                     bell();
622                 }
623             }
624
625             break;
626         }
627         case '?':
628             (void)show_file(player_ptr, true, _("joption.txt", "option.txt"), 0, 0);
629             term_clear();
630             break;
631         default: {
632             bell();
633             break;
634         }
635         }
636
637         msg_erase();
638     }
639
640     screen_load();
641     RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::EQUIPPY);
642 }
643
644 /*!
645  * @brief 標準オプションを変更するコマンドのサブルーチン /
646  * Interact with some options
647  * @param page オプションページ番号
648  * @param info 表示メッセージ
649  */
650 void do_cmd_options_aux(PlayerType *player_ptr, game_option_types page, concptr info)
651 {
652     char ch;
653     int i, k = 0, n = 0, l;
654     int opt[MAIN_TERM_MIN_ROWS]{};
655     bool browse_only = (page == OPT_PAGE_BIRTH) && w_ptr->character_generated && (!w_ptr->wizard || !allow_debug_opts);
656
657     for (i = 0; i < MAIN_TERM_MIN_ROWS; i++) {
658         opt[i] = 0;
659     }
660
661     for (i = 0; option_info[i].o_desc; i++) {
662         if (option_info[i].o_page == page) {
663             opt[n++] = i;
664         }
665     }
666
667     term_clear();
668     while (true) {
669         DIRECTION dir;
670         constexpr auto command = _("%s (リターン:次, %sESC:終了, ?:ヘルプ) ", "%s (RET:next, %s, ?:help) ");
671         prt(format(command, info, browse_only ? _("", "ESC:exit") : _("y/n:変更, ", "y/n:change, ESC:accept")), 0, 0);
672         if (page == OPT_PAGE_AUTODESTROY) {
673             constexpr auto mes = _("以下のオプションは、簡易自動破壊を使用するときのみ有効", "Following options will protect items from easy auto-destroyer.");
674             c_prt(TERM_YELLOW, mes, 6, _(6, 3));
675         }
676
677         for (i = 0; i < n; i++) {
678             byte a = TERM_WHITE;
679             if (i == k) {
680                 a = TERM_L_BLUE;
681             }
682
683             const auto reply = *option_info[opt[i]].o_var ? _("はい  ", "yes") : _("いいえ", "no ");
684             const auto label = format("%-48s: %s (%.19s)", option_info[opt[i]].o_desc, reply, option_info[opt[i]].o_text);
685             if ((page == OPT_PAGE_AUTODESTROY) && i > 2) {
686                 c_prt(a, label, i + 5, 0);
687             } else {
688                 c_prt(a, label, i + 2, 0);
689             }
690         }
691
692         if ((page == OPT_PAGE_AUTODESTROY) && (k > 2)) {
693             l = 3;
694         } else {
695             l = 0;
696         }
697
698         move_cursor(k + 2 + l, 50);
699         ch = inkey();
700         dir = get_keymap_dir(ch);
701         if ((dir == 2) || (dir == 4) || (dir == 6) || (dir == 8)) {
702             ch = I2D(dir);
703         }
704
705         switch (ch) {
706         case ESCAPE: {
707             return;
708         }
709         case '-':
710         case '8': {
711             k = (n + k - 1) % n;
712             break;
713         }
714         case ' ':
715         case '\n':
716         case '\r':
717         case '2': {
718             k = (k + 1) % n;
719             break;
720         }
721         case 'y':
722         case 'Y':
723         case '6': {
724             if (browse_only) {
725                 break;
726             }
727             (*option_info[opt[k]].o_var) = true;
728             k = (k + 1) % n;
729             break;
730         }
731         case 'n':
732         case 'N':
733         case '4': {
734             if (browse_only) {
735                 break;
736             }
737             (*option_info[opt[k]].o_var) = false;
738             k = (k + 1) % n;
739             break;
740         }
741         case 't':
742         case 'T': {
743             if (!browse_only) {
744                 (*option_info[opt[k]].o_var) = !(*option_info[opt[k]].o_var);
745             }
746             break;
747         }
748         case '?': {
749             (void)show_file(player_ptr, true, std::string(_("joption.txt#", "option.txt#")).append(option_info[opt[k]].o_text), 0, 0);
750             term_clear();
751             break;
752         }
753         default: {
754             bell();
755             break;
756         }
757         }
758     }
759 }