OSDN Git Service

[Refactor] #1717 Divided switch_completion()
[hengbandforosx/hengbandosx.git] / src / dungeon / quest-completion-checker.cpp
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/monster-info.h"
12 #include "object-enchant/item-apply-magic.h"
13 #include "system/floor-type-definition.h"
14 #include "system/grid-type-definition.h"
15 #include "system/monster-type-definition.h"
16 #include "system/object-type-definition.h"
17 #include "system/player-type-definition.h"
18 #include "util/bit-flags-calculator.h"
19 #include "view/display-messages.h"
20
21 QuestCompletionChecker::QuestCompletionChecker(player_type *player_ptr, monster_type *m_ptr)
22     : player_ptr(player_ptr)
23     , m_ptr(m_ptr)
24 {
25 }
26
27 /*!
28  * @brief 特定の敵を倒した際にクエスト達成処理 /
29  * Check for "Quest" completion when a quest monster is killed or charmed.
30  * @param player_ptr プレイヤーへの参照ポインタ
31  * @param m_ptr 撃破したモンスターの構造体参照ポインタ
32  */
33 void QuestCompletionChecker::complete()
34 {
35     this->set_quest_idx();
36     auto create_stairs = false;
37     auto reward = false;
38     if ((this->quest_idx > 0) && (quest[this->quest_idx].status == QuestStatusType::TAKEN)) {
39         this->q_ptr = &quest[this->quest_idx];
40         auto [tmp_create_stairs, tmp_reward] = this->switch_completion();
41         create_stairs = tmp_create_stairs;
42         reward = tmp_reward;
43     }
44
45     auto pos = this->make_stairs(create_stairs);
46     if (!reward) {
47         return;
48     }
49
50     this->make_reward(pos);
51 }
52
53 void QuestCompletionChecker::set_quest_idx()
54 {
55     auto *floor_ptr = this->player_ptr->current_floor_ptr;
56     this->quest_idx = floor_ptr->inside_quest;
57     if (this->quest_idx > 0) {
58         return;
59     }
60
61     short i;
62     for (i = max_q_idx - 1; i > 0; i--) {
63         auto *const quest_ptr = &quest[i];
64         if (quest_ptr->status != QuestStatusType::TAKEN) {
65             continue;
66         }
67
68         if (any_bits(quest_ptr->flags, QUEST_FLAG_PRESET)) {
69             continue;
70         }
71
72         if ((quest_ptr->level != floor_ptr->dun_level) && (quest_ptr->type != QuestKindType::KILL_ANY_LEVEL)) {
73             continue;
74         }
75
76         if ((quest_ptr->type == QuestKindType::FIND_ARTIFACT) || (quest_ptr->type == QuestKindType::FIND_EXIT)) {
77             continue;
78         }
79
80         auto kill_them_all = quest_ptr->type == QuestKindType::KILL_NUMBER;
81         kill_them_all |= quest_ptr->type == QuestKindType::TOWER;
82         kill_them_all |= quest_ptr->type == QuestKindType::KILL_ALL;
83         if (kill_them_all) {
84             break;
85         }
86
87         auto is_target = (quest_ptr->type == QuestKindType::RANDOM) && (quest_ptr->r_idx == this->m_ptr->r_idx);
88         if ((quest_ptr->type == QuestKindType::KILL_LEVEL) || (quest_ptr->type == QuestKindType::KILL_ANY_LEVEL) || is_target) {
89             break;
90         }
91     }
92
93     this->quest_idx = i;
94 }
95
96 std::tuple<bool, bool> QuestCompletionChecker::switch_completion()
97 {
98     switch (this->q_ptr->type) {
99     case QuestKindType::KILL_NUMBER:
100         this->complete_kill_number();
101         return std::make_tuple(false, false);
102     case QuestKindType::KILL_ALL:
103         this->complete_kill_all();
104         return std::make_tuple(false, false);
105     case QuestKindType::KILL_LEVEL:
106     case QuestKindType::RANDOM:
107         return this->complete_random();
108     case QuestKindType::KILL_ANY_LEVEL:
109         this->complete_kill_any_level();
110         return std::make_tuple(false, false);
111     case QuestKindType::TOWER:
112         this->complete_tower();
113         return std::make_tuple(false, false);
114     default:
115         return std::make_tuple(false, false);
116     }
117 }
118
119 void QuestCompletionChecker::complete_kill_number()
120 {
121     this->q_ptr->cur_num++;
122     if (this->q_ptr->cur_num >= this->q_ptr->num_mon) {
123         complete_quest(this->player_ptr, this->quest_idx);
124         this->q_ptr->cur_num = 0;
125     }
126 }
127
128 void QuestCompletionChecker::complete_kill_all()
129 {
130     if (!is_hostile(this->m_ptr) || (this->count_all_hostile_monsters() != 1)) {
131         return;
132     }
133
134     if (any_bits(this->q_ptr->flags, QUEST_FLAG_SILENT)) {
135         this->q_ptr->status = QuestStatusType::FINISHED;
136     } else {
137         complete_quest(this->player_ptr, this->quest_idx);
138     }
139 }
140
141 std::tuple<bool, bool> QuestCompletionChecker::complete_random()
142 {
143     if (this->q_ptr->r_idx != this->m_ptr->r_idx) {
144         return std::make_tuple(false, false);
145     }
146
147     this->q_ptr->cur_num++;
148     if (this->q_ptr->cur_num < this->q_ptr->max_num) {
149         return std::make_tuple(false, false);
150     }
151
152     complete_quest(this->player_ptr, this->quest_idx);
153     auto create_stairs = false;
154     if (none_bits(this->q_ptr->flags, QUEST_FLAG_PRESET)) {
155         create_stairs = true;
156         this->player_ptr->current_floor_ptr->inside_quest = 0;
157     }
158
159     if ((this->quest_idx == QUEST_OBERON) || (this->quest_idx == QUEST_SERPENT)) {
160         this->q_ptr->status = QuestStatusType::FINISHED;
161     }
162
163     auto reward = false;
164     if (this->q_ptr->type == QuestKindType::RANDOM) {
165         reward = true;
166         this->q_ptr->status = QuestStatusType::FINISHED;
167     }
168
169     return std::make_tuple(create_stairs, reward);
170 }
171
172 void QuestCompletionChecker::complete_kill_any_level()
173 {
174     this->q_ptr->cur_num++;
175     if (this->q_ptr->cur_num >= this->q_ptr->max_num) {
176         complete_quest(this->player_ptr, this->quest_idx);
177         this->q_ptr->cur_num = 0;
178     }
179 }
180
181 void QuestCompletionChecker::complete_tower()
182 {
183     if (!is_hostile(this->m_ptr)) {
184         return;
185     }
186
187     if (this->count_all_hostile_monsters() != 1) {
188         return;
189     }
190
191     this->q_ptr->status = QuestStatusType::STAGE_COMPLETED;
192     auto is_tower_completed = quest[QUEST_TOWER1].status == QuestStatusType::STAGE_COMPLETED;
193     is_tower_completed &= quest[QUEST_TOWER2].status == QuestStatusType::STAGE_COMPLETED;
194     is_tower_completed &= quest[QUEST_TOWER3].status == QuestStatusType::STAGE_COMPLETED;
195     if (is_tower_completed) {
196         complete_quest(this->player_ptr, QUEST_TOWER1);
197     }
198 }
199
200 /*!
201  * @brief 現在フロアに残っている敵モンスターの数を返す /
202  * @return 現在の敵モンスターの数
203  */
204 int QuestCompletionChecker::count_all_hostile_monsters()
205 {
206     auto *floor_ptr = this->player_ptr->current_floor_ptr;
207     auto number_mon = 0;
208     for (auto x = 0; x < floor_ptr->width; ++x) {
209         for (auto y = 0; y < floor_ptr->height; ++y) {
210             auto m_idx = floor_ptr->grid_array[y][x].m_idx;
211             if ((m_idx > 0) && is_hostile(&floor_ptr->m_list[m_idx])) {
212                 ++number_mon;
213             }
214         }
215     }
216
217     return number_mon;
218 }
219
220 Pos2D QuestCompletionChecker::make_stairs(const bool create_stairs)
221 {
222     auto y = this->m_ptr->fy;
223     auto x = this->m_ptr->fx;
224     if (!create_stairs) {
225         return Pos2D(y, x);
226     }
227
228     auto *floor_ptr = this->player_ptr->current_floor_ptr;
229     auto *g_ptr = &floor_ptr->grid_array[y][x];
230     while (cave_has_flag_bold(floor_ptr, y, x, FF::PERMANENT) || !g_ptr->o_idx_list.empty() || g_ptr->is_object()) {
231         int ny;
232         int nx;
233         scatter(this->player_ptr, &ny, &nx, y, x, 1, PROJECT_NONE);
234         y = ny;
235         x = nx;
236         g_ptr = &floor_ptr->grid_array[y][x];
237     }
238
239     msg_print(_("魔法の階段が現れた...", "A magical staircase appears..."));
240     cave_set_feat(this->player_ptr, y, x, feat_down_stair);
241     set_bits(this->player_ptr->update, PU_FLOW);
242     return Pos2D(y, x);
243 }
244
245 void QuestCompletionChecker::make_reward(const Pos2D pos)
246 {
247     auto dun_level = this->player_ptr->current_floor_ptr->dun_level;
248     for (auto i = 0; i < (dun_level / 15) + 1; i++) {
249         object_type item;
250         make_object(this->player_ptr, &item, AM_GOOD | AM_GREAT);
251         (void)drop_near(this->player_ptr, &item, -1, pos.y, pos.x);
252     }
253 }