OSDN Git Service

Replace sprintf() with std::string and/or format(). Does part of the work of resolvi...
[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     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             std::string note;
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                     if (q_ref.max_num > 1) {
91 #ifdef JP
92                         note = format(" - %d 体の%sを倒す。(あと %d 体)", (int)q_ref.max_num, r_ptr->name.data(), (int)(q_ref.max_num - q_ref.cur_num));
93 #else
94                         angband_strcpy(name, r_ptr->name.data(), sizeof(name));
95                         plural_aux(name);
96                         note = format(" - kill %d %s, have killed %d.", (int)q_ref.max_num, name, (int)q_ref.cur_num);
97 #endif
98                     } else {
99                         note = format(_(" - %sを倒す。", " - kill %s."), r_ptr->name.data());
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 bi_id = lookup_baseitem_id(a_ref.bi_key);
108                         item.prep(bi_id);
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                     note = format(_("\n   - %sを見つけ出す。", "\n   - Find %s."), name);
115                     break;
116                 case QuestKindType::FIND_EXIT:
117                     note = _(" - 出口に到達する。", " - Reach exit.");
118                     break;
119                 case QuestKindType::KILL_NUMBER:
120 #ifdef JP
121                     note = format(" - %d 体のモンスターを倒す。(あと %d 体)", (int)q_ref.max_num, (int)(q_ref.max_num - q_ref.cur_num));
122 #else
123                     note = format(" - 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                     note = _(" - 全てのモンスターを倒す。", " - Kill all monsters.");
130                     break;
131                 default:
132                     break;
133                 }
134             }
135
136             fprintf(fff, _("  %s (危険度:%d階相当)%s\n", "  %s (Danger level: %d)%s\n"), q_ref.name, (int)q_ref.level, note.data());
137             if (q_ref.status == QuestStatusType::COMPLETED) {
138                 fputs(_("    クエスト達成 - まだ報酬を受けとってない。\n", "    Quest Completed - Unrewarded\n"), fff);
139                 continue;
140             }
141
142             int k = 0;
143             while (quest_text[k][0] && k < 10) {
144                 fprintf(fff, "    %s\n", quest_text[k]);
145                 k++;
146             }
147
148             continue;
149         }
150
151         if (q_ref.level >= rand_level) {
152             continue;
153         }
154         rand_level = q_ref.level;
155         if (max_dlv[DUNGEON_ANGBAND] < rand_level) {
156             continue;
157         }
158
159         r_ptr = &monraces_info[q_ref.r_idx];
160         if (q_ref.max_num <= 1) {
161             rand_tmp_str = format(_("  %s (%d 階) - %sを倒す。\n", "  %s (Dungeon level: %d)\n  Kill %s.\n"), q_ref.name, (int)q_ref.level, r_ptr->name.data());
162             continue;
163         }
164
165 #ifdef JP
166         rand_tmp_str = format("  %s (%d 階) - %d 体の%sを倒す。(あと %d 体)\n", q_ref.name, (int)q_ref.level, (int)q_ref.max_num, r_ptr->name.data(),
167             (int)(q_ref.max_num - q_ref.cur_num));
168 #else
169         angband_strcpy(name, r_ptr->name.data(), sizeof(name));
170         plural_aux(name);
171
172         rand_tmp_str = format("  %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,
173             (int)q_ref.cur_num);
174 #endif
175     }
176
177     if (!rand_tmp_str.empty()) {
178         fputs(rand_tmp_str.data(), fff);
179     }
180
181     if (!total) {
182         fprintf(fff, _("  なし\n", "  Nothing.\n"));
183     }
184 }
185
186 static bool do_cmd_knowledge_quests_aux(PlayerType *player_ptr, FILE *fff, QuestId q_idx)
187 {
188     const auto &quest_list = QuestList::get_instance();
189     const auto &q_ref = quest_list[q_idx];
190
191     auto *floor_ptr = player_ptr->current_floor_ptr;
192     auto is_fixed_quest = quest_type::is_fixed(q_idx);
193     if (is_fixed_quest) {
194         QuestId old_quest = floor_ptr->quest_number;
195         floor_ptr->quest_number = q_idx;
196         init_flags = INIT_NAME_ONLY;
197         parse_fixed_map(player_ptr, QUEST_DEFINITION_LIST, 0, 0, 0, 0);
198         floor_ptr->quest_number = old_quest;
199         if (q_ref.flags & QUEST_FLAG_SILENT) {
200             return false;
201         }
202     }
203
204     std::string playtime_str = format("%02d:%02d:%02d", q_ref.comptime / (60 * 60), (q_ref.comptime / 60) % 60, q_ref.comptime % 60);
205
206     auto fputs_name_remain = [fff](const auto &name) {
207         for (auto i = 1U; i < name.size(); ++i) {
208             fprintf(fff, "  %s\n", name[i].data());
209         }
210     };
211
212     if (is_fixed_quest || !MonsterRace(q_ref.r_idx).is_valid()) {
213         auto name = str_separate(q_ref.name, 35);
214         fprintf(fff, _("  %-35s (危険度:%3d階相当) - レベル%2d - %s\n", "  %-35s (Danger  level: %3d) - level %2d - %s\n"), name.front().data(), (int)q_ref.level,
215             q_ref.complev, playtime_str.data());
216         fputs_name_remain(name);
217         return true;
218     }
219
220     auto name = str_separate(monraces_info[q_ref.r_idx].name, 35);
221     if (q_ref.complev == 0) {
222         fprintf(fff, _("  %-35s (%3d階)            -   不戦勝 - %s\n", "  %-35s (Dungeon level: %3d) - Unearned - %s\n"),
223             name.front().data(), (int)q_ref.level, playtime_str.data());
224         fputs_name_remain(name);
225         return true;
226     }
227
228     fprintf(fff, _("  %-35s (%3d階)            - レベル%2d - %s\n", "  %-35s (Dungeon level: %3d) - level %2d - %s\n"), name.front().data(),
229         (int)q_ref.level, q_ref.complev, playtime_str.data());
230     fputs_name_remain(name);
231     return true;
232 }
233
234 /*
235  * Print all finished quests
236  * @param player_ptr プレイヤーへの参照ポインタ
237  * @param fff セーブファイル (展開済?)
238  * @param quest_numbers 受注したことのあるクエスト群
239  */
240 void do_cmd_knowledge_quests_completed(PlayerType *player_ptr, FILE *fff, const std::vector<QuestId> &quest_numbers)
241 {
242     fprintf(fff, _("《達成したクエスト》\n", "< Completed Quest >\n"));
243     int16_t total = 0;
244     for (auto &q_idx : quest_numbers) {
245         const auto &quest_list = QuestList::get_instance();
246         const auto &q_ref = quest_list[q_idx];
247
248         if (q_ref.status == QuestStatusType::FINISHED && do_cmd_knowledge_quests_aux(player_ptr, fff, q_idx)) {
249             ++total;
250         }
251     }
252
253     if (total == 0) {
254         fprintf(fff, _("  なし\n", "  Nothing.\n"));
255     }
256 }
257
258 /*
259  * Print all failed quests
260  * @param player_ptr プレイヤーへの参照ポインタ
261  * @param fff セーブファイル (展開済?)
262  * @param quest_numbers 受注したことのあるクエスト群
263  */
264 void do_cmd_knowledge_quests_failed(PlayerType *player_ptr, FILE *fff, const std::vector<QuestId> &quest_numbers)
265 {
266     fprintf(fff, _("《失敗したクエスト》\n", "< Failed Quest >\n"));
267     int16_t total = 0;
268     for (auto &q_idx : quest_numbers) {
269         const auto &quest_list = QuestList::get_instance();
270         const auto &q_ref = quest_list[q_idx];
271
272         if (((q_ref.status == QuestStatusType::FAILED_DONE) || (q_ref.status == QuestStatusType::FAILED)) && do_cmd_knowledge_quests_aux(player_ptr, fff, q_idx)) {
273             ++total;
274         }
275     }
276
277     if (total == 0) {
278         fprintf(fff, _("  なし\n", "  Nothing.\n"));
279     }
280 }
281
282 /*
283  * Print all random quests
284  */
285 static void do_cmd_knowledge_quests_wiz_random(FILE *fff)
286 {
287     fprintf(fff, _("《残りのランダムクエスト》\n", "< Remaining Random Quest >\n"));
288     const auto &quest_list = QuestList::get_instance();
289     int16_t total = 0;
290     for (const auto &[q_idx, q_ref] : quest_list) {
291         if (q_ref.flags & QUEST_FLAG_SILENT) {
292             continue;
293         }
294
295         if ((q_ref.type == QuestKindType::RANDOM) && (q_ref.status == QuestStatusType::TAKEN)) {
296             total++;
297             fprintf(fff, _("  %s (%d階, %s)\n", "  %s (%d, %s)\n"), q_ref.name, (int)q_ref.level, monraces_info[q_ref.r_idx].name.data());
298         }
299     }
300
301     if (total == 0) {
302         fprintf(fff, _("  なし\n", "  Nothing.\n"));
303     }
304 }
305
306 /*
307  * Print quest status of all active quests
308  * @param player_ptr プレイヤーへの参照ポインタ
309  */
310 void do_cmd_knowledge_quests(PlayerType *player_ptr)
311 {
312     FILE *fff = nullptr;
313     GAME_TEXT file_name[FILE_NAME_SIZE];
314     if (!open_temporary_file(&fff, file_name)) {
315         return;
316     }
317
318     std::vector<QuestId> quest_numbers;
319     const auto &quest_list = QuestList::get_instance();
320     for (const auto &[q_idx, q_ref] : quest_list) {
321         quest_numbers.push_back(q_idx);
322     }
323     int dummy;
324     ang_sort(player_ptr, quest_numbers.data(), &dummy, quest_numbers.size(), ang_sort_comp_quest_num, ang_sort_swap_quest_num);
325
326     do_cmd_knowledge_quests_current(player_ptr, fff);
327     fputc('\n', fff);
328     do_cmd_knowledge_quests_completed(player_ptr, fff, quest_numbers);
329     fputc('\n', fff);
330     do_cmd_knowledge_quests_failed(player_ptr, fff, quest_numbers);
331     if (w_ptr->wizard) {
332         fputc('\n', fff);
333         do_cmd_knowledge_quests_wiz_random(fff);
334     }
335
336     angband_fclose(fff);
337     (void)show_file(player_ptr, true, file_name, _("クエスト達成状況", "Quest status"), 0, 0);
338     fd_kill(file_name);
339 }