OSDN Git Service

[Refactor] RIDING を新定義に合わせた
[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 <sstream>
49 #include <string>
50 #include <string_view>
51
52 static constexpr std::array<std::string_view, 6> wiz_spell_stat = { {
53     _("腕力", "STR"),
54     _("知能", "INT"),
55     _("賢さ", "WIS"),
56     _("器用さ", "DEX"),
57     _("耐久力", "CON"),
58     _("魅力", "CHR"),
59 } };
60
61 /**
62  * @brief 進化ツリーの一番根元となるモンスターのIDのリストを取得する
63  *
64  * @return 進化ツリーの一番根元となるモンスターのIDのリスト(std::setで、evol_root_sortによりソートされている)
65  */
66 static auto get_mon_evol_roots()
67 {
68     std::set<MonsterRaceId> evol_parents;
69     std::set<MonsterRaceId> evol_children;
70     for (const auto &[r_idx, r_ref] : monraces_info) {
71         if (MonsterRace(r_ref.next_r_idx).is_valid()) {
72             evol_parents.emplace(r_ref.idx);
73             evol_children.emplace(r_ref.next_r_idx);
74         }
75     }
76
77     auto evol_root_sort = [](MonsterRaceId i1, MonsterRaceId i2) {
78         auto &r1 = monraces_info[i1];
79         auto &r2 = monraces_info[i2];
80         if (r1.level != r2.level) {
81             return r1.level < r2.level;
82         }
83         if (r1.mexp != r2.mexp) {
84             return r1.mexp < r2.mexp;
85         }
86         return i1 <= i2;
87     };
88
89     std::set<MonsterRaceId, decltype(evol_root_sort)> evol_roots(evol_root_sort);
90     std::set_difference(evol_parents.begin(), evol_parents.end(), evol_children.begin(), evol_children.end(),
91         std::inserter(evol_roots, evol_roots.end()));
92
93     return evol_roots;
94 }
95
96 /*!
97  * @brief 進化ツリーをスポイラー出力するメインルーチン
98  *
99  * fprintf() に'%' (壁)を渡すとフォーマット指定子扱いされるので、関数の外でフォーマットしてはいけない.
100  */
101 static SpoilerOutputResultType spoil_mon_evol()
102 {
103     const auto &path = path_build(ANGBAND_DIR_USER, "mon-evol.txt");
104     spoiler_file = angband_fopen(path, FileOpenMode::WRITE);
105     if (!spoiler_file) {
106         return SpoilerOutputResultType::FILE_OPEN_FAILED;
107     }
108
109     std::stringstream ss;
110     ss << "Monster Spoilers for " << get_version() << '\n';
111     spoil_out(ss.str());
112     spoil_out("------------------------------------------\n\n");
113     for (auto r_idx : get_mon_evol_roots()) {
114         auto r_ptr = &monraces_info[r_idx];
115         constexpr auto fmt_before = _("[%d]: %s (レベル%d, '%c')\n", "[%d]: %s (Level %d, '%c')\n");
116         fprintf(spoiler_file, fmt_before, enum2i(r_idx), r_ptr->name.data(), (int)r_ptr->level, r_ptr->d_char);
117         for (auto n = 1; MonsterRace(r_ptr->next_r_idx).is_valid(); n++) {
118             fprintf(spoiler_file, "%*s-(%d)-> ", n * 2, "", r_ptr->next_exp);
119             fprintf(spoiler_file, "[%d]: ", enum2i(r_ptr->next_r_idx));
120             r_ptr = &monraces_info[r_ptr->next_r_idx];
121             constexpr auto fmt_after = _("%s (レベル%d, '%c')\n", "%s (Level %d, '%c')\n");
122             fprintf(spoiler_file, fmt_after, r_ptr->name.data(), (int)r_ptr->level, r_ptr->d_char);
123         }
124
125         fputc('\n', spoiler_file);
126     }
127
128     return ferror(spoiler_file) || angband_fclose(spoiler_file) ? SpoilerOutputResultType::FILE_CLOSE_FAILED
129                                                                 : SpoilerOutputResultType::SUCCESSFUL;
130 }
131
132 static SpoilerOutputResultType spoil_categorized_mon_desc()
133 {
134     auto status = spoil_mon_desc("mon-desc-ridable.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->misc_flags.has(MonsterMiscType::RIDING); });
135     if (status == SpoilerOutputResultType::SUCCESSFUL) {
136         status = spoil_mon_desc("mon-desc-wildonly.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_ONLY); });
137     }
138
139     if (status == SpoilerOutputResultType::SUCCESSFUL) {
140         status = spoil_mon_desc("mon-desc-town.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_TOWN); });
141     }
142
143     if (status == SpoilerOutputResultType::SUCCESSFUL) {
144         status = spoil_mon_desc("mon-desc-shore.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_SHORE); });
145     }
146
147     if (status == SpoilerOutputResultType::SUCCESSFUL) {
148         status = spoil_mon_desc("mon-desc-ocean.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_OCEAN); });
149     }
150
151     if (status == SpoilerOutputResultType::SUCCESSFUL) {
152         status = spoil_mon_desc("mon-desc-waste.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_WASTE); });
153     }
154
155     if (status == SpoilerOutputResultType::SUCCESSFUL) {
156         status = spoil_mon_desc("mon-desc-wood.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_WOOD); });
157     }
158
159     if (status == SpoilerOutputResultType::SUCCESSFUL) {
160         status = spoil_mon_desc("mon-desc-volcano.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_VOLCANO); });
161     }
162
163     if (status == SpoilerOutputResultType::SUCCESSFUL) {
164         status = spoil_mon_desc("mon-desc-mountain.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_MOUNTAIN); });
165     }
166
167     if (status == SpoilerOutputResultType::SUCCESSFUL) {
168         status = spoil_mon_desc("mon-desc-grass.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_GRASS); });
169     }
170
171     if (status == SpoilerOutputResultType::SUCCESSFUL) {
172         status = spoil_mon_desc("mon-desc-wildall.txt", [](const MonsterRaceInfo *r_ptr) { return r_ptr->wilderness_flags.has(MonsterWildernessType::WILD_ALL); });
173     }
174
175     return status;
176 }
177
178 static SpoilerOutputResultType spoil_player_spell()
179 {
180     const auto &path = path_build(ANGBAND_DIR_USER, "spells.txt");
181     spoiler_file = angband_fopen(path, FileOpenMode::WRITE);
182     if (!spoiler_file) {
183         return SpoilerOutputResultType::FILE_OPEN_FAILED;
184     }
185
186     spoil_out(format("Player spells for %s\n", get_version().data()));
187     spoil_out("------------------------------------------\n\n");
188
189     PlayerType dummy_p;
190     dummy_p.lev = 1;
191     for (auto c = 0; c < PLAYER_CLASS_TYPE_MAX; c++) {
192         auto class_ptr = &class_info[c];
193         spoil_out(format("[[Class: %s]]\n", class_ptr->title));
194
195         auto magic_ptr = &class_magics_info[c];
196         std::string book_name = _("なし", "None");
197         if (magic_ptr->spell_book != ItemKindType::NONE) {
198             ItemEntity book;
199             auto o_ptr = &book;
200             o_ptr->prep(lookup_baseitem_id({ magic_ptr->spell_book, 0 }));
201             book_name = describe_flavor(&dummy_p, o_ptr, OD_NAME_ONLY);
202             auto *s = angband_strchr(book_name.data(), '[');
203             if (s != nullptr) {
204                 book_name.erase(s - book_name.data());
205             }
206         }
207
208         constexpr auto mes = "BookType:%s Stat:%s Xtra:%x Type:%d Weight:%d\n";
209         const auto &spell = wiz_spell_stat[magic_ptr->spell_stat];
210         spoil_out(format(mes, book_name.data(), spell.data(), magic_ptr->spell_xtra, magic_ptr->spell_type, magic_ptr->spell_weight));
211         if (magic_ptr->spell_book == ItemKindType::NONE) {
212             spoil_out(_("呪文なし\n\n", "No spells.\n\n"));
213             continue;
214         }
215
216         for (int16_t r = 1; r < MAX_MAGIC; r++) {
217             spoil_out(format("[Realm: %s]\n", realm_names[r]));
218             spoil_out("Name                     Lv Cst Dif Exp\n");
219             for (SPELL_IDX i = 0; i < 32; i++) {
220                 auto spell_ptr = &magic_ptr->info[r][i];
221                 const auto spell_name = exe_spell(&dummy_p, r, i, SpellProcessType::NAME);
222                 spoil_out(format("%-24s %2d %3d %3d %3d\n", spell_name->data(), spell_ptr->slevel, spell_ptr->smana, spell_ptr->sfail, spell_ptr->sexp));
223             }
224             spoil_out("\n");
225         }
226     }
227
228     return ferror(spoiler_file) || angband_fclose(spoiler_file) ? SpoilerOutputResultType::FILE_CLOSE_FAILED
229                                                                 : SpoilerOutputResultType::SUCCESSFUL;
230 }
231
232 /*!
233  * @brief スポイラー出力を行うコマンドのメインルーチン /
234  * Create Spoiler files -BEN-
235  */
236 void exe_output_spoilers(void)
237 {
238     screen_save();
239     while (true) {
240         auto status = SpoilerOutputResultType::CANCELED;
241         term_clear();
242         prt("Create a spoiler file.", 2, 0);
243         prt("(1) Brief Object Info (obj-desc.txt)", 5, 5);
244         prt("(2) Brief Artifact Info (artifact.txt)", 6, 5);
245         prt("(3) Brief Monster Info (mon-desc.txt)", 7, 5);
246         prt("(4) Brief Categorized Monster Info (mon-desc-*.txt)", 8, 5);
247         prt("(5) Full Monster Info (mon-info.txt)", 9, 5);
248         prt("(6) Monster Evolution Info (mon-evol.txt)", 10, 5);
249         prt("(7) Player Spells Info (spells.txt)", 11, 5);
250         prt(_("コマンド:", "Command: "), _(18, 12), 0);
251         switch (inkey()) {
252         case ESCAPE:
253             screen_load();
254             return;
255         case '1':
256             status = spoil_obj_desc();
257             break;
258         case '2':
259             status = spoil_fixed_artifact();
260             break;
261         case '3':
262             status = spoil_mon_desc("mon-desc.txt");
263             break;
264         case '4':
265             status = spoil_categorized_mon_desc();
266             break;
267         case '5':
268             status = spoil_mon_info();
269             break;
270         case '6':
271             status = spoil_mon_evol();
272             break;
273         case '7':
274             status = spoil_player_spell();
275             break;
276         default:
277             bell();
278             break;
279         }
280
281         switch (status) {
282         case SpoilerOutputResultType::FILE_OPEN_FAILED:
283             msg_print("Cannot create spoiler file.");
284             break;
285         case SpoilerOutputResultType::FILE_CLOSE_FAILED:
286             msg_print("Cannot close spoiler file.");
287             break;
288         case SpoilerOutputResultType::SUCCESSFUL:
289             msg_print("Successfully created a spoiler file.");
290             break;
291         case SpoilerOutputResultType::CANCELED:
292             break;
293         }
294         msg_erase();
295     }
296 }
297
298 /*!
299  * @brief 全スポイラー出力を行うコマンドのメインルーチン /
300  * Create Spoiler files -BEN-
301  * @return 成功時SPOILER_OUTPUT_SUCCESS / 失敗時エラー状態
302  */
303 SpoilerOutputResultType output_all_spoilers()
304 {
305     auto status = spoil_obj_desc();
306     if (status != SpoilerOutputResultType::SUCCESSFUL) {
307         return status;
308     }
309
310     status = spoil_fixed_artifact();
311     if (status != SpoilerOutputResultType::SUCCESSFUL) {
312         return status;
313     }
314
315     status = spoil_mon_desc("mon-desc.txt");
316     if (status != SpoilerOutputResultType::SUCCESSFUL) {
317         return status;
318     }
319
320     status = spoil_categorized_mon_desc();
321     if (status != SpoilerOutputResultType::SUCCESSFUL) {
322         return status;
323     }
324
325     status = spoil_mon_info();
326     if (status != SpoilerOutputResultType::SUCCESSFUL) {
327         return status;
328     }
329
330     status = spoil_mon_evol();
331     if (status != SpoilerOutputResultType::SUCCESSFUL) {
332         return status;
333     }
334
335     return SpoilerOutputResultType::SUCCESSFUL;
336 }