OSDN Git Service

Merge pull request #3454 from habu1010/feature/http-client
[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_value_int("Input new number", 0, max - 1, initial_visual_id);
44         if (!new_visual_id.has_value()) {
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     char tmp[160];
86     bool need_redraw = false;
87     concptr empty_symbol = "<< ? >>";
88     if (use_bigtile) {
89         empty_symbol = "<< ?? >>";
90     }
91
92     screen_save();
93     while (true) {
94         term_clear();
95         print_visuals_menu(nullptr);
96         int i = inkey();
97         if (i == ESCAPE) {
98             break;
99         }
100
101         switch (i) {
102         case '0': {
103             prt(_("コマンド: ユーザー設定ファイルのロード", "Command: Load a user pref file"), 15, 0);
104             prt(_("ファイル: ", "File: "), 17, 0);
105             strnfmt(tmp, sizeof(tmp), "%s.prf", player_ptr->base_name);
106             if (!askfor(tmp, 70)) {
107                 continue;
108             }
109
110             (void)process_pref_file(player_ptr, tmp, 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             strnfmt(tmp, sizeof(tmp), "%s.prf", player_ptr->base_name);
118             if (!askfor(tmp, 70)) {
119                 continue;
120             }
121
122             const auto &path = path_build(ANGBAND_DIR_USER, tmp);
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             strnfmt(tmp, sizeof(tmp), "%s.prf", player_ptr->base_name);
142             if (!askfor(tmp, 70)) {
143                 continue;
144             }
145
146             const auto &path = path_build(ANGBAND_DIR_USER, tmp);
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             strnfmt(tmp, sizeof(tmp), "%s.prf", player_ptr->base_name);
179             if (!askfor(tmp, 70)) {
180                 continue;
181             }
182
183             const auto &path = path_build(ANGBAND_DIR_USER, tmp);
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 &f_ref : terrains_info) {
191                 if (f_ref.name.empty()) {
192                     continue;
193                 }
194                 if (f_ref.mimic != f_ref.idx) {
195                     continue;
196                 }
197
198                 auto_dump_printf(auto_dump_stream, "# %s\n", (f_ref.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", f_ref.idx, (byte)(f_ref.x_attr[F_LIT_STANDARD]),
200                     (byte)(f_ref.x_char[F_LIT_STANDARD]), (byte)(f_ref.x_attr[F_LIT_LITE]), (byte)(f_ref.x_char[F_LIT_LITE]),
201                     (byte)(f_ref.x_attr[F_LIT_DARK]), (byte)(f_ref.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                 i = inkey();
230                 if (i == ESCAPE) {
231                     break;
232                 }
233
234                 if (iscntrl(i)) {
235                     c = 'a' + i - KTRL('A');
236                 } else if (isupper(i)) {
237                     c = 'a' + i - 'A';
238                 } else {
239                     c = i;
240                 }
241
242                 switch (c) {
243                 case 'n': {
244                     const auto new_monrace_id_opt = input_new_visual_id(i, num, static_cast<short>(monraces_info.size()));
245                     if (!new_monrace_id_opt.has_value()) {
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(i, r_ptr->x_attr, 256);
256                     if (!visual_id.has_value()) {
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(i, r_ptr->x_char, 256);
266                     if (!visual_id.has_value()) {
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                 i = inkey();
308                 if (i == ESCAPE) {
309                     break;
310                 }
311
312                 if (iscntrl(i)) {
313                     c = 'a' + i - KTRL('A');
314                 } else if (isupper(i)) {
315                     c = 'a' + i - 'A';
316                 } else {
317                     c = i;
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(i, bi_id, static_cast<short>(baseitems_info.size()));
326                         if (!new_baseitem_id.has_value()) {
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(i, baseitem.x_attr, 256);
341                     if (!visual_id.has_value()) {
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(i, baseitem.x_char, 256);
351                     if (!visual_id.has_value()) {
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             while (true) {
375                 auto *f_ptr = &terrains_info[terrain_id];
376                 int c;
377                 TERM_COLOR da = f_ptr->d_attr[lighting_level];
378                 byte dc = f_ptr->d_char[lighting_level];
379                 TERM_COLOR ca = f_ptr->x_attr[lighting_level];
380                 byte cc = f_ptr->x_char[lighting_level];
381
382                 prt("", 17, 5);
383                 term_putstr(5, 17, -1, TERM_WHITE,
384                     format(_("地形 = %d, 名前 = %s, 明度 = %s", "Terrain = %d, Name = %s, Lighting = %s"), terrain_id, (f_ptr->name.data()),
385                         lighting_level_str[lighting_level]));
386                 term_putstr(10, 19, -1, TERM_WHITE, format(_("初期値  色 / 文字 = %3d / %3d", "Default attr/char = %3d / %3d"), da, dc));
387                 term_putstr(40, 19, -1, TERM_WHITE, empty_symbol);
388                 term_queue_bigchar(43, 19, da, dc, 0, 0);
389                 term_putstr(10, 20, -1, TERM_WHITE, format(_("現在値  色 / 文字 = %3d / %3d", "Current attr/char = %3d / %3d"), ca, cc));
390                 term_putstr(40, 20, -1, TERM_WHITE, empty_symbol);
391                 term_queue_bigchar(43, 20, ca, cc, 0, 0);
392                 term_putstr(0, 22, -1, TERM_WHITE,
393                     _("コマンド (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): "));
394
395                 i = inkey();
396                 if (i == ESCAPE) {
397                     break;
398                 }
399
400                 if (iscntrl(i)) {
401                     c = 'a' + i - KTRL('A');
402                 } else if (isupper(i)) {
403                     c = 'a' + i - 'A';
404                 } else {
405                     c = i;
406                 }
407
408                 switch (c) {
409                 case 'n': {
410                     std::optional<short> new_terrain_id(std::nullopt);
411                     const auto previous_terrain_id = terrain_id;
412                     while (true) {
413                         new_terrain_id = input_new_visual_id(i, terrain_id, static_cast<short>(terrains_info.size()));
414                         if (!new_terrain_id.has_value()) {
415                             terrain_id = previous_terrain_id;
416                             break;
417                         }
418
419                         terrain_id = new_terrain_id.value();
420                         if (!terrains_info[terrain_id].name.empty() && (terrains_info[terrain_id].mimic == terrain_id)) {
421                             break;
422                         }
423                     }
424
425                     break;
426                 }
427                 case 'a': {
428                     const auto visual_id = input_new_visual_id(i, f_ptr->x_attr[lighting_level], 256);
429                     if (!visual_id.has_value()) {
430                         break;
431                     }
432
433                     f_ptr->x_attr[lighting_level] = visual_id.value();
434                     need_redraw = true;
435                     break;
436                 }
437                 case 'c': {
438                     const auto visual_id = input_new_visual_id(i, f_ptr->x_char[lighting_level], 256);
439                     if (!visual_id.has_value()) {
440                         break;
441                     }
442
443                     f_ptr->x_char[lighting_level] = visual_id.value();
444                     need_redraw = true;
445                     break;
446                 }
447                 case 'l': {
448                     const auto visual_id = input_new_visual_id(i, lighting_level, F_LIT_MAX);
449                     if (!visual_id.has_value()) {
450                         break;
451                     }
452
453                     lighting_level = visual_id.value();
454                     break;
455                 }
456                 case 'd':
457                     apply_default_feat_lighting(f_ptr->x_attr, f_ptr->x_char);
458                     need_redraw = true;
459                     break;
460                 case 'v':
461                     do_cmd_knowledge_features(&need_redraw, true, terrain_id, &lighting_level);
462                     term_clear();
463                     print_visuals_menu(choice_msg);
464                     break;
465                 }
466             }
467
468             break;
469         }
470         case '7':
471             do_cmd_knowledge_monsters(player_ptr, &need_redraw, true);
472             break;
473         case '8':
474             do_cmd_knowledge_objects(player_ptr, &need_redraw, true, -1);
475             break;
476         case '9': {
477             IDX lighting_level = F_LIT_STANDARD;
478             do_cmd_knowledge_features(&need_redraw, true, -1, &lighting_level);
479             break;
480         }
481         case 'R':
482         case 'r':
483             reset_visuals(player_ptr);
484             msg_print(_("画面上の[色/文字]を初期値にリセットしました。", "Visual attr/char tables reset."));
485             need_redraw = true;
486             break;
487         default:
488             bell();
489             break;
490         }
491
492         msg_erase();
493     }
494
495     screen_load();
496     if (need_redraw) {
497         do_cmd_redraw(player_ptr);
498     }
499 }