OSDN Git Service

[Refactor] monster_idxと0との比較を関数化する
[hengbandforosx/hengbandosx.git] / src / target / projection-path-calculator.cpp
1 #include "target/projection-path-calculator.h"
2 #include "effect/effect-characteristics.h"
3 #include "effect/spells-effect-util.h"
4 #include "floor/cave.h"
5 #include "grid/feature-flag-types.h"
6 #include "monster/monster-util.h"
7 #include "spell-class/spells-mirror-master.h"
8 #include "system/angband-system.h"
9 #include "system/floor-type-definition.h"
10 #include "system/grid-type-definition.h"
11 #include "system/player-type-definition.h"
12 #include "util/bit-flags-calculator.h"
13
14 struct projection_path_type {
15     std::vector<std::pair<int, int>> *position;
16     POSITION range;
17     BIT_FLAGS flag;
18     POSITION y1;
19     POSITION x1;
20     POSITION y2;
21     POSITION x2;
22     POSITION y;
23     POSITION x;
24     POSITION ay;
25     POSITION ax;
26     POSITION sy;
27     POSITION sx;
28     int frac;
29     int m;
30     int half;
31     int full;
32     int k;
33 };
34
35 std::vector<std::pair<int, int>>::const_iterator projection_path::begin() const
36 {
37     return this->position.cbegin();
38 }
39
40 std::vector<std::pair<int, int>>::const_iterator projection_path::end() const
41 {
42     return this->position.cend();
43 }
44
45 const std::pair<int, int> &projection_path::front() const
46 {
47     return this->position.front();
48 }
49
50 const std::pair<int, int> &projection_path::back() const
51 {
52     return this->position.back();
53 }
54
55 const std::pair<int, int> &projection_path::operator[](int num) const
56 {
57     return this->position[num];
58 }
59
60 int projection_path::path_num() const
61 {
62     return static_cast<int>(this->position.size());
63 }
64
65 static projection_path_type *initialize_projection_path_type(
66     projection_path_type *pp_ptr, std::vector<std::pair<int, int>> *position, POSITION range, BIT_FLAGS flag, POSITION y1, POSITION x1, POSITION y2, POSITION x2)
67 {
68     pp_ptr->position = position;
69     pp_ptr->range = range;
70     pp_ptr->flag = flag;
71     pp_ptr->y1 = y1;
72     pp_ptr->x1 = x1;
73     pp_ptr->y2 = y2;
74     pp_ptr->x2 = x2;
75     return pp_ptr;
76 }
77
78 static void set_asxy(projection_path_type *pp_ptr)
79 {
80     if (pp_ptr->y2 < pp_ptr->y1) {
81         pp_ptr->ay = pp_ptr->y1 - pp_ptr->y2;
82         pp_ptr->sy = -1;
83     } else {
84         pp_ptr->ay = pp_ptr->y2 - pp_ptr->y1;
85         pp_ptr->sy = 1;
86     }
87
88     if (pp_ptr->x2 < pp_ptr->x1) {
89         pp_ptr->ax = pp_ptr->x1 - pp_ptr->x2;
90         pp_ptr->sx = -1;
91     } else {
92         pp_ptr->ax = pp_ptr->x2 - pp_ptr->x1;
93         pp_ptr->sx = 1;
94     }
95 }
96
97 static bool project_stop(PlayerType *player_ptr, projection_path_type *pp_ptr)
98 {
99     auto *floor_ptr = player_ptr->current_floor_ptr;
100     const Pos2D pos(pp_ptr->y, pp_ptr->x);
101     const Pos2D pos2(pp_ptr->y2, pp_ptr->x2);
102     if (none_bits(pp_ptr->flag, PROJECT_THRU) && (pos == pos2)) {
103         return true;
104     }
105
106     if (any_bits(pp_ptr->flag, PROJECT_DISI)) {
107         if (!pp_ptr->position->empty() && cave_stop_disintegration(floor_ptr, pos.y, pos.x)) {
108             return true;
109         }
110     } else if (any_bits(pp_ptr->flag, PROJECT_LOS)) {
111         if (!pp_ptr->position->empty() && !cave_los_bold(floor_ptr, pos.y, pos.x)) {
112             return true;
113         }
114     } else if (none_bits(pp_ptr->flag, PROJECT_PATH)) {
115         if (!pp_ptr->position->empty() && !cave_has_flag_bold(floor_ptr, pos.y, pos.x, TerrainCharacteristics::PROJECT)) {
116             return true;
117         }
118     }
119
120     const auto &grid = floor_ptr->get_grid(pos);
121     if (any_bits(pp_ptr->flag, PROJECT_MIRROR)) {
122         if (!pp_ptr->position->empty() && grid.is_mirror()) {
123             return true;
124         }
125     }
126
127     if (any_bits(pp_ptr->flag, PROJECT_STOP) && !pp_ptr->position->empty() && (player_ptr->is_located_at(pos) || is_monster(grid.m_idx))) {
128         return true;
129     }
130
131     if (!in_bounds(floor_ptr, pos.y, pos.x)) {
132         return true;
133     }
134
135     return false;
136 }
137
138 static void calc_frac(projection_path_type *pp_ptr, bool is_vertical)
139 {
140     if (pp_ptr->m == 0) {
141         return;
142     }
143
144     pp_ptr->frac += pp_ptr->m;
145     if (pp_ptr->frac <= pp_ptr->half) {
146         return;
147     }
148
149     if (is_vertical) {
150         pp_ptr->x += pp_ptr->sx;
151     } else {
152         pp_ptr->y += pp_ptr->sy;
153     }
154
155     pp_ptr->frac -= pp_ptr->full;
156     pp_ptr->k++;
157 }
158
159 static void calc_projection_to_target(PlayerType *player_ptr, projection_path_type *pp_ptr, bool is_vertical)
160 {
161     while (true) {
162         pp_ptr->position->emplace_back(pp_ptr->y, pp_ptr->x);
163         if (static_cast<int>(pp_ptr->position->size()) + pp_ptr->k / 2 >= pp_ptr->range) {
164             break;
165         }
166
167         if (project_stop(player_ptr, pp_ptr)) {
168             break;
169         }
170
171         calc_frac(pp_ptr, is_vertical);
172         if (is_vertical) {
173             pp_ptr->y += pp_ptr->sy;
174         } else {
175             pp_ptr->x += pp_ptr->sx;
176         }
177     }
178 }
179
180 static bool calc_vertical_projection(PlayerType *player_ptr, projection_path_type *pp_ptr)
181 {
182     if (pp_ptr->ay <= pp_ptr->ax) {
183         return false;
184     }
185
186     pp_ptr->m = pp_ptr->ax * pp_ptr->ax * 2;
187     pp_ptr->y = pp_ptr->y1 + pp_ptr->sy;
188     pp_ptr->x = pp_ptr->x1;
189     pp_ptr->frac = pp_ptr->m;
190     if (pp_ptr->frac > pp_ptr->half) {
191         pp_ptr->x += pp_ptr->sx;
192         pp_ptr->frac -= pp_ptr->full;
193         pp_ptr->k++;
194     }
195
196     calc_projection_to_target(player_ptr, pp_ptr, true);
197     return true;
198 }
199
200 static bool calc_horizontal_projection(PlayerType *player_ptr, projection_path_type *pp_ptr)
201 {
202     if (pp_ptr->ax <= pp_ptr->ay) {
203         return false;
204     }
205
206     pp_ptr->m = pp_ptr->ay * pp_ptr->ay * 2;
207     pp_ptr->y = pp_ptr->y1;
208     pp_ptr->x = pp_ptr->x1 + pp_ptr->sx;
209     pp_ptr->frac = pp_ptr->m;
210     if (pp_ptr->frac > pp_ptr->half) {
211         pp_ptr->y += pp_ptr->sy;
212         pp_ptr->frac -= pp_ptr->full;
213         pp_ptr->k++;
214     }
215
216     calc_projection_to_target(player_ptr, pp_ptr, false);
217     return true;
218 }
219
220 static void calc_projection_others(PlayerType *player_ptr, projection_path_type *pp_ptr)
221 {
222     while (true) {
223         pp_ptr->position->emplace_back(pp_ptr->y, pp_ptr->x);
224         if (static_cast<int>(pp_ptr->position->size()) * 3 / 2 >= pp_ptr->range) {
225             break;
226         }
227
228         if (project_stop(player_ptr, pp_ptr)) {
229             break;
230         }
231
232         pp_ptr->y += pp_ptr->sy;
233         pp_ptr->x += pp_ptr->sx;
234     }
235 }
236
237 /*!
238  * @brief 始点から終点への直線経路を返す /
239  * Determine the path taken by a projection.
240  * @param player_ptr プレイヤーへの参照ポインタ
241  * @param range 距離
242  * @param y1 始点Y座標
243  * @param x1 始点X座標
244  * @param y2 終点Y座標
245  * @param x2 終点X座標
246  * @param flag フラグID
247  * @return リストの長さ
248  */
249 projection_path::projection_path(PlayerType *player_ptr, POSITION range, POSITION y1, POSITION x1, POSITION y2, POSITION x2, BIT_FLAGS flag)
250 {
251     this->position.clear();
252     if ((x1 == x2) && (y1 == y2)) {
253         return;
254     }
255
256     projection_path_type tmp_projection_path;
257     auto *pp_ptr = initialize_projection_path_type(&tmp_projection_path, &this->position, range, flag, y1, x1, y2, x2);
258     set_asxy(pp_ptr);
259     pp_ptr->half = pp_ptr->ay * pp_ptr->ax;
260     pp_ptr->full = pp_ptr->half << 1;
261     pp_ptr->k = 0;
262
263     if (calc_vertical_projection(player_ptr, pp_ptr)) {
264         return;
265     }
266
267     if (calc_horizontal_projection(player_ptr, pp_ptr)) {
268         return;
269     }
270
271     pp_ptr->y = y1 + pp_ptr->sy;
272     pp_ptr->x = x1 + pp_ptr->sx;
273     calc_projection_others(player_ptr, pp_ptr);
274 }
275
276 /*
277  * Determine if a bolt spell cast from (y1,x1) to (y2,x2) will arrive
278  * at the final destination, assuming no monster gets in the way.
279  *
280  * This is slightly (but significantly) different from "los(y1,x1,y2,x2)".
281  */
282 bool projectable(PlayerType *player_ptr, POSITION y1, POSITION x1, POSITION y2, POSITION x2)
283 {
284     projection_path grid_g(player_ptr, (project_length ? project_length : AngbandSystem::get_instance().get_max_range()), y1, x1, y2, x2, 0);
285     if (grid_g.path_num() == 0) {
286         return true;
287     }
288
289     const auto &[y, x] = grid_g.back();
290     if ((y != y2) || (x != x2)) {
291         return false;
292     }
293
294     return true;
295 }
296
297 /*
298  * Convert a "grid" (G) into a "location" (Y)
299  */
300 POSITION get_grid_y(uint16_t grid)
301 {
302     return (int)(grid / 256U);
303 }
304
305 /*
306  * Convert a "grid" (G) into a "location" (X)
307  */
308 POSITION get_grid_x(uint16_t grid)
309 {
310     return (int)(grid % 256U);
311 }