OSDN Git Service

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