1 #include "dungeon/quest-completion-checker.h"
2 #include "core/player-update-types.h"
3 #include "dungeon/quest.h"
4 #include "effect/effect-characteristics.h"
5 #include "floor/cave.h"
6 #include "floor/floor-object.h"
7 #include "floor/floor-util.h"
8 #include "grid/feature-flag-types.h"
9 #include "grid/feature.h"
10 #include "grid/grid.h"
11 #include "monster-race/monster-race.h"
12 #include "monster/monster-info.h"
13 #include "object-enchant/item-apply-magic.h"
14 #include "object-enchant/object-ego.h"
15 #include "system/floor-type-definition.h"
16 #include "system/grid-type-definition.h"
17 #include "system/item-entity.h"
18 #include "system/monster-race-definition.h"
19 #include "system/monster-type-definition.h"
20 #include "system/player-type-definition.h"
21 #include "util/bit-flags-calculator.h"
22 #include "view/display-messages.h"
25 QuestCompletionChecker::QuestCompletionChecker(PlayerType *player_ptr, MonsterEntity *m_ptr)
26 : player_ptr(player_ptr)
32 * @brief 特定の敵を倒した際にクエスト達成処理 /
33 * Check for "Quest" completion when a quest monster is killed or charmed.
34 * @param player_ptr プレイヤーへの参照ポインタ
35 * @param m_ptr 撃破したモンスターの構造体参照ポインタ
37 void QuestCompletionChecker::complete()
39 this->set_quest_idx();
40 auto create_stairs = false;
42 auto &quest_list = QuestList::get_instance();
43 if (inside_quest(this->quest_idx) && (quest_list[this->quest_idx].status == QuestStatusType::TAKEN)) {
44 this->q_ptr = &quest_list[this->quest_idx];
45 auto [tmp_create_stairs, tmp_reward] = this->switch_completion();
46 create_stairs = tmp_create_stairs;
50 auto pos = this->make_stairs(create_stairs);
55 this->make_reward(pos);
58 static bool check_quest_completion(PlayerType *player_ptr, const quest_type &q_ref, MonsterEntity *m_ptr)
60 auto *floor_ptr = player_ptr->current_floor_ptr;
61 if (q_ref.status != QuestStatusType::TAKEN) {
65 if (any_bits(q_ref.flags, QUEST_FLAG_PRESET)) {
69 if ((q_ref.level != floor_ptr->dun_level)) {
73 if ((q_ref.type == QuestKindType::FIND_ARTIFACT) || (q_ref.type == QuestKindType::FIND_EXIT)) {
77 auto kill_them_all = q_ref.type == QuestKindType::KILL_NUMBER;
78 kill_them_all |= q_ref.type == QuestKindType::TOWER;
79 kill_them_all |= q_ref.type == QuestKindType::KILL_ALL;
84 auto is_target = (q_ref.type == QuestKindType::RANDOM) && (q_ref.r_idx == m_ptr->r_idx);
85 if ((q_ref.type == QuestKindType::KILL_LEVEL) || is_target) {
92 void QuestCompletionChecker::set_quest_idx()
94 auto *floor_ptr = this->player_ptr->current_floor_ptr;
95 const auto &quest_list = QuestList::get_instance();
96 this->quest_idx = floor_ptr->quest_number;
97 if (inside_quest(this->quest_idx)) {
100 auto q = std::find_if(quest_list.rbegin(), quest_list.rend(), [this](auto q) { return check_quest_completion(this->player_ptr, q.second, this->m_ptr); });
102 if (q != quest_list.rend()) {
103 this->quest_idx = q->first;
105 this->quest_idx = QuestId::NONE;
109 std::tuple<bool, bool> QuestCompletionChecker::switch_completion()
111 switch (this->q_ptr->type) {
112 case QuestKindType::KILL_NUMBER:
113 this->complete_kill_number();
114 return std::make_tuple(false, false);
115 case QuestKindType::KILL_ALL:
116 this->complete_kill_all();
117 return std::make_tuple(false, false);
118 case QuestKindType::KILL_LEVEL:
119 case QuestKindType::RANDOM:
120 return this->complete_random();
121 case QuestKindType::TOWER:
122 this->complete_tower();
123 return std::make_tuple(false, false);
125 return std::make_tuple(false, false);
129 void QuestCompletionChecker::complete_kill_number()
131 this->q_ptr->cur_num++;
132 if (this->q_ptr->cur_num >= this->q_ptr->num_mon) {
133 complete_quest(this->player_ptr, this->quest_idx);
134 this->q_ptr->cur_num = 0;
138 void QuestCompletionChecker::complete_kill_all()
140 if (!this->m_ptr->is_hostile() || (this->count_all_hostile_monsters() != 1)) {
144 if (any_bits(this->q_ptr->flags, QUEST_FLAG_SILENT)) {
145 this->q_ptr->status = QuestStatusType::FINISHED;
147 complete_quest(this->player_ptr, this->quest_idx);
151 std::tuple<bool, bool> QuestCompletionChecker::complete_random()
153 if (this->q_ptr->r_idx != this->m_ptr->r_idx) {
154 return std::make_tuple(false, false);
157 this->q_ptr->cur_num++;
158 if (this->q_ptr->cur_num < this->q_ptr->max_num) {
159 return std::make_tuple(false, false);
162 complete_quest(this->player_ptr, this->quest_idx);
163 auto create_stairs = false;
164 if (none_bits(this->q_ptr->flags, QUEST_FLAG_PRESET)) {
165 create_stairs = true;
166 this->player_ptr->current_floor_ptr->quest_number = QuestId::NONE;
169 if ((this->quest_idx == QuestId::OBERON) || (this->quest_idx == QuestId::SERPENT)) {
170 this->q_ptr->status = QuestStatusType::FINISHED;
174 if (this->q_ptr->type == QuestKindType::RANDOM) {
176 this->q_ptr->status = QuestStatusType::FINISHED;
179 return std::make_tuple(create_stairs, reward);
182 void QuestCompletionChecker::complete_tower()
184 const auto &quest_list = QuestList::get_instance();
185 if (!this->m_ptr->is_hostile()) {
189 if (this->count_all_hostile_monsters() != 1) {
193 this->q_ptr->status = QuestStatusType::STAGE_COMPLETED;
194 auto is_tower_completed = quest_list[QuestId::TOWER1].status == QuestStatusType::STAGE_COMPLETED;
195 is_tower_completed &= quest_list[QuestId::TOWER2].status == QuestStatusType::STAGE_COMPLETED;
196 is_tower_completed &= quest_list[QuestId::TOWER3].status == QuestStatusType::STAGE_COMPLETED;
197 if (is_tower_completed) {
198 complete_quest(this->player_ptr, QuestId::TOWER1);
203 * @brief 現在フロアに残っている敵モンスターの数を返す /
204 * @return 現在の敵モンスターの数
206 int QuestCompletionChecker::count_all_hostile_monsters()
208 auto *floor_ptr = this->player_ptr->current_floor_ptr;
210 for (auto x = 0; x < floor_ptr->width; ++x) {
211 for (auto y = 0; y < floor_ptr->height; ++y) {
212 auto m_idx = floor_ptr->grid_array[y][x].m_idx;
213 if ((m_idx > 0) && floor_ptr->m_list[m_idx].is_hostile()) {
222 Pos2D QuestCompletionChecker::make_stairs(const bool create_stairs)
224 auto y = this->m_ptr->fy;
225 auto x = this->m_ptr->fx;
226 if (!create_stairs) {
230 auto *floor_ptr = this->player_ptr->current_floor_ptr;
231 auto *g_ptr = &floor_ptr->grid_array[y][x];
232 while (cave_has_flag_bold(floor_ptr, y, x, TerrainCharacteristics::PERMANENT) || !g_ptr->o_idx_list.empty() || g_ptr->is_object()) {
235 scatter(this->player_ptr, &ny, &nx, y, x, 1, PROJECT_NONE);
238 g_ptr = &floor_ptr->grid_array[y][x];
241 msg_print(_("魔法の階段が現れた...", "A magical staircase appears..."));
242 cave_set_feat(this->player_ptr, y, x, feat_down_stair);
243 set_bits(this->player_ptr->update, PU_FLOW);
247 void QuestCompletionChecker::make_reward(const Pos2D pos)
249 auto dun_level = this->player_ptr->current_floor_ptr->dun_level;
250 for (auto i = 0; i < (dun_level / 15) + 1; i++) {
254 auto &r_ref = monraces_info[this->m_ptr->r_idx];
255 (void)make_object(this->player_ptr, &item, AM_GOOD | AM_GREAT, r_ref.level);
256 if (!this->check_quality(item)) {
260 (void)drop_near(this->player_ptr, &item, -1, pos.y, pos.x);
267 * @brief ランダムクエスト報酬の品質をチェックする
268 * @param item 生成された高級品への参照
275 bool QuestCompletionChecker::check_quality(ItemEntity &item)
277 auto is_good_reward = !item.is_cursed();
278 is_good_reward &= !item.is_ammo() || (item.is_ammo() && item.is_fixed_artifact());
279 is_good_reward &= item.ego_idx != EgoType::DIGGING;
280 return is_good_reward;