OSDN Git Service

[Refactor] #3399 cmd_visual_aux() のシグネチャを変えて扱いやすくした
[hengbandforosx/hengbandosx.git] / src / cmd-visual / cmd-visuals.cpp
1 #include "cmd-visual/cmd-visuals.h"
2 #include "cmd-visual/cmd-draw.h"
3 #include "core/asking-player.h"
4 #include "core/visuals-reseter.h"
5 #include "flavor/flavor-describer.h"
6 #include "flavor/object-flavor-types.h"
7 #include "flavor/object-flavor.h"
8 #include "game-option/special-options.h"
9 #include "grid/feature.h"
10 #include "io/files-util.h"
11 #include "io/input-key-acceptor.h"
12 #include "io/read-pref-file.h"
13 #include "knowledge/knowledge-features.h"
14 #include "knowledge/knowledge-items.h"
15 #include "knowledge/knowledge-monsters.h"
16 #include "knowledge/lighting-level-table.h"
17 #include "main/sound-of-music.h"
18 #include "monster-race/monster-race.h"
19 #include "system/baseitem-info.h"
20 #include "system/item-entity.h"
21 #include "system/monster-race-info.h"
22 #include "system/player-type-definition.h"
23 #include "system/terrain-type-definition.h"
24 #include "term/screen-processor.h"
25 #include "term/term-color-types.h"
26 #include "term/z-form.h"
27 #include "util/angband-files.h"
28 #include "util/int-char-converter.h"
29 #include "view/display-messages.h"
30 #include <optional>
31
32 /*!
33  * @brief キャラクタのビジュアルIDを変更する際の対象指定関数
34  * @param i 指定対象となるキャラクタコード
35  * @param initial_visual_id 指定されたビジュアルID
36  * @param max ビジュアルIDの最大数
37  * @return 指定が実際に行われたかと、その時のビジュアルIDの組み合わせ
38  */
39 static std::optional<short> cmd_visuals_aux(int i, int initial_visual_id, short max)
40 {
41     auto visual_id = initial_visual_id;
42     if (iscntrl(i)) {
43         if (!get_value("Input new number", 0, max - 1, &visual_id)) {
44             return std::nullopt;
45         }
46     }
47
48     if (isupper(i)) {
49         return static_cast<short>((visual_id + max - 1) % max);
50     }
51
52     return static_cast<short>((visual_id + 1) % max);
53 }
54
55 /*!
56  * @brief キャラクタの変更メニュー表示
57  * @param choice_msg 選択メッセージ
58  */
59 static void print_visuals_menu(concptr choice_msg)
60 {
61     prt(_("[ 画面表示の設定 ]", "Interact with Visuals"), 1, 0);
62     prt(_("(0) ユーザー設定ファイルのロード", "(0) Load a user pref file"), 3, 5);
63     prt(_("(1) モンスターの 色/文字 をファイルに書き出す", "(1) Dump monster attr/chars"), 4, 5);
64     prt(_("(2) アイテムの   色/文字 をファイルに書き出す", "(2) Dump object attr/chars"), 5, 5);
65     prt(_("(3) 地形の       色/文字 をファイルに書き出す", "(3) Dump feature attr/chars"), 6, 5);
66     prt(_("(4) モンスターの 色/文字 を変更する (数値操作)", "(4) Change monster attr/chars (numeric operation)"), 7, 5);
67     prt(_("(5) アイテムの   色/文字 を変更する (数値操作)", "(5) Change object attr/chars (numeric operation)"), 8, 5);
68     prt(_("(6) 地形の       色/文字 を変更する (数値操作)", "(6) Change feature attr/chars (numeric operation)"), 9, 5);
69     prt(_("(7) モンスターの 色/文字 を変更する (シンボルエディタ)", "(7) Change monster attr/chars (visual mode)"), 10, 5);
70     prt(_("(8) アイテムの   色/文字 を変更する (シンボルエディタ)", "(8) Change object attr/chars (visual mode)"), 11, 5);
71     prt(_("(9) 地形の       色/文字 を変更する (シンボルエディタ)", "(9) Change feature attr/chars (visual mode)"), 12, 5);
72     prt(_("(R) 画面表示方法の初期化", "(R) Reset visuals"), 13, 5);
73     prt(format(_("コマンド: %s", "Command: %s"), choice_msg ? choice_msg : _("", "")), 15, 0);
74 }
75
76 /*
77  * Interact with "visuals"
78  */
79 void do_cmd_visuals(PlayerType *player_ptr)
80 {
81     FILE *auto_dump_stream;
82     char tmp[160];
83     bool need_redraw = false;
84     concptr empty_symbol = "<< ? >>";
85     if (use_bigtile) {
86         empty_symbol = "<< ?? >>";
87     }
88
89     screen_save();
90     while (true) {
91         term_clear();
92         print_visuals_menu(nullptr);
93         int i = inkey();
94         if (i == ESCAPE) {
95             break;
96         }
97
98         switch (i) {
99         case '0': {
100             prt(_("コマンド: ユーザー設定ファイルのロード", "Command: Load a user pref file"), 15, 0);
101             prt(_("ファイル: ", "File: "), 17, 0);
102             strnfmt(tmp, sizeof(tmp), "%s.prf", player_ptr->base_name);
103             if (!askfor(tmp, 70)) {
104                 continue;
105             }
106
107             (void)process_pref_file(player_ptr, tmp, true);
108             need_redraw = true;
109             break;
110         }
111         case '1': {
112             prt(_("コマンド: モンスターの[色/文字]をファイルに書き出します", "Command: Dump monster attr/chars"), 15, 0);
113             prt(_("ファイル: ", "File: "), 17, 0);
114             strnfmt(tmp, sizeof(tmp), "%s.prf", player_ptr->base_name);
115             if (!askfor(tmp, 70)) {
116                 continue;
117             }
118
119             const auto &path = path_build(ANGBAND_DIR_USER, tmp);
120             constexpr auto mark = "Monster attr/chars";
121             if (!open_auto_dump(&auto_dump_stream, path, mark)) {
122                 continue;
123             }
124
125             auto_dump_printf(auto_dump_stream, _("\n# モンスターの[色/文字]の設定\n\n", "\n# Monster attr/char definitions\n\n"));
126             for (const auto &[r_idx, r_ref] : monraces_info) {
127                 if (r_ref.name.empty()) {
128                     continue;
129                 }
130
131                 auto_dump_printf(auto_dump_stream, "# %s\n", r_ref.name.data());
132                 auto_dump_printf(auto_dump_stream, "R:%d:0x%02X/0x%02X\n\n", enum2i(r_ref.idx), (byte)(r_ref.x_attr), (byte)(r_ref.x_char));
133             }
134
135             close_auto_dump(&auto_dump_stream, mark);
136             msg_print(_("モンスターの[色/文字]をファイルに書き出しました。", "Dumped monster attr/chars."));
137             break;
138         }
139         case '2': {
140             prt(_("コマンド: アイテムの[色/文字]をファイルに書き出します", "Command: Dump object attr/chars"), 15, 0);
141             prt(_("ファイル: ", "File: "), 17, 0);
142             strnfmt(tmp, sizeof(tmp), "%s.prf", player_ptr->base_name);
143             if (!askfor(tmp, 70)) {
144                 continue;
145             }
146
147             const auto &path = path_build(ANGBAND_DIR_USER, tmp);
148             constexpr auto mark = "Object attr/chars";
149             if (!open_auto_dump(&auto_dump_stream, path, mark)) {
150                 continue;
151             }
152
153             auto_dump_printf(auto_dump_stream, _("\n# アイテムの[色/文字]の設定\n\n", "\n# Object attr/char definitions\n\n"));
154             for (const auto &baseitem : baseitems_info) {
155                 if (baseitem.name.empty()) {
156                     continue;
157                 }
158
159                 std::string item_name;
160                 if (baseitem.flavor == 0) {
161                     item_name = strip_name(baseitem.idx);
162                 } else {
163                     ItemEntity dummy;
164                     dummy.prep(baseitem.idx);
165                     item_name = describe_flavor(player_ptr, &dummy, OD_FORCE_FLAVOR);
166                 }
167
168                 auto_dump_printf(auto_dump_stream, "# %s\n", item_name.data());
169                 auto_dump_printf(auto_dump_stream, "K:%d:0x%02X/0x%02X\n\n", (int)baseitem.idx, (byte)(baseitem.x_attr), (byte)(baseitem.x_char));
170             }
171
172             close_auto_dump(&auto_dump_stream, mark);
173             msg_print(_("アイテムの[色/文字]をファイルに書き出しました。", "Dumped object attr/chars."));
174             break;
175         }
176         case '3': {
177             prt(_("コマンド: 地形の[色/文字]をファイルに書き出します", "Command: Dump feature attr/chars"), 15, 0);
178             prt(_("ファイル: ", "File: "), 17, 0);
179             strnfmt(tmp, sizeof(tmp), "%s.prf", player_ptr->base_name);
180             if (!askfor(tmp, 70)) {
181                 continue;
182             }
183
184             const auto &path = path_build(ANGBAND_DIR_USER, tmp);
185             constexpr auto mark = "Feature attr/chars";
186             if (!open_auto_dump(&auto_dump_stream, path, mark)) {
187                 continue;
188             }
189
190             auto_dump_printf(auto_dump_stream, _("\n# 地形の[色/文字]の設定\n\n", "\n# Feature attr/char definitions\n\n"));
191             for (const auto &f_ref : terrains_info) {
192                 if (f_ref.name.empty()) {
193                     continue;
194                 }
195                 if (f_ref.mimic != f_ref.idx) {
196                     continue;
197                 }
198
199                 auto_dump_printf(auto_dump_stream, "# %s\n", (f_ref.name.data()));
200                 auto_dump_printf(auto_dump_stream, "F:%d:0x%02X/0x%02X:0x%02X/0x%02X:0x%02X/0x%02X\n\n", f_ref.idx, (byte)(f_ref.x_attr[F_LIT_STANDARD]),
201                     (byte)(f_ref.x_char[F_LIT_STANDARD]), (byte)(f_ref.x_attr[F_LIT_LITE]), (byte)(f_ref.x_char[F_LIT_LITE]),
202                     (byte)(f_ref.x_attr[F_LIT_DARK]), (byte)(f_ref.x_char[F_LIT_DARK]));
203             }
204
205             close_auto_dump(&auto_dump_stream, mark);
206             msg_print(_("地形の[色/文字]をファイルに書き出しました。", "Dumped feature attr/chars."));
207             break;
208         }
209         case '4': {
210             IDX num = 0;
211             static auto choice_msg = _("モンスターの[色/文字]を変更します", "Change monster attr/chars");
212             static auto monrace_id = monraces_info.begin()->second.idx;
213             prt(format(_("コマンド: %s", "Command: %s"), choice_msg), 15, 0);
214             while (true) {
215                 auto *r_ptr = &monraces_info[monrace_id];
216                 int c;
217                 TERM_COLOR da = r_ptr->d_attr;
218                 byte dc = r_ptr->d_char;
219                 TERM_COLOR ca = r_ptr->x_attr;
220                 byte cc = r_ptr->x_char;
221
222                 term_putstr(5, 17, -1, TERM_WHITE, format(_("モンスター = %d, 名前 = %-40.40s", "Monster = %d, Name = %-40.40s"), enum2i(monrace_id), r_ptr->name.data()));
223                 term_putstr(10, 19, -1, TERM_WHITE, format(_("初期値  色 / 文字 = %3u / %3u", "Default attr/char = %3u / %3u"), da, dc));
224                 term_putstr(40, 19, -1, TERM_WHITE, empty_symbol);
225                 term_queue_bigchar(43, 19, da, dc, 0, 0);
226                 term_putstr(10, 20, -1, TERM_WHITE, format(_("現在値  色 / 文字 = %3u / %3u", "Current attr/char = %3u / %3u"), ca, cc));
227                 term_putstr(40, 20, -1, TERM_WHITE, empty_symbol);
228                 term_queue_bigchar(43, 20, ca, cc, 0, 0);
229                 term_putstr(0, 22, -1, TERM_WHITE, _("コマンド (n/N/^N/a/A/^A/c/C/^C/v/V/^V): ", "Command (n/N/^N/a/A/^A/c/C/^C/v/V/^V): "));
230                 i = inkey();
231                 if (i == ESCAPE) {
232                     break;
233                 }
234
235                 if (iscntrl(i)) {
236                     c = 'a' + i - KTRL('A');
237                 } else if (isupper(i)) {
238                     c = 'a' + i - 'A';
239                 } else {
240                     c = i;
241                 }
242
243                 switch (c) {
244                 case 'n': {
245                     auto prev_r = monrace_id;
246                     do {
247                         const auto new_monrace_id_opt = cmd_visuals_aux(i, num, static_cast<short>(monraces_info.size()));
248                         if (!new_monrace_id_opt.has_value()) {
249                             monrace_id = prev_r;
250                             break;
251                         }
252
253                         const auto new_monrace_id = new_monrace_id_opt.value();
254                         monrace_id = i2enum<MonsterRaceId>(new_monrace_id);
255                         num = new_monrace_id;
256                     } while (monraces_info[monrace_id].name.empty());
257
258                     break;
259                 }
260                 case 'a': {
261                     const auto visual_id = cmd_visuals_aux(i, r_ptr->x_attr, 256);
262                     if (!visual_id.has_value()) {
263                         break;
264                     }
265
266                     r_ptr->x_attr = static_cast<byte>(visual_id.value());
267                     need_redraw = true;
268                     break;
269                 }
270                 case 'c': {
271                     const auto visual_id = cmd_visuals_aux(i, r_ptr->x_char, 256);
272                     if (!visual_id.has_value()) {
273                         break;
274                     }
275
276                     r_ptr->x_char = static_cast<byte>(visual_id.value());
277                     need_redraw = true;
278                     break;
279                 }
280                 case 'v':
281                     do_cmd_knowledge_monsters(player_ptr, &need_redraw, true, monrace_id);
282                     term_clear();
283                     print_visuals_menu(choice_msg);
284                     break;
285                 }
286             }
287
288             break;
289         }
290         case '5': {
291             static auto choice_msg = _("アイテムの[色/文字]を変更します", "Change object attr/chars");
292             static short bi_id = 0;
293             prt(format(_("コマンド: %s", "Command: %s"), choice_msg), 15, 0);
294             while (true) {
295                 auto &baseitem = baseitems_info[bi_id];
296                 int c;
297                 TERM_COLOR da = baseitem.d_attr;
298                 auto dc = baseitem.d_char;
299                 TERM_COLOR ca = baseitem.x_attr;
300                 auto cc = baseitem.x_char;
301
302                 term_putstr(5, 17, -1, TERM_WHITE,
303                     format(
304                         _("アイテム = %d, 名前 = %-40.40s", "Object = %d, Name = %-40.40s"), bi_id, (!baseitem.flavor ? baseitem.name : baseitem.flavor_name).data()));
305                 term_putstr(10, 19, -1, TERM_WHITE, format(_("初期値  色 / 文字 = %3d / %3d", "Default attr/char = %3d / %3d"), da, dc));
306                 term_putstr(40, 19, -1, TERM_WHITE, empty_symbol);
307                 term_queue_bigchar(43, 19, da, dc, 0, 0);
308                 term_putstr(10, 20, -1, TERM_WHITE, format(_("現在値  色 / 文字 = %3d / %3d", "Current attr/char = %3d / %3d"), ca, cc));
309                 term_putstr(40, 20, -1, TERM_WHITE, empty_symbol);
310                 term_queue_bigchar(43, 20, ca, cc, 0, 0);
311                 term_putstr(0, 22, -1, TERM_WHITE, _("コマンド (n/N/^N/a/A/^A/c/C/^C/v/V/^V): ", "Command (n/N/^N/a/A/^A/c/C/^C/v/V/^V): "));
312
313                 i = inkey();
314                 if (i == ESCAPE) {
315                     break;
316                 }
317
318                 if (iscntrl(i)) {
319                     c = 'a' + i - KTRL('A');
320                 } else if (isupper(i)) {
321                     c = 'a' + i - 'A';
322                 } else {
323                     c = i;
324                 }
325
326                 switch (c) {
327                 case 'n': {
328                     std::optional<short> new_baseitem_id(std::nullopt);
329                     const auto previous_bi_id = bi_id;
330                     while (true) {
331                         new_baseitem_id = cmd_visuals_aux(i, bi_id, static_cast<short>(baseitems_info.size()));
332                         if (!new_baseitem_id.has_value()) {
333                             bi_id = previous_bi_id;
334                             break;
335                         }
336
337                         bi_id = new_baseitem_id.value();
338                         if (!baseitems_info[bi_id].name.empty()) {
339                             break;
340                         }
341                     }
342
343                     break;
344                 }
345                 case 'a': {
346                     const auto visual_id = cmd_visuals_aux(i, baseitem.x_attr, 256);
347                     if (!visual_id.has_value()) {
348                         break;
349                     }
350
351                     baseitem.x_attr = static_cast<byte>(visual_id.value());
352                     need_redraw = true;
353                     break;
354                 }
355                 case 'c': {
356                     const auto visual_id = cmd_visuals_aux(i, baseitem.x_char, 256);
357                     if (!visual_id.has_value()) {
358                         break;
359                     }
360
361                     baseitem.x_char = static_cast<byte>(visual_id.value());
362                     need_redraw = true;
363                     break;
364                 }
365                 case 'v':
366                     do_cmd_knowledge_objects(player_ptr, &need_redraw, true, bi_id);
367                     term_clear();
368                     print_visuals_menu(choice_msg);
369                     break;
370                 }
371             }
372
373             break;
374         }
375         case '6': {
376             static auto choice_msg = _("地形の[色/文字]を変更します", "Change feature attr/chars");
377             static short terrain_id = 0;
378             static short lighting_level = F_LIT_STANDARD;
379             prt(format(_("コマンド: %s", "Command: %s"), choice_msg), 15, 0);
380             while (true) {
381                 auto *f_ptr = &terrains_info[terrain_id];
382                 int c;
383                 TERM_COLOR da = f_ptr->d_attr[lighting_level];
384                 byte dc = f_ptr->d_char[lighting_level];
385                 TERM_COLOR ca = f_ptr->x_attr[lighting_level];
386                 byte cc = f_ptr->x_char[lighting_level];
387
388                 prt("", 17, 5);
389                 term_putstr(5, 17, -1, TERM_WHITE,
390                     format(_("地形 = %d, 名前 = %s, 明度 = %s", "Terrain = %d, Name = %s, Lighting = %s"), terrain_id, (f_ptr->name.data()),
391                         lighting_level_str[lighting_level]));
392                 term_putstr(10, 19, -1, TERM_WHITE, format(_("初期値  色 / 文字 = %3d / %3d", "Default attr/char = %3d / %3d"), da, dc));
393                 term_putstr(40, 19, -1, TERM_WHITE, empty_symbol);
394                 term_queue_bigchar(43, 19, da, dc, 0, 0);
395                 term_putstr(10, 20, -1, TERM_WHITE, format(_("現在値  色 / 文字 = %3d / %3d", "Current attr/char = %3d / %3d"), ca, cc));
396                 term_putstr(40, 20, -1, TERM_WHITE, empty_symbol);
397                 term_queue_bigchar(43, 20, ca, cc, 0, 0);
398                 term_putstr(0, 22, -1, TERM_WHITE,
399                     _("コマンド (n/N/^N/a/A/^A/c/C/^C/l/L/^L/d/D/^D/v/V/^V): ", "Command (n/N/^N/a/A/^A/c/C/^C/l/L/^L/d/D/^D/v/V/^V): "));
400
401                 i = inkey();
402                 if (i == ESCAPE) {
403                     break;
404                 }
405
406                 if (iscntrl(i)) {
407                     c = 'a' + i - KTRL('A');
408                 } else if (isupper(i)) {
409                     c = 'a' + i - 'A';
410                 } else {
411                     c = i;
412                 }
413
414                 switch (c) {
415                 case 'n': {
416                     std::optional<short> new_terrain_id(std::nullopt);
417                     const auto previous_terrain_id = terrain_id;
418                     while (true) {
419                         new_terrain_id = cmd_visuals_aux(i, terrain_id, static_cast<short>(terrains_info.size()));
420                         if (!new_terrain_id.has_value()) {
421                             terrain_id = previous_terrain_id;
422                             break;
423                         }
424
425                         terrain_id = new_terrain_id.value();
426                         if (!terrains_info[terrain_id].name.empty() && (terrains_info[terrain_id].mimic == terrain_id)) {
427                             break;
428                         }
429                     }
430
431                     break;
432                 }
433                 case 'a': {
434                     const auto visual_id = cmd_visuals_aux(i, f_ptr->x_attr[lighting_level], 256);
435                     if (!visual_id.has_value()) {
436                         break;
437                     }
438
439                     f_ptr->x_attr[lighting_level] = static_cast<byte>(visual_id.value());
440                     need_redraw = true;
441                     break;
442                 }
443                 case 'c': {
444                     const auto visual_id = cmd_visuals_aux(i, f_ptr->x_char[lighting_level], 256);
445                     if (!visual_id.has_value()) {
446                         break;
447                     }
448
449                     f_ptr->x_char[lighting_level] = static_cast<byte>(visual_id.value());
450                     need_redraw = true;
451                     break;
452                 }
453                 case 'l': {
454                     const auto visual_id = cmd_visuals_aux(i, lighting_level, F_LIT_MAX);
455                     if (!visual_id.has_value()) {
456                         break;
457                     }
458
459                     lighting_level = visual_id.value();
460                     break;
461                 }
462                 case 'd':
463                     apply_default_feat_lighting(f_ptr->x_attr, f_ptr->x_char);
464                     need_redraw = true;
465                     break;
466                 case 'v':
467                     do_cmd_knowledge_features(&need_redraw, true, terrain_id, &lighting_level);
468                     term_clear();
469                     print_visuals_menu(choice_msg);
470                     break;
471                 }
472             }
473
474             break;
475         }
476         case '7':
477             do_cmd_knowledge_monsters(player_ptr, &need_redraw, true);
478             break;
479         case '8':
480             do_cmd_knowledge_objects(player_ptr, &need_redraw, true, -1);
481             break;
482         case '9': {
483             IDX lighting_level = F_LIT_STANDARD;
484             do_cmd_knowledge_features(&need_redraw, true, -1, &lighting_level);
485             break;
486         }
487         case 'R':
488         case 'r':
489             reset_visuals(player_ptr);
490             msg_print(_("画面上の[色/文字]を初期値にリセットしました。", "Visual attr/char tables reset."));
491             need_redraw = true;
492             break;
493         default:
494             bell();
495             break;
496         }
497
498         msg_erase();
499     }
500
501     screen_load();
502     if (need_redraw) {
503         do_cmd_redraw(player_ptr);
504     }
505 }