OSDN Git Service

[Refactor] モンスターの思い出表示のモードをenumに変更
[hengbandforosx/hengbandosx.git] / src / target / projection-path-calculator.c
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 "grid/grid.h"
7 #include "system/floor-type-definition.h"
8
9 typedef struct projection_path_type {
10     u16b *gp;
11     POSITION range;
12     BIT_FLAGS flag;
13     POSITION y1;
14     POSITION x1;
15     POSITION y2;
16     POSITION x2;
17     POSITION y;
18     POSITION x;
19     POSITION ay;
20     POSITION ax;
21     POSITION sy;
22     POSITION sx;
23     int frac;
24     int m;
25     int half;
26     int full;
27     int n;
28     int k;
29 } projection_path_type;
30
31 /*
32  * @brief Convert a "location" (Y, X) into a "grid" (G)
33  * @param y Y座標
34  * @param x X座標
35  * return 経路座標
36  */
37 static u16b location_to_grid(POSITION y, POSITION x) { return 256 * y + x; }
38
39 static projection_path_type *initialize_projection_path_type(
40     projection_path_type *pp_ptr, u16b *gp, POSITION range, BIT_FLAGS flag, POSITION y1, POSITION x1, POSITION y2, POSITION x2)
41 {
42     pp_ptr->gp = gp;
43     pp_ptr->range = range;
44     pp_ptr->flag = flag;
45     pp_ptr->y1 = y1;
46     pp_ptr->x1 = x1;
47     pp_ptr->y2 = y2;
48     pp_ptr->x2 = x2;
49     return pp_ptr;
50 }
51
52 static void set_asxy(projection_path_type *pp_ptr)
53 {
54     if (pp_ptr->y2 < pp_ptr->y1) {
55         pp_ptr->ay = pp_ptr->y1 - pp_ptr->y2;
56         pp_ptr->sy = -1;
57     } else {
58         pp_ptr->ay = pp_ptr->y2 - pp_ptr->y1;
59         pp_ptr->sy = 1;
60     }
61
62     if (pp_ptr->x2 < pp_ptr->x1) {
63         pp_ptr->ax = pp_ptr->x1 - pp_ptr->x2;
64         pp_ptr->sx = -1;
65     } else {
66         pp_ptr->ax = pp_ptr->x2 - pp_ptr->x1;
67         pp_ptr->sx = 1;
68     }
69 }
70
71 static void calc_frac(projection_path_type *pp_ptr, bool is_vertical)
72 {
73     if (pp_ptr->m == 0)
74         return;
75
76     pp_ptr->frac += pp_ptr->m;
77     if (pp_ptr->frac <= pp_ptr->half)
78         return;
79
80     if (is_vertical)
81         pp_ptr->x += pp_ptr->sx;
82     else
83         pp_ptr->y += pp_ptr->sy;
84
85     pp_ptr->frac -= pp_ptr->full;
86     pp_ptr->k++;
87 }
88
89 static void calc_projection_to_target(player_type *player_ptr, projection_path_type *pp_ptr, bool is_vertical)
90 {
91     floor_type *floor_ptr = player_ptr->current_floor_ptr;
92     while (TRUE) {
93         pp_ptr->gp[pp_ptr->n++] = location_to_grid(pp_ptr->y, pp_ptr->x);
94         if ((pp_ptr->n + (pp_ptr->k >> 1)) >= pp_ptr->range)
95             break;
96
97         if (!(pp_ptr->flag & PROJECT_THRU)) {
98             if ((pp_ptr->x == pp_ptr->x2) && (pp_ptr->y == pp_ptr->y2))
99                 break;
100         }
101
102         if (pp_ptr->flag & PROJECT_DISI) {
103             if ((pp_ptr->n > 0) && cave_stop_disintegration(floor_ptr, pp_ptr->y, pp_ptr->x))
104                 break;
105         } else if (pp_ptr->flag & PROJECT_LOS) {
106             if ((pp_ptr->n > 0) && !cave_los_bold(floor_ptr, pp_ptr->y, pp_ptr->x))
107                 break;
108         } else if (!(pp_ptr->flag & PROJECT_PATH)) {
109             if ((pp_ptr->n > 0) && !cave_has_flag_bold(floor_ptr, pp_ptr->y, pp_ptr->x, FF_PROJECT))
110                 break;
111         }
112
113         if (pp_ptr->flag & PROJECT_STOP) {
114             if ((pp_ptr->n > 0) && (player_bold(player_ptr, pp_ptr->y, pp_ptr->x) || floor_ptr->grid_array[pp_ptr->y][pp_ptr->x].m_idx != 0))
115                 break;
116         }
117
118         if (!in_bounds(floor_ptr, pp_ptr->y, pp_ptr->x))
119             break;
120
121         calc_frac(pp_ptr, is_vertical);
122         if (is_vertical)
123             pp_ptr->y += pp_ptr->sy;
124         else
125             pp_ptr->x += pp_ptr->sx;
126     }
127 }
128
129 static bool calc_vertical_projection(player_type *player_ptr, projection_path_type *pp_ptr)
130 {
131     if (pp_ptr->ay <= pp_ptr->ax)
132         return FALSE;
133
134     pp_ptr->m = pp_ptr->ax * pp_ptr->ax * 2;
135     pp_ptr->y = pp_ptr->y1 + pp_ptr->sy;
136     pp_ptr->x = pp_ptr->x1;
137     pp_ptr->frac = pp_ptr->m;
138     if (pp_ptr->frac > pp_ptr->half) {
139         pp_ptr->x += pp_ptr->sx;
140         pp_ptr->frac -= pp_ptr->full;
141         pp_ptr->k++;
142     }
143
144     calc_projection_to_target(player_ptr, pp_ptr, TRUE);
145     return TRUE;
146 }
147
148 static bool calc_horizontal_projection(player_type *player_ptr, projection_path_type *pp_ptr)
149 {
150     if (pp_ptr->ax <= pp_ptr->ay)
151         return FALSE;
152
153     pp_ptr->m = pp_ptr->ay * pp_ptr->ay * 2;
154     pp_ptr->y = pp_ptr->y1;
155     pp_ptr->x = pp_ptr->x1 + pp_ptr->sx;
156     pp_ptr->frac = pp_ptr->m;
157     if (pp_ptr->frac > pp_ptr->half) {
158         pp_ptr->y += pp_ptr->sy;
159         pp_ptr->frac -= pp_ptr->full;
160         pp_ptr->k++;
161     }
162
163     calc_projection_to_target(player_ptr, pp_ptr, FALSE);
164     return TRUE;
165 }
166
167 static void calc_projection_others(player_type *player_ptr, projection_path_type *pp_ptr)
168 {
169     floor_type *floor_ptr = player_ptr->current_floor_ptr;
170     while (TRUE) {
171         pp_ptr->gp[pp_ptr->n++] = location_to_grid(pp_ptr->y, pp_ptr->x);
172         if ((pp_ptr->n + (pp_ptr->n >> 1)) >= pp_ptr->range)
173             break;
174
175         if (((pp_ptr->flag & PROJECT_THRU) == 0) && (pp_ptr->x == pp_ptr->x2) && (pp_ptr->y == pp_ptr->y2))
176             break;
177
178         if (pp_ptr->flag & PROJECT_DISI) {
179             if ((pp_ptr->n > 0) && cave_stop_disintegration(floor_ptr, pp_ptr->y, pp_ptr->x))
180                 break;
181         } else if (pp_ptr->flag & PROJECT_LOS) {
182             if ((pp_ptr->n > 0) && !cave_los_bold(floor_ptr, pp_ptr->y, pp_ptr->x))
183                 break;
184         } else if (!(pp_ptr->flag & PROJECT_PATH)) {
185             if ((pp_ptr->n > 0) && !cave_has_flag_bold(floor_ptr, pp_ptr->y, pp_ptr->x, FF_PROJECT))
186                 break;
187         }
188
189         if (((pp_ptr->flag & PROJECT_STOP) != 0) && (pp_ptr->n > 0)
190             && (player_bold(player_ptr, pp_ptr->y, pp_ptr->x) || floor_ptr->grid_array[pp_ptr->y][pp_ptr->x].m_idx != 0))
191             break;
192
193         if (!in_bounds(floor_ptr, pp_ptr->y, pp_ptr->x))
194             break;
195
196         pp_ptr->y += pp_ptr->sy;
197         pp_ptr->x += pp_ptr->sx;
198     }
199 }
200
201 /*!
202  * @brief 始点から終点への直線経路を返す /
203  * Determine the path taken by a projection.
204  * @param player_ptr プレーヤーへの参照ポインタ
205  * @param gp 経路座標リストを返す参照ポインタ
206  * @param range 距離
207  * @param y1 始点Y座標
208  * @param x1 始点X座標
209  * @param y2 終点Y座標
210  * @param x2 終点X座標
211  * @param flag フラグID
212  * @return リストの長さ
213  */
214 int projection_path(player_type *player_ptr, u16b *gp, POSITION range, POSITION y1, POSITION x1, POSITION y2, POSITION x2, BIT_FLAGS flag)
215 {
216     if ((x1 == x2) && (y1 == y2))
217         return 0;
218
219     projection_path_type tmp_projection_path;
220     projection_path_type *pp_ptr = initialize_projection_path_type(&tmp_projection_path, gp, range, flag, y1, x1, y2, x2);
221     set_asxy(pp_ptr);
222     pp_ptr->half = pp_ptr->ay * pp_ptr->ax;
223     pp_ptr->full = pp_ptr->half << 1;
224     pp_ptr->n = 0;
225     pp_ptr->k = 0;
226
227     if (calc_vertical_projection(player_ptr, pp_ptr))
228         return pp_ptr->n;
229
230     if (calc_horizontal_projection(player_ptr, pp_ptr))
231         return pp_ptr->n;
232
233     pp_ptr->y = y1 + pp_ptr->sy;
234     pp_ptr->x = x1 + pp_ptr->sx;
235     calc_projection_others(player_ptr, pp_ptr);
236     return pp_ptr->n;
237 }
238
239 /*
240  * Determine if a bolt spell cast from (y1,x1) to (y2,x2) will arrive
241  * at the final destination, assuming no monster gets in the way.
242  *
243  * This is slightly (but significantly) different from "los(y1,x1,y2,x2)".
244  */
245 bool projectable(player_type *player_ptr, POSITION y1, POSITION x1, POSITION y2, POSITION x2)
246 {
247     u16b grid_g[512];
248     int grid_n = projection_path(player_ptr, grid_g, (project_length ? project_length : get_max_range(player_ptr)), y1, x1, y2, x2, 0);
249     if (!grid_n)
250         return TRUE;
251
252     POSITION y = get_grid_y(grid_g[grid_n - 1]);
253     POSITION x = get_grid_x(grid_g[grid_n - 1]);
254     if ((y != y2) || (x != x2))
255         return FALSE;
256
257     return TRUE;
258 }
259
260 /*!
261  * @briefプレイヤーの攻撃射程(マス) / Maximum range (spells, etc)
262  * @param creature_ptr プレーヤーへの参照ポインタ
263  * @return 射程
264  */
265 int get_max_range(player_type *creature_ptr) { return creature_ptr->phase_out ? 36 : 18; }
266
267 /*
268  * Convert a "grid" (G) into a "location" (Y)
269  */
270 POSITION get_grid_y(u16b grid) { return (int)(grid / 256U); }
271
272 /*
273  * Convert a "grid" (G) into a "location" (X)
274  */
275 POSITION get_grid_x(u16b grid) { return (int)(grid % 256U); }