OSDN Git Service

[Fix] #41359 ハウンドの挙動が以前と異なる
[hengband/hengband.git] / src / monster-floor / monster-safety-hiding.c
1 /*!
2  * @brief モンスターの逃走・隠匿に関する処理
3  * @date 2020/03/08
4  * @author Hourier
5  */
6
7 #include "monster-floor/monster-safety-hiding.h"
8 #include "floor/cave.h"
9 #include "grid/grid.h"
10 #include "monster-floor/monster-dist-offsets.h"
11 #include "monster-race/monster-race.h"
12 #include "monster/monster-flag-types.h"
13 #include "monster/monster-info.h"
14 #include "mspell/mspell-checker.h"
15 #include "system/floor-type-definition.h"
16 #include "target/projection-path-calculator.h"
17
18  /*!
19   * @brief モンスターが逃げ込める地点を走査する
20   * @param target_ptr プレーヤーへの参照ポインタ
21   * @param m_idx モンスターID
22   * @param y_offsets
23   * @param x_offsets
24   * @param d モンスターがいる地点からの距離
25   * @return 逃げ込める地点の候補地
26   */
27 static coordinate_candidate sweep_safe_coordinate(player_type *target_ptr, MONSTER_IDX m_idx, const POSITION *y_offsets, const POSITION *x_offsets, int d)
28 {
29         coordinate_candidate candidate = init_coordinate_candidate();
30         floor_type *floor_ptr = target_ptr->current_floor_ptr;
31         monster_type *m_ptr = &target_ptr->current_floor_ptr->m_list[m_idx];
32         for (POSITION i = 0, dx = x_offsets[0], dy = y_offsets[0];
33                 dx != 0 || dy != 0;
34                 i++, dx = x_offsets[i], dy = y_offsets[i])
35         {
36                 POSITION y = m_ptr->fy + dy;
37                 POSITION x = m_ptr->fx + dx;
38                 if (!in_bounds(floor_ptr, y, x)) continue;
39
40                 grid_type *g_ptr;
41                 g_ptr = &floor_ptr->grid_array[y][x];
42
43                 BIT_FLAGS16 riding_mode = (m_idx == target_ptr->riding) ? CEM_RIDING : 0;
44                 if (!monster_can_cross_terrain(target_ptr, g_ptr->feat, &r_info[m_ptr->r_idx], riding_mode))
45                         continue;
46
47                 if (!(m_ptr->mflag2 & MFLAG2_NOFLOW))
48                 {
49                         if (g_ptr->dist == 0) continue;
50                         if (g_ptr->dist > floor_ptr->grid_array[m_ptr->fy][m_ptr->fx].dist + 2 * d) continue;
51                 }
52
53                 if (projectable(target_ptr, target_ptr->y, target_ptr->x, y, x)) continue;
54
55                 POSITION dis = distance(y, x, target_ptr->y, target_ptr->x);
56                 if (dis <= candidate.gdis) continue;
57
58                 candidate.gy = y;
59                 candidate.gx = x;
60                 candidate.gdis = dis;
61         }
62
63         return candidate;
64 }
65
66
67 /*!
68  * @brief モンスターが逃げ込める安全な地点を返す /
69  * Choose a "safe" location near a monster for it to run toward.
70  * @param target_ptr プレーヤーへの参照ポインタ
71  * @param m_idx モンスターの参照ID
72  * @param yp 移動先のマスのY座標を返す参照ポインタ
73  * @param xp 移動先のマスのX座標を返す参照ポインタ
74  * @return 有効なマスがあった場合TRUEを返す
75  * @details
76  * A location is "safe" if it can be reached quickly and the player\n
77  * is not able to fire into it (it isn't a "clean shot").  So, this will\n
78  * cause monsters to "duck" behind walls.  Hopefully, monsters will also\n
79  * try to run towards corridor openings if they are in a room.\n
80  *\n
81  * This function may take lots of CPU time if lots of monsters are\n
82  * fleeing.\n
83  *\n
84  * Return TRUE if a safe location is available.\n
85  */
86 bool find_safety(player_type *target_ptr, MONSTER_IDX m_idx, POSITION *yp, POSITION *xp)
87 {
88         monster_type *m_ptr = &target_ptr->current_floor_ptr->m_list[m_idx];
89         for (POSITION d = 1; d < 10; d++)
90         {
91                 const POSITION *y_offsets;
92                 y_offsets = dist_offsets_y[d];
93
94                 const POSITION *x_offsets;
95                 x_offsets = dist_offsets_x[d];
96
97                 coordinate_candidate candidate = sweep_safe_coordinate(target_ptr, m_idx, y_offsets, x_offsets, d);
98
99                 if (candidate.gdis <= 0) continue;
100
101                 *yp = m_ptr->fy - candidate.gy;
102                 *xp = m_ptr->fx - candidate.gx;
103
104                 return TRUE;
105         }
106
107         return FALSE;
108 }
109
110
111 /*!
112  * @brief モンスターが隠れられる地点を走査する
113  * @param target_ptr プレーヤーへの参照ポインタ
114  * @param m_idx モンスターID
115  * @param y_offsets
116  * @param x_offsets
117  * @param candidate 隠れられる地点の候補地
118  * @return なし
119  */
120 static void sweep_hiding_candidate(player_type *target_ptr, monster_type *m_ptr, const POSITION *y_offsets, const POSITION *x_offsets, coordinate_candidate *candidate)
121 {
122         monster_race *r_ptr = &r_info[m_ptr->r_idx];
123         for (POSITION i = 0, dx = x_offsets[0], dy = y_offsets[0];
124                 dx != 0 || dy != 0;
125                 i++, dx = x_offsets[i], dy = y_offsets[i])
126         {
127                 POSITION y = m_ptr->fy + dy;
128                 POSITION x = m_ptr->fx + dx;
129                 if (!in_bounds(target_ptr->current_floor_ptr, y, x)) continue;
130                 if (!monster_can_enter(target_ptr, y, x, r_ptr, 0)) continue;
131                 if (projectable(target_ptr, target_ptr->y, target_ptr->x, y, x) || !clean_shot(target_ptr, m_ptr->fy, m_ptr->fx, y, x, FALSE))
132                         continue;
133
134                 POSITION dis = distance(y, x, target_ptr->y, target_ptr->x);
135                 if (dis < candidate->gdis && dis >= 2)
136                 {
137                         candidate->gy = y;
138                         candidate->gx = x;
139                         candidate->gdis = dis;
140                 }
141         }
142 }
143
144
145 /*!
146  * @brief モンスターが隠れ潜める地点を返す /
147  * Choose a good hiding place near a monster for it to run toward.
148  * @param target_ptr プレーヤーへの参照ポインタ
149  * @param m_idx モンスターの参照ID
150  * @param yp 移動先のマスのY座標を返す参照ポインタ
151  * @param xp 移動先のマスのX座標を返す参照ポインタ
152  * @return 有効なマスがあった場合TRUEを返す
153  * @details
154  * Pack monsters will use this to "ambush" the player and lure him out\n
155  * of corridors into open space so they can swarm him.\n
156  *\n
157  * Return TRUE if a good location is available.\n
158  */
159 bool find_hiding(player_type *target_ptr, MONSTER_IDX m_idx, POSITION *yp, POSITION *xp)
160 {
161         monster_type *m_ptr = &target_ptr->current_floor_ptr->m_list[m_idx];
162         coordinate_candidate candidate = init_coordinate_candidate();
163         candidate.gdis = 999;
164
165         for (POSITION d = 1; d < 10; d++)
166         {
167                 const POSITION *y_offsets;
168                 y_offsets = dist_offsets_y[d];
169
170                 const POSITION *x_offsets;
171                 x_offsets = dist_offsets_x[d];
172
173                 sweep_hiding_candidate(target_ptr, m_ptr, y_offsets, x_offsets, &candidate);
174                 if (candidate.gdis >= 999) continue;
175
176                 *yp = m_ptr->fy - candidate.gy;
177                 *xp = m_ptr->fx - candidate.gx;
178                 return TRUE;
179         }
180
181         return FALSE;
182 }