OSDN Git Service

Merge remote-tracking branch 'remotes/origin/feature/Fix-Sanity-Blast-by-AutoSave...
[hengband/hengband.git] / src / room / space-finder.c
1 #include "room/space-finder.h"
2 #include "dungeon/dungeon-flag-types.h"
3 #include "dungeon/dungeon.h"
4 #include "floor/cave.h"
5 #include "grid/grid.h"
6 #include "system/dungeon-data-definition.h"
7 #include "system/floor-type-definition.h"
8
9 /*!
10  * @brief 指定のマスが床系地形であるかを返す / Function that sees if a square is a floor.  (Includes range checking.)
11  * @param x チェックするマスのX座標
12  * @param y チェックするマスのY座標
13  * @return 床系地形ならばTRUE
14  */
15 static bool get_is_floor(floor_type *floor_ptr, POSITION x, POSITION y)
16 {
17     if (!in_bounds(floor_ptr, y, x)) {
18         return FALSE;
19     }
20
21     if (is_floor_bold(floor_ptr, y, x))
22         return TRUE;
23
24     return FALSE;
25 }
26
27 /*!
28  * @brief 指定のマスを床地形に変える / Set a square to be floor.  (Includes range checking.)
29  * @param player_ptr プレーヤーへの参照ポインタ
30  * @param x 地形を変えたいマスのX座標
31  * @param y 地形を変えたいマスのY座標
32  * @return なし
33  */
34 static void set_floor(player_type *player_ptr, POSITION x, POSITION y)
35 {
36     floor_type *floor_ptr = player_ptr->current_floor_ptr;
37     if (!in_bounds(floor_ptr, y, x))
38         return;
39
40     if (floor_ptr->grid_array[y][x].info & CAVE_ROOM)
41         return;
42
43     if (is_extra_bold(floor_ptr, y, x))
44         place_bold(player_ptr, y, x, GB_FLOOR);
45 }
46
47 /*!
48  * @brief
49  * 指定範囲に通路が通っていることを確認した上で床で埋める
50  * This function tunnels around a room if it will cut off part of a grid system.
51  * @param player_ptr プレーヤーへの参照ポインタ
52  * @param x1 範囲の左端
53  * @param y1 範囲の上端
54  * @param x2 範囲の右端
55  * @param y2 範囲の下端
56  * @return なし
57  */
58 static void check_room_boundary(player_type *player_ptr, POSITION x1, POSITION y1, POSITION x2, POSITION y2)
59 {
60     bool old_is_floor;
61     bool new_is_floor;
62     int count = 0;
63
64     floor_type *floor_ptr = player_ptr->current_floor_ptr;
65     old_is_floor = get_is_floor(floor_ptr, x1 - 1, y1);
66
67     for (POSITION x = x1; x <= x2; x++) {
68         new_is_floor = get_is_floor(floor_ptr, x, y1 - 1);
69         if (new_is_floor != old_is_floor)
70             count++;
71
72         old_is_floor = new_is_floor;
73     }
74
75     for (POSITION y = y1; y <= y2; y++) {
76         new_is_floor = get_is_floor(floor_ptr, x2 + 1, y);
77         if (new_is_floor != old_is_floor)
78             count++;
79
80         old_is_floor = new_is_floor;
81     }
82
83     for (POSITION x = x2; x >= x1; x--) {
84         new_is_floor = get_is_floor(floor_ptr, x, y2 + 1);
85         if (new_is_floor != old_is_floor)
86             count++;
87
88         old_is_floor = new_is_floor;
89     }
90
91     for (POSITION y = y2; y >= y1; y--) {
92         new_is_floor = get_is_floor(floor_ptr, x1 - 1, y);
93         if (new_is_floor != old_is_floor)
94             count++;
95
96         old_is_floor = new_is_floor;
97     }
98
99     if (count <= 2)
100         return;
101
102     for (POSITION y = y1; y <= y2; y++)
103         for (POSITION x = x1; x <= x2; x++)
104             set_floor(player_ptr, x, y);
105 }
106
107 /*!
108  * @brief
109  * find_space()の予備処理として部屋の生成が可能かを判定する /
110  * Helper function for find_space(). Is this a good location?
111  * @param blocks_high 範囲の高さ
112  * @param blocks_wide 範囲の幅
113  * @param block_y 範囲の上端
114  * @param block_x 範囲の左端
115  * @return なし
116  */
117 static bool find_space_aux(dun_data_type *dd_ptr, POSITION blocks_high, POSITION blocks_wide, POSITION block_y, POSITION block_x)
118 {
119     if (blocks_wide < 3) {
120         if ((blocks_wide == 2) && (block_x % 3) == 2)
121             return FALSE;
122     } else if ((blocks_wide % 3) == 0) {
123         if ((block_x % 3) != 0)
124             return FALSE;
125     } else {
126         if (block_x + (blocks_wide / 2) <= dd_ptr->col_rooms / 2) {
127             if (((block_x % 3) == 2) && ((blocks_wide % 3) == 2))
128                 return FALSE;
129             if ((block_x % 3) == 1)
130                 return FALSE;
131         } else {
132             if (((block_x % 3) == 2) && ((blocks_wide % 3) == 2))
133                 return FALSE;
134             if ((block_x % 3) == 1)
135                 return FALSE;
136         }
137     }
138
139     POSITION by1 = block_y;
140     POSITION bx1 = block_x;
141     POSITION by2 = block_y + blocks_high;
142     POSITION bx2 = block_x + blocks_wide;
143
144     if ((by1 < 0) || (by2 > dd_ptr->row_rooms) || (bx1 < 0) || (bx2 > dd_ptr->col_rooms))
145         return FALSE;
146
147     for (POSITION by = by1; by < by2; by++)
148         for (POSITION bx = bx1; bx < bx2; bx++)
149             if (dd_ptr->room_map[by][bx])
150                 return FALSE;
151
152     return TRUE;
153 }
154
155 /*!
156  * @brief 部屋生成が可能なスペースを確保する / Find a good spot for the next room.  -LM-
157  * @param player_ptr プレーヤーへの参照ポインタ
158  * @param y 部屋の生成が可能な中心Y座標を返す参照ポインタ
159  * @param x 部屋の生成が可能な中心X座標を返す参照ポインタ
160  * @param height 確保したい領域の高さ
161  * @param width 確保したい領域の幅
162  * @return 所定の範囲が確保できた場合TRUEを返す
163  * @details
164  * Find and allocate a free space in the dungeon large enough to hold\n
165  * the room calling this function.\n
166  *\n
167  * We allocate space in 11x11 blocks, but want to make sure that rooms\n
168  * align neatly on the standard screen.  Therefore, we make them use\n
169  * blocks in few 11x33 rectangles as possible.\n
170  *\n
171  * Be careful to include the edges of the room in height and width!\n
172  *\n
173  * Return TRUE and values for the center of the room if all went well.\n
174  * Otherwise, return FALSE.\n
175  */
176 bool find_space(player_type *player_ptr, dun_data_type *dd_ptr, POSITION *y, POSITION *x, POSITION height, POSITION width)
177 {
178     int pick;
179     POSITION block_y = 0;
180     POSITION block_x = 0;
181     POSITION blocks_high = 1 + ((height - 1) / BLOCK_HGT);
182     POSITION blocks_wide = 1 + ((width - 1) / BLOCK_WID);
183     if ((dd_ptr->row_rooms < blocks_high) || (dd_ptr->col_rooms < blocks_wide))
184         return FALSE;
185
186     int candidates = 0;
187     for (block_y = dd_ptr->row_rooms - blocks_high; block_y >= 0; block_y--) {
188         for (block_x = dd_ptr->col_rooms - blocks_wide; block_x >= 0; block_x--) {
189             if (find_space_aux(dd_ptr, blocks_high, blocks_wide, block_y, block_x)) {
190                 /* Find a valid place */
191                 candidates++;
192             }
193         }
194     }
195
196     if (!candidates)
197         return FALSE;
198
199     if (!(d_info[player_ptr->current_floor_ptr->dungeon_idx].flags1 & DF1_NO_CAVE))
200         pick = randint1(candidates);
201     else
202         pick = candidates / 2 + 1;
203
204     for (block_y = dd_ptr->row_rooms - blocks_high; block_y >= 0; block_y--) {
205         for (block_x = dd_ptr->col_rooms - blocks_wide; block_x >= 0; block_x--) {
206             if (find_space_aux(dd_ptr, blocks_high, blocks_wide, block_y, block_x)) {
207                 pick--;
208                 if (!pick)
209                     break;
210             }
211         }
212
213         if (!pick)
214             break;
215     }
216
217     POSITION by1 = block_y;
218     POSITION bx1 = block_x;
219     POSITION by2 = block_y + blocks_high;
220     POSITION bx2 = block_x + blocks_wide;
221     *y = ((by1 + by2) * BLOCK_HGT) / 2;
222     *x = ((bx1 + bx2) * BLOCK_WID) / 2;
223     if (dd_ptr->cent_n < CENT_MAX) {
224         dd_ptr->cent[dd_ptr->cent_n].y = (byte)*y;
225         dd_ptr->cent[dd_ptr->cent_n].x = (byte)*x;
226         dd_ptr->cent_n++;
227     }
228
229     for (POSITION by = by1; by < by2; by++)
230         for (POSITION bx = bx1; bx < bx2; bx++)
231             dd_ptr->room_map[by][bx] = TRUE;
232
233     check_room_boundary(player_ptr, *x - width / 2 - 1, *y - height / 2 - 1, *x + (width - 1) / 2 + 1, *y + (height - 1) / 2 + 1);
234     return TRUE;
235 }