OSDN Git Service

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