OSDN Git Service

[Refactor] #2628 Renamed object-type-definition.cpp/h to item-entity.cpp/h
[hengbandforosx/hengbandosx.git] / src / monster-floor / monster-sweep-grid.cpp
1 /*!
2  * @brief モンスターの移動方向を走査する処理
3  * @date 2020/03/08
4  * @author Hourier
5  */
6
7 #include "monster-floor/monster-sweep-grid.h"
8 #include "floor/cave.h"
9 #include "floor/geometry.h"
10 #include "floor/line-of-sight.h"
11 #include "grid/feature.h"
12 #include "grid/grid.h"
13 #include "monster-floor/monster-safety-hiding.h"
14 #include "monster-race/monster-race.h"
15 #include "monster-race/race-ability-mask.h"
16 #include "monster-race/race-flags1.h"
17 #include "monster-race/race-flags2.h"
18 #include "monster-race/race-flags3.h"
19 #include "monster/monster-flag-types.h"
20 #include "monster/monster-info.h"
21 #include "monster/monster-processor-util.h"
22 #include "monster/monster-status.h"
23 #include "player/player-status-flags.h"
24 #include "system/floor-type-definition.h"
25 #include "system/grid-type-definition.h"
26 #include "system/monster-race-definition.h"
27 #include "system/monster-type-definition.h"
28 #include "system/player-type-definition.h"
29 #include "target/projection-path-calculator.h"
30 #include "util/bit-flags-calculator.h"
31
32 /*
33  * @brief コンストラクタ
34  * @param player_ptr プレイヤーへの参照ポインタ
35  * @param m_idx 逃走するモンスターの参照ID
36  * @param mm 移動方向を返す方向IDの参照ポインタ
37  */
38 MonsterSweepGrid::MonsterSweepGrid(PlayerType *player_ptr, MONSTER_IDX m_idx, DIRECTION *mm)
39     : player_ptr(player_ptr)
40     , m_idx(m_idx)
41     , mm(mm)
42 {
43 }
44
45 /*!
46  * @brief モンスターの移動方向を返す /
47  * Choose "logical" directions for monster movement
48  * @return 有効方向があった場合TRUEを返す
49  * @todo 分割したいが条件が多すぎて適切な関数名と詳細処理を追いきれない……
50  */
51 bool MonsterSweepGrid::get_movable_grid()
52 {
53     auto *floor_ptr = this->player_ptr->current_floor_ptr;
54     auto *m_ptr = &floor_ptr->m_list[this->m_idx];
55     auto *r_ptr = &monraces_info[m_ptr->r_idx];
56     POSITION y = 0;
57     POSITION x = 0;
58     auto y2 = this->player_ptr->y;
59     auto x2 = this->player_ptr->x;
60     this->will_run = this->mon_will_run();
61     const auto no_flow = m_ptr->mflag2.has(MonsterConstantFlagType::NOFLOW) && (floor_ptr->grid_array[m_ptr->fy][m_ptr->fx].get_cost(r_ptr) > 2);
62     this->can_pass_wall = r_ptr->feature_flags.has(MonsterFeatureType::PASS_WALL) && ((this->m_idx != this->player_ptr->riding) || has_pass_wall(this->player_ptr));
63     if (!this->will_run && m_ptr->target_y) {
64         int t_m_idx = floor_ptr->grid_array[m_ptr->target_y][m_ptr->target_x].m_idx;
65         if (t_m_idx > 0) {
66             const auto is_enemies = are_enemies(this->player_ptr, *m_ptr, floor_ptr->m_list[t_m_idx]);
67             const auto is_los = los(this->player_ptr, m_ptr->fy, m_ptr->fx, m_ptr->target_y, m_ptr->target_x);
68             const auto is_projectable = projectable(this->player_ptr, m_ptr->fy, m_ptr->fx, m_ptr->target_y, m_ptr->target_x);
69             if (is_enemies && is_los && is_projectable) {
70                 y = m_ptr->fy - m_ptr->target_y;
71                 x = m_ptr->fx - m_ptr->target_x;
72                 this->done = true;
73             }
74         }
75     }
76
77     this->check_hiding_grid(&y, &x, &y2, &x2);
78     if (!this->done) {
79         this->sweep_movable_grid(&y2, &x2, no_flow);
80         y = m_ptr->fy - y2;
81         x = m_ptr->fx - x2;
82     }
83
84     this->search_pet_runnable_grid(&y, &x, no_flow);
85     if (!x && !y) {
86         return false;
87     }
88
89     store_moves_val(this->mm, y, x);
90     return true;
91 }
92
93 /*!
94  * @brief モンスターがプレイヤーから逃走するかどうかを返す /
95  * Returns whether a given monster will try to run from the player.
96  * @return モンスターがプレイヤーから逃走するならばTRUEを返す。
97  */
98 bool MonsterSweepGrid::mon_will_run()
99 {
100     auto *m_ptr = &this->player_ptr->current_floor_ptr->m_list[this->m_idx];
101     auto *r_ptr = &monraces_info[m_ptr->r_idx];
102     if (m_ptr->is_pet()) {
103         return (this->player_ptr->pet_follow_distance < 0) && (m_ptr->cdis <= (0 - this->player_ptr->pet_follow_distance));
104     }
105
106     if (m_ptr->cdis > MAX_PLAYER_SIGHT + 5) {
107         return false;
108     }
109
110     if (m_ptr->is_fearful()) {
111         return true;
112     }
113
114     if (m_ptr->cdis <= 5) {
115         return false;
116     }
117
118     auto p_lev = this->player_ptr->lev;
119     auto m_lev = r_ptr->level + (this->m_idx & 0x08) + 25;
120     if (m_lev > p_lev + 4) {
121         return false;
122     }
123
124     if (m_lev + 4 <= p_lev) {
125         return true;
126     }
127
128     auto p_chp = this->player_ptr->chp;
129     auto p_mhp = this->player_ptr->mhp;
130     auto m_chp = m_ptr->hp;
131     auto m_mhp = m_ptr->maxhp;
132     uint32_t p_val = (p_lev * p_mhp) + (p_chp << 2);
133     uint32_t m_val = (m_lev * m_mhp) + (m_chp << 2);
134     return p_val * m_mhp > m_val * p_mhp;
135 }
136
137 void MonsterSweepGrid::check_hiding_grid(POSITION *y, POSITION *x, POSITION *y2, POSITION *x2)
138 {
139     auto *floor_ptr = this->player_ptr->current_floor_ptr;
140     auto *m_ptr = &floor_ptr->m_list[this->m_idx];
141     auto *r_ptr = &monraces_info[m_ptr->r_idx];
142     if (this->done || this->will_run || !m_ptr->is_hostile() || none_bits(r_ptr->flags1, RF1_FRIENDS)) {
143         return;
144     }
145
146     if ((!los(this->player_ptr, m_ptr->fy, m_ptr->fx, this->player_ptr->y, this->player_ptr->x) || !projectable(this->player_ptr, m_ptr->fy, m_ptr->fx, this->player_ptr->y, this->player_ptr->x))) {
147         if (floor_ptr->grid_array[m_ptr->fy][m_ptr->fx].get_distance(r_ptr) >= MAX_PLAYER_SIGHT / 2) {
148             return;
149         }
150     }
151
152     this->search_room_to_run(y, x);
153     if (this->done || (floor_ptr->grid_array[m_ptr->fy][m_ptr->fx].get_distance(r_ptr) >= 3)) {
154         return;
155     }
156
157     for (auto i = 0; i < 8; i++) {
158         *y2 = this->player_ptr->y + ddy_ddd[(this->m_idx + i) & 7];
159         *x2 = this->player_ptr->x + ddx_ddd[(this->m_idx + i) & 7];
160         if ((m_ptr->fy == *y2) && (m_ptr->fx == *x2)) {
161             *y2 = this->player_ptr->y;
162             *x2 = this->player_ptr->x;
163             break;
164         }
165
166         if (!in_bounds2(floor_ptr, *y2, *x2) || !monster_can_enter(this->player_ptr, *y2, *x2, r_ptr, 0)) {
167             continue;
168         }
169
170         break;
171     }
172
173     *y = m_ptr->fy - *y2;
174     *x = m_ptr->fx - *x2;
175     this->done = true;
176 }
177
178 void MonsterSweepGrid::search_room_to_run(POSITION *y, POSITION *x)
179 {
180     auto *floor_ptr = this->player_ptr->current_floor_ptr;
181     auto *r_ptr = &monraces_info[floor_ptr->m_list[this->m_idx].r_idx];
182     if (r_ptr->kind_flags.has_not(MonsterKindType::ANIMAL) || this->can_pass_wall || r_ptr->feature_flags.has(MonsterFeatureType::KILL_WALL)) {
183         return;
184     }
185
186     auto room = 0;
187     for (auto i = 0; i < 8; i++) {
188         auto xx = this->player_ptr->x + ddx_ddd[i];
189         auto yy = this->player_ptr->y + ddy_ddd[i];
190         if (!in_bounds2(floor_ptr, yy, xx)) {
191             continue;
192         }
193
194         auto *g_ptr = &floor_ptr->grid_array[yy][xx];
195         if (monster_can_cross_terrain(this->player_ptr, g_ptr->feat, r_ptr, 0)) {
196             room++;
197         }
198     }
199
200     if (floor_ptr->grid_array[this->player_ptr->y][this->player_ptr->x].is_room()) {
201         room -= 2;
202     }
203
204     if (r_ptr->ability_flags.none()) {
205         room -= 2;
206     }
207
208     if (room >= (8 * (this->player_ptr->chp + this->player_ptr->csp)) / (this->player_ptr->mhp + this->player_ptr->msp)) {
209         return;
210     }
211
212     if (find_hiding(this->player_ptr, this->m_idx, y, x)) {
213         this->done = true;
214     }
215 }
216
217 void MonsterSweepGrid::search_pet_runnable_grid(POSITION *y, POSITION *x, bool no_flow)
218 {
219     auto *floor_ptr = this->player_ptr->current_floor_ptr;
220     auto *m_ptr = &floor_ptr->m_list[this->m_idx];
221     if (m_ptr->is_pet() && this->will_run) {
222         *y = -(*y);
223         *x = -(*x);
224         return;
225     }
226
227     if (this->done || !this->will_run) {
228         return;
229     }
230
231     auto tmp_x = -(*x);
232     auto tmp_y = -(*y);
233     if (find_safety(this->player_ptr, this->m_idx, y, x) && !no_flow && this->sweep_runnable_away_grid(y, x)) {
234         this->done = true;
235     }
236
237     if (this->done) {
238         return;
239     }
240
241     *y = tmp_y;
242     *x = tmp_x;
243 }
244
245 /*!
246  * @brief モンスターがプレイヤーに向けて接近することが可能なマスを走査する /
247  * Choose the "best" direction for "flowing"
248  * @param yp 移動先のマスのY座標を返す参照ポインタ
249  * @param xp 移動先のマスのX座標を返す参照ポインタ
250  * @param no_flow モンスターにFLOWフラグが経っていない状態でTRUE
251  */
252 void MonsterSweepGrid::sweep_movable_grid(POSITION *yp, POSITION *xp, bool no_flow)
253 {
254     auto *floor_ptr = this->player_ptr->current_floor_ptr;
255     auto *m_ptr = &floor_ptr->m_list[this->m_idx];
256     auto *r_ptr = &monraces_info[m_ptr->r_idx];
257     if (!this->check_movable_grid(yp, xp, no_flow)) {
258         return;
259     }
260
261     auto y1 = m_ptr->fy;
262     auto x1 = m_ptr->fx;
263     auto *g_ptr = &floor_ptr->grid_array[y1][x1];
264     if (player_has_los_bold(this->player_ptr, y1, x1) && projectable(this->player_ptr, this->player_ptr->y, this->player_ptr->x, y1, x1)) {
265         if ((distance(y1, x1, this->player_ptr->y, this->player_ptr->x) == 1) || (r_ptr->freq_spell > 0) || (g_ptr->get_cost(r_ptr) > 5)) {
266             return;
267         }
268     }
269
270     auto use_scent = false;
271     if (g_ptr->get_cost(r_ptr)) {
272         this->best = 999;
273     } else if (g_ptr->when) {
274         if (floor_ptr->grid_array[this->player_ptr->y][this->player_ptr->x].when - g_ptr->when > 127) {
275             return;
276         }
277
278         use_scent = true;
279         this->best = 0;
280     } else {
281         return;
282     }
283
284     this->determine_when_cost(yp, xp, y1, x1, use_scent);
285 }
286
287 bool MonsterSweepGrid::check_movable_grid(POSITION *yp, POSITION *xp, const bool no_flow)
288 {
289     auto *r_ptr = &monraces_info[this->player_ptr->current_floor_ptr->m_list[this->m_idx].r_idx];
290     if ((r_ptr->ability_flags.has_any_of(RF_ABILITY_ATTACK_MASK)) && (sweep_ranged_attack_grid(yp, xp))) {
291         return false;
292     }
293
294     if (no_flow) {
295         return false;
296     }
297
298     if (r_ptr->feature_flags.has(MonsterFeatureType::PASS_WALL) && ((this->m_idx != this->player_ptr->riding) || has_pass_wall(this->player_ptr))) {
299         return false;
300     }
301
302     if (r_ptr->feature_flags.has(MonsterFeatureType::KILL_WALL) && (this->m_idx != this->player_ptr->riding)) {
303         return false;
304     }
305
306     return true;
307 }
308
309 /*!
310  * @brief モンスターがプレイヤーに向けて遠距離攻撃を行うことが可能なマスを走査する /
311  * Search spell castable grid
312  * @param yp 適したマスのY座標を返す参照ポインタ
313  * @param xp 適したマスのX座標を返す参照ポインタ
314  * @return 有効なマスがあった場合TRUEを返す
315  */
316 bool MonsterSweepGrid::sweep_ranged_attack_grid(POSITION *yp, POSITION *xp)
317 {
318     auto *floor_ptr = this->player_ptr->current_floor_ptr;
319     auto *m_ptr = &floor_ptr->m_list[this->m_idx];
320     auto *r_ptr = &monraces_info[m_ptr->r_idx];
321     auto y1 = m_ptr->fy;
322     auto x1 = m_ptr->fx;
323     if (projectable(this->player_ptr, y1, x1, this->player_ptr->y, this->player_ptr->x)) {
324         return false;
325     }
326
327     auto now_cost = (int)floor_ptr->grid_array[y1][x1].get_cost(r_ptr);
328     if (now_cost == 0) {
329         now_cost = 999;
330     }
331
332     if (r_ptr->behavior_flags.has_any_of({ MonsterBehaviorType::BASH_DOOR, MonsterBehaviorType::OPEN_DOOR })) {
333         this->can_open_door = true;
334     }
335
336     for (auto i = 7; i >= 0; i--) {
337         auto y = y1 + ddy_ddd[i];
338         auto x = x1 + ddx_ddd[i];
339         if (!in_bounds2(floor_ptr, y, x)) {
340             continue;
341         }
342
343         if (player_bold(this->player_ptr, y, x)) {
344             return false;
345         }
346
347         auto *g_ptr = &floor_ptr->grid_array[y][x];
348         this->cost = (int)g_ptr->get_cost(r_ptr);
349         if (!this->is_best_cost(y, x, now_cost)) {
350             continue;
351         }
352
353         this->best = this->cost;
354         *yp = y1 + ddy_ddd[i];
355         *xp = x1 + ddx_ddd[i];
356     }
357
358     return this->best != 999;
359 }
360
361 bool MonsterSweepGrid::is_best_cost(const POSITION y, const POSITION x, const int now_cost)
362 {
363     auto *floor_ptr = this->player_ptr->current_floor_ptr;
364     auto *r_ptr = &monraces_info[floor_ptr->m_list[this->m_idx].r_idx];
365     auto is_riding = this->m_idx == this->player_ptr->riding;
366     if ((r_ptr->feature_flags.has_not(MonsterFeatureType::PASS_WALL) || (is_riding && !has_pass_wall(this->player_ptr))) && (r_ptr->feature_flags.has_not(MonsterFeatureType::KILL_WALL) || is_riding)) {
367         if (this->cost == 0) {
368             return false;
369         }
370
371         auto *g_ptr = &floor_ptr->grid_array[y][x];
372         if (!this->can_open_door && is_closed_door(this->player_ptr, g_ptr->feat)) {
373             return false;
374         }
375     }
376
377     if (this->cost == 0) {
378         this->cost = 998;
379     }
380
381     if (now_cost < this->cost) {
382         return false;
383     }
384
385     if (!projectable(this->player_ptr, y, x, this->player_ptr->y, this->player_ptr->x)) {
386         return false;
387     }
388
389     if (this->best < this->cost) {
390         return false;
391     }
392
393     return true;
394 }
395
396 /*!
397  * @brief モンスターがプレイヤーから逃走することが可能なマスを走査する /
398  * Provide a location to flee to, but give the player a wide berth.
399  * @param yp 移動先のマスのY座標を返す参照ポインタ
400  * @param xp 移動先のマスのX座標を返す参照ポインタ
401  * @return 有効なマスがあった場合TRUEを返す
402  */
403 bool MonsterSweepGrid::sweep_runnable_away_grid(POSITION *yp, POSITION *xp)
404 {
405     auto gy = 0;
406     auto gx = 0;
407     auto *floor_ptr = this->player_ptr->current_floor_ptr;
408     auto *m_ptr = &floor_ptr->m_list[this->m_idx];
409     auto *r_ptr = &monraces_info[m_ptr->r_idx];
410     auto fy = m_ptr->fy;
411     auto fx = m_ptr->fx;
412     auto y1 = fy - *yp;
413     auto x1 = fx - *xp;
414     auto score = -1;
415     for (auto i = 7; i >= 0; i--) {
416         auto y = fy + ddy_ddd[i];
417         auto x = fx + ddx_ddd[i];
418         if (!in_bounds2(floor_ptr, y, x)) {
419             continue;
420         }
421
422         auto dis = distance(y, x, y1, x1);
423         auto s = 5000 / (dis + 3) - 500 / (floor_ptr->grid_array[y][x].get_distance(r_ptr) + 1);
424         if (s < 0) {
425             s = 0;
426         }
427
428         if (s < score) {
429             continue;
430         }
431
432         score = s;
433         gy = y;
434         gx = x;
435     }
436
437     if (score == -1) {
438         return false;
439     }
440
441     *yp = fy - gy;
442     *xp = fx - gx;
443     return true;
444 }
445
446 void MonsterSweepGrid::determine_when_cost(POSITION *yp, POSITION *xp, POSITION y1, POSITION x1, const bool use_scent)
447 {
448     auto *floor_ptr = this->player_ptr->current_floor_ptr;
449     for (auto i = 7; i >= 0; i--) {
450         auto y = y1 + ddy_ddd[i];
451         auto x = x1 + ddx_ddd[i];
452         if (!in_bounds2(floor_ptr, y, x)) {
453             continue;
454         }
455
456         auto *g_ptr = &floor_ptr->grid_array[y][x];
457         if (use_scent) {
458             int when = g_ptr->when;
459             if (this->best > when) {
460                 continue;
461             }
462
463             this->best = when;
464         } else {
465             auto *r_ptr = &monraces_info[floor_ptr->m_list[this->m_idx].r_idx];
466             this->cost = r_ptr->behavior_flags.has_any_of({ MonsterBehaviorType::BASH_DOOR, MonsterBehaviorType::OPEN_DOOR }) ? g_ptr->get_distance(r_ptr) : g_ptr->get_cost(r_ptr);
467             if ((this->cost == 0) || (this->best < this->cost)) {
468                 continue;
469             }
470
471             this->best = this->cost;
472         }
473
474         *yp = this->player_ptr->y + 16 * ddy_ddd[i];
475         *xp = this->player_ptr->x + 16 * ddx_ddd[i];
476     }
477 }