OSDN Git Service

6cc547dad592d0bce02f1553df40fd288ad3d239
[hengbandforosx/hengbandosx.git] / src / room / rooms-city.cpp
1 #include "room/rooms-city.h"
2 #include "floor/floor-generator.h"
3 #include "floor/floor-town.h"
4 #include "game-option/cheat-types.h"
5 #include "grid/feature.h"
6 #include "grid/grid.h"
7 #include "room/space-finder.h"
8 #include "store/store-util.h"
9 #include "store/store.h"
10 #include "system/angband-exceptions.h"
11 #include "system/floor-type-definition.h"
12 #include "system/grid-type-definition.h"
13 #include "system/player-type-definition.h"
14 #include "system/terrain-type-definition.h"
15 #include "util/bit-flags-calculator.h"
16 #include "wizard/wizard-messages.h"
17 #include <algorithm>
18
19 namespace {
20 const std::vector<StoreSaleType> stores = {
21     StoreSaleType::GENERAL,
22     StoreSaleType::ARMOURY,
23     StoreSaleType::WEAPON,
24     StoreSaleType::TEMPLE,
25     StoreSaleType::ALCHEMIST,
26     StoreSaleType::MAGIC,
27     StoreSaleType::BLACK,
28     StoreSaleType::BOOK,
29 };
30
31 /*
32  * Precalculate buildings' location of underground arcade
33  */
34 std::optional<std::vector<ugbldg_type>> precalc_ugarcade(int town_hgt, int town_wid)
35 {
36     const auto n = std::ssize(stores);
37     std::vector<ugbldg_type> underground_buildings(n);
38     const auto max_buildings_height = 3 * town_hgt / MAX_TOWN_HGT;
39     const auto max_buildings_width = 5 * town_wid / MAX_TOWN_WID;
40     std::vector<std::vector<bool>> ugarcade_used(town_hgt, std::vector<bool>(town_wid));
41     int i;
42     auto attempt = 10000;
43     auto should_abort = false;
44     for (i = 0; i < n; i++) {
45         auto &cur_ugbldg = underground_buildings[i];
46         do {
47             const auto center_y = rand_range(2, town_hgt - 3);
48             const auto center_x = rand_range(2, town_wid - 3);
49             auto tmp = center_y - randint1(max_buildings_height);
50             cur_ugbldg.y0 = std::max(tmp, 1);
51             tmp = center_x - randint1(max_buildings_width);
52             cur_ugbldg.x0 = std::max(tmp, 1);
53             tmp = center_y + randint1(max_buildings_height);
54             cur_ugbldg.y1 = std::min(tmp, town_hgt - 2);
55             tmp = center_x + randint1(max_buildings_width);
56             cur_ugbldg.x1 = std::min(tmp, town_wid - 2);
57             should_abort = false;
58             for (auto y = cur_ugbldg.y0; (y <= cur_ugbldg.y1) && !should_abort; y++) {
59                 for (auto x = cur_ugbldg.x0; x <= cur_ugbldg.x1; x++) {
60                     if (ugarcade_used[y][x]) {
61                         should_abort = true;
62                         break;
63                     }
64                 }
65             }
66
67             attempt--;
68         } while (should_abort && (attempt > 0));
69
70         if (attempt == 0) {
71             break;
72         }
73
74         for (auto y = cur_ugbldg.y0 - 1; y <= cur_ugbldg.y1 + 1; y++) {
75             for (auto x = cur_ugbldg.x0 - 1; x <= cur_ugbldg.x1 + 1; x++) {
76                 ugarcade_used[y][x] = true;
77             }
78         }
79     }
80
81     if (i != n) {
82         return std::nullopt;
83     }
84
85     return underground_buildings;
86 }
87
88 /* Create a new floor room with optional light */
89 void generate_room_floor(PlayerType *player_ptr, POSITION y1, POSITION x1, POSITION y2, POSITION x2, int light)
90 {
91     for (auto y = y1; y <= y2; y++) {
92         for (auto x = x1; x <= x2; x++) {
93             auto &grid = player_ptr->current_floor_ptr->grid_array[y][x];
94             place_grid(player_ptr, &grid, GB_FLOOR);
95             grid.info |= (CAVE_ROOM);
96             if (light) {
97                 grid.info |= (CAVE_GLOW);
98             }
99         }
100     }
101 }
102
103 void generate_fill_perm_bold(PlayerType *player_ptr, POSITION y1, POSITION x1, POSITION y2, POSITION x2)
104 {
105     for (auto y = y1; y <= y2; y++) {
106         for (auto x = x1; x <= x2; x++) {
107             place_bold(player_ptr, y, x, GB_INNER_PERM);
108         }
109     }
110 }
111
112 Pos2D pick_door_direction(const ugbldg_type &cur_ugbldg)
113 {
114     switch (randint0(4)) {
115     case 0: // South
116         return { cur_ugbldg.y1, rand_range(cur_ugbldg.x0, cur_ugbldg.x1) };
117     case 1: // North
118         return { cur_ugbldg.y0, rand_range(cur_ugbldg.x0, cur_ugbldg.x1) };
119     case 2: // East
120         return { rand_range(cur_ugbldg.y0, cur_ugbldg.y1), cur_ugbldg.x1 };
121     case 3: // West
122         return { rand_range(cur_ugbldg.y0, cur_ugbldg.y1), cur_ugbldg.x0 };
123     default:
124         THROW_EXCEPTION(std::logic_error, "RNG is broken!");
125     }
126 }
127
128 /*!
129  * @brief タイプ16の部屋…地下都市生成のサブルーチン / Actually create buildings
130  * @param player_ptr プレイヤーへの参照ポインタ
131  * @param pos_ug 地下都市エリアの左上座標
132  * @param underground_buildings 生成する店舗のリスト
133  */
134 void build_stores(PlayerType *player_ptr, const Pos2D &pos_ug, const std::vector<ugbldg_type> &underground_buildings)
135 {
136     for (const auto &ug_building : underground_buildings) {
137         generate_room_floor(player_ptr, pos_ug.y + ug_building.y0 - 2, pos_ug.x + ug_building.x0 - 2, pos_ug.y + ug_building.y1 + 2, pos_ug.x + ug_building.x1 + 2, false);
138     }
139
140     for (auto i = 0; i < std::ssize(underground_buildings); i++) {
141         const auto &ug_building = underground_buildings[i];
142         generate_fill_perm_bold(player_ptr, pos_ug.y + ug_building.y0, pos_ug.x + ug_building.x0, pos_ug.y + ug_building.y1, pos_ug.x + ug_building.x1);
143         const auto pos = pick_door_direction(ug_building);
144         const auto &terrains = TerrainList::get_instance();
145         const auto end = terrains.end();
146         const auto it = std::find_if(terrains.begin(), end,
147             [subtype = stores[i]](const TerrainType &terrain) {
148                 return terrain.flags.has(TerrainCharacteristics::STORE) && (i2enum<StoreSaleType>(static_cast<int>(terrain.subtype)) == subtype);
149             });
150         if (it == end) {
151             continue;
152         }
153
154         cave_set_feat(player_ptr, pos_ug.y + pos.y, pos_ug.x + pos.x, it->idx);
155         store_init(VALID_TOWNS, stores[i]);
156     }
157 }
158 }
159
160 /*!
161  * @brief タイプ16の部屋…地下都市の生成 / Type 16 -- Underground Arcade
162  * @details
163  * Town logic flow for generation of new town\n
164  * Originally from Vanilla 3.0.3\n
165  *\n
166  * We start with a fully wiped grids of normal floors.\n
167  *\n
168  * Note that town_gen_hack() plays games with the R.N.G.\n
169  *\n
170  * This function does NOT do anything about the owners of the stores,\n
171  * nor the contents thereof.  It only handles the physical layout.\n
172  */
173 bool build_type16(PlayerType *player_ptr, dun_data_type *dd_ptr)
174 {
175     const auto town_hgt = rand_range(MIN_TOWN_HGT, MAX_TOWN_HGT);
176     const auto town_wid = rand_range(MIN_TOWN_WID, MAX_TOWN_WID);
177     const auto underground_buildings = precalc_ugarcade(town_hgt, town_wid);
178     if (!underground_buildings) {
179         return false;
180     }
181
182     int yval;
183     int xval;
184     if (!find_space(player_ptr, dd_ptr, &yval, &xval, town_hgt + 4, town_wid + 4)) {
185         return false;
186     }
187
188     const Pos2D pos(yval - (town_hgt / 2), xval - (town_wid / 2));
189     generate_room_floor(player_ptr, pos.y + town_hgt / 3, pos.x + town_wid / 3, pos.y + town_hgt * 2 / 3, pos.x + town_wid * 2 / 3, false);
190     build_stores(player_ptr, pos, *underground_buildings);
191     msg_print_wizard(player_ptr, CHEAT_DUNGEON, _("地下街を生成しました", "Underground arcade was generated."));
192     return true;
193 }