OSDN Git Service

Merge pull request #3569 from sikabane-works/release/3.0.0.88-alpha
[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-info.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(FloorType *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 = *player_ptr->current_floor_ptr;
86     const auto &dungeon = floor.get_dungeon_definition();
87     if (f_ptr->flags.has(TerrainCharacteristics::LESS)) {
88         if (ironman_downward || !floor.dun_level) {
89             return true;
90         }
91
92         if (floor.dun_level > dungeon.mindepth) {
93             shaft_num = (randint1(num + 1)) / 2;
94         }
95     } else if (f_ptr->flags.has(TerrainCharacteristics::MORE)) {
96         auto q_idx = quest_number(floor, floor.dun_level);
97         const auto &quest_list = QuestList::get_instance();
98         if (floor.dun_level > 1 && inside_quest(q_idx)) {
99             auto *r_ptr = &monraces_info[quest_list[q_idx].r_idx];
100             if (r_ptr->kind_flags.has_not(MonsterKindType::UNIQUE) || 0 < r_ptr->max_num) {
101                 return true;
102             }
103         }
104
105         if (floor.dun_level >= dungeon.maxdepth) {
106             return true;
107         }
108
109         if ((floor.dun_level < dungeon.maxdepth - 1) && !inside_quest(quest_number(floor, floor.dun_level + 1))) {
110             shaft_num = (randint1(num) + 1) / 2;
111         }
112     } else {
113         return false;
114     }
115
116     for (int i = 0; i < num; i++) {
117         while (true) {
118             grid_type *g_ptr;
119             int candidates = 0;
120             const POSITION max_x = floor.width - 1;
121             for (POSITION y = 1; y < floor.height - 1; y++) {
122                 for (POSITION x = 1; x < max_x; x++) {
123                     if (alloc_stairs_aux(player_ptr, y, x, walls)) {
124                         candidates++;
125                     }
126                 }
127             }
128
129             if (!candidates) {
130                 if (walls <= 0) {
131                     return false;
132                 }
133
134                 walls--;
135                 continue;
136             }
137
138             int pick = randint1(candidates);
139             POSITION y;
140             POSITION x = max_x;
141             for (y = 1; y < floor.height - 1; y++) {
142                 for (x = 1; x < floor.width - 1; x++) {
143                     if (alloc_stairs_aux(player_ptr, y, x, walls)) {
144                         pick--;
145                         if (pick == 0) {
146                             break;
147                         }
148                     }
149                 }
150
151                 if (pick == 0) {
152                     break;
153                 }
154             }
155
156             g_ptr = &floor.grid_array[y][x];
157             g_ptr->mimic = 0;
158             g_ptr->feat = (i < shaft_num) ? feat_state(&floor, feat, TerrainCharacteristics::SHAFT) : feat;
159             g_ptr->info &= ~(CAVE_FLOOR);
160             break;
161         }
162     }
163
164     return true;
165 }
166
167 /*!
168  * @brief 指定座標に瓦礫を配置する
169  * @param Y 指定Y座標
170  * @param X 指定X座標
171  */
172 static void place_rubble(FloorType *floor_ptr, POSITION y, POSITION x)
173 {
174     set_cave_feat(floor_ptr, y, x, feat_rubble);
175 }
176
177 /*!
178  * @brief フロア上のランダム位置に各種オブジェクトを配置する / Allocates some objects (using "place" and "type")
179  * @param set 配置したい地形の種類
180  * @param typ 配置したいオブジェクトの種類
181  * @param num 配置したい数
182  * @return 規定数通りに生成に成功したらTRUEを返す。
183  */
184 void alloc_object(PlayerType *player_ptr, dap_type set, dungeon_allocation_type typ, int num)
185 {
186     POSITION y = 0;
187     POSITION x = 0;
188     int dummy = 0;
189     grid_type *g_ptr;
190     auto *floor_ptr = player_ptr->current_floor_ptr;
191     num = num * floor_ptr->height * floor_ptr->width / (MAX_HGT * MAX_WID) + 1;
192     for (int k = 0; k < num; k++) {
193         while (dummy < SAFE_MAX_ATTEMPTS) {
194             dummy++;
195             y = randint0(floor_ptr->height);
196             x = randint0(floor_ptr->width);
197             g_ptr = &floor_ptr->grid_array[y][x];
198             if (!g_ptr->is_floor() || !g_ptr->o_idx_list.empty() || g_ptr->m_idx) {
199                 continue;
200             }
201
202             if (player_bold(player_ptr, y, x)) {
203                 continue;
204             }
205
206             auto is_room = floor_ptr->grid_array[y][x].is_room();
207             if (((set == ALLOC_SET_CORR) && is_room) || ((set == ALLOC_SET_ROOM) && !is_room)) {
208                 continue;
209             }
210
211             break;
212         }
213
214         if (dummy >= SAFE_MAX_ATTEMPTS) {
215             msg_print_wizard(player_ptr, CHEAT_DUNGEON, _("アイテムの配置に失敗しました。", "Failed to place object."));
216             return;
217         }
218
219         switch (typ) {
220         case ALLOC_TYP_RUBBLE:
221             place_rubble(floor_ptr, y, x);
222             floor_ptr->grid_array[y][x].info &= ~(CAVE_FLOOR);
223             break;
224         case ALLOC_TYP_TRAP:
225             place_trap(player_ptr, y, x);
226             floor_ptr->grid_array[y][x].info &= ~(CAVE_FLOOR);
227             break;
228         case ALLOC_TYP_GOLD:
229             place_gold(player_ptr, y, x);
230             break;
231         case ALLOC_TYP_OBJECT:
232             place_object(player_ptr, y, x, 0L);
233             break;
234         default:
235             break;
236         }
237     }
238 }