OSDN Git Service

16dd986956aaf2dd7de2bbc1b697231244141c9a
[hengbandforosx/hengbandosx.git] / src / room / rooms-pit-nest.cpp
1 #include "room/rooms-pit-nest.h"
2 #include "floor/floor-generator.h"
3 #include "game-option/cheat-options.h"
4 #include "game-option/cheat-types.h"
5 #include "grid/door.h"
6 #include "grid/feature.h"
7 #include "grid/grid.h"
8 #include "monster-floor/monster-generator.h"
9 #include "monster-floor/place-monster-types.h"
10 #include "monster-race/monster-race-hook.h"
11 #include "monster-race/monster-race.h"
12 #include "monster-race/race-flags-resistance.h"
13 #include "monster-race/race-flags1.h"
14 #include "monster-race/race-flags2.h"
15 #include "monster-race/race-flags3.h"
16 #include "monster-race/race-flags7.h"
17 #include "monster/monster-info.h"
18 #include "monster/monster-list.h"
19 #include "monster/monster-util.h"
20 #include "room/door-definition.h"
21 #include "room/space-finder.h"
22 #include "system/dungeon-info.h"
23 #include "system/floor-type-definition.h"
24 #include "system/grid-type-definition.h"
25 #include "system/monster-entity.h"
26 #include "system/monster-race-info.h"
27 #include "system/player-type-definition.h"
28 #include "util/probability-table.h"
29 #include "util/sort.h"
30 #include "view/display-messages.h"
31 #include "wizard/wizard-messages.h"
32 #include <optional>
33 #include <vector>
34
35 /*!
36  * @brief ダンジョン毎に指定されたピット配列を基準にランダムなpit/nestタイプを決める
37  * @param l_ptr 選択されたpit/nest情報を返す参照ポインタ
38  * @param allow_flag_mask 生成が許されるpit/nestのビット配列
39  * @return 選択されたpit/nestのID、選択失敗した場合-1を返す。
40  */
41 static int pick_vault_type(FloorType *floor_ptr, std::vector<nest_pit_type> &l_ptr, BIT_FLAGS16 allow_flag_mask)
42 {
43     ProbabilityTable<int> table;
44     for (size_t i = 0; i < l_ptr.size(); i++) {
45         nest_pit_type *n_ptr = &l_ptr.at(i);
46
47         if (n_ptr->level > floor_ptr->dun_level) {
48             continue;
49         }
50
51         if (!(allow_flag_mask & (1UL << i))) {
52             continue;
53         }
54
55         table.entry_item(i, n_ptr->chance * MAX_DEPTH / (std::min(floor_ptr->dun_level, MAX_DEPTH - 1) - n_ptr->level + 5));
56     }
57
58     return !table.empty() ? table.pick_one_at_random() : -1;
59 }
60
61 /*!
62  * @brief デバッグ時に生成されたpit/nestの型を出力する処理
63  * @param type pit/nestの型ID
64  * @param nest TRUEならばnest、FALSEならばpit
65  * @return デバッグ表示文字列の参照ポインタ
66  * @details
67  * Hack -- Get the string describing subtype of pit/nest
68  * Determined in prepare function (some pit/nest only)
69  */
70 static std::string pit_subtype_string(int type, bool nest)
71 {
72     if (nest) {
73         switch (type) {
74         case NEST_TYPE_CLONE:
75             return std::string("(").append(monraces_info[vault_aux_race].name).append(1, ')');
76         case NEST_TYPE_SYMBOL_GOOD:
77         case NEST_TYPE_SYMBOL_EVIL:
78             return std::string("(").append(1, vault_aux_char).append(1, ')');
79         }
80
81         return std::string();
82     }
83
84     /* Pits */
85     switch (type) {
86     case PIT_TYPE_SYMBOL_GOOD:
87     case PIT_TYPE_SYMBOL_EVIL:
88         return std::string("(").append(1, vault_aux_char).append(1, ')');
89         break;
90     case PIT_TYPE_DRAGON:
91         if (vault_aux_dragon_mask4.has_all_of({ MonsterAbilityType::BR_ACID, MonsterAbilityType::BR_ELEC, MonsterAbilityType::BR_FIRE, MonsterAbilityType::BR_COLD, MonsterAbilityType::BR_POIS })) {
92             return _("(万色)", "(multi-hued)");
93         } else if (vault_aux_dragon_mask4.has(MonsterAbilityType::BR_ACID)) {
94             return _("(酸)", "(acid)");
95         } else if (vault_aux_dragon_mask4.has(MonsterAbilityType::BR_ELEC)) {
96             return _("(稲妻)", "(lightning)");
97         } else if (vault_aux_dragon_mask4.has(MonsterAbilityType::BR_FIRE)) {
98             return _("(火炎)", "(fire)");
99         } else if (vault_aux_dragon_mask4.has(MonsterAbilityType::BR_COLD)) {
100             return _("(冷気)", "(frost)");
101         } else if (vault_aux_dragon_mask4.has(MonsterAbilityType::BR_POIS)) {
102             return _("(毒)", "(poison)");
103         } else {
104             return _("(未定義)", "(undefined)");
105         }
106         break;
107     }
108
109     return std::string();
110 }
111
112 /*
113  *! @brief nestのモンスターリストをソートするための関数 /
114  *  Comp function for sorting nest monster information
115  *  @param u ソート処理対象配列ポインタ
116  *  @param v 未使用
117  *  @param a 比較対象参照ID1
118  *  @param b 比較対象参照ID2
119  *  TODO: to sort.c
120  */
121 static bool ang_sort_comp_nest_mon_info(PlayerType *player_ptr, vptr u, vptr v, int a, int b)
122 {
123     /* Unused */
124     (void)player_ptr;
125     (void)v;
126
127     nest_mon_info_type *nest_mon_info = (nest_mon_info_type *)u;
128     auto w1 = nest_mon_info[a].r_idx;
129     auto w2 = nest_mon_info[b].r_idx;
130     MonsterRaceInfo *r1_ptr = &monraces_info[w1];
131     MonsterRaceInfo *r2_ptr = &monraces_info[w2];
132     int z1 = nest_mon_info[a].used;
133     int z2 = nest_mon_info[b].used;
134
135     if (z1 < z2) {
136         return false;
137     }
138     if (z1 > z2) {
139         return true;
140     }
141
142     if (r1_ptr->level < r2_ptr->level) {
143         return true;
144     }
145     if (r1_ptr->level > r2_ptr->level) {
146         return false;
147     }
148
149     if (r1_ptr->mexp < r2_ptr->mexp) {
150         return true;
151     }
152     if (r1_ptr->mexp > r2_ptr->mexp) {
153         return false;
154     }
155
156     return w1 <= w2;
157 }
158
159 /*!
160  * @brief nestのモンスターリストをスワップするための関数 /
161  * Swap function for sorting nest monster information
162  * @param u スワップ処理対象配列ポインタ
163  * @param v 未使用
164  * @param a スワップ対象参照ID1
165  * @param b スワップ対象参照ID2
166  * TODO: to sort.c
167  */
168 static void ang_sort_swap_nest_mon_info(PlayerType *player_ptr, vptr u, vptr v, int a, int b)
169 {
170     /* Unused */
171     (void)player_ptr;
172     (void)v;
173
174     nest_mon_info_type *nest_mon_info = (nest_mon_info_type *)u;
175     nest_mon_info_type holder = nest_mon_info[a];
176     nest_mon_info[a] = nest_mon_info[b];
177     nest_mon_info[b] = holder;
178 }
179
180 /*!
181  * @brief 生成するNestの情報テーブル
182  */
183 std::vector<nest_pit_type> nest_types = {
184     { _("クローン", "clone"), vault_aux_clone, vault_prep_clone, 5, 3 },
185     { _("ゼリー", "jelly"), vault_aux_jelly, nullptr, 5, 6 },
186     { _("シンボル(善)", "symbol good"), vault_aux_symbol_g, vault_prep_symbol, 25, 2 },
187     { _("シンボル(悪)", "symbol evil"), vault_aux_symbol_e, vault_prep_symbol, 25, 2 },
188     { _("ミミック", "mimic"), vault_aux_mimic, nullptr, 30, 4 },
189     { _("狂気", "lovecraftian"), vault_aux_cthulhu, nullptr, 70, 2 },
190     { _("犬小屋", "kennel"), vault_aux_kennel, nullptr, 45, 4 },
191     { _("動物園", "animal"), vault_aux_animal, nullptr, 35, 5 },
192     { _("教会", "chapel"), vault_aux_chapel_g, nullptr, 75, 4 },
193     { _("アンデッド", "undead"), vault_aux_undead, nullptr, 75, 5 },
194 };
195
196 /*!
197  * @todo intをenumに変更する
198  */
199 static void add_cave_info(Grid *g_ptr, int cave_mask)
200 {
201     g_ptr->info |= cave_mask;
202 }
203
204 /*!
205  * @brief タイプ5の部屋…nestを生成する / Type 5 -- Monster nests
206  * @param player_ptr プレイヤーへの参照ポインタ
207  * @details
208  * A monster nest is a "big" room, with an "inner" room, containing\n
209  * a "collection" of monsters of a given type strewn about the room.\n
210  *\n
211  * The monsters are chosen from a set of 64 randomly selected monster\n
212  * races, to allow the nest creation to fail instead of having "holes".\n
213  *\n
214  * Note the use of the "get_mon_num_prep()" function, and the special\n
215  * "get_mon_num_hook()" restriction function, to prepare the "monster\n
216  * allocation table" in such a way as to optimize the selection of\n
217  * "appropriate" non-unique monsters for the nest.\n
218  *\n
219  * Note that the "get_mon_num()" function may (rarely) fail, in which\n
220  * case the nest will be empty.\n
221  *\n
222  * Note that "monster nests" will never contain "unique" monsters.\n
223  */
224 bool build_type5(PlayerType *player_ptr, dun_data_type *dd_ptr)
225 {
226     POSITION y, x, y1, x1, y2, x2, xval, yval;
227     int i;
228     nest_mon_info_type nest_mon_info[NUM_NEST_MON_TYPE];
229
230     MonsterEntity align;
231
232     Grid *g_ptr;
233
234     auto *floor_ptr = player_ptr->current_floor_ptr;
235     int cur_nest_type = pick_vault_type(floor_ptr, nest_types, floor_ptr->get_dungeon_definition().nest);
236     nest_pit_type *n_ptr;
237
238     /* No type available */
239     if (cur_nest_type < 0) {
240         return false;
241     }
242
243     n_ptr = &nest_types[cur_nest_type];
244
245     /* Process a preparation function if necessary */
246     if (n_ptr->prep_func) {
247         (*(n_ptr->prep_func))(player_ptr);
248     }
249     get_mon_num_prep(player_ptr, n_ptr->hook_func, nullptr);
250
251     align.sub_align = SUB_ALIGN_NEUTRAL;
252
253     /* Pick some monster types */
254     for (i = 0; i < NUM_NEST_MON_TYPE; i++) {
255         auto select_r_idx = [player_ptr, floor_ptr, &align]() -> std::optional<MonsterRaceId> {
256             for (auto attempts = 100; attempts > 0; attempts--) {
257                 /* Get a (hard) monster type */
258                 auto r_idx = get_mon_num(player_ptr, 0, floor_ptr->dun_level + 11, PM_NONE);
259                 auto *r_ptr = &monraces_info[r_idx];
260
261                 /* Decline incorrect alignment */
262                 if (monster_has_hostile_align(player_ptr, &align, 0, 0, r_ptr)) {
263                     continue;
264                 }
265
266                 /* Accept this monster */
267                 if (MonsterRace(r_idx).is_valid()) {
268                     return r_idx;
269                 } else {
270                     return std::nullopt;
271                 }
272             }
273
274             return std::nullopt;
275         };
276
277         const auto r_idx = select_r_idx();
278         if (!r_idx.has_value()) {
279             return false;
280         }
281
282         const auto *r_ptr = &monraces_info[r_idx.value()];
283
284         /* Note the alignment */
285         if (r_ptr->kind_flags.has(MonsterKindType::EVIL)) {
286             align.sub_align |= SUB_ALIGN_EVIL;
287         }
288         if (r_ptr->kind_flags.has(MonsterKindType::GOOD)) {
289             align.sub_align |= SUB_ALIGN_GOOD;
290         }
291
292         nest_mon_info[i].r_idx = r_idx.value();
293         nest_mon_info[i].used = false;
294     }
295
296     /* Find and reserve some space in the dungeon.  Get center of room. */
297     if (!find_space(player_ptr, dd_ptr, &yval, &xval, 11, 25)) {
298         return false;
299     }
300
301     /* Large room */
302     y1 = yval - 4;
303     y2 = yval + 4;
304     x1 = xval - 11;
305     x2 = xval + 11;
306
307     /* Place the floor area */
308     for (y = y1 - 1; y <= y2 + 1; y++) {
309         for (x = x1 - 1; x <= x2 + 1; x++) {
310             g_ptr = &floor_ptr->grid_array[y][x];
311             place_grid(player_ptr, g_ptr, GB_FLOOR);
312             g_ptr->info |= (CAVE_ROOM);
313         }
314     }
315
316     /* Place the outer walls */
317     for (y = y1 - 1; y <= y2 + 1; y++) {
318         g_ptr = &floor_ptr->grid_array[y][x1 - 1];
319         place_grid(player_ptr, g_ptr, GB_OUTER);
320         g_ptr = &floor_ptr->grid_array[y][x2 + 1];
321         place_grid(player_ptr, g_ptr, GB_OUTER);
322     }
323     for (x = x1 - 1; x <= x2 + 1; x++) {
324         g_ptr = &floor_ptr->grid_array[y1 - 1][x];
325         place_grid(player_ptr, g_ptr, GB_OUTER);
326         g_ptr = &floor_ptr->grid_array[y2 + 1][x];
327         place_grid(player_ptr, g_ptr, GB_OUTER);
328     }
329
330     /* Advance to the center room */
331     y1 = y1 + 2;
332     y2 = y2 - 2;
333     x1 = x1 + 2;
334     x2 = x2 - 2;
335
336     /* The inner walls */
337     for (y = y1 - 1; y <= y2 + 1; y++) {
338         g_ptr = &floor_ptr->grid_array[y][x1 - 1];
339         place_grid(player_ptr, g_ptr, GB_INNER);
340         g_ptr = &floor_ptr->grid_array[y][x2 + 1];
341         place_grid(player_ptr, g_ptr, GB_INNER);
342     }
343
344     for (x = x1 - 1; x <= x2 + 1; x++) {
345         g_ptr = &floor_ptr->grid_array[y1 - 1][x];
346         place_grid(player_ptr, g_ptr, GB_INNER);
347         g_ptr = &floor_ptr->grid_array[y2 + 1][x];
348         place_grid(player_ptr, g_ptr, GB_INNER);
349     }
350     for (y = y1; y <= y2; y++) {
351         for (x = x1; x <= x2; x++) {
352             add_cave_info(&floor_ptr->grid_array[y][x], CAVE_ICKY);
353         }
354     }
355
356     /* Place a secret door */
357     switch (randint1(4)) {
358     case 1:
359         place_secret_door(player_ptr, y1 - 1, xval, DOOR_DEFAULT);
360         break;
361     case 2:
362         place_secret_door(player_ptr, y2 + 1, xval, DOOR_DEFAULT);
363         break;
364     case 3:
365         place_secret_door(player_ptr, yval, x1 - 1, DOOR_DEFAULT);
366         break;
367     case 4:
368         place_secret_door(player_ptr, yval, x2 + 1, DOOR_DEFAULT);
369         break;
370     }
371
372     msg_format_wizard(
373         player_ptr, CHEAT_DUNGEON, _("モンスター部屋(nest)(%s%s)を生成します。", "Monster nest (%s%s)"), n_ptr->name, pit_subtype_string(cur_nest_type, true).data());
374
375     /* Place some monsters */
376     for (y = yval - 2; y <= yval + 2; y++) {
377         for (x = xval - 9; x <= xval + 9; x++) {
378             MonsterRaceId r_idx;
379
380             i = randint0(NUM_NEST_MON_TYPE);
381             r_idx = nest_mon_info[i].r_idx;
382
383             /* Place that "random" monster (no groups) */
384             (void)place_specific_monster(player_ptr, 0, y, x, r_idx, 0L);
385
386             nest_mon_info[i].used = true;
387         }
388     }
389
390     if (cheat_room) {
391         ang_sort(player_ptr, nest_mon_info, nullptr, NUM_NEST_MON_TYPE, ang_sort_comp_nest_mon_info, ang_sort_swap_nest_mon_info);
392
393         /* Dump the entries (prevent multi-printing) */
394         for (i = 0; i < NUM_NEST_MON_TYPE; i++) {
395             if (!nest_mon_info[i].used) {
396                 break;
397             }
398             for (; i < NUM_NEST_MON_TYPE - 1; i++) {
399                 if (nest_mon_info[i].r_idx != nest_mon_info[i + 1].r_idx) {
400                     break;
401                 }
402                 if (!nest_mon_info[i + 1].used) {
403                     break;
404                 }
405             }
406
407             constexpr auto fmt = _("Nest構成モンスターNo.%d: %s", "Nest monster No.%d: %s");
408             msg_format_wizard(player_ptr, CHEAT_DUNGEON, fmt, i, monraces_info[nest_mon_info[i].r_idx].name.data());
409         }
410     }
411
412     return true;
413 }
414
415 /*!
416  * @brief 生成するPitの情報テーブル
417  */
418 std::vector<nest_pit_type> pit_types = {
419     { _("オーク", "orc"), vault_aux_orc, nullptr, 5, 6 },
420     { _("トロル", "troll"), vault_aux_troll, nullptr, 20, 6 },
421     { _("巨人", "giant"), vault_aux_giant, nullptr, 50, 6 },
422     { _("狂気", "lovecraftian"), vault_aux_cthulhu, nullptr, 80, 2 },
423     { _("シンボル(善)", "symbol good"), vault_aux_symbol_g, vault_prep_symbol, 70, 1 },
424     { _("シンボル(悪)", "symbol evil"), vault_aux_symbol_e, vault_prep_symbol, 70, 1 },
425     { _("教会", "chapel"), vault_aux_chapel_g, nullptr, 65, 2 },
426     { _("ドラゴン", "dragon"), vault_aux_dragon, vault_prep_dragon, 70, 6 },
427     { _("デーモン", "demon"), vault_aux_demon, nullptr, 80, 6 },
428     { _("ダークエルフ", "dark elf"), vault_aux_dark_elf, nullptr, 45, 4 },
429 };
430
431 /*!
432  * @brief タイプ6の部屋…pitを生成する / Type 6 -- Monster pits
433  * @details
434  * A monster pit is a "big" room, with an "inner" room, containing\n
435  * a "collection" of monsters of a given type organized in the room.\n
436  *\n
437  * The inside room in a monster pit appears as shown below, where the\n
438  * actual monsters in each location depend on the type of the pit\n
439  *\n
440  *   XXXXXXXXXXXXXXXXXXXXX\n
441  *   X0000000000000000000X\n
442  *   X0112233455543322110X\n
443  *   X0112233467643322110X\n
444  *   X0112233455543322110X\n
445  *   X0000000000000000000X\n
446  *   XXXXXXXXXXXXXXXXXXXXX\n
447  *\n
448  * Note that the monsters in the pit are now chosen by using "get_mon_num()"\n
449  * to request 16 "appropriate" monsters, sorting them by level, and using\n
450  * the "even" entries in this sorted list for the contents of the pit.\n
451  *\n
452  * Hack -- all of the "dragons" in a "dragon" pit must be the same "color",\n
453  * which is handled by requiring a specific "breath" attack for all of the\n
454  * dragons.  This may include "multi-hued" breath.  Note that "wyrms" may\n
455  * be present in many of the dragon pits, if they have the proper breath.\n
456  *\n
457  * Note the use of the "get_mon_num_prep()" function, and the special\n
458  * "get_mon_num_hook()" restriction function, to prepare the "monster\n
459  * allocation table" in such a way as to optimize the selection of\n
460  * "appropriate" non-unique monsters for the pit.\n
461  *\n
462  * Note that the "get_mon_num()" function may (rarely) fail, in which case\n
463  * the pit will be empty.\n
464  *\n
465  * Note that "monster pits" will never contain "unique" monsters.\n
466  */
467 bool build_type6(PlayerType *player_ptr, dun_data_type *dd_ptr)
468 {
469     POSITION y, x, y1, x1, y2, x2, xval, yval;
470     int i, j;
471
472     MonsterRaceId what[16];
473
474     MonsterEntity align;
475
476     Grid *g_ptr;
477
478     auto *floor_ptr = player_ptr->current_floor_ptr;
479     int cur_pit_type = pick_vault_type(floor_ptr, pit_types, floor_ptr->get_dungeon_definition().pit);
480     nest_pit_type *n_ptr;
481
482     /* No type available */
483     if (cur_pit_type < 0) {
484         return false;
485     }
486
487     n_ptr = &pit_types[cur_pit_type];
488
489     /* Process a preparation function if necessary */
490     if (n_ptr->prep_func) {
491         (*(n_ptr->prep_func))(player_ptr);
492     }
493     get_mon_num_prep(player_ptr, n_ptr->hook_func, nullptr);
494
495     align.sub_align = SUB_ALIGN_NEUTRAL;
496
497     /* Pick some monster types */
498     for (i = 0; i < 16; i++) {
499         auto r_idx = MonsterRace::empty_id();
500         int attempts = 100;
501         MonsterRaceInfo *r_ptr = nullptr;
502
503         while (attempts--) {
504             /* Get a (hard) monster type */
505             r_idx = get_mon_num(player_ptr, 0, floor_ptr->dun_level + 11, PM_NONE);
506             r_ptr = &monraces_info[r_idx];
507
508             /* Decline incorrect alignment */
509             if (monster_has_hostile_align(player_ptr, &align, 0, 0, r_ptr)) {
510                 continue;
511             }
512
513             /* Accept this monster */
514             break;
515         }
516
517         /* Notice failure */
518         if (!MonsterRace(r_idx).is_valid() || !attempts) {
519             return false;
520         }
521
522         /* Note the alignment */
523         if (r_ptr->kind_flags.has(MonsterKindType::EVIL)) {
524             align.sub_align |= SUB_ALIGN_EVIL;
525         }
526         if (r_ptr->kind_flags.has(MonsterKindType::GOOD)) {
527             align.sub_align |= SUB_ALIGN_GOOD;
528         }
529
530         what[i] = r_idx;
531     }
532
533     /* Find and reserve some space in the dungeon.  Get center of room. */
534     if (!find_space(player_ptr, dd_ptr, &yval, &xval, 11, 25)) {
535         return false;
536     }
537
538     /* Large room */
539     y1 = yval - 4;
540     y2 = yval + 4;
541     x1 = xval - 11;
542     x2 = xval + 11;
543
544     /* Place the floor area */
545     for (y = y1 - 1; y <= y2 + 1; y++) {
546         for (x = x1 - 1; x <= x2 + 1; x++) {
547             g_ptr = &floor_ptr->grid_array[y][x];
548             place_grid(player_ptr, g_ptr, GB_FLOOR);
549             g_ptr->info |= (CAVE_ROOM);
550         }
551     }
552
553     /* Place the outer walls */
554     for (y = y1 - 1; y <= y2 + 1; y++) {
555         g_ptr = &floor_ptr->grid_array[y][x1 - 1];
556         place_grid(player_ptr, g_ptr, GB_OUTER);
557         g_ptr = &floor_ptr->grid_array[y][x2 + 1];
558         place_grid(player_ptr, g_ptr, GB_OUTER);
559     }
560     for (x = x1 - 1; x <= x2 + 1; x++) {
561         g_ptr = &floor_ptr->grid_array[y1 - 1][x];
562         place_grid(player_ptr, g_ptr, GB_OUTER);
563         g_ptr = &floor_ptr->grid_array[y2 + 1][x];
564         place_grid(player_ptr, g_ptr, GB_OUTER);
565     }
566
567     /* Advance to the center room */
568     y1 = y1 + 2;
569     y2 = y2 - 2;
570     x1 = x1 + 2;
571     x2 = x2 - 2;
572
573     /* The inner walls */
574     for (y = y1 - 1; y <= y2 + 1; y++) {
575         g_ptr = &floor_ptr->grid_array[y][x1 - 1];
576         place_grid(player_ptr, g_ptr, GB_INNER);
577         g_ptr = &floor_ptr->grid_array[y][x2 + 1];
578         place_grid(player_ptr, g_ptr, GB_INNER);
579     }
580     for (x = x1 - 1; x <= x2 + 1; x++) {
581         g_ptr = &floor_ptr->grid_array[y1 - 1][x];
582         place_grid(player_ptr, g_ptr, GB_INNER);
583         g_ptr = &floor_ptr->grid_array[y2 + 1][x];
584         place_grid(player_ptr, g_ptr, GB_INNER);
585     }
586     for (y = y1; y <= y2; y++) {
587         for (x = x1; x <= x2; x++) {
588             add_cave_info(&floor_ptr->grid_array[y][x], CAVE_ICKY);
589         }
590     }
591
592     /* Place a secret door */
593     switch (randint1(4)) {
594     case 1:
595         place_secret_door(player_ptr, y1 - 1, xval, DOOR_DEFAULT);
596         break;
597     case 2:
598         place_secret_door(player_ptr, y2 + 1, xval, DOOR_DEFAULT);
599         break;
600     case 3:
601         place_secret_door(player_ptr, yval, x1 - 1, DOOR_DEFAULT);
602         break;
603     case 4:
604         place_secret_door(player_ptr, yval, x2 + 1, DOOR_DEFAULT);
605         break;
606     }
607
608     /* Sort the entries */
609     for (i = 0; i < 16 - 1; i++) {
610         /* Sort the entries */
611         for (j = 0; j < 16 - 1; j++) {
612             int i1 = j;
613             int i2 = j + 1;
614
615             int p1 = monraces_info[what[i1]].level;
616             int p2 = monraces_info[what[i2]].level;
617
618             /* Bubble */
619             if (p1 > p2) {
620                 MonsterRaceId tmp = what[i1];
621                 what[i1] = what[i2];
622                 what[i2] = tmp;
623             }
624         }
625     }
626
627     msg_format_wizard(
628         player_ptr, CHEAT_DUNGEON, _("モンスター部屋(pit)(%s%s)を生成します。", "Monster pit (%s%s)"), n_ptr->name, pit_subtype_string(cur_pit_type, false).data());
629
630     /* Select the entries */
631     for (i = 0; i < 8; i++) {
632         /* Every other entry */
633         what[i] = what[i * 2];
634         msg_format_wizard(player_ptr, CHEAT_DUNGEON, _("Nest構成モンスター選択No.%d:%s", "Nest Monster Select No.%d:%s"), i, monraces_info[what[i]].name.data());
635     }
636
637     /* Top and bottom rows */
638     for (x = xval - 9; x <= xval + 9; x++) {
639         place_specific_monster(player_ptr, 0, yval - 2, x, what[0], PM_NO_KAGE);
640         place_specific_monster(player_ptr, 0, yval + 2, x, what[0], PM_NO_KAGE);
641     }
642
643     /* Middle columns */
644     for (y = yval - 1; y <= yval + 1; y++) {
645         place_specific_monster(player_ptr, 0, y, xval - 9, what[0], PM_NO_KAGE);
646         place_specific_monster(player_ptr, 0, y, xval + 9, what[0], PM_NO_KAGE);
647
648         place_specific_monster(player_ptr, 0, y, xval - 8, what[1], PM_NO_KAGE);
649         place_specific_monster(player_ptr, 0, y, xval + 8, what[1], PM_NO_KAGE);
650
651         place_specific_monster(player_ptr, 0, y, xval - 7, what[1], PM_NO_KAGE);
652         place_specific_monster(player_ptr, 0, y, xval + 7, what[1], PM_NO_KAGE);
653
654         place_specific_monster(player_ptr, 0, y, xval - 6, what[2], PM_NO_KAGE);
655         place_specific_monster(player_ptr, 0, y, xval + 6, what[2], PM_NO_KAGE);
656
657         place_specific_monster(player_ptr, 0, y, xval - 5, what[2], PM_NO_KAGE);
658         place_specific_monster(player_ptr, 0, y, xval + 5, what[2], PM_NO_KAGE);
659
660         place_specific_monster(player_ptr, 0, y, xval - 4, what[3], PM_NO_KAGE);
661         place_specific_monster(player_ptr, 0, y, xval + 4, what[3], PM_NO_KAGE);
662
663         place_specific_monster(player_ptr, 0, y, xval - 3, what[3], PM_NO_KAGE);
664         place_specific_monster(player_ptr, 0, y, xval + 3, what[3], PM_NO_KAGE);
665
666         place_specific_monster(player_ptr, 0, y, xval - 2, what[4], PM_NO_KAGE);
667         place_specific_monster(player_ptr, 0, y, xval + 2, what[4], PM_NO_KAGE);
668     }
669
670     /* Above/Below the center monster */
671     for (x = xval - 1; x <= xval + 1; x++) {
672         place_specific_monster(player_ptr, 0, yval + 1, x, what[5], PM_NO_KAGE);
673         place_specific_monster(player_ptr, 0, yval - 1, x, what[5], PM_NO_KAGE);
674     }
675
676     /* Next to the center monster */
677     place_specific_monster(player_ptr, 0, yval, xval + 1, what[6], PM_NO_KAGE);
678     place_specific_monster(player_ptr, 0, yval, xval - 1, what[6], PM_NO_KAGE);
679
680     /* Center monster */
681     place_specific_monster(player_ptr, 0, yval, xval, what[7], PM_NO_KAGE);
682
683     return true;
684 }
685
686 // clang-format off
687 /*!
688  * @brief 開門トラップのモンスター配置テーブル
689  * @details
690  * 中央からの相対座標(X,Y)、モンスターの強さ
691  */
692 const int place_table_trapped_pit[TRAPPED_PIT_MONSTER_PLACE_MAX][3] = {
693     { -2, -9, 0 }, { -2, -8, 0 }, { -3, -7, 0 }, { -3, -6, 0 }, { +2, -9, 0 }, { +2, -8, 0 }, { +3, -7, 0 }, { +3, -6, 0 },
694     { -2, +9, 0 }, { -2, +8, 0 }, { -3, +7, 0 }, { -3, +6, 0 }, { +2, +9, 0 }, { +2, +8, 0 }, { +3, +7, 0 }, { +3, +6, 0 },
695     { -2, -7, 1 }, { -3, -5, 1 }, { -3, -4, 1 }, { -2, +7, 1 }, { -3, +5, 1 }, { -3, +4, 1 },
696     { +2, -7, 1 }, { +3, -5, 1 }, { +3, -4, 1 }, { +2, +7, 1 }, { +3, +5, 1 }, { +3, +4, 1 },
697     { -2, -6, 2 }, { -2, -5, 2 }, { -3, -3, 2 }, { -2, +6, 2 }, { -2, +5, 2 }, { -3, +3, 2 },
698     { +2, -6, 2 }, { +2, -5, 2 }, { +3, -3, 2 }, { +2, +6, 2 }, { +2, +5, 2 }, { +3, +3, 2 },
699     { -2, -4, 3 }, { -3, -2, 3 }, { -2, +4, 3 }, { -3, +2, 3 },
700     { +2, -4, 3 }, { +3, -2, 3 }, { +2, +4, 3 }, { +3, +2, 3 },
701     { -2, -3, 4 }, { -3, -1, 4 }, { +2, -3, 4 }, { +3, -1, 4 },
702     { -2, +3, 4 }, { -3, +1, 4 }, { +2, +3, 4 }, { +3, +1, 4 },
703     { -2, -2, 5 }, { -3, 0, 5 }, { -2, +2, 5 }, { +2, -2, 5 }, { +3, 0, 5 }, { +2, +2, 5 },
704     { -2, -1, 6 }, { -2, +1, 6 }, { +2, -1, 6 }, { +2, +1, 6 },
705     { -2, 0, 7 }, { +2, 0, 7 },
706     { 0, 0, -1 } };
707 // clang-format on
708
709 /*!
710  * @brief 開門トラップに配置するモンスターの条件フィルタ
711  * @detai;
712  * 穴を掘るモンスター、壁を抜けるモンスターは却下
713  */
714 static bool vault_aux_trapped_pit(PlayerType *player_ptr, MonsterRaceId r_idx)
715 {
716     /* Unused */
717     (void)player_ptr;
718
719     auto *r_ptr = &monraces_info[r_idx];
720
721     if (!vault_monster_okay(player_ptr, r_idx)) {
722         return false;
723     }
724
725     /* No wall passing monster */
726     if (r_ptr->feature_flags.has_any_of({ MonsterFeatureType::PASS_WALL, MonsterFeatureType::KILL_WALL })) {
727         return false;
728     }
729
730     return true;
731 }
732
733 /*!
734  * @brief タイプ13の部屋…開門トラップpitの生成 / Type 13 -- Trapped monster pits
735  * @details
736  * A trapped monster pit is a "big" room with a straight corridor in\n
737  * which wall opening traps are placed, and with two "inner" rooms\n
738  * containing a "collection" of monsters of a given type organized in\n
739  * the room.\n
740  *\n
741  * The trapped monster pit appears as shown below, where the actual\n
742  * monsters in each location depend on the type of the pit\n
743  *\n
744  *  XXXXXXXXXXXXXXXXXXXXXXXXX\n
745  *  X                       X\n
746  *  XXXXXXXXXXXXXXXXXXXXXXX X\n
747  *  XXXXX001123454321100XXX X\n
748  *  XXX0012234567654322100X X\n
749  *  XXXXXXXXXXXXXXXXXXXXXXX X\n
750  *  X           ^           X\n
751  *  X XXXXXXXXXXXXXXXXXXXXXXX\n
752  *  X X0012234567654322100XXX\n
753  *  X XXX001123454321100XXXXX\n
754  *  X XXXXXXXXXXXXXXXXXXXXXXX\n
755  *  X                       X\n
756  *  XXXXXXXXXXXXXXXXXXXXXXXXX\n
757  *\n
758  * Note that the monsters in the pit are now chosen by using "get_mon_num()"\n
759  * to request 16 "appropriate" monsters, sorting them by level, and using\n
760  * the "even" entries in this sorted list for the contents of the pit.\n
761  *\n
762  * Hack -- all of the "dragons" in a "dragon" pit must be the same "color",\n
763  * which is handled by requiring a specific "breath" attack for all of the\n
764  * dragons.  This may include "multi-hued" breath.  Note that "wyrms" may\n
765  * be present in many of the dragon pits, if they have the proper breath.\n
766  *\n
767  * Note the use of the "get_mon_num_prep()" function, and the special\n
768  * "get_mon_num_hook()" restriction function, to prepare the "monster\n
769  * allocation table" in such a way as to optimize the selection of\n
770  * "appropriate" non-unique monsters for the pit.\n
771  *\n
772  * Note that the "get_mon_num()" function may (rarely) fail, in which case\n
773  * the pit will be empty.\n
774  *\n
775  * Note that "monster pits" will never contain "unique" monsters.\n
776  */
777 bool build_type13(PlayerType *player_ptr, dun_data_type *dd_ptr)
778 {
779     POSITION y, x, y1, x1, y2, x2, xval, yval;
780     int i, j;
781
782     MonsterRaceId what[16];
783
784     MonsterEntity align;
785
786     Grid *g_ptr;
787
788     auto *floor_ptr = player_ptr->current_floor_ptr;
789     int cur_pit_type = pick_vault_type(floor_ptr, pit_types, floor_ptr->get_dungeon_definition().pit);
790     nest_pit_type *n_ptr;
791
792     /* Only in Angband */
793     if (floor_ptr->dungeon_idx != DUNGEON_ANGBAND) {
794         return false;
795     }
796
797     /* No type available */
798     if (cur_pit_type < 0) {
799         return false;
800     }
801
802     n_ptr = &pit_types[cur_pit_type];
803
804     /* Process a preparation function if necessary */
805     if (n_ptr->prep_func) {
806         (*(n_ptr->prep_func))(player_ptr);
807     }
808     get_mon_num_prep(player_ptr, n_ptr->hook_func, vault_aux_trapped_pit);
809
810     align.sub_align = SUB_ALIGN_NEUTRAL;
811
812     /* Pick some monster types */
813     for (i = 0; i < 16; i++) {
814         auto r_idx = MonsterRace::empty_id();
815         int attempts = 100;
816         MonsterRaceInfo *r_ptr = nullptr;
817
818         while (attempts--) {
819             /* Get a (hard) monster type */
820             r_idx = get_mon_num(player_ptr, 0, floor_ptr->dun_level + 0, PM_NONE);
821             r_ptr = &monraces_info[r_idx];
822
823             /* Decline incorrect alignment */
824             if (monster_has_hostile_align(player_ptr, &align, 0, 0, r_ptr)) {
825                 continue;
826             }
827
828             /* Accept this monster */
829             break;
830         }
831
832         /* Notice failure */
833         if (!MonsterRace(r_idx).is_valid() || !attempts) {
834             return false;
835         }
836
837         /* Note the alignment */
838         if (r_ptr->kind_flags.has(MonsterKindType::EVIL)) {
839             align.sub_align |= SUB_ALIGN_EVIL;
840         }
841         if (r_ptr->kind_flags.has(MonsterKindType::GOOD)) {
842             align.sub_align |= SUB_ALIGN_GOOD;
843         }
844
845         what[i] = r_idx;
846     }
847
848     /* Find and reserve some space in the dungeon.  Get center of room. */
849     if (!find_space(player_ptr, dd_ptr, &yval, &xval, 13, 25)) {
850         return false;
851     }
852
853     /* Large room */
854     y1 = yval - 5;
855     y2 = yval + 5;
856     x1 = xval - 11;
857     x2 = xval + 11;
858
859     /* Fill with inner walls */
860     for (y = y1 - 1; y <= y2 + 1; y++) {
861         for (x = x1 - 1; x <= x2 + 1; x++) {
862             g_ptr = &floor_ptr->grid_array[y][x];
863             place_grid(player_ptr, g_ptr, GB_INNER);
864             g_ptr->info |= (CAVE_ROOM);
865         }
866     }
867
868     /* Place the floor area 1 */
869     for (x = x1 + 3; x <= x2 - 3; x++) {
870         g_ptr = &floor_ptr->grid_array[yval - 2][x];
871         place_grid(player_ptr, g_ptr, GB_FLOOR);
872         add_cave_info(&floor_ptr->grid_array[yval - 2][x], CAVE_ICKY);
873
874         g_ptr = &floor_ptr->grid_array[yval + 2][x];
875         place_grid(player_ptr, g_ptr, GB_FLOOR);
876         add_cave_info(&floor_ptr->grid_array[yval + 2][x], CAVE_ICKY);
877     }
878
879     /* Place the floor area 2 */
880     for (x = x1 + 5; x <= x2 - 5; x++) {
881         g_ptr = &floor_ptr->grid_array[yval - 3][x];
882         place_grid(player_ptr, g_ptr, GB_FLOOR);
883         add_cave_info(&floor_ptr->grid_array[yval - 3][x], CAVE_ICKY);
884
885         g_ptr = &floor_ptr->grid_array[yval + 3][x];
886         place_grid(player_ptr, g_ptr, GB_FLOOR);
887         add_cave_info(&floor_ptr->grid_array[yval + 3][x], CAVE_ICKY);
888     }
889
890     /* Corridor */
891     for (x = x1; x <= x2; x++) {
892         g_ptr = &floor_ptr->grid_array[yval][x];
893         place_grid(player_ptr, g_ptr, GB_FLOOR);
894         g_ptr = &floor_ptr->grid_array[y1][x];
895         place_grid(player_ptr, g_ptr, GB_FLOOR);
896         g_ptr = &floor_ptr->grid_array[y2][x];
897         place_grid(player_ptr, g_ptr, GB_FLOOR);
898     }
899
900     /* Place the outer walls */
901     for (y = y1 - 1; y <= y2 + 1; y++) {
902         g_ptr = &floor_ptr->grid_array[y][x1 - 1];
903         place_grid(player_ptr, g_ptr, GB_OUTER);
904         g_ptr = &floor_ptr->grid_array[y][x2 + 1];
905         place_grid(player_ptr, g_ptr, GB_OUTER);
906     }
907     for (x = x1 - 1; x <= x2 + 1; x++) {
908         g_ptr = &floor_ptr->grid_array[y1 - 1][x];
909         place_grid(player_ptr, g_ptr, GB_OUTER);
910         g_ptr = &floor_ptr->grid_array[y2 + 1][x];
911         place_grid(player_ptr, g_ptr, GB_OUTER);
912     }
913
914     /* Random corridor */
915     if (one_in_(2)) {
916         for (y = y1; y <= yval; y++) {
917             place_bold(player_ptr, y, x2, GB_FLOOR);
918             place_bold(player_ptr, y, x1 - 1, GB_SOLID);
919         }
920         for (y = yval; y <= y2 + 1; y++) {
921             place_bold(player_ptr, y, x1, GB_FLOOR);
922             place_bold(player_ptr, y, x2 + 1, GB_SOLID);
923         }
924     } else {
925         for (y = yval; y <= y2 + 1; y++) {
926             place_bold(player_ptr, y, x1, GB_FLOOR);
927             place_bold(player_ptr, y, x2 + 1, GB_SOLID);
928         }
929         for (y = y1; y <= yval; y++) {
930             place_bold(player_ptr, y, x2, GB_FLOOR);
931             place_bold(player_ptr, y, x1 - 1, GB_SOLID);
932         }
933     }
934
935     /* Place the wall open trap */
936     floor_ptr->grid_array[yval][xval].mimic = floor_ptr->grid_array[yval][xval].feat;
937     floor_ptr->grid_array[yval][xval].feat = feat_trap_open;
938
939     /* Sort the entries */
940     for (i = 0; i < 16 - 1; i++) {
941         /* Sort the entries */
942         for (j = 0; j < 16 - 1; j++) {
943             int i1 = j;
944             int i2 = j + 1;
945
946             int p1 = monraces_info[what[i1]].level;
947             int p2 = monraces_info[what[i2]].level;
948
949             /* Bubble */
950             if (p1 > p2) {
951                 MonsterRaceId tmp = what[i1];
952                 what[i1] = what[i2];
953                 what[i2] = tmp;
954             }
955         }
956     }
957
958     msg_format_wizard(
959         player_ptr, CHEAT_DUNGEON, _("%s%sの罠ピットが生成されました。", "Trapped monster pit (%s%s)"), n_ptr->name, pit_subtype_string(cur_pit_type, false).data());
960
961     /* Select the entries */
962     for (i = 0; i < 8; i++) {
963         /* Every other entry */
964         what[i] = what[i * 2];
965
966         if (cheat_hear) {
967             msg_print(monraces_info[what[i]].name);
968         }
969     }
970
971     for (i = 0; place_table_trapped_pit[i][2] >= 0; i++) {
972         y = yval + place_table_trapped_pit[i][0];
973         x = xval + place_table_trapped_pit[i][1];
974         place_specific_monster(player_ptr, 0, y, x, what[place_table_trapped_pit[i][2]], PM_NO_KAGE);
975     }
976
977     return true;
978 }