OSDN Git Service

[Refactor] #2604 describe_flavor() の引数char* を廃止し、戻り値をstd::string に変えた
[hengbandforosx/hengbandosx.git] / src / wizard / wizard-spoiler.cpp
1 /*!
2  * @brief スポイラー出力処理 (行数の都合でモンスター進化ツリーもここに入っている)
3  * @date 2014/02/17
4  * @author
5  * Copyright (c) 1997 Ben Harrison, and others
6  * This software may be copied and distributed for educational, research,
7  * and not for profit purposes provided that this copyright and statement
8  * are included in all such copies.  Other copyrights may also apply.
9  * 2013 Deskull rearranged comment for Doxygen.
10  * 2020 Hourier rearranged for decreasing lines.
11  */
12
13 #include "wizard/wizard-spoiler.h"
14 #include "flavor/flavor-describer.h"
15 #include "flavor/object-flavor-types.h"
16 #include "io/files-util.h"
17 #include "io/input-key-acceptor.h"
18 #include "main/sound-of-music.h"
19 #include "monster-race/monster-race.h"
20 #include "monster-race/race-flags7.h"
21 #include "monster-race/race-flags8.h"
22 #include "object/object-kind-hook.h"
23 #include "player-info/class-info.h"
24 #include "realm/realm-names-table.h"
25 #include "spell/spells-execution.h"
26 #include "spell/spells-util.h"
27 #include "system/angband-version.h"
28 #include "system/baseitem-info.h"
29 #include "system/item-entity.h"
30 #include "system/monster-race-info.h"
31 #include "system/player-type-definition.h"
32 #include "term/screen-processor.h"
33 #include "term/z-form.h"
34 #include "util/angband-files.h"
35 #include "util/bit-flags-calculator.h"
36 #include "util/int-char-converter.h"
37 #include "util/sort.h"
38 #include "util/string-processor.h"
39 #include "view/display-messages.h"
40 #include "wizard/fixed-artifacts-spoiler.h"
41 #include "wizard/items-spoiler.h"
42 #include "wizard/monster-info-spoiler.h"
43 #include "wizard/spoiler-util.h"
44 #include <algorithm>
45 #include <array>
46 #include <iterator>
47 #include <set>
48 #include <string>
49
50 static constexpr std::array<std::string_view, 6> wiz_spell_stat = { {
51     _("腕力", "STR"),
52     _("知能", "INT"),
53     _("賢さ", "WIS"),
54     _("器用さ", "DEX"),
55     _("耐久力", "CON"),
56     _("魅力", "CHR"),
57 } };
58
59 /**
60  * @brief 進化ツリーの一番根元となるモンスターのIDのリストを取得する
61  *
62  * @return 進化ツリーの一番根元となるモンスターのIDのリスト(std::setで、evol_root_sortによりソートされている)
63  */
64 static auto get_mon_evol_roots(void)
65 {
66     std::set<MonsterRaceId> evol_parents;
67     std::set<MonsterRaceId> evol_children;
68     for (const auto &[r_idx, r_ref] : monraces_info) {
69         if (MonsterRace(r_ref.next_r_idx).is_valid()) {
70             evol_parents.emplace(r_ref.idx);
71             evol_children.emplace(r_ref.next_r_idx);
72         }
73     }
74
75     auto evol_root_sort = [](MonsterRaceId i1, MonsterRaceId i2) {
76         auto &r1 = monraces_info[i1];
77         auto &r2 = monraces_info[i2];
78         if (r1.level != r2.level) {
79             return r1.level < r2.level;
80         }
81         if (r1.mexp != r2.mexp) {
82             return r1.mexp < r2.mexp;
83         }
84         return i1 <= i2;
85     };
86
87     std::set<MonsterRaceId, decltype(evol_root_sort)> evol_roots(evol_root_sort);
88     std::set_difference(evol_parents.begin(), evol_parents.end(), evol_children.begin(), evol_children.end(),
89         std::inserter(evol_roots, evol_roots.end()));
90
91     return evol_roots;
92 }
93
94 /*!
95  * @brief 進化ツリーをスポイラー出力するメインルーチン /
96  * Print monsters' evolution information to file
97  * @param fname 出力ファイル名
98  */
99 static SpoilerOutputResultType spoil_mon_evol(concptr fname)
100 {
101     char buf[1024];
102     path_build(buf, sizeof buf, ANGBAND_DIR_USER, fname);
103     spoiler_file = angband_fopen(buf, "w");
104     if (!spoiler_file) {
105         return SpoilerOutputResultType::FILE_OPEN_FAILED;
106     }
107
108     spoil_out(std::string("Monster Spoilers for ").append(get_version()).append("\n"));
109
110     spoil_out("------------------------------------------\n\n");
111
112     for (auto r_idx : get_mon_evol_roots()) {
113         auto r_ptr = &monraces_info[r_idx];
114         fprintf(spoiler_file, _("[%d]: %s (レベル%d, '%c')\n", "[%d]: %s (Level %d, '%c')\n"), enum2i(r_idx), r_ptr->name.data(), (int)r_ptr->level, r_ptr->d_char);
115
116         for (auto n = 1; MonsterRace(r_ptr->next_r_idx).is_valid(); n++) {
117             fprintf(spoiler_file, "%*s-(%d)-> ", n * 2, "", r_ptr->next_exp);
118             fprintf(spoiler_file, "[%d]: ", enum2i(r_ptr->next_r_idx));
119             r_ptr = &monraces_info[r_ptr->next_r_idx];
120
121             fprintf(spoiler_file, _("%s (レベル%d, '%c')\n", "%s (Level %d, '%c')\n"), r_ptr->name.data(), (int)r_ptr->level, r_ptr->d_char);
122         }
123
124         fputc('\n', spoiler_file);
125     }
126
127     return ferror(spoiler_file) || angband_fclose(spoiler_file) ? SpoilerOutputResultType::FILE_CLOSE_FAILED
128                                                                 : SpoilerOutputResultType::SUCCESSFUL;
129 }
130
131 static SpoilerOutputResultType spoil_categorized_mon_desc()
132 {
133     auto status = spoil_mon_desc("mon-desc-ridable.txt", [](const MonsterRaceInfo *r_ptr) { return any_bits(r_ptr->flags7, RF7_RIDING); });
134     if (status == SpoilerOutputResultType::SUCCESSFUL) {
135         status = spoil_mon_desc("mon-desc-wildonly.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_ONLY); });
136     }
137
138     if (status == SpoilerOutputResultType::SUCCESSFUL) {
139         status = spoil_mon_desc("mon-desc-town.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_TOWN); });
140     }
141
142     if (status == SpoilerOutputResultType::SUCCESSFUL) {
143         status = spoil_mon_desc("mon-desc-shore.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_SHORE); });
144     }
145
146     if (status == SpoilerOutputResultType::SUCCESSFUL) {
147         status = spoil_mon_desc("mon-desc-ocean.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_OCEAN); });
148     }
149
150     if (status == SpoilerOutputResultType::SUCCESSFUL) {
151         status = spoil_mon_desc("mon-desc-waste.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_WASTE); });
152     }
153
154     if (status == SpoilerOutputResultType::SUCCESSFUL) {
155         status = spoil_mon_desc("mon-desc-wood.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_WOOD); });
156     }
157
158     if (status == SpoilerOutputResultType::SUCCESSFUL) {
159         status = spoil_mon_desc("mon-desc-volcano.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_VOLCANO); });
160     }
161
162     if (status == SpoilerOutputResultType::SUCCESSFUL) {
163         status = spoil_mon_desc("mon-desc-mountain.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_MOUNTAIN); });
164     }
165
166     if (status == SpoilerOutputResultType::SUCCESSFUL) {
167         status = spoil_mon_desc("mon-desc-grass.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_GRASS); });
168     }
169
170     if (status == SpoilerOutputResultType::SUCCESSFUL) {
171         status = spoil_mon_desc("mon-desc-wildall.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_ALL); });
172     }
173
174     return status;
175 }
176
177 static SpoilerOutputResultType spoil_player_spell(concptr fname)
178 {
179     char buf[1024];
180
181     path_build(buf, sizeof buf, ANGBAND_DIR_USER, fname);
182     spoiler_file = angband_fopen(buf, "w");
183     if (!spoiler_file) {
184         return SpoilerOutputResultType::FILE_OPEN_FAILED;
185     }
186
187     spoil_out(format("Player spells for %s\n", get_version().data()));
188     spoil_out("------------------------------------------\n\n");
189
190     PlayerType dummy_p;
191     dummy_p.lev = 1;
192
193     for (int c = 0; c < PLAYER_CLASS_TYPE_MAX; c++) {
194         auto class_ptr = &class_info[c];
195         spoil_out(format("[[Class: %s]]\n", class_ptr->title));
196
197         auto magic_ptr = &class_magics_info[c];
198         concptr book_name = "なし";
199         if (magic_ptr->spell_book != ItemKindType::NONE) {
200             ItemEntity book;
201             auto o_ptr = &book;
202             o_ptr->prep(lookup_baseitem_id({ magic_ptr->spell_book, 0 }));
203             const auto item_name = describe_flavor(&dummy_p, o_ptr, OD_NAME_ONLY);
204             book_name = item_name.data();
205             char *s = angband_strchr(book_name, '[');
206             *s = '\0';
207         }
208
209         constexpr auto mes = "BookType:%s Stat:%s Xtra:%x Type:%d Weight:%d\n";
210         const auto &spell = wiz_spell_stat[magic_ptr->spell_stat];
211         spoil_out(format(mes, book_name, spell.data(), magic_ptr->spell_xtra, magic_ptr->spell_type, magic_ptr->spell_weight));
212         if (magic_ptr->spell_book == ItemKindType::NONE) {
213             spoil_out(_("呪文なし\n\n", "No spells.\n\n"));
214             continue;
215         }
216
217         for (int16_t r = 1; r < MAX_MAGIC; r++) {
218             spoil_out(format("[Realm: %s]\n", realm_names[r]));
219             spoil_out("Name                     Lv Cst Dif Exp\n");
220             for (SPELL_IDX i = 0; i < 32; i++) {
221                 auto spell_ptr = &magic_ptr->info[r][i];
222                 const auto spell_name = exe_spell(&dummy_p, r, i, SpellProcessType::NAME);
223                 spoil_out(format("%-24s %2d %3d %3d %3d\n", spell_name->data(), spell_ptr->slevel, spell_ptr->smana, spell_ptr->sfail, spell_ptr->sexp));
224             }
225             spoil_out("\n");
226         }
227     }
228
229     return ferror(spoiler_file) || angband_fclose(spoiler_file) ? SpoilerOutputResultType::FILE_CLOSE_FAILED
230                                                                 : SpoilerOutputResultType::SUCCESSFUL;
231 }
232
233 /*!
234  * @brief スポイラー出力を行うコマンドのメインルーチン /
235  * Create Spoiler files -BEN-
236  */
237 void exe_output_spoilers(void)
238 {
239     screen_save();
240     while (true) {
241         auto status = SpoilerOutputResultType::CANCELED;
242         term_clear();
243         prt("Create a spoiler file.", 2, 0);
244         prt("(1) Brief Object Info (obj-desc.txt)", 5, 5);
245         prt("(2) Brief Artifact Info (artifact.txt)", 6, 5);
246         prt("(3) Brief Monster Info (mon-desc.txt)", 7, 5);
247         prt("(4) Brief Categorized Monster Info (mon-desc-*.txt)", 8, 5);
248         prt("(5) Full Monster Info (mon-info.txt)", 9, 5);
249         prt("(6) Monster Evolution Info (mon-evol.txt)", 10, 5);
250         prt("(7) Player Spells Info (spells.txt)", 11, 5);
251         prt(_("コマンド:", "Command: "), _(18, 12), 0);
252         switch (inkey()) {
253         case ESCAPE:
254             screen_load();
255             return;
256         case '1':
257             status = spoil_obj_desc("obj-desc.txt");
258             break;
259         case '2':
260             status = spoil_fixed_artifact("artifact.txt");
261             break;
262         case '3':
263             status = spoil_mon_desc("mon-desc.txt");
264             break;
265         case '4':
266             status = spoil_categorized_mon_desc();
267             break;
268         case '5':
269             status = spoil_mon_info("mon-info.txt");
270             break;
271         case '6':
272             status = spoil_mon_evol("mon-evol.txt");
273             break;
274         case '7':
275             status = spoil_player_spell("spells.txt");
276             break;
277         default:
278             bell();
279             break;
280         }
281
282         switch (status) {
283         case SpoilerOutputResultType::FILE_OPEN_FAILED:
284             msg_print("Cannot create spoiler file.");
285             break;
286         case SpoilerOutputResultType::FILE_CLOSE_FAILED:
287             msg_print("Cannot close spoiler file.");
288             break;
289         case SpoilerOutputResultType::SUCCESSFUL:
290             msg_print("Successfully created a spoiler file.");
291             break;
292         case SpoilerOutputResultType::CANCELED:
293             break;
294         }
295         msg_erase();
296     }
297 }
298
299 /*!
300  * @brief 全スポイラー出力を行うコマンドのメインルーチン /
301  * Create Spoiler files -BEN-
302  * @return 成功時SPOILER_OUTPUT_SUCCESS / 失敗時エラー状態
303  */
304 SpoilerOutputResultType output_all_spoilers(void)
305 {
306     auto status = spoil_obj_desc("obj-desc.txt");
307     if (status != SpoilerOutputResultType::SUCCESSFUL) {
308         return status;
309     }
310
311     status = spoil_fixed_artifact("artifact.txt");
312     if (status != SpoilerOutputResultType::SUCCESSFUL) {
313         return status;
314     }
315
316     status = spoil_mon_desc("mon-desc.txt");
317     if (status != SpoilerOutputResultType::SUCCESSFUL) {
318         return status;
319     }
320
321     status = spoil_categorized_mon_desc();
322     if (status != SpoilerOutputResultType::SUCCESSFUL) {
323         return status;
324     }
325
326     status = spoil_mon_info("mon-info.txt");
327     if (status != SpoilerOutputResultType::SUCCESSFUL) {
328         return status;
329     }
330
331     status = spoil_mon_evol("mon-evol.txt");
332     if (status != SpoilerOutputResultType::SUCCESSFUL) {
333         return status;
334     }
335
336     return SpoilerOutputResultType::SUCCESSFUL;
337 }