2 * @brief スポイラー出力処理 (行数の都合でモンスター進化ツリーもここに入っている)
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.
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"
50 static constexpr std::array<std::string_view, 6> wiz_spell_stat = { {
60 * @brief 進化ツリーの一番根元となるモンスターのIDのリストを取得する
62 * @return 進化ツリーの一番根元となるモンスターのIDのリスト(std::setで、evol_root_sortによりソートされている)
64 static auto get_mon_evol_roots(void)
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);
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;
81 if (r1.mexp != r2.mexp) {
82 return r1.mexp < r2.mexp;
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()));
95 * @brief 進化ツリーをスポイラー出力するメインルーチン /
96 * Print monsters' evolution information to file
97 * @param fname 出力ファイル名
99 static SpoilerOutputResultType spoil_mon_evol(concptr fname)
102 path_build(buf, sizeof buf, ANGBAND_DIR_USER, fname);
103 spoiler_file = angband_fopen(buf, "w");
105 return SpoilerOutputResultType::FILE_OPEN_FAILED;
108 spoil_out(std::string("Monster Spoilers for ").append(get_version()).append("\n"));
110 spoil_out("------------------------------------------\n\n");
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);
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];
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);
124 fputc('\n', spoiler_file);
127 return ferror(spoiler_file) || angband_fclose(spoiler_file) ? SpoilerOutputResultType::FILE_CLOSE_FAILED
128 : SpoilerOutputResultType::SUCCESSFUL;
131 static SpoilerOutputResultType spoil_categorized_mon_desc()
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); });
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); });
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); });
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); });
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); });
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); });
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); });
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); });
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); });
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); });
177 static SpoilerOutputResultType spoil_player_spell(concptr fname)
181 path_build(buf, sizeof buf, ANGBAND_DIR_USER, fname);
182 spoiler_file = angband_fopen(buf, "w");
184 return SpoilerOutputResultType::FILE_OPEN_FAILED;
187 spoil_out(format("Player spells for %s\n", get_version().data()));
188 spoil_out("------------------------------------------\n\n");
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));
197 auto magic_ptr = &class_magics_info[c];
198 concptr book_name = "なし";
199 if (magic_ptr->spell_book != ItemKindType::NONE) {
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, '[');
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"));
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));
229 return ferror(spoiler_file) || angband_fclose(spoiler_file) ? SpoilerOutputResultType::FILE_CLOSE_FAILED
230 : SpoilerOutputResultType::SUCCESSFUL;
234 * @brief スポイラー出力を行うコマンドのメインルーチン /
235 * Create Spoiler files -BEN-
237 void exe_output_spoilers(void)
241 auto status = SpoilerOutputResultType::CANCELED;
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);
257 status = spoil_obj_desc("obj-desc.txt");
260 status = spoil_fixed_artifact("artifact.txt");
263 status = spoil_mon_desc("mon-desc.txt");
266 status = spoil_categorized_mon_desc();
269 status = spoil_mon_info("mon-info.txt");
272 status = spoil_mon_evol("mon-evol.txt");
275 status = spoil_player_spell("spells.txt");
283 case SpoilerOutputResultType::FILE_OPEN_FAILED:
284 msg_print("Cannot create spoiler file.");
286 case SpoilerOutputResultType::FILE_CLOSE_FAILED:
287 msg_print("Cannot close spoiler file.");
289 case SpoilerOutputResultType::SUCCESSFUL:
290 msg_print("Successfully created a spoiler file.");
292 case SpoilerOutputResultType::CANCELED:
300 * @brief 全スポイラー出力を行うコマンドのメインルーチン /
301 * Create Spoiler files -BEN-
302 * @return 成功時SPOILER_OUTPUT_SUCCESS / 失敗時エラー状態
304 SpoilerOutputResultType output_all_spoilers(void)
306 auto status = spoil_obj_desc("obj-desc.txt");
307 if (status != SpoilerOutputResultType::SUCCESSFUL) {
311 status = spoil_fixed_artifact("artifact.txt");
312 if (status != SpoilerOutputResultType::SUCCESSFUL) {
316 status = spoil_mon_desc("mon-desc.txt");
317 if (status != SpoilerOutputResultType::SUCCESSFUL) {
321 status = spoil_categorized_mon_desc();
322 if (status != SpoilerOutputResultType::SUCCESSFUL) {
326 status = spoil_mon_info("mon-info.txt");
327 if (status != SpoilerOutputResultType::SUCCESSFUL) {
331 status = spoil_mon_evol("mon-evol.txt");
332 if (status != SpoilerOutputResultType::SUCCESSFUL) {
336 return SpoilerOutputResultType::SUCCESSFUL;