OSDN Git Service

[Refactor] #2807 Renamed monster-race-definition.h to monster-race-info.h
[hengbandforosx/hengbandosx.git] / src / knowledge / knowledge-quests.cpp
1 /*!
2  * @brief 既知のクエストを表示する
3  * @date 2020/04/23
4  * @author Hourier
5  */
6
7 #include "knowledge/knowledge-quests.h"
8 #include "artifact/fixed-art-types.h"
9 #include "core/show-file.h"
10 #include "dungeon/quest.h"
11 #include "flavor/flavor-describer.h"
12 #include "flavor/object-flavor-types.h"
13 #include "info-reader/fixed-map-parser.h"
14 #include "io-dump/dump-util.h"
15 #include "locale/english.h"
16 #include "monster-race/monster-race.h"
17 #include "object-enchant/special-object-flags.h"
18 #include "object/object-kind-hook.h"
19 #include "system/artifact-type-definition.h"
20 #include "system/baseitem-info-definition.h"
21 #include "system/dungeon-info.h"
22 #include "system/floor-type-definition.h"
23 #include "system/item-entity.h"
24 #include "system/monster-race-info.h"
25 #include "system/player-type-definition.h"
26 #include "term/screen-processor.h"
27 #include "util/angband-files.h"
28 #include "util/enum-converter.h"
29 #include "util/sort.h"
30 #include "util/string-processor.h"
31 #include "world/world.h"
32 #include <numeric>
33
34 /*!
35  * @brief Check on the status of an active quest
36  * @param player_ptr プレイヤーへの参照ポインタ
37  */
38 void do_cmd_checkquest(PlayerType *player_ptr)
39 {
40     screen_save();
41     do_cmd_knowledge_quests(player_ptr);
42     screen_load();
43 }
44
45 /*!
46  * @brief Print all active quests
47  * @param player_ptr プレイヤーへの参照ポインタ
48  * @todo PlayerTypeではなくQUEST_IDXを引数にすべきかもしれない
49  */
50 static void do_cmd_knowledge_quests_current(PlayerType *player_ptr, FILE *fff)
51 {
52     const auto &quest_list = QuestList::get_instance();
53     char tmp_str[1024];
54     char rand_tmp_str[512] = "\0";
55     GAME_TEXT name[MAX_NLEN];
56     MonsterRaceInfo *r_ptr;
57     int rand_level = 100;
58     int total = 0;
59
60     fprintf(fff, _("《遂行中のクエスト》\n", "< Current Quest >\n"));
61
62     for (const auto &[q_idx, q_ref] : quest_list) {
63         bool is_print = q_ref.status == QuestStatusType::TAKEN;
64         is_print |= (q_ref.status == QuestStatusType::STAGE_COMPLETED) && (q_ref.type == QuestKindType::TOWER);
65         is_print |= q_ref.status == QuestStatusType::COMPLETED;
66         if (!is_print) {
67             continue;
68         }
69
70         QuestId old_quest = player_ptr->current_floor_ptr->quest_number;
71         for (int j = 0; j < 10; j++) {
72             quest_text[j][0] = '\0';
73         }
74
75         quest_text_line = 0;
76         player_ptr->current_floor_ptr->quest_number = q_idx;
77         init_flags = INIT_SHOW_TEXT;
78         parse_fixed_map(player_ptr, QUEST_DEFINITION_LIST, 0, 0, 0, 0);
79         player_ptr->current_floor_ptr->quest_number = old_quest;
80         if (q_ref.flags & QUEST_FLAG_SILENT) {
81             continue;
82         }
83         total++;
84         if (q_ref.type != QuestKindType::RANDOM) {
85             char note[512] = "\0";
86             if (q_ref.status == QuestStatusType::TAKEN || q_ref.status == QuestStatusType::STAGE_COMPLETED) {
87                 switch (q_ref.type) {
88                 case QuestKindType::KILL_LEVEL:
89                     r_ptr = &monraces_info[q_ref.r_idx];
90                     strcpy(name, r_ptr->name.data());
91                     if (q_ref.max_num > 1) {
92 #ifdef JP
93                         sprintf(note, " - %d 体の%sを倒す。(あと %d 体)", (int)q_ref.max_num, name, (int)(q_ref.max_num - q_ref.cur_num));
94 #else
95                         plural_aux(name);
96                         sprintf(note, " - kill %d %s, have killed %d.", (int)q_ref.max_num, name, (int)q_ref.cur_num);
97 #endif
98                     } else {
99                         sprintf(note, _(" - %sを倒す。", " - kill %s."), name);
100                     }
101
102                     break;
103                 case QuestKindType::FIND_ARTIFACT:
104                     if (q_ref.reward_artifact_idx != FixedArtifactId::NONE) {
105                         const auto &a_ref = artifacts_info.at(q_ref.reward_artifact_idx);
106                         ItemEntity item;
107                         auto k_idx = lookup_baseitem_id(a_ref.bi_key);
108                         item.prep(k_idx);
109                         item.fixed_artifact_idx = q_ref.reward_artifact_idx;
110                         item.ident = IDENT_STORE;
111                         describe_flavor(player_ptr, name, &item, OD_NAME_ONLY);
112                     }
113
114                     sprintf(note, _("\n   - %sを見つけ出す。", "\n   - Find %s."), name);
115                     break;
116                 case QuestKindType::FIND_EXIT:
117                     sprintf(note, _(" - 出口に到達する。", " - Reach exit."));
118                     break;
119                 case QuestKindType::KILL_NUMBER:
120 #ifdef JP
121                     sprintf(note, " - %d 体のモンスターを倒す。(あと %d 体)", (int)q_ref.max_num, (int)(q_ref.max_num - q_ref.cur_num));
122 #else
123                     sprintf(note, " - Kill %d monsters, have killed %d.", (int)q_ref.max_num, (int)q_ref.cur_num);
124 #endif
125                     break;
126
127                 case QuestKindType::KILL_ALL:
128                 case QuestKindType::TOWER:
129                     sprintf(note, _(" - 全てのモンスターを倒す。", " - Kill all monsters."));
130                     break;
131                 default:
132                     break;
133                 }
134             }
135
136             sprintf(tmp_str, _("  %s (危険度:%d階相当)%s\n", "  %s (Danger level: %d)%s\n"), q_ref.name, (int)q_ref.level, note);
137             fputs(tmp_str, fff);
138             if (q_ref.status == QuestStatusType::COMPLETED) {
139                 sprintf(tmp_str, _("    クエスト達成 - まだ報酬を受けとってない。\n", "    Quest Completed - Unrewarded\n"));
140                 fputs(tmp_str, fff);
141                 continue;
142             }
143
144             int k = 0;
145             while (quest_text[k][0] && k < 10) {
146                 fprintf(fff, "    %s\n", quest_text[k]);
147                 k++;
148             }
149
150             continue;
151         }
152
153         if (q_ref.level >= rand_level) {
154             continue;
155         }
156         rand_level = q_ref.level;
157         if (max_dlv[DUNGEON_ANGBAND] < rand_level) {
158             continue;
159         }
160
161         r_ptr = &monraces_info[q_ref.r_idx];
162         strcpy(name, r_ptr->name.data());
163         if (q_ref.max_num <= 1) {
164             sprintf(rand_tmp_str, _("  %s (%d 階) - %sを倒す。\n", "  %s (Dungeon level: %d)\n  Kill %s.\n"), q_ref.name, (int)q_ref.level, name);
165             continue;
166         }
167
168 #ifdef JP
169         sprintf(rand_tmp_str, "  %s (%d 階) - %d 体の%sを倒す。(あと %d 体)\n", q_ref.name, (int)q_ref.level, (int)q_ref.max_num, name,
170             (int)(q_ref.max_num - q_ref.cur_num));
171 #else
172         plural_aux(name);
173
174         sprintf(rand_tmp_str, "  %s (Dungeon level: %d)\n  Kill %d %s, have killed %d.\n", q_ref.name, (int)q_ref.level, (int)q_ref.max_num, name,
175             (int)q_ref.cur_num);
176 #endif
177     }
178
179     if (rand_tmp_str[0]) {
180         fputs(rand_tmp_str, fff);
181     }
182
183     if (!total) {
184         fprintf(fff, _("  なし\n", "  Nothing.\n"));
185     }
186 }
187
188 static bool do_cmd_knowledge_quests_aux(PlayerType *player_ptr, FILE *fff, QuestId q_idx)
189 {
190     char tmp_str[120];
191     char playtime_str[16];
192     const auto &quest_list = QuestList::get_instance();
193     const auto &q_ref = quest_list[q_idx];
194
195     auto *floor_ptr = player_ptr->current_floor_ptr;
196     auto is_fixed_quest = quest_type::is_fixed(q_idx);
197     if (is_fixed_quest) {
198         QuestId old_quest = floor_ptr->quest_number;
199         floor_ptr->quest_number = q_idx;
200         init_flags = INIT_NAME_ONLY;
201         parse_fixed_map(player_ptr, QUEST_DEFINITION_LIST, 0, 0, 0, 0);
202         floor_ptr->quest_number = old_quest;
203         if (q_ref.flags & QUEST_FLAG_SILENT) {
204             return false;
205         }
206     }
207
208     strnfmt(playtime_str, sizeof(playtime_str), "%02d:%02d:%02d", q_ref.comptime / (60 * 60), (q_ref.comptime / 60) % 60, q_ref.comptime % 60);
209
210     auto fputs_name_remain = [fff](const auto &name) {
211         for (auto i = 1U; i < name.size(); ++i) {
212             fprintf(fff, "  %s\n", name[i].data());
213         }
214     };
215
216     if (is_fixed_quest || !MonsterRace(q_ref.r_idx).is_valid()) {
217         auto name = str_separate(q_ref.name, 35);
218         sprintf(tmp_str, _("  %-35s (危険度:%3d階相当) - レベル%2d - %s\n", "  %-35s (Danger  level: %3d) - level %2d - %s\n"), name.front().data(), (int)q_ref.level,
219             q_ref.complev, playtime_str);
220         fputs(tmp_str, fff);
221         fputs_name_remain(name);
222         return true;
223     }
224
225     auto name = str_separate(monraces_info[q_ref.r_idx].name, 35);
226     if (q_ref.complev == 0) {
227         sprintf(tmp_str, _("  %-35s (%3d階)            -   不戦勝 - %s\n", "  %-35s (Dungeon level: %3d) - Unearned - %s\n"),
228             name.front().data(), (int)q_ref.level, playtime_str);
229         fputs(tmp_str, fff);
230         fputs_name_remain(name);
231         return true;
232     }
233
234     sprintf(tmp_str, _("  %-35s (%3d階)            - レベル%2d - %s\n", "  %-35s (Dungeon level: %3d) - level %2d - %s\n"), name.front().data(),
235         (int)q_ref.level, q_ref.complev, playtime_str);
236     fputs(tmp_str, fff);
237     fputs_name_remain(name);
238     return true;
239 }
240
241 /*
242  * Print all finished quests
243  * @param player_ptr プレイヤーへの参照ポインタ
244  * @param fff セーブファイル (展開済?)
245  * @param quest_numbers 受注したことのあるクエスト群
246  */
247 void do_cmd_knowledge_quests_completed(PlayerType *player_ptr, FILE *fff, const std::vector<QuestId> &quest_numbers)
248 {
249     fprintf(fff, _("《達成したクエスト》\n", "< Completed Quest >\n"));
250     int16_t total = 0;
251     for (auto &q_idx : quest_numbers) {
252         const auto &quest_list = QuestList::get_instance();
253         const auto &q_ref = quest_list[q_idx];
254
255         if (q_ref.status == QuestStatusType::FINISHED && do_cmd_knowledge_quests_aux(player_ptr, fff, q_idx)) {
256             ++total;
257         }
258     }
259
260     if (total == 0) {
261         fprintf(fff, _("  なし\n", "  Nothing.\n"));
262     }
263 }
264
265 /*
266  * Print all failed quests
267  * @param player_ptr プレイヤーへの参照ポインタ
268  * @param fff セーブファイル (展開済?)
269  * @param quest_numbers 受注したことのあるクエスト群
270  */
271 void do_cmd_knowledge_quests_failed(PlayerType *player_ptr, FILE *fff, const std::vector<QuestId> &quest_numbers)
272 {
273     fprintf(fff, _("《失敗したクエスト》\n", "< Failed Quest >\n"));
274     int16_t total = 0;
275     for (auto &q_idx : quest_numbers) {
276         const auto &quest_list = QuestList::get_instance();
277         const auto &q_ref = quest_list[q_idx];
278
279         if (((q_ref.status == QuestStatusType::FAILED_DONE) || (q_ref.status == QuestStatusType::FAILED)) && do_cmd_knowledge_quests_aux(player_ptr, fff, q_idx)) {
280             ++total;
281         }
282     }
283
284     if (total == 0) {
285         fprintf(fff, _("  なし\n", "  Nothing.\n"));
286     }
287 }
288
289 /*
290  * Print all random quests
291  */
292 static void do_cmd_knowledge_quests_wiz_random(FILE *fff)
293 {
294     fprintf(fff, _("《残りのランダムクエスト》\n", "< Remaining Random Quest >\n"));
295     const auto &quest_list = QuestList::get_instance();
296     GAME_TEXT tmp_str[120];
297     int16_t total = 0;
298     for (const auto &[q_idx, q_ref] : quest_list) {
299         if (q_ref.flags & QUEST_FLAG_SILENT) {
300             continue;
301         }
302
303         if ((q_ref.type == QuestKindType::RANDOM) && (q_ref.status == QuestStatusType::TAKEN)) {
304             total++;
305             sprintf(tmp_str, _("  %s (%d階, %s)\n", "  %s (%d, %s)\n"), q_ref.name, (int)q_ref.level, monraces_info[q_ref.r_idx].name.data());
306             fputs(tmp_str, fff);
307         }
308     }
309
310     if (total == 0) {
311         fprintf(fff, _("  なし\n", "  Nothing.\n"));
312     }
313 }
314
315 /*
316  * Print quest status of all active quests
317  * @param player_ptr プレイヤーへの参照ポインタ
318  */
319 void do_cmd_knowledge_quests(PlayerType *player_ptr)
320 {
321     FILE *fff = nullptr;
322     GAME_TEXT file_name[FILE_NAME_SIZE];
323     if (!open_temporary_file(&fff, file_name)) {
324         return;
325     }
326
327     std::vector<QuestId> quest_numbers;
328     const auto &quest_list = QuestList::get_instance();
329     for (const auto &[q_idx, q_ref] : quest_list) {
330         quest_numbers.push_back(q_idx);
331     }
332     int dummy;
333     ang_sort(player_ptr, quest_numbers.data(), &dummy, quest_numbers.size(), ang_sort_comp_quest_num, ang_sort_swap_quest_num);
334
335     do_cmd_knowledge_quests_current(player_ptr, fff);
336     fputc('\n', fff);
337     do_cmd_knowledge_quests_completed(player_ptr, fff, quest_numbers);
338     fputc('\n', fff);
339     do_cmd_knowledge_quests_failed(player_ptr, fff, quest_numbers);
340     if (w_ptr->wizard) {
341         fputc('\n', fff);
342         do_cmd_knowledge_quests_wiz_random(fff);
343     }
344
345     angband_fclose(fff);
346     (void)show_file(player_ptr, true, file_name, _("クエスト達成状況", "Quest status"), 0, 0);
347     fd_kill(file_name);
348 }