OSDN Git Service

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