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 #include <string_view>
52 static constexpr std::array<std::string_view, 6> wiz_spell_stat = { {
62 * @brief 進化ツリーの一番根元となるモンスターのIDのリストを取得する
64 * @return 進化ツリーの一番根元となるモンスターのIDのリスト(std::setで、evol_root_sortによりソートされている)
66 static auto get_mon_evol_roots()
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);
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;
83 if (r1.mexp != r2.mexp) {
84 return r1.mexp < r2.mexp;
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()));
97 * @brief 進化ツリーをスポイラー出力するメインルーチン
99 * fprintf() に'%' (壁)を渡すとフォーマット指定子扱いされるので、関数の外でフォーマットしてはいけない.
101 static SpoilerOutputResultType spoil_mon_evol()
103 const auto &path = path_build(ANGBAND_DIR_USER, "mon-evol.txt");
104 spoiler_file = angband_fopen(path, FileOpenMode::WRITE);
106 return SpoilerOutputResultType::FILE_OPEN_FAILED;
109 std::stringstream ss;
110 ss << "Monster Spoilers for " << get_version() << '\n';
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);
125 fputc('\n', spoiler_file);
128 return ferror(spoiler_file) || angband_fclose(spoiler_file) ? SpoilerOutputResultType::FILE_CLOSE_FAILED
129 : SpoilerOutputResultType::SUCCESSFUL;
132 static SpoilerOutputResultType spoil_categorized_mon_desc()
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); });
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); });
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); });
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); });
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); });
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); });
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); });
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); });
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); });
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); });
178 static SpoilerOutputResultType spoil_player_spell()
180 const auto &path = path_build(ANGBAND_DIR_USER, "spells.txt");
181 spoiler_file = angband_fopen(path, FileOpenMode::WRITE);
183 return SpoilerOutputResultType::FILE_OPEN_FAILED;
186 spoil_out(format("Player spells for %s\n", get_version().data()));
187 spoil_out("------------------------------------------\n\n");
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));
195 auto magic_ptr = &class_magics_info[c];
196 std::string book_name = _("なし", "None");
197 if (magic_ptr->spell_book != ItemKindType::NONE) {
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(), '[');
204 book_name.erase(s - book_name.data());
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"));
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));
228 return ferror(spoiler_file) || angband_fclose(spoiler_file) ? SpoilerOutputResultType::FILE_CLOSE_FAILED
229 : SpoilerOutputResultType::SUCCESSFUL;
233 * @brief スポイラー出力を行うコマンドのメインルーチン /
234 * Create Spoiler files -BEN-
236 void exe_output_spoilers(void)
240 auto status = SpoilerOutputResultType::CANCELED;
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);
256 status = spoil_obj_desc();
259 status = spoil_fixed_artifact();
262 status = spoil_mon_desc("mon-desc.txt");
265 status = spoil_categorized_mon_desc();
268 status = spoil_mon_info();
271 status = spoil_mon_evol();
274 status = spoil_player_spell();
282 case SpoilerOutputResultType::FILE_OPEN_FAILED:
283 msg_print("Cannot create spoiler file.");
285 case SpoilerOutputResultType::FILE_CLOSE_FAILED:
286 msg_print("Cannot close spoiler file.");
288 case SpoilerOutputResultType::SUCCESSFUL:
289 msg_print("Successfully created a spoiler file.");
291 case SpoilerOutputResultType::CANCELED:
299 * @brief 全スポイラー出力を行うコマンドのメインルーチン /
300 * Create Spoiler files -BEN-
301 * @return 成功時SPOILER_OUTPUT_SUCCESS / 失敗時エラー状態
303 SpoilerOutputResultType output_all_spoilers()
305 auto status = spoil_obj_desc();
306 if (status != SpoilerOutputResultType::SUCCESSFUL) {
310 status = spoil_fixed_artifact();
311 if (status != SpoilerOutputResultType::SUCCESSFUL) {
315 status = spoil_mon_desc("mon-desc.txt");
316 if (status != SpoilerOutputResultType::SUCCESSFUL) {
320 status = spoil_categorized_mon_desc();
321 if (status != SpoilerOutputResultType::SUCCESSFUL) {
325 status = spoil_mon_info();
326 if (status != SpoilerOutputResultType::SUCCESSFUL) {
330 status = spoil_mon_evol();
331 if (status != SpoilerOutputResultType::SUCCESSFUL) {
335 return SpoilerOutputResultType::SUCCESSFUL;