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"
40 std::vector<std::string> quest_text_lines; /*!< Quest text */
41 QuestId leaving_quest = QuestId::NONE;
44 * @brief クエスト突入時のメッセージテーブル
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"),
57 * @brief 該当IDが固定クエストかどうかを判定する.
58 * @param quest_id クエストID
59 * @return 固定クエストならばTRUEを返す
61 bool QuestType::is_fixed(QuestId quest_id)
63 return (enum2i(quest_id) < MIN_RANDOM_QUEST) || (enum2i(quest_id) > MAX_RANDOM_QUEST);
66 bool QuestType::has_reward() const
68 return this->reward_fa_id != FixedArtifactId::NONE;
71 ArtifactType &QuestType::get_reward() const
73 auto &artifacts = ArtifactList::get_instance();
74 return artifacts.get_artifact(this->reward_fa_id);
78 * @brief 討伐対象モンスターを返す. いなければプレイヤー (無効値の意)
81 MonsterRaceInfo &QuestType::get_bounty()
83 return MonraceList::get_instance().get_monrace(this->r_idx);
87 * @brief 討伐対象モンスターを返す. いなければプレイヤー (無効値の意)
90 const MonsterRaceInfo &QuestType::get_bounty() const
92 return MonraceList::get_instance().get_monrace(this->r_idx);
95 QuestList QuestList::instance{};
97 QuestList &QuestList::get_instance()
104 * @details ソフトウェア起動時ではパース関数が動作しないので、各種初期化シーケンス時に遅延初期化する
106 void QuestList::initialize()
109 const auto quest_numbers = parse_quest_info(QUEST_DEFINITION_LIST);
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);
116 } catch (const std::runtime_error &r) {
117 std::stringstream ss;
118 ss << _("ファイル読み込みエラー: ", "File loading error: ") << r.what();
121 quit(_("クエスト初期化エラー", "Error of quests initializing"));
125 QuestType &QuestList::get_quest(QuestId id)
127 return this->quests.at(id);
130 const QuestType &QuestList::get_quest(QuestId id) const
132 return this->quests.at(id);
135 std::map<QuestId, QuestType>::iterator QuestList::begin()
137 return this->quests.begin();
140 std::map<QuestId, QuestType>::const_iterator QuestList::begin() const
142 return this->quests.cbegin();
145 std::map<QuestId, QuestType>::iterator QuestList::end()
147 return this->quests.end();
150 std::map<QuestId, QuestType>::const_iterator QuestList::end() const
152 return this->quests.cend();
155 std::map<QuestId, QuestType>::reverse_iterator QuestList::rbegin()
157 return this->quests.rbegin();
160 std::map<QuestId, QuestType>::const_reverse_iterator QuestList::rbegin() const
162 return this->quests.crbegin();
165 std::map<QuestId, QuestType>::reverse_iterator QuestList::rend()
167 return this->quests.rend();
170 std::map<QuestId, QuestType>::const_reverse_iterator QuestList::rend() const
172 return this->quests.crend();
175 std::map<QuestId, QuestType>::iterator QuestList::find(QuestId id)
177 return this->quests.find(id);
180 std::map<QuestId, QuestType>::const_iterator QuestList::find(QuestId id) const
182 return this->quests.find(id);
185 size_t QuestList::size() const
187 return this->quests.size();
190 std::vector<QuestId> QuestList::get_sorted_quest_ids() const
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); });
198 bool QuestList::order_completed(QuestId id1, QuestId id2) const
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);
206 * @brief ランダムクエストの討伐ユニークを決める / Determine the random quest uniques
207 * @param quest クエスト構造体への参照
209 void determine_random_questor(PlayerType *player_ptr, QuestType &quest)
211 get_mon_num_prep(player_ptr, mon_hook_quest, nullptr);
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)) {
220 if (monrace.misc_flags.has(MonsterMiscType::NO_QUEST)) {
224 if (monrace.misc_flags.has(MonsterMiscType::QUESTOR)) {
228 if (monrace.rarity > 100) {
232 if (monrace.behavior_flags.has(MonsterBehaviorType::FRIENDLY)) {
236 if (monrace.feature_flags.has(MonsterFeatureType::AQUATIC)) {
240 if (monrace.wilderness_flags.has(MonsterWildernessType::WILD_ONLY)) {
244 if (MonraceList::get_instance().can_unify_separate(r_idx)) {
249 * Accept monsters that are 2 - 6 levels
250 * out of depth depending on the quest level
252 if (monrace.level > (quest.level + (quest.level / 20))) {
261 * @brief クエストの最終状態を記録する(成功or失敗、時間)
262 * @param PlayerType プレイヤー情報への参照ポインタ
263 * @param q_ptr クエスト情報への参照ポインタ
264 * @param stat ステータス(成功or失敗)
266 void record_quest_final_status(QuestType *q_ptr, PLAYER_LEVEL lev, QuestStatusType stat)
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;
276 * @brief クエストを達成状態にする /
277 * @param player_ptr プレイヤーへの参照ポインタ
278 * @param quest_id 達成状態にしたいクエストのID
280 void complete_quest(PlayerType *player_ptr, QuestId quest_id)
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);
291 if (record_fix_quest) {
292 exe_write_diary_quest(player_ptr, DiaryKind::FIX_QUEST_C, quest_id);
297 record_quest_final_status(&quest, player_ptr->lev, QuestStatusType::COMPLETED);
298 if (quest.flags & QUEST_FLAG_SILENT) {
302 play_music(TERM_XTRA_MUSIC_BASIC, MUSIC_BASIC_QUEST_CLEAR);
303 msg_print(_("クエストを達成した!", "You just completed your quest!"));
308 * @brief 特定のアーティファクトを入手した際のクエスト達成処理 /
309 * Check for "Quest" completion when a quest monster is killed or charmed.
310 * @param player_ptr プレイヤーへの参照ポインタ
311 * @param o_ptr 入手したオブジェクトの構造体参照ポインタ
313 void check_find_art_quest_completion(PlayerType *player_ptr, ItemEntity *o_ptr)
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);
328 * @brief クエストの導入メッセージを表示する
329 * @param quest_id 開始されたクエストのID
331 void quest_discovery(QuestId quest_id)
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)) {
340 const auto num_subjugation = quest.max_num;
342 const auto &name = monrace.name;
344 const auto &name = (num_subjugation != 1) ? pluralize(monrace.name) : monrace.name.string();
347 msg_print(rand_choice(quest_entered_messages));
349 if (num_subjugation != 1) {
350 msg_format(_("注意しろ!この階は%d体の%sによって守られている!", "Be warned, this level is guarded by %d %s!"), num_subjugation, name.data());
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());
361 msg_print(_("この階は以前は誰かによって守られていたようだ…。", "It seems that this level was protected by someone before..."));
362 record_quest_final_status(&quest, 0, QuestStatusType::FINISHED);
366 * @brief クエスト階層から離脱する際の処理
367 * @param player_ptr プレイヤーへの参照ポインタ
369 void leave_quest_check(PlayerType *player_ptr)
371 leaving_quest = player_ptr->current_floor_ptr->quest_number;
372 if (!inside_quest(leaving_quest)) {
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) {
383 record_quest_final_status(&quest, player_ptr->lev, QuestStatusType::FAILED);
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;
391 case QuestKindType::FIND_ARTIFACT:
392 quest.get_reward().gen_flags.reset(ItemGenerationTraitType::QUESTITEM);
394 case QuestKindType::RANDOM:
395 quest.get_bounty().misc_flags.reset(MonsterMiscType::QUESTOR);
396 FloorChangeModesStore::get_instace()->set(FloorChangeMode::NO_RETURN);
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);
410 if (record_fix_quest) {
411 exe_write_diary_quest(player_ptr, DiaryKind::FIX_QUEST_F, leaving_quest);
416 * @brief 「塔」クエストの各階層から離脱する際の処理
418 void leave_tower_check(PlayerType *player_ptr)
420 auto &quests = QuestList::get_instance();
421 leaving_quest = player_ptr->current_floor_ptr->quest_number;
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) {
430 if (quests.get_quest(leaving_quest).type != QuestKindType::TOWER) {
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;
441 * @brief Player enters a new quest
443 void exe_enter_quest(PlayerType *player_ptr, QuestId quest_id)
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;
449 player_ptr->current_floor_ptr->quest_number = quest_id;
450 player_ptr->leaving = true;
454 * @brief クエスト入り口にプレイヤーが乗った際の処理 / Do building commands
455 * @param player_ptr プレイヤーへの参照ポインタ
457 void do_cmd_quest(PlayerType *player_ptr)
459 if (AngbandWorld::get_instance().is_wild_mode()) {
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."));
470 msg_print(_("ここにはクエストへの入口があります。", "There is an entry of a quest."));
471 if (!input_check(_("クエストに入りますか?", "Do you enter? "))) {
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!\""));
480 player_ptr->oldpy = 0;
481 player_ptr->oldpx = 0;
482 leave_quest_check(player_ptr);
484 exe_enter_quest(player_ptr, i2enum<QuestId>(floor.get_grid(player_ptr->get_position()).special));
487 bool inside_quest(QuestId id)
489 return id != QuestId::NONE;