OSDN Git Service

[Refactor] #3677 grid_type 構造体をGrid クラスへ改名した
[hengbandforosx/hengbandosx.git] / src / floor / tunnel-generator.cpp
1 #include "floor/tunnel-generator.h"
2 #include "floor/cave.h"
3 #include "floor/dungeon-tunnel-util.h"
4 #include "floor/geometry.h"
5 #include "grid/grid.h"
6 #include "system/dungeon-data-definition.h"
7 #include "system/floor-type-definition.h"
8 #include "system/grid-type-definition.h"
9 #include "system/player-type-definition.h"
10
11 /*!
12  * @brief build_tunnel用に通路を掘るための方向をランダムに決める / Pick a random direction
13  * @param rdir Y方向に取るべきベクトル値を返す参照ポインタ
14  * @param cdir X方向に取るべきベクトル値を返す参照ポインタ
15  */
16 static void rand_dir(POSITION *rdir, POSITION *cdir)
17 {
18     int i = randint0(4);
19     *rdir = ddy_ddd[i];
20     *cdir = ddx_ddd[i];
21 }
22
23 /*!
24  * @brief build_tunnel用に通路を掘るための方向を位置関係通りに決める / Always picks a correct direction
25  * @param rdir Y方向に取るべきベクトル値を返す参照ポインタ
26  * @param cdir X方向に取るべきベクトル値を返す参照ポインタ
27  * @param y1 始点Y座標
28  * @param x1 始点X座標
29  * @param y2 終点Y座標
30  * @param x2 終点X座標
31  */
32 static void correct_dir(POSITION *rdir, POSITION *cdir, POSITION y1, POSITION x1, POSITION y2, POSITION x2)
33 {
34     *rdir = (y1 == y2) ? 0 : (y1 < y2) ? 1
35                                        : -1;
36     *cdir = (x1 == x2) ? 0 : (x1 < x2) ? 1
37                                        : -1;
38     if (*rdir && *cdir) {
39         if (randint0(100) < 50) {
40             *rdir = 0;
41         } else {
42             *cdir = 0;
43         }
44     }
45 }
46
47 /*!
48  * @brief 部屋間のトンネルを生成する / Constructs a tunnel between two points
49  * @param player_ptr プレイヤーへの参照ポインタ
50  * @param row1 始点Y座標
51  * @param col1 始点X座標
52  * @param row2 終点Y座標
53  * @param col2 終点X座標
54  * @return 生成に成功したらTRUEを返す
55  */
56 bool build_tunnel(PlayerType *player_ptr, dun_data_type *dd_ptr, dt_type *dt_ptr, POSITION row1, POSITION col1, POSITION row2, POSITION col2)
57 {
58     POSITION tmp_row, tmp_col;
59     POSITION row_dir, col_dir;
60     POSITION start_row, start_col;
61     int main_loop_count = 0;
62     bool door_flag = false;
63     start_row = row1;
64     start_col = col1;
65     correct_dir(&row_dir, &col_dir, row1, col1, row2, col2);
66     auto *floor_ptr = player_ptr->current_floor_ptr;
67     while ((row1 != row2) || (col1 != col2)) {
68         if (main_loop_count++ > 2000) {
69             return false;
70         }
71
72         if (randint0(100) < dt_ptr->dun_tun_chg) {
73             correct_dir(&row_dir, &col_dir, row1, col1, row2, col2);
74             if (randint0(100) < dt_ptr->dun_tun_rnd) {
75                 rand_dir(&row_dir, &col_dir);
76             }
77         }
78
79         tmp_row = row1 + row_dir;
80         tmp_col = col1 + col_dir;
81         while (!in_bounds(floor_ptr, tmp_row, tmp_col)) {
82             correct_dir(&row_dir, &col_dir, row1, col1, row2, col2);
83             if (randint0(100) < dt_ptr->dun_tun_rnd) {
84                 rand_dir(&row_dir, &col_dir);
85             }
86
87             tmp_row = row1 + row_dir;
88             tmp_col = col1 + col_dir;
89         }
90
91         auto *tmp_g_ptr = &floor_ptr->grid_array[tmp_row][tmp_col];
92         if (tmp_g_ptr->is_solid()) {
93             continue;
94         }
95
96         if (tmp_g_ptr->is_outer()) {
97             POSITION y = tmp_row + row_dir;
98             POSITION x = tmp_col + col_dir;
99             auto *g_ptr = &floor_ptr->grid_array[y][x];
100             if (g_ptr->is_outer() || g_ptr->is_solid()) {
101                 continue;
102             }
103
104             row1 = tmp_row;
105             col1 = tmp_col;
106             if (dd_ptr->wall_n >= WALL_MAX) {
107                 return false;
108             }
109
110             dd_ptr->wall[dd_ptr->wall_n].y = row1;
111             dd_ptr->wall[dd_ptr->wall_n].x = col1;
112             dd_ptr->wall_n++;
113             for (y = row1 - 1; y <= row1 + 1; y++) {
114                 for (x = col1 - 1; x <= col1 + 1; x++) {
115                     if (floor_ptr->grid_array[y][x].is_outer()) {
116                         place_bold(player_ptr, y, x, GB_SOLID_NOPERM);
117                     }
118                 }
119             }
120
121         } else if (tmp_g_ptr->info & (CAVE_ROOM)) {
122             row1 = tmp_row;
123             col1 = tmp_col;
124         } else if (tmp_g_ptr->is_extra() || tmp_g_ptr->is_inner() || tmp_g_ptr->is_solid()) {
125             row1 = tmp_row;
126             col1 = tmp_col;
127             if (dd_ptr->tunn_n >= TUNN_MAX) {
128                 return false;
129             }
130
131             dd_ptr->tunn[dd_ptr->tunn_n].y = row1;
132             dd_ptr->tunn[dd_ptr->tunn_n].x = col1;
133             dd_ptr->tunn_n++;
134             door_flag = false;
135         } else {
136             row1 = tmp_row;
137             col1 = tmp_col;
138             if (!door_flag) {
139                 if (dd_ptr->door_n >= DOOR_MAX) {
140                     return false;
141                 }
142
143                 dd_ptr->door[dd_ptr->door_n].y = row1;
144                 dd_ptr->door[dd_ptr->door_n].x = col1;
145                 dd_ptr->door_n++;
146                 door_flag = true;
147             }
148
149             if (randint0(100) >= dt_ptr->dun_tun_con) {
150                 tmp_row = row1 - start_row;
151                 if (tmp_row < 0) {
152                     tmp_row = (-tmp_row);
153                 }
154
155                 tmp_col = col1 - start_col;
156                 if (tmp_col < 0) {
157                     tmp_col = (-tmp_col);
158                 }
159
160                 if ((tmp_row > 10) || (tmp_col > 10)) {
161                     break;
162                 }
163             }
164         }
165     }
166
167     return true;
168 }
169
170 /*!
171  * @brief トンネル生成のための基準点を指定する。
172  * @param player_ptr プレイヤーへの参照ポインタ
173  * @param x 基準点を指定するX座標の参照ポインタ、適時値が修正される。
174  * @param y 基準点を指定するY座標の参照ポインタ、適時値が修正される。
175  * @param affectwall (調査中)
176  * @todo 特に詳細な処理の意味を調査すべし
177  */
178 static bool set_tunnel(PlayerType *player_ptr, dun_data_type *dd_ptr, POSITION *x, POSITION *y, bool affectwall)
179 {
180     auto *floor_ptr = player_ptr->current_floor_ptr;
181     auto *g_ptr = &floor_ptr->grid_array[*y][*x];
182     if (!in_bounds(floor_ptr, *y, *x) || g_ptr->is_inner()) {
183         return true;
184     }
185
186     if (g_ptr->is_extra()) {
187         if (dd_ptr->tunn_n >= TUNN_MAX) {
188             return false;
189         }
190
191         dd_ptr->tunn[dd_ptr->tunn_n].y = *y;
192         dd_ptr->tunn[dd_ptr->tunn_n].x = *x;
193         dd_ptr->tunn_n++;
194         return true;
195     }
196
197     if (g_ptr->is_floor()) {
198         return true;
199     }
200
201     if (g_ptr->is_outer() && affectwall) {
202         if (dd_ptr->wall_n >= WALL_MAX) {
203             return false;
204         }
205
206         dd_ptr->wall[dd_ptr->wall_n].y = *y;
207         dd_ptr->wall[dd_ptr->wall_n].x = *x;
208         dd_ptr->wall_n++;
209         for (int j = *y - 1; j <= *y + 1; j++) {
210             for (int i = *x - 1; i <= *x + 1; i++) {
211                 if (floor_ptr->grid_array[j][i].is_outer()) {
212                     place_bold(player_ptr, j, i, GB_SOLID_NOPERM);
213                 }
214             }
215         }
216
217         floor_ptr->grid_array[*y][*x].mimic = 0;
218         place_bold(player_ptr, *y, *x, GB_FLOOR);
219         return true;
220     }
221
222     if (g_ptr->is_solid() && affectwall) {
223         int i = 50;
224         int dy = 0;
225         int dx = 0;
226         while ((i > 0) && floor_ptr->grid_array[*y + dy][*x + dx].is_solid()) {
227             dy = randint0(3) - 1;
228             dx = randint0(3) - 1;
229             if (!in_bounds(floor_ptr, *y + dy, *x + dx)) {
230                 dx = 0;
231                 dy = 0;
232             }
233
234             i--;
235         }
236
237         if (i == 0) {
238             place_grid(player_ptr, g_ptr, GB_OUTER);
239             dx = 0;
240             dy = 0;
241         }
242
243         *x = *x + dx;
244         *y = *y + dy;
245         return false;
246     }
247
248     return true;
249 }
250
251 /*!
252  * @brief 外壁を削って「カタコンベ状」の通路を作成する / This routine creates the catacomb-like tunnels by removing extra rock.
253  * @param player_ptr プレイヤーへの参照ポインタ
254  * @param x 基準点のX座標
255  * @param y 基準点のY座標
256  */
257 static void create_cata_tunnel(PlayerType *player_ptr, dun_data_type *dd_ptr, POSITION x, POSITION y)
258 {
259     POSITION x1 = x - 1;
260     POSITION y1 = y;
261     set_tunnel(player_ptr, dd_ptr, &x1, &y1, false);
262
263     x1 = x + 1;
264     y1 = y;
265     set_tunnel(player_ptr, dd_ptr, &x1, &y1, false);
266
267     x1 = x;
268     y1 = y - 1;
269     set_tunnel(player_ptr, dd_ptr, &x1, &y1, false);
270
271     x1 = x;
272     y1 = y + 1;
273     set_tunnel(player_ptr, dd_ptr, &x1, &y1, false);
274 }
275
276 /*!
277  * @brief トンネル生成処理(詳細調査中)/ This routine does the bulk of the work in creating the new types of tunnels.
278  * @param player_ptr プレイヤーへの参照ポインタ
279  * @todo 詳細用調査
280  */
281 static void short_seg_hack(
282     PlayerType *player_ptr, dun_data_type *dd_ptr, const POSITION x1, const POSITION y1, const POSITION x2, const POSITION y2, int type, int count, bool *fail)
283 {
284     if (!(*fail)) {
285         return;
286     }
287
288     int length = distance(x1, y1, x2, y2);
289     count++;
290     POSITION x, y;
291     if ((type == 1) && (length != 0)) {
292         for (int i = 0; i <= length; i++) {
293             x = x1 + i * (x2 - x1) / length;
294             y = y1 + i * (y2 - y1) / length;
295             if (!set_tunnel(player_ptr, dd_ptr, &x, &y, true)) {
296                 if (count > 50) {
297                     *fail = false;
298                     return;
299                 }
300
301                 short_seg_hack(player_ptr, dd_ptr, x, y, x1 + (i - 1) * (x2 - x1) / length, y1 + (i - 1) * (y2 - y1) / length, 1, count, fail);
302                 short_seg_hack(player_ptr, dd_ptr, x, y, x1 + (i + 1) * (x2 - x1) / length, y1 + (i + 1) * (y2 - y1) / length, 1, count, fail);
303             }
304         }
305
306         return;
307     }
308
309     if ((type != 2) && (type != 3)) {
310         return;
311     }
312
313     if (x1 < x2) {
314         for (int i = x1; i <= x2; i++) {
315             x = i;
316             y = y1;
317             if (!set_tunnel(player_ptr, dd_ptr, &x, &y, true)) {
318                 short_seg_hack(player_ptr, dd_ptr, x, y, i - 1, y1, 1, count, fail);
319                 short_seg_hack(player_ptr, dd_ptr, x, y, i + 1, y1, 1, count, fail);
320             }
321
322             if ((type == 3) && ((x + y) % 2)) {
323                 create_cata_tunnel(player_ptr, dd_ptr, i, y1);
324             }
325         }
326     } else {
327         for (int i = x2; i <= x1; i++) {
328             x = i;
329             y = y1;
330             if (!set_tunnel(player_ptr, dd_ptr, &x, &y, true)) {
331                 short_seg_hack(player_ptr, dd_ptr, x, y, i - 1, y1, 1, count, fail);
332                 short_seg_hack(player_ptr, dd_ptr, x, y, i + 1, y1, 1, count, fail);
333             }
334
335             if ((type == 3) && ((x + y) % 2)) {
336                 create_cata_tunnel(player_ptr, dd_ptr, i, y1);
337             }
338         }
339     }
340
341     if (y1 < y2) {
342         for (int i = y1; i <= y2; i++) {
343             x = x2;
344             y = i;
345             if (!set_tunnel(player_ptr, dd_ptr, &x, &y, true)) {
346                 short_seg_hack(player_ptr, dd_ptr, x, y, x2, i - 1, 1, count, fail);
347                 short_seg_hack(player_ptr, dd_ptr, x, y, x2, i + 1, 1, count, fail);
348             }
349
350             if ((type == 3) && ((x + y) % 2)) {
351                 create_cata_tunnel(player_ptr, dd_ptr, x2, i);
352             }
353         }
354     } else {
355         for (int i = y2; i <= y1; i++) {
356             x = x2;
357             y = i;
358             if (!set_tunnel(player_ptr, dd_ptr, &x, &y, true)) {
359                 short_seg_hack(player_ptr, dd_ptr, x, y, x2, i - 1, 1, count, fail);
360                 short_seg_hack(player_ptr, dd_ptr, x, y, x2, i + 1, 1, count, fail);
361             }
362
363             if ((type == 3) && ((x + y) % 2)) {
364                 create_cata_tunnel(player_ptr, dd_ptr, x2, i);
365             }
366         }
367     }
368 }
369
370 /*!
371  * @brief 特定の壁(永久壁など)を避けながら部屋間の通路を作成する / This routine maps a path from (x1, y1) to (x2, y2) avoiding SOLID walls.
372  * @todo 詳細要調査
373  */
374 bool build_tunnel2(PlayerType *player_ptr, dun_data_type *dd_ptr, POSITION x1, POSITION y1, POSITION x2, POSITION y2, int type, int cutoff)
375 {
376     POSITION x3, y3, dx, dy;
377     POSITION changex, changey;
378     bool retval, firstsuccede;
379     Grid *g_ptr;
380
381     int length = distance(x1, y1, x2, y2);
382     auto *floor_ptr = player_ptr->current_floor_ptr;
383     if (length <= cutoff) {
384         retval = true;
385         short_seg_hack(player_ptr, dd_ptr, x1, y1, x2, y2, type, 0, &retval);
386         return true;
387     }
388
389     dx = (x2 - x1) / 2;
390     dy = (y2 - y1) / 2;
391     changex = (randint0(abs(dy) + 2) * 2 - abs(dy) - 1) / 2;
392     changey = (randint0(abs(dx) + 2) * 2 - abs(dx) - 1) / 2;
393     x3 = x1 + dx + changex;
394     y3 = y1 + dy + changey;
395     if (!in_bounds(floor_ptr, y3, x3)) {
396         x3 = (x1 + x2) / 2;
397         y3 = (y1 + y2) / 2;
398     }
399
400     g_ptr = &floor_ptr->grid_array[y3][x3];
401     if (g_ptr->is_solid()) {
402         int i = 50;
403         dy = 0;
404         dx = 0;
405         while ((i > 0) && floor_ptr->grid_array[y3 + dy][x3 + dx].is_solid()) {
406             dy = randint0(3) - 1;
407             dx = randint0(3) - 1;
408             if (!in_bounds(floor_ptr, y3 + dy, x3 + dx)) {
409                 dx = 0;
410                 dy = 0;
411             }
412             i--;
413         }
414
415         if (i == 0) {
416             place_bold(player_ptr, y3, x3, GB_OUTER);
417             dx = 0;
418             dy = 0;
419         }
420
421         y3 += dy;
422         x3 += dx;
423         g_ptr = &floor_ptr->grid_array[y3][x3];
424     }
425
426     if (g_ptr->is_floor()) {
427         if (build_tunnel2(player_ptr, dd_ptr, x1, y1, x3, y3, type, cutoff)) {
428             if (floor_ptr->grid_array[y3][x3].is_room() || (randint1(100) > 95)) {
429                 retval = build_tunnel2(player_ptr, dd_ptr, x3, y3, x2, y2, type, cutoff);
430             } else {
431                 retval = false;
432                 if (dd_ptr->door_n >= DOOR_MAX) {
433                     return false;
434                 }
435
436                 dd_ptr->door[dd_ptr->door_n].y = y3;
437                 dd_ptr->door[dd_ptr->door_n].x = x3;
438                 dd_ptr->door_n++;
439             }
440
441             firstsuccede = true;
442         } else {
443             retval = false;
444             firstsuccede = false;
445         }
446     } else {
447         if (build_tunnel2(player_ptr, dd_ptr, x1, y1, x3, y3, type, cutoff)) {
448             retval = build_tunnel2(player_ptr, dd_ptr, x3, y3, x2, y2, type, cutoff);
449             firstsuccede = true;
450         } else {
451             retval = false;
452             firstsuccede = false;
453         }
454     }
455
456     if (firstsuccede) {
457         set_tunnel(player_ptr, dd_ptr, &x3, &y3, true);
458     }
459
460     return retval;
461 }