OSDN Git Service

Merge pull request #716 from sikabane-works/release/3.0.0Alpha16
[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/line-of-sight.h"
10 #include "grid/feature.h"
11 #include "grid/grid.h"
12 #include "monster-floor/monster-safety-hiding.h"
13 #include "monster-race/monster-race.h"
14 #include "monster-race/race-flags-ability1.h"
15 #include "monster-race/race-flags-ability2.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-race/race-flags4.h"
20 #include "monster/monster-flag-types.h"
21 #include "monster/monster-info.h"
22 #include "monster/monster-status.h"
23 #include "mspell/mspell-mask-definitions.h"
24 #include "player/player-status-flags.h"
25 #include "system/floor-type-definition.h"
26 #include "target/projection-path-calculator.h"
27
28 /*!
29  * @brief モンスターがプレイヤーから逃走するかどうかを返す /
30  * Returns whether a given monster will try to run from the player.
31  * @param m_idx 逃走するモンスターの参照ID
32  * @return モンスターがプレイヤーから逃走するならばTRUEを返す。
33  * @details
34  * Monsters will attempt to avoid very powerful players.  See below.\n
35  *\n
36  * Because this function is called so often, little details are important\n
37  * for efficiency.  Like not using "mod" or "div" when possible.  And\n
38  * attempting to check the conditions in an optimal order.  Note that\n
39  * "(x << 2) == (x * 4)" if "x" has enough bits to hold the result.\n
40  *\n
41  * Note that this function is responsible for about one to five percent\n
42  * of the processor use in normal conditions...\n
43  */
44 static bool mon_will_run(player_type *target_ptr, MONSTER_IDX m_idx)
45 {
46     monster_type *m_ptr = &target_ptr->current_floor_ptr->m_list[m_idx];
47     monster_race *r_ptr = &r_info[m_ptr->r_idx];
48
49     if (is_pet(m_ptr)) {
50         return ((target_ptr->pet_follow_distance < 0) && (m_ptr->cdis <= (0 - target_ptr->pet_follow_distance)));
51     }
52
53     if (m_ptr->cdis > MAX_SIGHT + 5)
54         return FALSE;
55     if (monster_fear_remaining(m_ptr))
56         return TRUE;
57     if (m_ptr->cdis <= 5)
58         return FALSE;
59
60     PLAYER_LEVEL p_lev = target_ptr->lev;
61     DEPTH m_lev = r_ptr->level + (m_idx & 0x08) + 25;
62     if (m_lev > p_lev + 4)
63         return FALSE;
64     if (m_lev + 4 <= p_lev)
65         return TRUE;
66
67     HIT_POINT p_chp = target_ptr->chp;
68     HIT_POINT p_mhp = target_ptr->mhp;
69     HIT_POINT m_chp = m_ptr->hp;
70     HIT_POINT m_mhp = m_ptr->maxhp;
71     u32b p_val = (p_lev * p_mhp) + (p_chp << 2);
72     u32b m_val = (m_lev * m_mhp) + (m_chp << 2);
73     if (p_val * m_mhp > m_val * p_mhp)
74         return TRUE;
75
76     return FALSE;
77 }
78
79 /*!
80  * @brief モンスターがプレイヤーに向けて遠距離攻撃を行うことが可能なマスを走査する /
81  * Search spell castable grid
82  * @param target_ptr プレーヤーへの参照ポインタ
83  * @param m_idx モンスターの参照ID
84  * @param yp 適したマスのY座標を返す参照ポインタ
85  * @param xp 適したマスのX座標を返す参照ポインタ
86  * @return 有効なマスがあった場合TRUEを返す
87  */
88 static bool sweep_ranged_attack_grid(player_type *target_ptr, MONSTER_IDX m_idx, POSITION *yp, POSITION *xp)
89 {
90     floor_type *floor_ptr = target_ptr->current_floor_ptr;
91     monster_type *m_ptr = &floor_ptr->m_list[m_idx];
92     monster_race *r_ptr = &r_info[m_ptr->r_idx];
93
94     POSITION y1 = m_ptr->fy;
95     POSITION x1 = m_ptr->fx;
96
97     if (projectable(target_ptr, y1, x1, target_ptr->y, target_ptr->x))
98         return FALSE;
99
100     int now_cost = grid_cost(&floor_ptr->grid_array[y1][x1], r_ptr);
101     if (now_cost == 0)
102         now_cost = 999;
103
104     bool can_open_door = FALSE;
105     if (r_ptr->flags2 & (RF2_BASH_DOOR | RF2_OPEN_DOOR)) {
106         can_open_door = TRUE;
107     }
108
109     int best = 999;
110     for (int i = 7; i >= 0; i--) {
111         POSITION y = y1 + ddy_ddd[i];
112         POSITION x = x1 + ddx_ddd[i];
113         if (!in_bounds2(floor_ptr, y, x))
114             continue;
115         if (player_bold(target_ptr, y, x))
116             return FALSE;
117
118         grid_type *g_ptr;
119         g_ptr = &floor_ptr->grid_array[y][x];
120         int cost = grid_cost(g_ptr, r_ptr);
121         if (!(((r_ptr->flags2 & RF2_PASS_WALL) && ((m_idx != target_ptr->riding) || has_pass_wall(target_ptr)))
122                 || ((r_ptr->flags2 & RF2_KILL_WALL) && (m_idx != target_ptr->riding)))) {
123             if (cost == 0)
124                 continue;
125             if (!can_open_door && is_closed_door(target_ptr, g_ptr->feat))
126                 continue;
127         }
128
129         if (cost == 0)
130             cost = 998;
131
132         if (now_cost < cost)
133             continue;
134         if (!projectable(target_ptr, y, x, target_ptr->y, target_ptr->x))
135             continue;
136         if (best < cost)
137             continue;
138
139         best = cost;
140         *yp = y1 + ddy_ddd[i];
141         *xp = x1 + ddx_ddd[i];
142     }
143
144     if (best == 999)
145         return FALSE;
146
147     return TRUE;
148 }
149
150 /*!
151  * @brief モンスターがプレイヤーに向けて接近することが可能なマスを走査する /
152  * Choose the "best" direction for "flowing"
153  * @param m_idx モンスターの参照ID
154  * @param yp 移動先のマスのY座標を返す参照ポインタ
155  * @param xp 移動先のマスのX座標を返す参照ポインタ
156  * @param no_flow モンスターにFLOWフラグが経っていない状態でTRUE
157  * @return なし
158  * @details
159  * Note that ghosts and rock-eaters are never allowed to "flow",\n
160  * since they should move directly towards the player.\n
161  *\n
162  * Prefer "non-diagonal" directions, but twiddle them a little\n
163  * to angle slightly towards the player's actual location.\n
164  *\n
165  * Allow very perceptive monsters to track old "spoor" left by\n
166  * previous locations occupied by the player.  This will tend\n
167  * to have monsters end up either near the player or on a grid\n
168  * recently occupied by the player (and left via "teleport").\n
169  *\n
170  * Note that if "smell" is turned on, all monsters get vicious.\n
171  *\n
172  * Also note that teleporting away from a location will cause\n
173  * the monsters who were chasing you to converge on that location\n
174  * as long as you are still near enough to "annoy" them without\n
175  * being close enough to chase directly.  I have no idea what will\n
176  * happen if you combine "smell" with low "aaf" values.\n
177  */
178 static void sweep_movable_grid(player_type *target_ptr, MONSTER_IDX m_idx, POSITION *yp, POSITION *xp, bool no_flow)
179 {
180     grid_type *g_ptr;
181     floor_type *floor_ptr = target_ptr->current_floor_ptr;
182     monster_type *m_ptr = &floor_ptr->m_list[m_idx];
183     monster_race *r_ptr = &r_info[m_ptr->r_idx];
184
185     if (r_ptr->flags4 & (RF4_ATTACK_MASK) || r_ptr->a_ability_flags1 & (RF5_ATTACK_MASK) || r_ptr->a_ability_flags2 & (RF6_ATTACK_MASK)) {
186         if (sweep_ranged_attack_grid(target_ptr, m_idx, yp, xp))
187             return;
188     }
189
190     if (no_flow)
191         return;
192     if ((r_ptr->flags2 & RF2_PASS_WALL) && ((m_idx != target_ptr->riding) || has_pass_wall(target_ptr)))
193         return;
194     if ((r_ptr->flags2 & RF2_KILL_WALL) && (m_idx != target_ptr->riding))
195         return;
196
197     POSITION y1 = m_ptr->fy;
198     POSITION x1 = m_ptr->fx;
199     g_ptr = &floor_ptr->grid_array[y1][x1];
200     if (player_has_los_bold(target_ptr, y1, x1) && projectable(target_ptr, target_ptr->y, target_ptr->x, y1, x1)) {
201         if (distance(y1, x1, target_ptr->y, target_ptr->x) == 1)
202             return;
203         if (r_ptr->freq_spell > 0)
204             return;
205         if (grid_cost(g_ptr, r_ptr) > 5)
206             return;
207     }
208
209     int best;
210     bool use_scent = FALSE;
211     if (grid_cost(g_ptr, r_ptr)) {
212         best = 999;
213     } else if (g_ptr->when) {
214         if (floor_ptr->grid_array[target_ptr->y][target_ptr->x].when - g_ptr->when > 127)
215             return;
216
217         use_scent = TRUE;
218         best = 0;
219     } else {
220         return;
221     }
222
223     for (int i = 7; i >= 0; i--) {
224         POSITION y = y1 + ddy_ddd[i];
225         POSITION x = x1 + ddx_ddd[i];
226
227         if (!in_bounds2(floor_ptr, y, x))
228             continue;
229
230         g_ptr = &floor_ptr->grid_array[y][x];
231         if (use_scent) {
232             int when = g_ptr->when;
233             if (best > when)
234                 continue;
235
236             best = when;
237         } else {
238             int cost;
239             if (r_ptr->flags2 & (RF2_BASH_DOOR | RF2_OPEN_DOOR)) {
240                 cost = grid_dist(g_ptr, r_ptr);
241             } else {
242                 cost = grid_cost(g_ptr, r_ptr);
243             }
244
245             if ((cost == 0) || (best < cost))
246                 continue;
247
248             best = cost;
249         }
250
251         *yp = target_ptr->y + 16 * ddy_ddd[i];
252         *xp = target_ptr->x + 16 * ddx_ddd[i];
253     }
254 }
255
256 /*!
257  * @brief モンスターがプレイヤーから逃走することが可能なマスを走査する /
258  * Provide a location to flee to, but give the player a wide berth.
259  * @param m_idx モンスターの参照ID
260  * @param yp 移動先のマスのY座標を返す参照ポインタ
261  * @param xp 移動先のマスのX座標を返す参照ポインタ
262  * @return 有効なマスがあった場合TRUEを返す
263  * @details
264  * A monster may wish to flee to a location that is behind the player,\n
265  * but instead of heading directly for it, the monster should "swerve"\n
266  * around the player so that he has a smaller chance of getting hit.\n
267  */
268 static bool sweep_runnable_away_grid(floor_type *floor_ptr, MONSTER_IDX m_idx, POSITION *yp, POSITION *xp)
269 {
270     POSITION gy = 0, gx = 0;
271
272     monster_type *m_ptr = &floor_ptr->m_list[m_idx];
273     monster_race *r_ptr = &r_info[m_ptr->r_idx];
274     POSITION fy = m_ptr->fy;
275     POSITION fx = m_ptr->fx;
276
277     POSITION y1 = fy - (*yp);
278     POSITION x1 = fx - (*xp);
279
280     int score = -1;
281     for (int i = 7; i >= 0; i--) {
282         POSITION y = fy + ddy_ddd[i];
283         POSITION x = fx + ddx_ddd[i];
284         if (!in_bounds2(floor_ptr, y, x))
285             continue;
286
287         POSITION dis = distance(y, x, y1, x1);
288         POSITION s = 5000 / (dis + 3) - 500 / (grid_dist(&floor_ptr->grid_array[y][x], r_ptr) + 1);
289         if (s < 0)
290             s = 0;
291
292         if (s < score)
293             continue;
294
295         score = s;
296         gy = y;
297         gx = x;
298     }
299
300     if (score == -1)
301         return FALSE;
302
303     (*yp) = fy - gy;
304     (*xp) = fx - gx;
305
306     return TRUE;
307 }
308
309 /*!
310  * @brief モンスターの移動方向を返す /
311  * Choose "logical" directions for monster movement
312  * @param target_ptr プレーヤーへの参照ポインタ
313  * @param m_idx モンスターの参照ID
314  * @param mm 移動方向を返す方向IDの参照ポインタ
315  * @return 有効方向があった場合TRUEを返す
316  * @todo 分割したいが条件が多すぎて適切な関数名と詳細処理を追いきれない……
317  */
318 bool get_movable_grid(player_type *target_ptr, MONSTER_IDX m_idx, DIRECTION *mm)
319 {
320     floor_type *floor_ptr = target_ptr->current_floor_ptr;
321     monster_type *m_ptr = &floor_ptr->m_list[m_idx];
322     monster_race *r_ptr = &r_info[m_ptr->r_idx];
323     POSITION y = 0, x = 0;
324     POSITION y2 = target_ptr->y;
325     POSITION x2 = target_ptr->x;
326     bool done = FALSE;
327     bool will_run = mon_will_run(target_ptr, m_idx);
328     grid_type *g_ptr;
329     bool no_flow = m_ptr->mflag2.has(MFLAG2::NOFLOW) && grid_cost(&floor_ptr->grid_array[m_ptr->fy][m_ptr->fx], r_ptr) > 2;
330     bool can_pass_wall = ((r_ptr->flags2 & RF2_PASS_WALL) != 0) && ((m_idx != target_ptr->riding) || has_pass_wall(target_ptr));
331
332     if (!will_run && m_ptr->target_y) {
333         int t_m_idx = floor_ptr->grid_array[m_ptr->target_y][m_ptr->target_x].m_idx;
334         if ((t_m_idx > 0) && are_enemies(target_ptr, m_ptr, &floor_ptr->m_list[t_m_idx])
335             && los(target_ptr, m_ptr->fy, m_ptr->fx, m_ptr->target_y, m_ptr->target_x)
336             && projectable(target_ptr, m_ptr->fy, m_ptr->fx, m_ptr->target_y, m_ptr->target_x)) {
337             y = m_ptr->fy - m_ptr->target_y;
338             x = m_ptr->fx - m_ptr->target_x;
339             done = TRUE;
340         }
341     }
342
343     if (!done && !will_run && is_hostile(m_ptr) && (r_ptr->flags1 & RF1_FRIENDS)
344         && ((los(target_ptr, m_ptr->fy, m_ptr->fx, target_ptr->y, target_ptr->x) && projectable(target_ptr, m_ptr->fy, m_ptr->fx, target_ptr->y, target_ptr->x))
345             || (grid_dist(&floor_ptr->grid_array[m_ptr->fy][m_ptr->fx], r_ptr) < MAX_SIGHT / 2))) {
346         if ((r_ptr->flags3 & RF3_ANIMAL) && !can_pass_wall && !(r_ptr->flags2 & RF2_KILL_WALL)) {
347             int room = 0;
348             for (int i = 0; i < 8; i++) {
349                 int xx = target_ptr->x + ddx_ddd[i];
350                 int yy = target_ptr->y + ddy_ddd[i];
351
352                 if (!in_bounds2(floor_ptr, yy, xx))
353                     continue;
354
355                 g_ptr = &floor_ptr->grid_array[yy][xx];
356                 if (monster_can_cross_terrain(target_ptr, g_ptr->feat, r_ptr, 0)) {
357                     room++;
358                 }
359             }
360
361             if (floor_ptr->grid_array[target_ptr->y][target_ptr->x].info & CAVE_ROOM)
362                 room -= 2;
363             if (!r_ptr->flags4 && !r_ptr->a_ability_flags1 && !r_ptr->a_ability_flags2)
364                 room -= 2;
365
366             if (room < (8 * (target_ptr->chp + target_ptr->csp)) / (target_ptr->mhp + target_ptr->msp)) {
367                 if (find_hiding(target_ptr, m_idx, &y, &x))
368                     done = TRUE;
369             }
370         }
371
372         if (!done && grid_dist(&floor_ptr->grid_array[m_ptr->fy][m_ptr->fx], r_ptr) < 3) {
373             for (int i = 0; i < 8; i++) {
374                 y2 = target_ptr->y + ddy_ddd[(m_idx + i) & 7];
375                 x2 = target_ptr->x + ddx_ddd[(m_idx + i) & 7];
376                 if ((m_ptr->fy == y2) && (m_ptr->fx == x2)) {
377                     y2 = target_ptr->y;
378                     x2 = target_ptr->x;
379                     break;
380                 }
381
382                 if (!in_bounds2(floor_ptr, y2, x2))
383                     continue;
384                 if (!monster_can_enter(target_ptr, y2, x2, r_ptr, 0))
385                     continue;
386
387                 break;
388             }
389
390             y = m_ptr->fy - y2;
391             x = m_ptr->fx - x2;
392             done = TRUE;
393         }
394     }
395
396     if (!done) {
397         sweep_movable_grid(target_ptr, m_idx, &y2, &x2, no_flow);
398         y = m_ptr->fy - y2;
399         x = m_ptr->fx - x2;
400     }
401
402     if (is_pet(m_ptr) && will_run) {
403         y = (-y), x = (-x);
404     } else {
405         if (!done && will_run) {
406             int tmp_x = (-x);
407             int tmp_y = (-y);
408             if (find_safety(target_ptr, m_idx, &y, &x) && !no_flow) {
409                 if (sweep_runnable_away_grid(target_ptr->current_floor_ptr, m_idx, &y, &x))
410                     done = TRUE;
411             }
412
413             if (!done) {
414                 y = tmp_y;
415                 x = tmp_x;
416             }
417         }
418     }
419
420     if (!x && !y)
421         return FALSE;
422
423     store_moves_val(mm, y, x);
424     return TRUE;
425 }