OSDN Git Service

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