OSDN Git Service

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