OSDN Git Service

5982c66285434edec669671bf96d80d7f24402d9
[hengbandforosx/hengbandosx.git] / src / floor / object-allocator.cpp
1 #include "floor/object-allocator.h"
2 #include "dungeon/quest.h"
3 #include "floor/cave.h"
4 #include "floor/dungeon-tunnel-util.h"
5 #include "floor/floor-allocation-types.h"
6 #include "floor/floor-generator-util.h"
7 #include "game-option/birth-options.h"
8 #include "game-option/cheat-types.h"
9 #include "grid/feature.h"
10 #include "grid/grid.h"
11 #include "grid/object-placer.h"
12 #include "grid/trap.h"
13 #include "monster-race/monster-race.h"
14 #include "monster-race/race-flags1.h"
15 #include "system/dungeon-info.h"
16 #include "system/floor-type-definition.h"
17 #include "system/grid-type-definition.h"
18 #include "system/monster-race-definition.h"
19 #include "system/player-type-definition.h"
20 #include "system/terrain-type-definition.h"
21 #include "util/bit-flags-calculator.h"
22 #include "wizard/wizard-messages.h"
23
24 /*!
25  * @brief 上下左右の外壁数をカウントする / Count the number of walls adjacent to the given grid.
26  * @param y 基準のy座標
27  * @param x 基準のx座標
28  * @return 隣接する外壁の数
29  * @note Assumes "in_bounds()"
30  * @details We count only granite walls and permanent walls.
31  */
32 static int next_to_walls(floor_type *floor_ptr, POSITION y, POSITION x)
33 {
34     int k = 0;
35     if (in_bounds(floor_ptr, y + 1, x) && floor_ptr->grid_array[y + 1][x].is_extra()) {
36         k++;
37     }
38
39     if (in_bounds(floor_ptr, y - 1, x) && floor_ptr->grid_array[y - 1][x].is_extra()) {
40         k++;
41     }
42
43     if (in_bounds(floor_ptr, y, x + 1) && floor_ptr->grid_array[y][x + 1].is_extra()) {
44         k++;
45     }
46
47     if (in_bounds(floor_ptr, y, x - 1) && floor_ptr->grid_array[y][x - 1].is_extra()) {
48         k++;
49     }
50
51     return k;
52 }
53
54 /*!
55  * @brief alloc_stairs()の補助として指定の位置に階段を生成できるかの判定を行う / Helper function for alloc_stairs(). Is this a good location for stairs?
56  * @param player_ptr プレイヤーへの参照ポインタ
57  * @param y 基準のy座標
58  * @param x 基準のx座標
59  * @param walls 最低減隣接させたい外壁の数
60  * @return 階段を生成して問題がないならばTRUEを返す。
61  */
62 static bool alloc_stairs_aux(PlayerType *player_ptr, POSITION y, POSITION x, int walls)
63 {
64     auto *floor_ptr = player_ptr->current_floor_ptr;
65     auto *g_ptr = &floor_ptr->grid_array[y][x];
66     if (!g_ptr->is_floor() || pattern_tile(floor_ptr, y, x) || !g_ptr->o_idx_list.empty() || (g_ptr->m_idx != 0) || next_to_walls(floor_ptr, y, x) < walls) {
67         return false;
68     }
69
70     return true;
71 }
72
73 /*!
74  * @brief 外壁に隣接させて階段を生成する / Places some staircases near walls
75  * @param player_ptr プレイヤーへの参照ポインタ
76  * @param feat 配置したい地形ID
77  * @param num 配置したい階段の数
78  * @param walls 最低減隣接させたい外壁の数
79  * @return 規定数通りに生成に成功したらTRUEを返す。
80  */
81 bool alloc_stairs(PlayerType *player_ptr, FEAT_IDX feat, int num, int walls)
82 {
83     int shaft_num = 0;
84     auto *f_ptr = &terrains_info[feat];
85     auto *floor_ptr = player_ptr->current_floor_ptr;
86     if (f_ptr->flags.has(TerrainCharacteristics::LESS)) {
87         if (ironman_downward || !floor_ptr->dun_level) {
88             return true;
89         }
90
91         if (floor_ptr->dun_level > dungeons_info[floor_ptr->dungeon_idx].mindepth) {
92             shaft_num = (randint1(num + 1)) / 2;
93         }
94     } else if (f_ptr->flags.has(TerrainCharacteristics::MORE)) {
95         auto q_idx = quest_number(player_ptr, floor_ptr->dun_level);
96         const auto &quest_list = QuestList::get_instance();
97         if (floor_ptr->dun_level > 1 && inside_quest(q_idx)) {
98             auto *r_ptr = &monraces_info[quest_list[q_idx].r_idx];
99             if (r_ptr->kind_flags.has_not(MonsterKindType::UNIQUE) || 0 < r_ptr->max_num) {
100                 return true;
101             }
102         }
103
104         if (floor_ptr->dun_level >= dungeons_info[floor_ptr->dungeon_idx].maxdepth) {
105             return true;
106         }
107
108         if ((floor_ptr->dun_level < dungeons_info[floor_ptr->dungeon_idx].maxdepth - 1) && !inside_quest(quest_number(player_ptr, floor_ptr->dun_level + 1))) {
109             shaft_num = (randint1(num) + 1) / 2;
110         }
111     } else {
112         return false;
113     }
114
115     for (int i = 0; i < num; i++) {
116         while (true) {
117             grid_type *g_ptr;
118             int candidates = 0;
119             const POSITION max_x = floor_ptr->width - 1;
120             for (POSITION y = 1; y < floor_ptr->height - 1; y++) {
121                 for (POSITION x = 1; x < max_x; x++) {
122                     if (alloc_stairs_aux(player_ptr, y, x, walls)) {
123                         candidates++;
124                     }
125                 }
126             }
127
128             if (!candidates) {
129                 if (walls <= 0) {
130                     return false;
131                 }
132
133                 walls--;
134                 continue;
135             }
136
137             int pick = randint1(candidates);
138             POSITION y;
139             POSITION x = max_x;
140             for (y = 1; y < floor_ptr->height - 1; y++) {
141                 for (x = 1; x < floor_ptr->width - 1; x++) {
142                     if (alloc_stairs_aux(player_ptr, y, x, walls)) {
143                         pick--;
144                         if (pick == 0) {
145                             break;
146                         }
147                     }
148                 }
149
150                 if (pick == 0) {
151                     break;
152                 }
153             }
154
155             g_ptr = &floor_ptr->grid_array[y][x];
156             g_ptr->mimic = 0;
157             g_ptr->feat = (i < shaft_num) ? feat_state(player_ptr->current_floor_ptr, feat, TerrainCharacteristics::SHAFT) : feat;
158             g_ptr->info &= ~(CAVE_FLOOR);
159             break;
160         }
161     }
162
163     return true;
164 }
165
166 /*!
167  * @brief 指定座標に瓦礫を配置する
168  * @param Y 指定Y座標
169  * @param X 指定X座標
170  */
171 static void place_rubble(floor_type *floor_ptr, POSITION y, POSITION x)
172 {
173     set_cave_feat(floor_ptr, y, x, feat_rubble);
174 }
175
176 /*!
177  * @brief フロア上のランダム位置に各種オブジェクトを配置する / Allocates some objects (using "place" and "type")
178  * @param set 配置したい地形の種類
179  * @param typ 配置したいオブジェクトの種類
180  * @param num 配置したい数
181  * @return 規定数通りに生成に成功したらTRUEを返す。
182  */
183 void alloc_object(PlayerType *player_ptr, dap_type set, dungeon_allocation_type typ, int num)
184 {
185     POSITION y = 0;
186     POSITION x = 0;
187     int dummy = 0;
188     grid_type *g_ptr;
189     auto *floor_ptr = player_ptr->current_floor_ptr;
190     num = num * floor_ptr->height * floor_ptr->width / (MAX_HGT * MAX_WID) + 1;
191     for (int k = 0; k < num; k++) {
192         while (dummy < SAFE_MAX_ATTEMPTS) {
193             dummy++;
194             y = randint0(floor_ptr->height);
195             x = randint0(floor_ptr->width);
196             g_ptr = &floor_ptr->grid_array[y][x];
197             if (!g_ptr->is_floor() || !g_ptr->o_idx_list.empty() || g_ptr->m_idx) {
198                 continue;
199             }
200
201             if (player_bold(player_ptr, y, x)) {
202                 continue;
203             }
204
205             auto is_room = floor_ptr->grid_array[y][x].is_room();
206             if (((set == ALLOC_SET_CORR) && is_room) || ((set == ALLOC_SET_ROOM) && !is_room)) {
207                 continue;
208             }
209
210             break;
211         }
212
213         if (dummy >= SAFE_MAX_ATTEMPTS) {
214             msg_print_wizard(player_ptr, CHEAT_DUNGEON, _("アイテムの配置に失敗しました。", "Failed to place object."));
215             return;
216         }
217
218         switch (typ) {
219         case ALLOC_TYP_RUBBLE:
220             place_rubble(floor_ptr, y, x);
221             floor_ptr->grid_array[y][x].info &= ~(CAVE_FLOOR);
222             break;
223         case ALLOC_TYP_TRAP:
224             place_trap(player_ptr, y, x);
225             floor_ptr->grid_array[y][x].info &= ~(CAVE_FLOOR);
226             break;
227         case ALLOC_TYP_GOLD:
228             place_gold(player_ptr, y, x);
229             break;
230         case ALLOC_TYP_OBJECT:
231             place_object(player_ptr, y, x, 0L);
232             break;
233         default:
234             break;
235         }
236     }
237 }