OSDN Git Service

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