OSDN Git Service

Merge branch 'master' of https://github.com/hengband/hengband
[hengbandforosx/hengbandosx.git] / src / dungeon / quest.cpp
1 #include "dungeon/quest.h"
2 #include "artifact/fixed-art-types.h"
3 #include "cmd-io/cmd-dump.h"
4 #include "core/asking-player.h"
5 #include "floor/cave.h"
6 #include "floor/floor-events.h"
7 #include "floor/floor-mode-changer.h"
8 #include "floor/floor-object.h"
9 #include "game-option/play-record-options.h"
10 #include "info-reader/fixed-map-parser.h"
11 #include "io/write-diary.h"
12 #include "locale/english.h"
13 #include "main/music-definitions-table.h"
14 #include "main/sound-of-music.h"
15 #include "monster-floor/place-monster-types.h"
16 #include "monster-race/monster-race-hook.h"
17 #include "monster/monster-info.h"
18 #include "monster/monster-list.h"
19 #include "monster/monster-util.h"
20 #include "monster/smart-learn-types.h"
21 #include "object-enchant/item-apply-magic.h"
22 #include "object-enchant/trg-types.h"
23 #include "player-status/player-energy.h"
24 #include "player/player-personality-types.h"
25 #include "player/player-status.h"
26 #include "system/artifact-type-definition.h"
27 #include "system/dungeon-info.h"
28 #include "system/floor-type-definition.h" // @todo 相互参照、将来的に削除する.
29 #include "system/grid-type-definition.h"
30 #include "system/item-entity.h"
31 #include "system/monster-race-info.h"
32 #include "system/player-type-definition.h"
33 #include "system/terrain-type-definition.h"
34 #include "util/bit-flags-calculator.h"
35 #include "view/display-messages.h"
36 #include "world/world.h"
37 #include <sstream>
38 #include <stdexcept>
39
40 std::vector<std::string> quest_text_lines; /*!< Quest text */
41 QuestId leaving_quest = QuestId::NONE;
42
43 /*!
44  * @brief クエスト突入時のメッセージテーブル
45  */
46 namespace {
47 const std::vector<std::string> quest_entered_messages = {
48     _("床にメッセージが刻まれている:", "You find the following inscription in the floor"),
49     _("壁にメッセージが刻まれている:", "You see a message inscribed in the wall"),
50     _("メッセージを見つけた:", "There is a sign saying"),
51     _("何かが階段の上に書いてある:", "Something is written on the staircase"),
52     _("巻物を見つけた。メッセージが書いてある:", "You find a scroll with the following message"),
53 };
54 }
55
56 /*!
57  * @brief 該当IDが固定クエストかどうかを判定する.
58  * @param quest_id クエストID
59  * @return 固定クエストならばTRUEを返す
60  */
61 bool QuestType::is_fixed(QuestId quest_id)
62 {
63     return (enum2i(quest_id) < MIN_RANDOM_QUEST) || (enum2i(quest_id) > MAX_RANDOM_QUEST);
64 }
65
66 bool QuestType::has_reward() const
67 {
68     return this->reward_fa_id != FixedArtifactId::NONE;
69 }
70
71 ArtifactType &QuestType::get_reward() const
72 {
73     auto &artifacts = ArtifactList::get_instance();
74     return artifacts.get_artifact(this->reward_fa_id);
75 }
76
77 /*!
78  * @brief 討伐対象モンスターを返す. いなければプレイヤー (無効値の意)
79  * @return 討伐対象モンスター
80  */
81 MonsterRaceInfo &QuestType::get_bounty()
82 {
83     return MonraceList::get_instance().get_monrace(this->r_idx);
84 }
85
86 /*!
87  * @brief 討伐対象モンスターを返す. いなければプレイヤー (無効値の意)
88  * @return 討伐対象モンスター
89  */
90 const MonsterRaceInfo &QuestType::get_bounty() const
91 {
92     return MonraceList::get_instance().get_monrace(this->r_idx);
93 }
94
95 QuestList QuestList::instance{};
96
97 QuestList &QuestList::get_instance()
98 {
99     return instance;
100 }
101
102 /*!
103  * @brief クエストの初期化
104  * @details ソフトウェア起動時ではパース関数が動作しないので、各種初期化シーケンス時に遅延初期化する
105  */
106 void QuestList::initialize()
107 {
108     try {
109         const auto quest_numbers = parse_quest_info(QUEST_DEFINITION_LIST);
110         QuestType quest{};
111         quest.status = QuestStatusType::UNTAKEN;
112         this->quests.emplace(QuestId::NONE, quest);
113         for (const auto q : quest_numbers) {
114             this->quests.emplace(q, quest);
115         }
116     } catch (const std::runtime_error &r) {
117         std::stringstream ss;
118         ss << _("ファイル読み込みエラー: ", "File loading error: ") << r.what();
119         msg_print(ss.str());
120         msg_print(nullptr);
121         quit(_("クエスト初期化エラー", "Error of quests initializing"));
122     }
123 }
124
125 QuestType &QuestList::get_quest(QuestId id)
126 {
127     return this->quests.at(id);
128 }
129
130 const QuestType &QuestList::get_quest(QuestId id) const
131 {
132     return this->quests.at(id);
133 }
134
135 std::map<QuestId, QuestType>::iterator QuestList::begin()
136 {
137     return this->quests.begin();
138 }
139
140 std::map<QuestId, QuestType>::const_iterator QuestList::begin() const
141 {
142     return this->quests.cbegin();
143 }
144
145 std::map<QuestId, QuestType>::iterator QuestList::end()
146 {
147     return this->quests.end();
148 }
149
150 std::map<QuestId, QuestType>::const_iterator QuestList::end() const
151 {
152     return this->quests.cend();
153 }
154
155 std::map<QuestId, QuestType>::reverse_iterator QuestList::rbegin()
156 {
157     return this->quests.rbegin();
158 }
159
160 std::map<QuestId, QuestType>::const_reverse_iterator QuestList::rbegin() const
161 {
162     return this->quests.crbegin();
163 }
164
165 std::map<QuestId, QuestType>::reverse_iterator QuestList::rend()
166 {
167     return this->quests.rend();
168 }
169
170 std::map<QuestId, QuestType>::const_reverse_iterator QuestList::rend() const
171 {
172     return this->quests.crend();
173 }
174
175 std::map<QuestId, QuestType>::iterator QuestList::find(QuestId id)
176 {
177     return this->quests.find(id);
178 }
179
180 std::map<QuestId, QuestType>::const_iterator QuestList::find(QuestId id) const
181 {
182     return this->quests.find(id);
183 }
184
185 size_t QuestList::size() const
186 {
187     return this->quests.size();
188 }
189
190 std::vector<QuestId> QuestList::get_sorted_quest_ids() const
191 {
192     std::vector<QuestId> quest_ids;
193     std::transform(++this->quests.begin(), this->quests.end(), std::back_inserter(quest_ids), [](const auto &x) { return x.first; });
194     std::stable_sort(quest_ids.begin(), quest_ids.end(), [this](auto x, auto y) { return this->order_completed(x, y); });
195     return quest_ids;
196 }
197
198 bool QuestList::order_completed(QuestId id1, QuestId id2) const
199 {
200     const auto &quest1 = this->get_quest(id1);
201     const auto &quest2 = this->get_quest(id2);
202     return (quest1.comptime != quest2.comptime) ? (quest1.comptime < quest2.comptime) : (quest1.level < quest2.level);
203 }
204
205 /*!
206  * @brief ランダムクエストの討伐ユニークを決める / Determine the random quest uniques
207  * @param quest クエスト構造体への参照
208  */
209 void determine_random_questor(PlayerType *player_ptr, QuestType &quest)
210 {
211     get_mon_num_prep(player_ptr, mon_hook_quest, nullptr);
212     MonsterRaceId r_idx;
213     while (true) {
214         r_idx = get_mon_num(player_ptr, 0, quest.level + 5 + randint1(quest.level / 10), PM_ARENA);
215         const auto &monrace = monraces_info[r_idx];
216         if (monrace.kind_flags.has_not(MonsterKindType::UNIQUE)) {
217             continue;
218         }
219
220         if (monrace.misc_flags.has(MonsterMiscType::NO_QUEST)) {
221             continue;
222         }
223
224         if (monrace.misc_flags.has(MonsterMiscType::QUESTOR)) {
225             continue;
226         }
227
228         if (monrace.rarity > 100) {
229             continue;
230         }
231
232         if (monrace.behavior_flags.has(MonsterBehaviorType::FRIENDLY)) {
233             continue;
234         }
235
236         if (monrace.feature_flags.has(MonsterFeatureType::AQUATIC)) {
237             continue;
238         }
239
240         if (monrace.wilderness_flags.has(MonsterWildernessType::WILD_ONLY)) {
241             continue;
242         }
243
244         if (MonraceList::get_instance().can_unify_separate(r_idx)) {
245             continue;
246         }
247
248         /*
249          * Accept monsters that are 2 - 6 levels
250          * out of depth depending on the quest level
251          */
252         if (monrace.level > (quest.level + (quest.level / 20))) {
253             break;
254         }
255     }
256
257     quest.r_idx = r_idx;
258 }
259
260 /*!
261  * @brief クエストの最終状態を記録する(成功or失敗、時間)
262  * @param PlayerType プレイヤー情報への参照ポインタ
263  * @param q_ptr クエスト情報への参照ポインタ
264  * @param stat ステータス(成功or失敗)
265  */
266 void record_quest_final_status(QuestType *q_ptr, PLAYER_LEVEL lev, QuestStatusType stat)
267 {
268     q_ptr->status = stat;
269     q_ptr->complev = lev;
270     auto &world = AngbandWorld::get_instance();
271     world.update_playtime();
272     q_ptr->comptime = world.play_time;
273 }
274
275 /*!
276  * @brief クエストを達成状態にする /
277  * @param player_ptr プレイヤーへの参照ポインタ
278  * @param quest_id 達成状態にしたいクエストのID
279  */
280 void complete_quest(PlayerType *player_ptr, QuestId quest_id)
281 {
282     auto &quests = QuestList::get_instance();
283     auto &quest = quests.get_quest(quest_id);
284     switch (quest.type) {
285     case QuestKindType::RANDOM:
286         if (record_rand_quest) {
287             exe_write_diary_quest(player_ptr, DiaryKind::RAND_QUEST_C, quest_id);
288         }
289         break;
290     default:
291         if (record_fix_quest) {
292             exe_write_diary_quest(player_ptr, DiaryKind::FIX_QUEST_C, quest_id);
293         }
294         break;
295     }
296
297     record_quest_final_status(&quest, player_ptr->lev, QuestStatusType::COMPLETED);
298     if (quest.flags & QUEST_FLAG_SILENT) {
299         return;
300     }
301
302     play_music(TERM_XTRA_MUSIC_BASIC, MUSIC_BASIC_QUEST_CLEAR);
303     msg_print(_("クエストを達成した!", "You just completed your quest!"));
304     msg_print(nullptr);
305 }
306
307 /*!
308  * @brief 特定のアーティファクトを入手した際のクエスト達成処理 /
309  * Check for "Quest" completion when a quest monster is killed or charmed.
310  * @param player_ptr プレイヤーへの参照ポインタ
311  * @param o_ptr 入手したオブジェクトの構造体参照ポインタ
312  */
313 void check_find_art_quest_completion(PlayerType *player_ptr, ItemEntity *o_ptr)
314 {
315     const auto &quests = QuestList::get_instance();
316     /* Check if completed a quest */
317     for (const auto &[quest_id, quest] : quests) {
318         auto found_artifact = (quest.type == QuestKindType::FIND_ARTIFACT);
319         found_artifact &= (quest.status == QuestStatusType::TAKEN);
320         found_artifact &= (o_ptr->is_specific_artifact(quest.reward_fa_id));
321         if (found_artifact) {
322             complete_quest(player_ptr, quest_id);
323         }
324     }
325 }
326
327 /*!
328  * @brief クエストの導入メッセージを表示する
329  * @param quest_id 開始されたクエストのID
330  */
331 void quest_discovery(QuestId quest_id)
332 {
333     auto &quests = QuestList::get_instance();
334     auto &quest = quests.get_quest(quest_id);
335     const auto &monrace = quest.get_bounty();
336     if (!inside_quest(quest_id)) {
337         return;
338     }
339
340     const auto num_subjugation = quest.max_num;
341 #ifdef JP
342     const auto &name = monrace.name;
343 #else
344     const auto &name = (num_subjugation != 1) ? pluralize(monrace.name) : monrace.name.string();
345 #endif
346
347     msg_print(rand_choice(quest_entered_messages));
348     msg_print(nullptr);
349     if (num_subjugation != 1) {
350         msg_format(_("注意しろ!この階は%d体の%sによって守られている!", "Be warned, this level is guarded by %d %s!"), num_subjugation, name.data());
351         return;
352     }
353
354     auto is_random_quest_skipped = monrace.kind_flags.has(MonsterKindType::UNIQUE);
355     is_random_quest_skipped &= monrace.max_num == 0;
356     if (!is_random_quest_skipped) {
357         msg_format(_("注意せよ!この階は%sによって守られている!", "Beware, this level is protected by %s!"), name.data());
358         return;
359     }
360
361     msg_print(_("この階は以前は誰かによって守られていたようだ…。", "It seems that this level was protected by someone before..."));
362     record_quest_final_status(&quest, 0, QuestStatusType::FINISHED);
363 }
364
365 /*!
366  * @brief クエスト階層から離脱する際の処理
367  * @param player_ptr プレイヤーへの参照ポインタ
368  */
369 void leave_quest_check(PlayerType *player_ptr)
370 {
371     leaving_quest = player_ptr->current_floor_ptr->quest_number;
372     if (!inside_quest(leaving_quest)) {
373         return;
374     }
375
376     auto &quests = QuestList::get_instance();
377     auto &quest = quests.get_quest(leaving_quest);
378     auto is_one_time_quest = ((quest.flags & QUEST_FLAG_ONCE) || (quest.type == QuestKindType::RANDOM)) && (quest.status == QuestStatusType::TAKEN);
379     if (!is_one_time_quest) {
380         return;
381     }
382
383     record_quest_final_status(&quest, player_ptr->lev, QuestStatusType::FAILED);
384
385     /* Additional settings */
386     switch (quest.type) {
387     case QuestKindType::TOWER:
388         quests.get_quest(QuestId::TOWER1).status = QuestStatusType::FAILED;
389         quests.get_quest(QuestId::TOWER1).complev = player_ptr->lev;
390         break;
391     case QuestKindType::FIND_ARTIFACT:
392         quest.get_reward().gen_flags.reset(ItemGenerationTraitType::QUESTITEM);
393         break;
394     case QuestKindType::RANDOM:
395         quest.get_bounty().misc_flags.reset(MonsterMiscType::QUESTOR);
396         FloorChangeModesStore::get_instace()->set(FloorChangeMode::NO_RETURN);
397         break;
398     default:
399         break;
400     }
401
402     /* Record finishing a quest */
403     if (quest.type == QuestKindType::RANDOM) {
404         if (record_rand_quest) {
405             exe_write_diary_quest(player_ptr, DiaryKind::RAND_QUEST_F, leaving_quest);
406         }
407         return;
408     }
409
410     if (record_fix_quest) {
411         exe_write_diary_quest(player_ptr, DiaryKind::FIX_QUEST_F, leaving_quest);
412     }
413 }
414
415 /*!
416  * @brief 「塔」クエストの各階層から離脱する際の処理
417  */
418 void leave_tower_check(PlayerType *player_ptr)
419 {
420     auto &quests = QuestList::get_instance();
421     leaving_quest = player_ptr->current_floor_ptr->quest_number;
422
423     auto &tower1 = quests.get_quest(QuestId::TOWER1);
424     auto is_leaving_from_tower = inside_quest(leaving_quest);
425     is_leaving_from_tower &= quests.get_quest(leaving_quest).type == QuestKindType::TOWER;
426     is_leaving_from_tower &= tower1.status != QuestStatusType::COMPLETED;
427     if (!is_leaving_from_tower) {
428         return;
429     }
430     if (quests.get_quest(leaving_quest).type != QuestKindType::TOWER) {
431         return;
432     }
433     tower1.status = QuestStatusType::FAILED;
434     tower1.complev = player_ptr->lev;
435     auto &world = AngbandWorld::get_instance();
436     world.update_playtime();
437     tower1.comptime = world.play_time;
438 }
439
440 /*!
441  * @brief Player enters a new quest
442  */
443 void exe_enter_quest(PlayerType *player_ptr, QuestId quest_id)
444 {
445     const auto &quests = QuestList::get_instance();
446     if (quests.get_quest(quest_id).type != QuestKindType::RANDOM) {
447         player_ptr->current_floor_ptr->dun_level = 1;
448     }
449     player_ptr->current_floor_ptr->quest_number = quest_id;
450     player_ptr->leaving = true;
451 }
452
453 /*!
454  * @brief クエスト入り口にプレイヤーが乗った際の処理 / Do building commands
455  * @param player_ptr プレイヤーへの参照ポインタ
456  */
457 void do_cmd_quest(PlayerType *player_ptr)
458 {
459     if (AngbandWorld::get_instance().is_wild_mode()) {
460         return;
461     }
462
463     PlayerEnergy(player_ptr).set_player_turn_energy(100);
464     const auto &floor = *player_ptr->current_floor_ptr;
465     if (!cave_has_flag_bold(&floor, player_ptr->y, player_ptr->x, TerrainCharacteristics::QUEST_ENTER)) {
466         msg_print(_("ここにはクエストの入口はない。", "You see no quest level here."));
467         return;
468     }
469
470     msg_print(_("ここにはクエストへの入口があります。", "There is an entry of a quest."));
471     if (!input_check(_("クエストに入りますか?", "Do you enter? "))) {
472         return;
473     }
474     if (is_echizen(player_ptr)) {
475         msg_print(_("『とにかく入ってみようぜぇ。』", "\"Let's go in anyway.\""));
476     } else if (is_chargeman(player_ptr)) {
477         msg_print(_("『全滅してやるぞ!』", "\"I'll annihilate THEM!\""));
478     }
479
480     player_ptr->oldpy = 0;
481     player_ptr->oldpx = 0;
482     leave_quest_check(player_ptr);
483
484     exe_enter_quest(player_ptr, i2enum<QuestId>(floor.get_grid(player_ptr->get_position()).special));
485 }
486
487 bool inside_quest(QuestId id)
488 {
489     return id != QuestId::NONE;
490 }