OSDN Git Service

Merge pull request #3569 from sikabane-works/release/3.0.0.88-alpha
[hengbandforosx/hengbandosx.git] / src / monster-floor / monster-direction.cpp
1 /*!
2  * @brief モンスターの移動方向を決定する処理
3  * @date 2020/03/08
4  * @author Hourier
5  */
6
7 #include "monster-floor/monster-direction.h"
8 #include "floor/cave.h"
9 #include "monster-floor/monster-sweep-grid.h"
10 #include "monster-race/monster-race.h"
11 #include "monster-race/race-flags1.h"
12 #include "monster-race/race-flags2.h"
13 #include "monster/monster-info.h"
14 #include "monster/monster-processor-util.h"
15 #include "monster/monster-status.h"
16 #include "pet/pet-util.h"
17 #include "player/player-status-flags.h"
18 #include "spell/range-calc.h"
19 #include "system/floor-type-definition.h"
20 #include "system/monster-entity.h"
21 #include "system/monster-race-info.h"
22 #include "system/player-type-definition.h"
23 #include "target/projection-path-calculator.h"
24
25 /*!
26  * @brief ペットが敵に接近するための方向を決定する
27  * @param player_ptr プレイヤーへの参照ポインタ
28  * @param m_ptr 移動を試みているモンスターへの参照ポインタ
29  * @param t_ptr 移動先モンスターへの参照ポインタ
30  * @param plus モンスターIDの増減 (1/2 の確率で+1、1/2の確率で-1)
31  * @return ペットがモンスターに近づくならばTRUE
32  */
33 static bool decide_pet_approch_direction(PlayerType *player_ptr, MonsterEntity *m_ptr, MonsterEntity *t_ptr)
34 {
35     auto *r_ptr = &monraces_info[m_ptr->r_idx];
36     if (!m_ptr->is_pet()) {
37         return false;
38     }
39
40     if (player_ptr->pet_follow_distance < 0) {
41         if (t_ptr->cdis <= (0 - player_ptr->pet_follow_distance)) {
42             return true;
43         }
44     } else if ((m_ptr->cdis < t_ptr->cdis) && (t_ptr->cdis > player_ptr->pet_follow_distance)) {
45         return true;
46     }
47
48     return r_ptr->aaf < t_ptr->cdis;
49 }
50
51 /*!
52  * @brief モンスターが敵に接近するための方向を決定する
53  * @param player_ptr プレイヤーへの参照ポインタ
54  * @param m_idx モンスターID
55  * @param start モンスターIDの開始
56  * @param plus モンスターIDの増減 (1/2 の確率で+1、1/2の確率で-1)
57  * @param y モンスターの移動方向Y
58  * @param x モンスターの移動方向X
59  */
60 static void decide_enemy_approch_direction(PlayerType *player_ptr, MONSTER_IDX m_idx, int start, int plus, POSITION *y, POSITION *x)
61 {
62     auto *floor_ptr = player_ptr->current_floor_ptr;
63     auto *m_ptr = &floor_ptr->m_list[m_idx];
64     auto *r_ptr = &monraces_info[m_ptr->r_idx];
65     for (int i = start; ((i < start + floor_ptr->m_max) && (i > start - floor_ptr->m_max)); i += plus) {
66         MONSTER_IDX dummy = (i % floor_ptr->m_max);
67         if (dummy == 0) {
68             continue;
69         }
70
71         MONSTER_IDX t_idx = dummy;
72         MonsterEntity *t_ptr;
73         t_ptr = &floor_ptr->m_list[t_idx];
74         if (t_ptr == m_ptr) {
75             continue;
76         }
77         if (!t_ptr->is_valid()) {
78             continue;
79         }
80         if (decide_pet_approch_direction(player_ptr, m_ptr, t_ptr)) {
81             continue;
82         }
83         if (!are_enemies(player_ptr, *m_ptr, *t_ptr)) {
84             continue;
85         }
86
87         const auto can_pass_wall = r_ptr->feature_flags.has(MonsterFeatureType::PASS_WALL) && ((m_idx != player_ptr->riding) || has_pass_wall(player_ptr));
88         const auto can_kill_wall = r_ptr->feature_flags.has(MonsterFeatureType::KILL_WALL) && (m_idx != player_ptr->riding);
89         if (can_pass_wall || can_kill_wall) {
90             if (!in_disintegration_range(floor_ptr, m_ptr->fy, m_ptr->fx, t_ptr->fy, t_ptr->fx)) {
91                 continue;
92             }
93         } else {
94             if (!projectable(player_ptr, m_ptr->fy, m_ptr->fx, t_ptr->fy, t_ptr->fx)) {
95                 continue;
96             }
97         }
98
99         *y = t_ptr->fy;
100         *x = t_ptr->fx;
101         return;
102     }
103 }
104
105 /*!
106  * @brief モンスターが敵に接近するための方向を計算するメインルーチン
107  * Calculate the direction to the next enemy
108  * @param player_ptr プレイヤーへの参照ポインタ
109  * @param m_idx モンスターの参照ID
110  * @param mm 移動するべき方角IDを返す参照ポインタ
111  * @return 方向が確定した場合TRUE、接近する敵がそもそもいない場合FALSEを返す
112  */
113 bool get_enemy_dir(PlayerType *player_ptr, MONSTER_IDX m_idx, int *mm)
114 {
115     auto *floor_ptr = player_ptr->current_floor_ptr;
116     auto *m_ptr = &floor_ptr->m_list[m_idx];
117
118     POSITION x = 0, y = 0;
119     if (player_ptr->riding_t_m_idx && player_bold(player_ptr, m_ptr->fy, m_ptr->fx)) {
120         y = floor_ptr->m_list[player_ptr->riding_t_m_idx].fy;
121         x = floor_ptr->m_list[player_ptr->riding_t_m_idx].fx;
122     } else if (m_ptr->is_pet() && player_ptr->pet_t_m_idx) {
123         y = floor_ptr->m_list[player_ptr->pet_t_m_idx].fy;
124         x = floor_ptr->m_list[player_ptr->pet_t_m_idx].fx;
125     } else {
126         int start;
127         int plus = 1;
128         if (player_ptr->phase_out) {
129             start = randint1(floor_ptr->m_max - 1) + floor_ptr->m_max;
130             if (randint0(2)) {
131                 plus = -1;
132             }
133         } else {
134             start = floor_ptr->m_max + 1;
135         }
136
137         decide_enemy_approch_direction(player_ptr, m_idx, start, plus, &y, &x);
138
139         if ((x == 0) && (y == 0)) {
140             return false;
141         }
142     }
143
144     x -= m_ptr->fx;
145     y -= m_ptr->fy;
146
147     store_enemy_approch_direction(mm, y, x);
148     return true;
149 }
150
151 /*!
152  * @brief 不規則歩行フラグを持つモンスターの移動方向をその確率に基づいて決定する
153  * @param player_ptr プレイヤーへの参照ポインタ
154  * @param mm 移動方向
155  * @param m_ptr モンスターへの参照ポインタ
156  * @return 不規則な方向へ歩くことになったらTRUE
157  * @todo "5"とはもしかして「その場に留まる」という意味か?
158  */
159 static bool random_walk(PlayerType *player_ptr, DIRECTION *mm, MonsterEntity *m_ptr)
160 {
161     auto *r_ptr = &monraces_info[m_ptr->r_idx];
162     if (r_ptr->behavior_flags.has_all_of({ MonsterBehaviorType::RAND_MOVE_50, MonsterBehaviorType::RAND_MOVE_25 }) && (randint0(100) < 75)) {
163         if (is_original_ap_and_seen(player_ptr, m_ptr)) {
164             r_ptr->r_behavior_flags.set({ MonsterBehaviorType::RAND_MOVE_50, MonsterBehaviorType::RAND_MOVE_25 });
165         }
166
167         mm[0] = mm[1] = mm[2] = mm[3] = 5;
168         return true;
169     }
170
171     if (r_ptr->behavior_flags.has(MonsterBehaviorType::RAND_MOVE_50) && (randint0(100) < 50)) {
172         if (is_original_ap_and_seen(player_ptr, m_ptr)) {
173             r_ptr->r_behavior_flags.set(MonsterBehaviorType::RAND_MOVE_50);
174         }
175
176         mm[0] = mm[1] = mm[2] = mm[3] = 5;
177         return true;
178     }
179
180     if (r_ptr->behavior_flags.has(MonsterBehaviorType::RAND_MOVE_25) && (randint0(100) < 25)) {
181         if (is_original_ap_and_seen(player_ptr, m_ptr)) {
182             r_ptr->r_behavior_flags.set(MonsterBehaviorType::RAND_MOVE_25);
183         }
184
185         mm[0] = mm[1] = mm[2] = mm[3] = 5;
186         return true;
187     }
188
189     return false;
190 }
191
192 /*!
193  * @brief ペットや友好的なモンスターがフロアから逃げる処理を行う
194  * @param player_ptr プレイヤーへの参照ポインタ
195  * @param mm 移動方向
196  * @param m_idx モンスターID
197  * @return モンスターがペットであればTRUE
198  */
199 static bool decide_pet_movement_direction(MonsterSweepGrid *msd)
200 {
201     auto *m_ptr = &msd->player_ptr->current_floor_ptr->m_list[msd->m_idx];
202     if (!m_ptr->is_pet()) {
203         return false;
204     }
205
206     bool avoid = ((msd->player_ptr->pet_follow_distance < 0) && (m_ptr->cdis <= (0 - msd->player_ptr->pet_follow_distance)));
207     bool lonely = (!avoid && (m_ptr->cdis > msd->player_ptr->pet_follow_distance));
208     bool distant = (m_ptr->cdis > PET_SEEK_DIST);
209     msd->mm[0] = msd->mm[1] = msd->mm[2] = msd->mm[3] = 5;
210     if (get_enemy_dir(msd->player_ptr, msd->m_idx, msd->mm)) {
211         return true;
212     }
213
214     if (!avoid && !lonely && !distant) {
215         return true;
216     }
217
218     POSITION dis = msd->player_ptr->pet_follow_distance;
219     if (msd->player_ptr->pet_follow_distance > PET_SEEK_DIST) {
220         msd->player_ptr->pet_follow_distance = PET_SEEK_DIST;
221     }
222
223     (void)msd->get_movable_grid();
224     msd->player_ptr->pet_follow_distance = (int16_t)dis;
225     return true;
226 }
227
228 /*!
229  * @brief モンスターの移動パターンを決定する
230  * @param player_ptr プレイヤーへの参照ポインタ
231  * @param mm 移動方向
232  * @param m_idx モンスターID
233  * @param aware モンスターがプレイヤーに気付いているならばTRUE、超隠密状態ならばFALSE
234  * @return 移動先が存在すればTRUE
235  */
236 bool decide_monster_movement_direction(PlayerType *player_ptr, DIRECTION *mm, MONSTER_IDX m_idx, bool aware)
237 {
238     auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
239     auto *r_ptr = &monraces_info[m_ptr->r_idx];
240
241     if (m_ptr->is_confused() || !aware) {
242         mm[0] = mm[1] = mm[2] = mm[3] = 5;
243         return true;
244     }
245
246     if (random_walk(player_ptr, mm, m_ptr)) {
247         return true;
248     }
249
250     if (r_ptr->behavior_flags.has(MonsterBehaviorType::NEVER_MOVE) && (m_ptr->cdis > 1)) {
251         mm[0] = mm[1] = mm[2] = mm[3] = 5;
252         return true;
253     }
254
255     MonsterSweepGrid msd(player_ptr, m_idx, mm);
256     if (decide_pet_movement_direction(&msd)) {
257         return true;
258     }
259
260     if (!m_ptr->is_hostile()) {
261         mm[0] = mm[1] = mm[2] = mm[3] = 5;
262         get_enemy_dir(player_ptr, m_idx, mm);
263         return true;
264     }
265
266     return msd.get_movable_grid();
267 }