OSDN Git Service

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