OSDN Git Service

English: use "power" rather than "magic" for elementalist power menu
[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 = &r_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     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 = any_bits(r_ptr->flags2, RF2_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) && are_enemies(this->player_ptr, m_ptr, &floor_ptr->m_list[t_m_idx]) && los(this->player_ptr, m_ptr->fy, m_ptr->fx, m_ptr->target_y, m_ptr->target_x) && projectable(this->player_ptr, m_ptr->fy, m_ptr->fx, m_ptr->target_y, m_ptr->target_x)) {
66             y = m_ptr->fy - m_ptr->target_y;
67             x = m_ptr->fx - m_ptr->target_x;
68             this->done = true;
69         }
70     }
71
72     this->check_hiding_grid(&y, &x, &y2, &x2);
73     if (!this->done) {
74         this->sweep_movable_grid(&y2, &x2, no_flow);
75         y = m_ptr->fy - y2;
76         x = m_ptr->fx - x2;
77     }
78
79     this->search_pet_runnable_grid(&y, &x, no_flow);
80     if (!x && !y) {
81         return false;
82     }
83
84     store_moves_val(this->mm, y, x);
85     return true;
86 }
87
88 /*!
89  * @brief モンスターがプレイヤーから逃走するかどうかを返す /
90  * Returns whether a given monster will try to run from the player.
91  * @return モンスターがプレイヤーから逃走するならばTRUEを返す。
92  */
93 bool MonsterSweepGrid::mon_will_run()
94 {
95     auto *m_ptr = &this->player_ptr->current_floor_ptr->m_list[this->m_idx];
96     auto *r_ptr = &r_info[m_ptr->r_idx];
97     if (is_pet(m_ptr)) {
98         return (this->player_ptr->pet_follow_distance < 0) && (m_ptr->cdis <= (0 - this->player_ptr->pet_follow_distance));
99     }
100
101     if (m_ptr->cdis > MAX_SIGHT + 5) {
102         return false;
103     }
104
105     if (monster_fear_remaining(m_ptr)) {
106         return true;
107     }
108
109     if (m_ptr->cdis <= 5) {
110         return false;
111     }
112
113     auto p_lev = this->player_ptr->lev;
114     auto m_lev = r_ptr->level + (this->m_idx & 0x08) + 25;
115     if (m_lev > p_lev + 4) {
116         return false;
117     }
118
119     if (m_lev + 4 <= p_lev) {
120         return true;
121     }
122
123     auto p_chp = this->player_ptr->chp;
124     auto p_mhp = this->player_ptr->mhp;
125     auto m_chp = m_ptr->hp;
126     auto m_mhp = m_ptr->maxhp;
127     uint32_t p_val = (p_lev * p_mhp) + (p_chp << 2);
128     uint32_t m_val = (m_lev * m_mhp) + (m_chp << 2);
129     return p_val * m_mhp > m_val * p_mhp;
130 }
131
132 void MonsterSweepGrid::check_hiding_grid(POSITION *y, POSITION *x, POSITION *y2, POSITION *x2)
133 {
134     auto *floor_ptr = this->player_ptr->current_floor_ptr;
135     auto *m_ptr = &floor_ptr->m_list[this->m_idx];
136     auto *r_ptr = &r_info[m_ptr->r_idx];
137     if (this->done || this->will_run || !is_hostile(m_ptr) || none_bits(r_ptr->flags1, RF1_FRIENDS)) {
138         return;
139     }
140
141     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))) {
142         if (floor_ptr->grid_array[m_ptr->fy][m_ptr->fx].get_distance(r_ptr) >= MAX_SIGHT / 2) {
143             return;
144         }
145     }
146
147     this->search_room_to_run(y, x);
148     if (this->done || (floor_ptr->grid_array[m_ptr->fy][m_ptr->fx].get_distance(r_ptr) >= 3)) {
149         return;
150     }
151
152     for (auto i = 0; i < 8; i++) {
153         *y2 = this->player_ptr->y + ddy_ddd[(this->m_idx + i) & 7];
154         *x2 = this->player_ptr->x + ddx_ddd[(this->m_idx + i) & 7];
155         if ((m_ptr->fy == *y2) && (m_ptr->fx == *x2)) {
156             *y2 = this->player_ptr->y;
157             *x2 = this->player_ptr->x;
158             break;
159         }
160
161         if (!in_bounds2(floor_ptr, *y2, *x2) || !monster_can_enter(this->player_ptr, *y2, *x2, r_ptr, 0)) {
162             continue;
163         }
164
165         break;
166     }
167
168     *y = m_ptr->fy - *y2;
169     *x = m_ptr->fx - *x2;
170     this->done = true;
171 }
172
173 void MonsterSweepGrid::search_room_to_run(POSITION *y, POSITION *x)
174 {
175     auto *floor_ptr = this->player_ptr->current_floor_ptr;
176     auto *r_ptr = &r_info[floor_ptr->m_list[this->m_idx].r_idx];
177     if (r_ptr->kind_flags.has_not(MonsterKindType::ANIMAL) || this->can_pass_wall || any_bits(r_ptr->flags2, RF2_KILL_WALL)) {
178         return;
179     }
180
181     auto room = 0;
182     for (auto i = 0; i < 8; i++) {
183         auto xx = this->player_ptr->x + ddx_ddd[i];
184         auto yy = this->player_ptr->y + ddy_ddd[i];
185         if (!in_bounds2(floor_ptr, yy, xx)) {
186             continue;
187         }
188
189         auto *g_ptr = &floor_ptr->grid_array[yy][xx];
190         if (monster_can_cross_terrain(this->player_ptr, g_ptr->feat, r_ptr, 0)) {
191             room++;
192         }
193     }
194
195     if (floor_ptr->grid_array[this->player_ptr->y][this->player_ptr->x].is_room()) {
196         room -= 2;
197     }
198
199     if (r_ptr->ability_flags.none()) {
200         room -= 2;
201     }
202
203     if (room >= (8 * (this->player_ptr->chp + this->player_ptr->csp)) / (this->player_ptr->mhp + this->player_ptr->msp)) {
204         return;
205     }
206
207     if (find_hiding(this->player_ptr, this->m_idx, y, x)) {
208         this->done = true;
209     }
210 }
211
212 void MonsterSweepGrid::search_pet_runnable_grid(POSITION *y, POSITION *x, bool no_flow)
213 {
214     auto *floor_ptr = this->player_ptr->current_floor_ptr;
215     auto *m_ptr = &floor_ptr->m_list[this->m_idx];
216     if (is_pet(m_ptr) && this->will_run) {
217         *y = -(*y);
218         *x = -(*x);
219         return;
220     }
221
222     if (this->done || !this->will_run) {
223         return;
224     }
225
226     auto tmp_x = -(*x);
227     auto tmp_y = -(*y);
228     if (find_safety(this->player_ptr, this->m_idx, y, x) && !no_flow && this->sweep_runnable_away_grid(y, x)) {
229         this->done = true;
230     }
231
232     if (this->done) {
233         return;
234     }
235
236     *y = tmp_y;
237     *x = tmp_x;
238 }
239
240 /*!
241  * @brief モンスターがプレイヤーに向けて接近することが可能なマスを走査する /
242  * Choose the "best" direction for "flowing"
243  * @param yp 移動先のマスのY座標を返す参照ポインタ
244  * @param xp 移動先のマスのX座標を返す参照ポインタ
245  * @param no_flow モンスターにFLOWフラグが経っていない状態でTRUE
246  */
247 void MonsterSweepGrid::sweep_movable_grid(POSITION *yp, POSITION *xp, bool no_flow)
248 {
249     auto *floor_ptr = this->player_ptr->current_floor_ptr;
250     auto *m_ptr = &floor_ptr->m_list[this->m_idx];
251     auto *r_ptr = &r_info[m_ptr->r_idx];
252     if (!this->check_movable_grid(yp, xp, no_flow)) {
253         return;
254     }
255
256     auto y1 = m_ptr->fy;
257     auto x1 = m_ptr->fx;
258     auto *g_ptr = &floor_ptr->grid_array[y1][x1];
259     if (player_has_los_bold(this->player_ptr, y1, x1) && projectable(this->player_ptr, this->player_ptr->y, this->player_ptr->x, y1, x1)) {
260         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)) {
261             return;
262         }
263     }
264
265     auto use_scent = false;
266     if (g_ptr->get_cost(r_ptr)) {
267         this->best = 999;
268     } else if (g_ptr->when) {
269         if (floor_ptr->grid_array[this->player_ptr->y][this->player_ptr->x].when - g_ptr->when > 127) {
270             return;
271         }
272
273         use_scent = true;
274         this->best = 0;
275     } else {
276         return;
277     }
278
279     this->determine_when_cost(yp, xp, y1, x1, use_scent);
280 }
281
282 bool MonsterSweepGrid::check_movable_grid(POSITION *yp, POSITION *xp, const bool no_flow)
283 {
284     auto *r_ptr = &r_info[this->player_ptr->current_floor_ptr->m_list[this->m_idx].r_idx];
285     if ((r_ptr->ability_flags.has_any_of(RF_ABILITY_ATTACK_MASK)) && (sweep_ranged_attack_grid(yp, xp))) {
286         return false;
287     }
288
289     if (no_flow) {
290         return false;
291     }
292
293     if (any_bits(r_ptr->flags2, RF2_PASS_WALL) && ((this->m_idx != this->player_ptr->riding) || has_pass_wall(this->player_ptr))) {
294         return false;
295     }
296
297     if (any_bits(r_ptr->flags2, RF2_KILL_WALL) && (this->m_idx != this->player_ptr->riding)) {
298         return false;
299     }
300
301     return true;
302 }
303
304 /*!
305  * @brief モンスターがプレイヤーに向けて遠距離攻撃を行うことが可能なマスを走査する /
306  * Search spell castable grid
307  * @param yp 適したマスのY座標を返す参照ポインタ
308  * @param xp 適したマスのX座標を返す参照ポインタ
309  * @return 有効なマスがあった場合TRUEを返す
310  */
311 bool MonsterSweepGrid::sweep_ranged_attack_grid(POSITION *yp, POSITION *xp)
312 {
313     auto *floor_ptr = this->player_ptr->current_floor_ptr;
314     auto *m_ptr = &floor_ptr->m_list[this->m_idx];
315     auto *r_ptr = &r_info[m_ptr->r_idx];
316     auto y1 = m_ptr->fy;
317     auto x1 = m_ptr->fx;
318     if (projectable(this->player_ptr, y1, x1, this->player_ptr->y, this->player_ptr->x)) {
319         return false;
320     }
321
322     auto now_cost = (int)floor_ptr->grid_array[y1][x1].get_cost(r_ptr);
323     if (now_cost == 0) {
324         now_cost = 999;
325     }
326
327     if (r_ptr->behavior_flags.has_any_of({ MonsterBehaviorType::BASH_DOOR, MonsterBehaviorType::OPEN_DOOR })) {
328         this->can_open_door = true;
329     }
330
331     for (auto i = 7; i >= 0; i--) {
332         auto y = y1 + ddy_ddd[i];
333         auto x = x1 + ddx_ddd[i];
334         if (!in_bounds2(floor_ptr, y, x)) {
335             continue;
336         }
337
338         if (player_bold(this->player_ptr, y, x)) {
339             return false;
340         }
341
342         auto *g_ptr = &floor_ptr->grid_array[y][x];
343         this->cost = (int)g_ptr->get_cost(r_ptr);
344         if (!this->is_best_cost(y, x, now_cost)) {
345             continue;
346         }
347
348         this->best = this->cost;
349         *yp = y1 + ddy_ddd[i];
350         *xp = x1 + ddx_ddd[i];
351     }
352
353     return this->best != 999;
354 }
355
356 bool MonsterSweepGrid::is_best_cost(const POSITION y, const POSITION x, const int now_cost)
357 {
358     auto *floor_ptr = this->player_ptr->current_floor_ptr;
359     auto *r_ptr = &r_info[floor_ptr->m_list[this->m_idx].r_idx];
360     auto is_riding = this->m_idx == this->player_ptr->riding;
361     if ((none_bits(r_ptr->flags2, RF2_PASS_WALL) || (is_riding && !has_pass_wall(this->player_ptr))) && (none_bits(r_ptr->flags2, RF2_KILL_WALL) || is_riding)) {
362         if (this->cost == 0) {
363             return false;
364         }
365
366         auto *g_ptr = &floor_ptr->grid_array[y][x];
367         if (!this->can_open_door && is_closed_door(this->player_ptr, g_ptr->feat)) {
368             return false;
369         }
370     }
371
372     if (this->cost == 0) {
373         this->cost = 998;
374     }
375
376     if (now_cost < this->cost) {
377         return false;
378     }
379
380     if (!projectable(this->player_ptr, y, x, this->player_ptr->y, this->player_ptr->x)) {
381         return false;
382     }
383
384     if (this->best < this->cost) {
385         return false;
386     }
387
388     return true;
389 }
390
391 /*!
392  * @brief モンスターがプレイヤーから逃走することが可能なマスを走査する /
393  * Provide a location to flee to, but give the player a wide berth.
394  * @param yp 移動先のマスのY座標を返す参照ポインタ
395  * @param xp 移動先のマスのX座標を返す参照ポインタ
396  * @return 有効なマスがあった場合TRUEを返す
397  */
398 bool MonsterSweepGrid::sweep_runnable_away_grid(POSITION *yp, POSITION *xp)
399 {
400     auto gy = 0;
401     auto gx = 0;
402     auto *floor_ptr = this->player_ptr->current_floor_ptr;
403     auto *m_ptr = &floor_ptr->m_list[this->m_idx];
404     auto *r_ptr = &r_info[m_ptr->r_idx];
405     auto fy = m_ptr->fy;
406     auto fx = m_ptr->fx;
407     auto y1 = fy - *yp;
408     auto x1 = fx - *xp;
409     auto score = -1;
410     for (auto i = 7; i >= 0; i--) {
411         auto y = fy + ddy_ddd[i];
412         auto x = fx + ddx_ddd[i];
413         if (!in_bounds2(floor_ptr, y, x)) {
414             continue;
415         }
416
417         auto dis = distance(y, x, y1, x1);
418         auto s = 5000 / (dis + 3) - 500 / (floor_ptr->grid_array[y][x].get_distance(r_ptr) + 1);
419         if (s < 0) {
420             s = 0;
421         }
422
423         if (s < score) {
424             continue;
425         }
426
427         score = s;
428         gy = y;
429         gx = x;
430     }
431
432     if (score == -1) {
433         return false;
434     }
435
436     *yp = fy - gy;
437     *xp = fx - gx;
438     return true;
439 }
440
441 void MonsterSweepGrid::determine_when_cost(POSITION *yp, POSITION *xp, POSITION y1, POSITION x1, const bool use_scent)
442 {
443     auto *floor_ptr = this->player_ptr->current_floor_ptr;
444     for (auto i = 7; i >= 0; i--) {
445         auto y = y1 + ddy_ddd[i];
446         auto x = x1 + ddx_ddd[i];
447         if (!in_bounds2(floor_ptr, y, x)) {
448             continue;
449         }
450
451         auto *g_ptr = &floor_ptr->grid_array[y][x];
452         if (use_scent) {
453             int when = g_ptr->when;
454             if (this->best > when) {
455                 continue;
456             }
457
458             this->best = when;
459         } else {
460             auto *r_ptr = &r_info[floor_ptr->m_list[this->m_idx].r_idx];
461             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);
462             if ((this->cost == 0) || (this->best < this->cost)) {
463                 continue;
464             }
465
466             this->best = this->cost;
467         }
468
469         *yp = this->player_ptr->y + 16 * ddy_ddd[i];
470         *xp = this->player_ptr->x + 16 * ddx_ddd[i];
471     }
472 }