OSDN Git Service

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