3 #include "artifact/fixed-art-types.h"
4 #include "avatar/avatar.h"
5 #include "cmd-building/cmd-building.h"
6 #include "dungeon/dungeon.h"
7 #include "dungeon/quest.h"
8 #include "flavor/flavor-describer.h"
9 #include "floor/floor-town.h"
10 #include "game-option/birth-options.h"
11 #include "game-option/game-play-options.h"
12 #include "inventory/inventory-slot-types.h"
13 #include "io-dump/character-dump.h"
14 #include "io-dump/player-status-dump.h"
15 #include "io-dump/special-class-dump.h"
16 #include "io/mutations-dump.h"
17 #include "io/write-diary.h"
18 #include "knowledge/knowledge-quests.h"
19 #include "main/angband-headers.h"
20 #include "market/arena-info-table.h"
21 #include "monster-race/monster-race.h"
22 #include "monster-race/race-flags1.h"
23 #include "monster/monster-describer.h"
24 #include "monster/monster-description-types.h"
25 #include "monster/monster-info.h"
26 #include "monster/monster-status.h"
27 #include "monster/smart-learn-types.h"
28 #include "object/object-info.h"
29 #include "pet/pet-util.h"
30 #include "player-info/alignment.h"
31 #include "player/player-status-flags.h"
32 #include "player/player-status-table.h"
33 #include "player/race-info-table.h"
34 #include "realm/realm-names-table.h"
35 #include "store/store-util.h"
36 #include "store/store.h"
37 #include "system/angband-version.h"
38 #include "system/building-type-definition.h"
39 #include "system/floor-type-definition.h"
40 #include "system/monster-race-definition.h"
41 #include "system/monster-type-definition.h"
42 #include "system/object-type-definition.h"
43 #include "system/player-type-definition.h"
44 #include "util/enum-converter.h"
45 #include "util/int-char-converter.h"
46 #include "util/sort.h"
47 #include "util/string-processor.h"
48 #include "view/display-messages.h"
49 #include "world/world.h"
54 * @brief プレイヤーのペット情報をファイルにダンプする
55 * @param player_ptr プレイヤーへの参照ポインタ
58 static void dump_aux_pet(PlayerType *player_ptr, FILE *fff)
61 bool pet_settings = false;
62 for (int i = player_ptr->current_floor_ptr->m_max - 1; i >= 1; i--) {
63 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[i];
65 if (!m_ptr->is_valid()) {
68 if (!m_ptr->is_pet()) {
72 if (!m_ptr->nickname && (player_ptr->riding != i)) {
76 fprintf(fff, _("\n\n [主なペット]\n\n", "\n\n [Leading Pets]\n\n"));
80 GAME_TEXT pet_name[MAX_NLEN];
81 monster_desc(player_ptr, pet_name, m_ptr, MD_ASSUME_VISIBLE | MD_INDEF_VISIBLE);
82 fprintf(fff, "%s\n", pet_name);
89 fprintf(fff, _("\n\n [ペットへの命令]\n", "\n\n [Command for Pets]\n"));
91 fprintf(fff, _("\n ドアを開ける: %s", "\n Pets open doors: %s"),
92 (player_ptr->pet_extra_flags & PF_OPEN_DOORS) ? "ON" : "OFF");
94 fprintf(fff, _("\n アイテムを拾う: %s", "\n Pets pick up items: %s"),
95 (player_ptr->pet_extra_flags & PF_PICKUP_ITEMS) ? "ON" : "OFF");
97 fprintf(fff, _("\n テレポート系魔法を使う: %s", "\n Allow teleport: %s"),
98 (player_ptr->pet_extra_flags & PF_TELEPORT) ? "ON" : "OFF");
100 fprintf(fff, _("\n 攻撃魔法を使う: %s", "\n Allow cast attack spell: %s"),
101 (player_ptr->pet_extra_flags & PF_ATTACK_SPELL) ? "ON" : "OFF");
103 fprintf(fff, _("\n 召喚魔法を使う: %s", "\n Allow cast summon spell: %s"),
104 (player_ptr->pet_extra_flags & PF_SUMMON_SPELL) ? "ON" : "OFF");
106 fprintf(fff, _("\n プレイヤーを巻き込む範囲魔法を使う: %s", "\n Allow involve player in area spell: %s"),
107 (player_ptr->pet_extra_flags & PF_BALL_SPELL) ? "ON" : "OFF");
113 * @brief クエスト情報をファイルにダンプする
114 * @param player_ptr プレイヤーへの参照ポインタ
115 * @param fff ファイルポインタ
117 static void dump_aux_quest(PlayerType *player_ptr, FILE *fff)
119 fprintf(fff, _("\n\n [クエスト情報]\n", "\n\n [Quest Information]\n"));
121 const auto &quest_list = QuestList::get_instance();
122 std::vector<QuestId> quest_numbers;
123 for (const auto &[q_idx, q_ref] : quest_list) {
124 quest_numbers.push_back(q_idx);
127 ang_sort(player_ptr, quest_numbers.data(), &dummy, quest_numbers.size(), ang_sort_comp_quest_num, ang_sort_swap_quest_num);
130 do_cmd_knowledge_quests_completed(player_ptr, fff, quest_numbers);
132 do_cmd_knowledge_quests_failed(player_ptr, fff, quest_numbers);
137 * @brief 死の直前メッセージ並びに遺言をファイルにダンプする
138 * @param player_ptr プレイヤーへの参照ポインタ
139 * @param fff ファイルポインタ
141 static void dump_aux_last_message(PlayerType *player_ptr, FILE *fff)
143 if (!player_ptr->is_dead) {
147 if (!w_ptr->total_winner) {
148 fprintf(fff, _("\n [死ぬ直前のメッセージ]\n\n", "\n [Last Messages]\n\n"));
149 for (int i = std::min(message_num(), 30); i >= 0; i--) {
150 fprintf(fff, "> %s\n", message_str((int16_t)i));
157 if (player_ptr->last_message) {
158 fprintf(fff, _("\n [*勝利*メッセージ]\n\n", "\n [*Winning* Message]\n\n"));
159 fprintf(fff, " %s\n", player_ptr->last_message);
165 * @brief 帰還場所情報をファイルにダンプする
166 * @param fff ファイルポインタ
168 static void dump_aux_recall(FILE *fff)
170 fprintf(fff, _("\n [帰還場所]\n\n", "\n [Recall Depth]\n\n"));
171 for (const auto &d_ref : d_info) {
174 if (d_ref.idx == 0 || !d_ref.maxdepth) {
177 if (!max_dlv[d_ref.idx]) {
180 if (MonsterRace(d_ref.final_guardian).is_valid()) {
181 if (!r_info[d_ref.final_guardian].max_num) {
184 } else if (max_dlv[d_ref.idx] == d_ref.maxdepth) {
188 fprintf(fff, _(" %c%-12s: %3d 階\n", " %c%-16s: level %3d\n"), seiha ? '!' : ' ', d_ref.name.c_str(), (int)max_dlv[d_ref.idx]);
193 * @brief オプション情報をファイルにダンプする
194 * @param fff ファイルポインタ
196 static void dump_aux_options(FILE *fff)
198 fprintf(fff, _("\n [オプション設定]\n", "\n [Option Settings]\n"));
200 fprintf(fff, _("\n 保存モード: ON", "\n Preserve Mode: ON"));
202 fprintf(fff, _("\n 保存モード: OFF", "\n Preserve Mode: OFF"));
205 if (ironman_small_levels) {
206 fprintf(fff, _("\n 小さいダンジョン: ALWAYS", "\n Small Levels: ALWAYS"));
207 } else if (always_small_levels) {
208 fprintf(fff, _("\n 小さいダンジョン: ON", "\n Small Levels: ON"));
209 } else if (small_levels) {
210 fprintf(fff, _("\n 小さいダンジョン: ENABLED", "\n Small Levels: ENABLED"));
212 fprintf(fff, _("\n 小さいダンジョン: OFF", "\n Small Levels: OFF"));
216 fprintf(fff, _("\n 元祖の町のみ: ON", "\n Vanilla Town: ON"));
217 } else if (lite_town) {
218 fprintf(fff, _("\n 小規模な町: ON", "\n Lite Town: ON"));
222 fprintf(fff, _("\n 店なし: ON", "\n No Shops: ON"));
225 if (ironman_downward) {
226 fprintf(fff, _("\n 階段を上がれない: ON", "\n Diving Only: ON"));
230 fprintf(fff, _("\n 普通でない部屋: ON", "\n Unusual Rooms: ON"));
233 if (ironman_nightmare) {
234 fprintf(fff, _("\n 悪夢モード: ON", "\n Nightmare Mode: ON"));
237 if (ironman_empty_levels) {
238 fprintf(fff, _("\n アリーナ: ALWAYS", "\n Arena Levels: ALWAYS"));
239 } else if (empty_levels) {
240 fprintf(fff, _("\n アリーナ: ENABLED", "\n Arena Levels: ENABLED"));
242 fprintf(fff, _("\n アリーナ: OFF", "\n Arena Levels: OFF"));
247 if (w_ptr->noscore) {
248 fprintf(fff, _("\n 何か不正なことをしてしまっています。\n", "\n You have done something illegal.\n"));
255 * @brief 闘技場の情報をファイルにダンプする
256 * @param player_ptr プレイヤーへの参照ポインタ
257 * @param fff ファイルポインタ
259 static void dump_aux_arena(PlayerType *player_ptr, FILE *fff)
261 if (lite_town || vanilla_town) {
265 if (player_ptr->arena_number < 0) {
266 if (player_ptr->arena_number <= ARENA_DEFEATED_OLD_VER) {
267 fprintf(fff, _("\n 闘技場: 敗北\n", "\n Arena: Defeated\n"));
271 fff, "\n 闘技場: %d回戦で%sの前に敗北\n", -player_ptr->arena_number, r_info[arena_info[-1 - player_ptr->arena_number].r_idx].name.c_str());
273 fprintf(fff, "\n Arena: Defeated by %s in the %d%s fight\n", r_info[arena_info[-1 - player_ptr->arena_number].r_idx].name.c_str(),
274 -player_ptr->arena_number, get_ordinal_number_suffix(-player_ptr->arena_number));
282 if (player_ptr->arena_number > MAX_ARENA_MONS + 2) {
283 fprintf(fff, _("\n 闘技場: 真のチャンピオン\n", "\n Arena: True Champion\n"));
288 if (player_ptr->arena_number > MAX_ARENA_MONS - 1) {
289 fprintf(fff, _("\n 闘技場: チャンピオン\n", "\n Arena: Champion\n"));
295 fprintf(fff, "\n 闘技場: %2d勝\n", (player_ptr->arena_number > MAX_ARENA_MONS ? MAX_ARENA_MONS : player_ptr->arena_number));
297 fprintf(fff, "\n Arena: %2d Victor%s\n", (player_ptr->arena_number > MAX_ARENA_MONS ? MAX_ARENA_MONS : player_ptr->arena_number),
298 (player_ptr->arena_number > 1) ? "ies" : "y");
304 * @brief 撃破モンスターの情報をファイルにダンプする
305 * @param fff ファイルポインタ
307 static void dump_aux_monsters(PlayerType *player_ptr, FILE *fff)
309 fprintf(fff, _("\n [倒したモンスター]\n\n", "\n [Defeated Monsters]\n\n"));
311 /* Allocate the "who" array */
313 std::vector<MonsterRaceId> who;
315 /* Count monster kills */
317 for (const auto &[r_idx, r_ref] : r_info) {
318 /* Ignore unused index */
319 if (!MonsterRace(r_ref.idx).is_valid() || r_ref.name.empty()) {
323 if (r_ref.kind_flags.has(MonsterKindType::UNIQUE)) {
324 bool dead = (r_ref.max_num == 0);
328 /* Add a unique monster to the list */
329 who.push_back(r_ref.idx);
335 if (r_ref.r_pkills > 0) {
336 norm_total += r_ref.r_pkills;
340 /* No monsters is defeated */
341 if (norm_total < 1) {
342 fprintf(fff, _("まだ敵を倒していません。\n", "You have defeated no enemies yet.\n"));
346 auto uniq_total = static_cast<int>(who.size());
347 /* Defeated more than one normal monsters */
348 if (uniq_total == 0) {
350 fprintf(fff, "%d体の敵を倒しています。\n", norm_total);
352 fprintf(fff, "You have defeated %d %s.\n", norm_total, norm_total == 1 ? "enemy" : "enemies");
357 /* Defeated more than one unique monsters */
359 fprintf(fff, "%d体のユニーク・モンスターを含む、合計%d体の敵を倒しています。\n", uniq_total, norm_total);
361 fprintf(fff, "You have defeated %d %s including %d unique monster%s in total.\n", norm_total, norm_total == 1 ? "enemy" : "enemies", uniq_total,
362 (uniq_total == 1 ? "" : "s"));
365 /* Sort the array by dungeon depth of monsters */
366 ang_sort(player_ptr, who.data(), &why, uniq_total, ang_sort_comp_hook, ang_sort_swap_hook);
367 fprintf(fff, _("\n《上位%d体のユニーク・モンスター》\n", "\n< Unique monsters top %d >\n"), std::min(uniq_total, 10));
370 for (auto it = who.rbegin(); it != who.rend() && std::distance(who.rbegin(), it) < 10; it++) {
371 auto *r_ptr = &r_info[*it];
372 if (r_ptr->defeat_level && r_ptr->defeat_time) {
373 sprintf(buf, _(" - レベル%2d - %d:%02d:%02d", " - level %2d - %d:%02d:%02d"), r_ptr->defeat_level, r_ptr->defeat_time / (60 * 60),
374 (r_ptr->defeat_time / 60) % 60, r_ptr->defeat_time % 60);
379 auto name = str_separate(r_ptr->name, 40);
380 fprintf(fff, _(" %-40s (レベル%3d)%s\n", " %-40s (level %3d)%s\n"), name.front().c_str(), (int)r_ptr->level, buf);
381 for (auto i = 1U; i < name.size(); ++i) {
382 fprintf(fff, " %s\n", name[i].c_str());
388 * @brief 元種族情報をファイルにダンプする
389 * @param player_ptr プレイヤーへの参照ポインタ
390 * @param fff ファイルポインタ
392 static void dump_aux_race_history(PlayerType *player_ptr, FILE *fff)
394 if (!player_ptr->old_race1 && !player_ptr->old_race2) {
398 fprintf(fff, _("\n\n あなたは%sとして生まれた。", "\n\n You were born as %s."), race_info[enum2i(player_ptr->start_race)].title);
399 for (int i = 0; i < MAX_RACES; i++) {
400 if (enum2i(player_ptr->start_race) == i) {
404 if (!(player_ptr->old_race1 & 1UL << i)) {
408 if (!(player_ptr->old_race2 & 1UL << (i - 32))) {
413 fprintf(fff, _("\n あなたはかつて%sだった。", "\n You were a %s before."), race_info[i].title);
420 * @brief 元魔法領域情報をファイルにダンプする
421 * @param player_ptr プレイヤーへの参照ポインタ
422 * @param fff ファイルポインタ
424 static void dump_aux_realm_history(PlayerType *player_ptr, FILE *fff)
426 if (player_ptr->old_realm == 0) {
431 for (int i = 0; i < MAX_MAGIC; i++) {
432 if (!(player_ptr->old_realm & 1UL << i)) {
435 fprintf(fff, _("\n あなたはかつて%s魔法を使えた。", "\n You were able to use %s magic before."), realm_names[i + 1]);
442 * @brief 徳の情報をファイルにダンプする
443 * @param player_ptr プレイヤーへの参照ポインタ
444 * @param fff ファイルポインタ
446 static void dump_aux_virtues(PlayerType *player_ptr, FILE *fff)
448 fprintf(fff, _("\n\n [自分に関する情報]\n\n", "\n\n [HP-rate & Max stat & Virtues]\n\n"));
450 int percent = (int)(((long)player_ptr->player_hp[PY_MAX_LEVEL - 1] * 200L) / (2 * player_ptr->hitdie + ((PY_MAX_LEVEL - 1 + 3) * (player_ptr->hitdie + 1))));
453 if (player_ptr->knowledge & KNOW_HPRATE) {
454 fprintf(fff, "現在の体力ランク : %d/100\n\n", percent);
456 fprintf(fff, "現在の体力ランク : ???\n\n");
458 fprintf(fff, "能力の最大値\n");
460 if (player_ptr->knowledge & KNOW_HPRATE) {
461 fprintf(fff, "Your current Life Rating is %d/100.\n\n", percent);
463 fprintf(fff, "Your current Life Rating is ???.\n\n");
465 fprintf(fff, "Limits of maximum stats\n");
467 for (int v_nr = 0; v_nr < A_MAX; v_nr++) {
468 if ((player_ptr->knowledge & KNOW_STAT) || player_ptr->stat_max[v_nr] == player_ptr->stat_max_max[v_nr]) {
469 fprintf(fff, "%s 18/%d\n", stat_names[v_nr], player_ptr->stat_max_max[v_nr] - 18);
471 fprintf(fff, "%s ???\n", stat_names[v_nr]);
475 std::string alg = PlayerAlignment(player_ptr).get_alignment_description();
476 fprintf(fff, _("\n属性 : %s\n", "\nYour alignment : %s\n"), alg.c_str());
478 dump_virtues(player_ptr, fff);
482 * @brief 突然変異の情報をファイルにダンプする
483 * @param player_ptr プレイヤーへの参照ポインタ
484 * @param fff ファイルポインタ
486 static void dump_aux_mutations(PlayerType *player_ptr, FILE *fff)
488 if (player_ptr->muta.any()) {
489 fprintf(fff, _("\n\n [突然変異]\n\n", "\n\n [Mutations]\n\n"));
490 dump_mutations(player_ptr, fff);
495 * @brief 所持品の情報をファイルにダンプする
496 * @param player_ptr プレイヤーへの参照ポインタ
497 * @param fff ファイルポインタ
499 static void dump_aux_equipment_inventory(PlayerType *player_ptr, FILE *fff)
501 GAME_TEXT o_name[MAX_NLEN];
502 if (player_ptr->equip_cnt) {
503 fprintf(fff, _(" [キャラクタの装備]\n\n", " [Character Equipment]\n\n"));
504 for (int i = INVEN_MAIN_HAND; i < INVEN_TOTAL; i++) {
505 describe_flavor(player_ptr, o_name, &player_ptr->inventory_list[i], 0);
506 if ((((i == INVEN_MAIN_HAND) && can_attack_with_sub_hand(player_ptr)) || ((i == INVEN_SUB_HAND) && can_attack_with_main_hand(player_ptr))) && has_two_handed_weapons(player_ptr)) {
507 strcpy(o_name, _("(武器を両手持ち)", "(wielding with two-hands)"));
510 fprintf(fff, "%c) %s\n", index_to_label(i), o_name);
513 fprintf(fff, "\n\n");
516 fprintf(fff, _(" [キャラクタの持ち物]\n\n", " [Character Inventory]\n\n"));
518 for (int i = 0; i < INVEN_PACK; i++) {
519 if (!player_ptr->inventory_list[i].k_idx) {
522 describe_flavor(player_ptr, o_name, &player_ptr->inventory_list[i], 0);
523 fprintf(fff, "%c) %s\n", index_to_label(i), o_name);
526 fprintf(fff, "\n\n");
530 * @brief 我が家と博物館のオブジェクト情報をファイルにダンプする
531 * @param fff ファイルポインタ
533 static void dump_aux_home_museum(PlayerType *player_ptr, FILE *fff)
535 store_type *store_ptr;
536 store_ptr = &town_info[1].store[enum2i(StoreSaleType::HOME)];
538 GAME_TEXT o_name[MAX_NLEN];
539 if (store_ptr->stock_num) {
540 fprintf(fff, _(" [我が家のアイテム]\n", " [Home Inventory]\n"));
543 for (int i = 0; i < store_ptr->stock_num; i++) {
545 fprintf(fff, _("\n ( %d ページ )\n", "\n ( page %d )\n"), x++);
547 describe_flavor(player_ptr, o_name, &store_ptr->stock[i], 0);
548 fprintf(fff, "%c) %s\n", I2A(i % 12), o_name);
551 fprintf(fff, "\n\n");
554 store_ptr = &town_info[1].store[enum2i(StoreSaleType::MUSEUM)];
556 if (store_ptr->stock_num == 0) {
560 fprintf(fff, _(" [博物館のアイテム]\n", " [Museum]\n"));
563 for (int i = 0; i < store_ptr->stock_num; i++) {
565 fprintf(fff, _("\n ( %d ページ )\n", "\n ( page %d )\n"), x++);
567 describe_flavor(player_ptr, o_name, &store_ptr->stock[i], 0);
568 fprintf(fff, "%c) %s\n", I2A(i % 12), o_name);
571 fprintf(fff, "\n\n");
575 * @brief チェックサム情報を出力 / Get check sum in string form
576 * @return チェックサム情報の文字列
578 static concptr get_check_sum(void)
580 return format("%02x%02x%02x%02x%02x%02x%02x%02x%02x", f_head.checksum, k_head.checksum, a_head.checksum, e_head.checksum, r_head.checksum, d_head.checksum,
581 m_head.checksum, s_head.checksum, v_head.checksum);
585 * @brief ダンプ出力のメインルーチン
586 * Output the character dump to a file
587 * @param player_ptr プレイヤーへの参照ポインタ
588 * @param fff ファイルポインタ
591 void make_character_dump(PlayerType *player_ptr, FILE *fff)
595 fprintf(fff, _(" [%s キャラクタ情報]\n\n", " [%s Character Dump]\n\n"), title);
597 dump_aux_player_status(player_ptr, fff);
598 dump_aux_last_message(player_ptr, fff);
599 dump_aux_options(fff);
600 dump_aux_recall(fff);
601 dump_aux_quest(player_ptr, fff);
602 dump_aux_arena(player_ptr, fff);
603 dump_aux_monsters(player_ptr, fff);
604 dump_aux_virtues(player_ptr, fff);
605 dump_aux_race_history(player_ptr, fff);
606 dump_aux_realm_history(player_ptr, fff);
607 dump_aux_class_special(player_ptr, fff);
608 dump_aux_mutations(player_ptr, fff);
609 dump_aux_pet(player_ptr, fff);
611 dump_aux_equipment_inventory(player_ptr, fff);
612 dump_aux_home_museum(player_ptr, fff);
614 fprintf(fff, _(" [チェックサム: \"%s\"]\n\n", " [Check Sum: \"%s\"]\n\n"), get_check_sum());