OSDN Git Service

Merge branch 'master' of https://github.com/hengband/hengband
[hengbandforosx/hengbandosx.git] / src / room / room-generator.cpp
1 #include "room/room-generator.h"
2 #include "dungeon/dungeon-flag-types.h"
3 #include "game-option/birth-options.h"
4 #include "game-option/cheat-types.h"
5 #include "room/door-definition.h"
6 #include "room/room-info-table.h"
7 #include "room/room-types.h"
8 #include "room/rooms-city.h"
9 #include "room/rooms-fractal.h"
10 #include "room/rooms-normal.h"
11 #include "room/rooms-pit-nest.h"
12 #include "room/rooms-special.h"
13 #include "room/rooms-trap.h"
14 #include "room/rooms-vault.h"
15 #include "system/dungeon-data-definition.h"
16 #include "system/dungeon-info.h"
17 #include "system/floor-type-definition.h"
18 #include "system/grid-type-definition.h"
19 #include "system/player-type-definition.h"
20 #include "util/probability-table.h"
21 #include "wizard/wizard-messages.h"
22
23 /*!
24  * @brief 与えられた部屋型IDに応じて部屋の生成処理分岐を行い結果を返す / Attempt to build a room of the given type at the given block
25  * @param player_ptr プレイヤーへの参照ポインタ
26  * @param type 部屋型ID
27  * @note that we restrict the number of "crowded" rooms to reduce the chance of overflowing the monster list during level creation.
28  * @return 部屋の生成に成功した場合 TRUE を返す。
29  */
30 static bool room_build(PlayerType *player_ptr, dun_data_type *dd_ptr, RoomType typ)
31 {
32     switch (typ) {
33     case RoomType::NORMAL:
34         return build_type1(player_ptr, dd_ptr);
35     case RoomType::OVERLAP:
36         return build_type2(player_ptr, dd_ptr);
37     case RoomType::CROSS:
38         return build_type3(player_ptr, dd_ptr);
39     case RoomType::INNER_FEAT:
40         return build_type4(player_ptr, dd_ptr);
41     case RoomType::NEST:
42         return build_type5(player_ptr, dd_ptr);
43     case RoomType::PIT:
44         return build_type6(player_ptr, dd_ptr);
45     case RoomType::LESSER_VAULT:
46         return build_fixed_room(player_ptr, dd_ptr, 7, false);
47     case RoomType::GREATER_VAULT:
48         return build_fixed_room(player_ptr, dd_ptr, 8, true);
49     case RoomType::FRACAVE:
50         return build_type9(player_ptr, dd_ptr);
51     case RoomType::RANDOM_VAULT:
52         return build_type10(player_ptr, dd_ptr);
53     case RoomType::OVAL:
54         return build_type11(player_ptr, dd_ptr);
55     case RoomType::CRYPT:
56         return build_type12(player_ptr, dd_ptr);
57     case RoomType::TRAP_PIT:
58         return build_type13(player_ptr, dd_ptr);
59     case RoomType::TRAP:
60         return build_type14(player_ptr, dd_ptr);
61     case RoomType::GLASS:
62         return build_type15(player_ptr, dd_ptr);
63     case RoomType::ARCADE:
64         return build_type16(player_ptr, dd_ptr);
65     case RoomType::FIXED:
66         return build_fixed_room(player_ptr, dd_ptr, 17, false);
67     default:
68         return false;
69     }
70 }
71
72 /*!
73  * @brief 指定した部屋の生成確率を別の部屋に加算し、指定した部屋の生成率を0にする
74  * @param dst 確率を移す先の部屋種ID
75  * @param src 確率を与える元の部屋種ID
76  */
77 static void move_prob_list(RoomType dst, RoomType src, std::map<RoomType, int> &prob_list)
78 {
79     prob_list[dst] += prob_list[src];
80     prob_list[src] = 0;
81 }
82
83 /*!
84  * @brief 部屋生成処理のメインルーチン(Sangbandを経由してOangbandからの実装を引用) / Generate rooms in dungeon.  Build bigger rooms at first. [from SAngband
85  * (originally from OAngband)]
86  * @param player_ptr プレイヤーへの参照ポインタ
87  * @return 部屋生成に成功した場合 TRUE を返す。
88  */
89 bool generate_rooms(PlayerType *player_ptr, dun_data_type *dd_ptr)
90 {
91     auto *floor_ptr = player_ptr->current_floor_ptr;
92     int crowded = 0;
93     std::map<RoomType, int> prob_list;
94     int rooms_built = 0;
95     int area_size = 100 * (floor_ptr->height * floor_ptr->width) / (MAX_HGT * MAX_WID);
96     int level_index = std::min(10, div_round(floor_ptr->dun_level, 10));
97     std::map<RoomType, int> room_num;
98     int dun_rooms = DUN_ROOMS_MAX * area_size / 100;
99     room_info_type *room_info_ptr = room_info_normal;
100     for (auto r : ROOM_TYPE_LIST) {
101         if (floor_ptr->dun_level < room_info_ptr[enum2i(r)].min_level) {
102             prob_list[r] = 0;
103         } else {
104             prob_list[r] = room_info_ptr[enum2i(r)].prob[level_index];
105         }
106     }
107
108     /*!
109      * @details ダンジョンにBEGINNER、CHAMELEON、SMALLESTいずれのフラグもなく、
110      * かつ「常に通常でない部屋を生成する」フラグがONならば、
111      * GRATER_VAULTのみを生成対象とする。 / Ironman sees only Greater Vaults
112      */
113     const auto &dungeon = floor_ptr->get_dungeon_definition();
114     if (ironman_rooms && dungeon.flags.has_none_of({ DungeonFeatureType::BEGINNER, DungeonFeatureType::CHAMELEON, DungeonFeatureType::SMALLEST })) {
115         for (auto r : ROOM_TYPE_LIST) {
116             if (r == RoomType::GREATER_VAULT) {
117                 prob_list[r] = 1;
118             } else {
119                 prob_list[r] = 0;
120             }
121         }
122     } else if (dungeon.flags.has(DungeonFeatureType::NO_VAULT)) {
123         /*! @details ダンジョンにNO_VAULTフラグがあるならば、LESSER_VAULT / GREATER_VAULT/ RANDOM_VAULTを除外 / Forbidden vaults */
124         prob_list[RoomType::LESSER_VAULT] = 0;
125         prob_list[RoomType::GREATER_VAULT] = 0;
126         prob_list[RoomType::RANDOM_VAULT] = 0;
127     }
128
129     /*! @details ダンジョンにBEGINNERフラグがあるならば、FIXED_ROOMを除外 / Forbidden vaults */
130     if (dungeon.flags.has(DungeonFeatureType::BEGINNER)) {
131         prob_list[RoomType::FIXED] = 0;
132     }
133
134     /*! @details ダンジョンにNO_CAVEフラグがある場合、FRACAVEの生成枠がNORMALに与えられる。CRIPT、OVALの生成枠がINNER_Fに与えられる。/ NO_CAVE dungeon */
135     if (dungeon.flags.has(DungeonFeatureType::NO_CAVE)) {
136         move_prob_list(RoomType::NORMAL, RoomType::FRACAVE, prob_list);
137         move_prob_list(RoomType::INNER_FEAT, RoomType::CRYPT, prob_list);
138         move_prob_list(RoomType::INNER_FEAT, RoomType::OVAL, prob_list);
139     } else if (dungeon.flags.has(DungeonFeatureType::CAVE)) {
140         /*! @details ダンジョンにCAVEフラグがある場合、NORMALの生成枠がFRACAVEに与えられる。/ CAVE dungeon (Orc floor_ptr->grid_array etc.) */
141         move_prob_list(RoomType::FRACAVE, RoomType::NORMAL, prob_list);
142     } else if (dd_ptr->cavern || dd_ptr->empty_level) {
143         /*! @details ダンジョンの基本地形が最初から渓谷かアリーナ型の場合 FRACAVE は生成から除外。 /  No caves when a (random) cavern exists: they look bad */
144         prob_list[RoomType::FRACAVE] = 0;
145     }
146
147     /*! @details ダンジョンに最初からGLASS_ROOMフラグがある場合、GLASS を生成から除外。/ Forbidden glass rooms */
148     if (dungeon.flags.has_not(DungeonFeatureType::GLASS_ROOM)) {
149         prob_list[RoomType::GLASS] = 0;
150     }
151
152     /*! @details ARCADEは同フラグがダンジョンにないと生成されない。 / Forbidden glass rooms */
153     if (dungeon.flags.has_not(DungeonFeatureType::ARCADE)) {
154         prob_list[RoomType::ARCADE] = 0;
155     }
156
157     ProbabilityTable<RoomType> prob_table;
158     for (auto i : ROOM_TYPE_LIST) {
159         room_num[i] = 0;
160         prob_table.entry_item(i, prob_list[i]);
161     }
162
163     for (int i = dun_rooms; i > 0; i--) {
164         auto r = prob_table.empty() ? RoomType::NORMAL : prob_table.pick_one_at_random();
165
166         room_num[r]++;
167         switch (r) {
168         case RoomType::NEST:
169         case RoomType::PIT:
170         case RoomType::LESSER_VAULT:
171         case RoomType::TRAP_PIT:
172         case RoomType::GLASS:
173         case RoomType::ARCADE:
174             /* Large room */
175             i -= 2;
176             break;
177         case RoomType::GREATER_VAULT:
178         case RoomType::RANDOM_VAULT:
179             /* Largest room */
180             i -= 3;
181             break;
182         default:
183             break;
184         }
185     }
186
187     bool remain;
188     while (true) {
189         remain = false;
190         for (auto i = 0; i < ROOM_TYPE_MAX; i++) {
191             auto room_type = room_build_order[i];
192             if (!room_num[room_type]) {
193                 continue;
194             }
195
196             room_num[room_type]--;
197             if (!room_build(player_ptr, dd_ptr, room_type)) {
198                 continue;
199             }
200
201             rooms_built++;
202             remain = true;
203             switch (room_type) {
204             case RoomType::PIT:
205             case RoomType::NEST:
206             case RoomType::TRAP_PIT:
207                 if (++crowded >= 2) {
208                     room_num[RoomType::PIT] = 0;
209                     room_num[RoomType::NEST] = 0;
210                     room_num[RoomType::TRAP_PIT] = 0;
211                 }
212
213                 break;
214             case RoomType::ARCADE:
215                 room_num[RoomType::ARCADE] = 0;
216                 break;
217             default:
218                 break;
219             }
220         }
221
222         if (!remain) {
223             break;
224         }
225     }
226
227     /*! @details 部屋生成数が2未満の場合生成失敗を返す */
228     if (rooms_built < 2) {
229         msg_print_wizard(player_ptr, CHEAT_DUNGEON, _("部屋数が2未満でした。生成を再試行します。", "Number of rooms was under 2. Retry."));
230         return false;
231     }
232
233     msg_format_wizard(player_ptr, CHEAT_DUNGEON, _("このダンジョンの部屋数は %d です。", "Number of Rooms: %d"), rooms_built);
234     return true;
235 }