OSDN Git Service

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