OSDN Git Service

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