OSDN Git Service

Merge remote-tracking branch 'remotes/origin/feature/Fix-saved-floor-exceed' into...
[hengband/hengband.git] / src / spell / range-calc.c
1 /*!
2  * @brief 魔法による距離やエリアの計算
3  * @date 2014/07/10
4  * @author Ben Harrison, James E. Wilson, Robert A. Koeneke, deskull and Hourier
5  */
6
7 #include "spell/range-calc.h"
8 #include "floor/cave.h"
9 #include "floor/line-of-sight.h"
10 #include "grid/feature.h"
11 #include "grid/grid.h"
12 #include "spell/spell-types.h"
13 #include "system/floor-type-definition.h"
14 #include "target/projection-path-calculator.h"
15 #include "util/bit-flags-calculator.h"
16
17 /*
18  * Find the distance from (x, y) to a line.
19  */
20 POSITION dist_to_line(POSITION y, POSITION x, POSITION y1, POSITION x1, POSITION y2, POSITION x2)
21 {
22         POSITION py = y1 - y;
23         POSITION px = x1 - x;
24         POSITION ny = x2 - x1;
25         POSITION nx = y1 - y2;
26         POSITION pd = distance(y1, x1, y, x);
27         POSITION nd = distance(y1, x1, y2, x2);
28
29         if (pd > nd) return distance(y, x, y2, x2);
30
31         nd = ((nd) ? ((py * ny + px * nx) / nd) : 0);
32         return((nd >= 0) ? nd : 0 - nd);
33 }
34
35
36 /*
37  *
38  * Modified version of los() for calculation of disintegration balls.
39  * Disintegration effects are stopped by permanent walls.
40  */
41 bool in_disintegration_range(floor_type *floor_ptr, POSITION y1, POSITION x1, POSITION y2, POSITION x2)
42 {
43         POSITION delta_y = y2 - y1;
44         POSITION delta_x = x2 - x1;
45         POSITION absolute_y = ABS(delta_y);
46         POSITION absolute_x = ABS(delta_x);
47         if ((absolute_x < 2) && (absolute_y < 2)) return TRUE;
48
49         POSITION scanner_y;
50         if (!delta_x)
51         {
52                 /* South -- check for walls */
53                 if (delta_y > 0)
54                 {
55                         for (scanner_y = y1 + 1; scanner_y < y2; scanner_y++)
56                         {
57                                 if (cave_stop_disintegration(floor_ptr, scanner_y, x1)) return FALSE;
58                         }
59                 }
60
61                 /* North -- check for walls */
62                 else
63                 {
64                         for (scanner_y = y1 - 1; scanner_y > y2; scanner_y--)
65                         {
66                                 if (cave_stop_disintegration(floor_ptr, scanner_y, x1)) return FALSE;
67                         }
68                 }
69
70                 return TRUE;
71         }
72
73         /* Directly East/West */
74         POSITION scanner_x;
75         if (!delta_y)
76         {
77                 /* East -- check for walls */
78                 if (delta_x > 0)
79                 {
80                         for (scanner_x = x1 + 1; scanner_x < x2; scanner_x++)
81                         {
82                                 if (cave_stop_disintegration(floor_ptr, y1, scanner_x)) return FALSE;
83                         }
84                 }
85
86                 /* West -- check for walls */
87                 else
88                 {
89                         for (scanner_x = x1 - 1; scanner_x > x2; scanner_x--)
90                         {
91                                 if (cave_stop_disintegration(floor_ptr, y1, scanner_x)) return FALSE;
92                         }
93                 }
94
95                 return TRUE;
96         }
97
98         POSITION sign_x = (delta_x < 0) ? -1 : 1;
99         POSITION sign_y = (delta_y < 0) ? -1 : 1;
100         if (absolute_x == 1)
101         {
102                 if (absolute_y == 2)
103                 {
104                         if (!cave_stop_disintegration(floor_ptr, y1 + sign_y, x1)) return TRUE;
105                 }
106         }
107         else if (absolute_y == 1)
108         {
109                 if (absolute_x == 2)
110                 {
111                         if (!cave_stop_disintegration(floor_ptr, y1, x1 + sign_x)) return TRUE;
112                 }
113         }
114
115         POSITION scale_factor_2 = (absolute_x * absolute_y);
116         POSITION scale_factor_1 = scale_factor_2 << 1;
117         POSITION fraction_y;
118         POSITION m; /* Slope, or 1/Slope, of LOS */
119         if (absolute_x >= absolute_y)
120         {
121                 fraction_y = absolute_y * absolute_y;
122                 m = fraction_y << 1;
123                 scanner_x = x1 + sign_x;
124                 if (fraction_y == scale_factor_2)
125                 {
126                         scanner_y = y1 + sign_y;
127                         fraction_y -= scale_factor_1;
128                 }
129                 else
130                 {
131                         scanner_y = y1;
132                 }
133
134                 /* Note (below) the case (qy == f2), where */
135                 /* the LOS exactly meets the corner of a tile. */
136                 while (x2 - scanner_x)
137                 {
138                         if (cave_stop_disintegration(floor_ptr, scanner_y, scanner_x)) return FALSE;
139
140                         fraction_y += m;
141
142                         if (fraction_y < scale_factor_2)
143                         {
144                                 scanner_x += sign_x;
145                         }
146                         else if (fraction_y > scale_factor_2)
147                         {
148                                 scanner_y += sign_y;
149                                 if (cave_stop_disintegration(floor_ptr, scanner_y, scanner_x)) return FALSE;
150                                 fraction_y -= scale_factor_1;
151                                 scanner_x += sign_x;
152                         }
153                         else
154                         {
155                                 scanner_y += sign_y;
156                                 fraction_y -= scale_factor_1;
157                                 scanner_x += sign_x;
158                         }
159                 }
160
161                 return TRUE;
162         }
163
164         POSITION fraction_x = absolute_x * absolute_x;
165         m = fraction_x << 1;
166         scanner_y = y1 + sign_y;
167         if (fraction_x == scale_factor_2)
168         {
169                 scanner_x = x1 + sign_x;
170                 fraction_x -= scale_factor_1;
171         }
172         else
173         {
174                 scanner_x = x1;
175         }
176
177         /* Note (below) the case (qx == f2), where */
178         /* the LOS exactly meets the corner of a tile. */
179         while (y2 - scanner_y)
180         {
181                 if (cave_stop_disintegration(floor_ptr, scanner_y, scanner_x)) return FALSE;
182
183                 fraction_x += m;
184
185                 if (fraction_x < scale_factor_2)
186                 {
187                         scanner_y += sign_y;
188                 }
189                 else if (fraction_x > scale_factor_2)
190                 {
191                         scanner_x += sign_x;
192                         if (cave_stop_disintegration(floor_ptr, scanner_y, scanner_x)) return FALSE;
193                         fraction_x -= scale_factor_1;
194                         scanner_y += sign_y;
195                 }
196                 else
197                 {
198                         scanner_x += sign_x;
199                         fraction_x -= scale_factor_1;
200                         scanner_y += sign_y;
201                 }
202         }
203
204         return TRUE;
205 }
206
207
208 /*
209  * breath shape
210  */
211 void breath_shape(player_type *caster_ptr, u16b *path_g, int dist, int *pgrids, POSITION *gx, POSITION *gy, POSITION *gm, POSITION *pgm_rad, POSITION rad, POSITION y1, POSITION x1, POSITION y2, POSITION x2, EFFECT_ID typ)
212 {
213         POSITION by = y1;
214         POSITION bx = x1;
215         int brad = 0;
216         int brev = rad * rad / dist;
217         int bdis = 0;
218         int cdis;
219         int path_n = 0;
220         int mdis = distance(y1, x1, y2, x2) + rad;
221
222         floor_type *floor_ptr = caster_ptr->current_floor_ptr;
223         while (bdis <= mdis)
224         {
225                 if ((0 < dist) && (path_n < dist))
226                 {
227                         POSITION ny = get_grid_y(path_g[path_n]);
228                         POSITION nx = get_grid_x(path_g[path_n]);
229                         POSITION nd = distance(ny, nx, y1, x1);
230
231                         if (bdis >= nd)
232                         {
233                                 by = ny;
234                                 bx = nx;
235                                 path_n++;
236                         }
237                 }
238
239                 /* Travel from center outward */
240                 for (cdis = 0; cdis <= brad; cdis++)
241                 {
242                         for (POSITION y = by - cdis; y <= by + cdis; y++)
243                         {
244                                 for (POSITION x = bx - cdis; x <= bx + cdis; x++)
245                                 {
246                                         if (!in_bounds(floor_ptr, y, x)) continue;
247                                         if (distance(y1, x1, y, x) != bdis) continue;
248                                         if (distance(by, bx, y, x) != cdis) continue;
249
250                                         switch (typ)
251                                         {
252                                         case GF_LITE:
253                                         case GF_LITE_WEAK:
254                                                 /* Lights are stopped by opaque terrains */
255                                                 if (!los(caster_ptr, by, bx, y, x)) continue;
256                                                 break;
257                                         case GF_DISINTEGRATE:
258                                                 /* Disintegration are stopped only by perma-walls */
259                                                 if (!in_disintegration_range(floor_ptr, by, bx, y, x)) continue;
260                                                 break;
261                                         default:
262                                                 /* Ball explosions are stopped by walls */
263                                                 if (!projectable(caster_ptr, by, bx, y, x)) continue;
264                                                 break;
265                                         }
266
267                                         gy[*pgrids] = y;
268                                         gx[*pgrids] = x;
269                                         (*pgrids)++;
270                                 }
271                         }
272                 }
273
274                 gm[bdis + 1] = *pgrids;
275                 brad = rad * (path_n + brev) / (dist + brev);
276                 bdis++;
277         }
278
279         *pgm_rad = bdis;
280 }