OSDN Git Service

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