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"
6 #include "grid/feature.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"
36 * @brief ダンジョン毎に指定されたピット配列を基準にランダムなpit/nestタイプを決める
37 * @param l_ptr 選択されたpit/nest情報を返す参照ポインタ
38 * @param allow_flag_mask 生成が許されるpit/nestのビット配列
39 * @return 選択されたpit/nestのID、選択失敗した場合-1を返す。
41 static int pick_vault_type(FloorType *floor_ptr, std::vector<nest_pit_type> &l_ptr, BIT_FLAGS16 allow_flag_mask)
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);
47 if (n_ptr->level > floor_ptr->dun_level) {
51 if (!(allow_flag_mask & (1UL << i))) {
55 table.entry_item(i, n_ptr->chance * MAX_DEPTH / (std::min(floor_ptr->dun_level, MAX_DEPTH - 1) - n_ptr->level + 5));
58 return !table.empty() ? table.pick_one_at_random() : -1;
62 * @brief デバッグ時に生成されたpit/nestの型を出力する処理
63 * @param type pit/nestの型ID
64 * @param nest TRUEならばnest、FALSEならばpit
65 * @return デバッグ表示文字列の参照ポインタ
67 * Hack -- Get the string describing subtype of pit/nest
68 * Determined in prepare function (some pit/nest only)
70 static std::string pit_subtype_string(int type, bool nest)
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, ')');
86 case PIT_TYPE_SYMBOL_GOOD:
87 case PIT_TYPE_SYMBOL_EVIL:
88 return std::string("(").append(1, vault_aux_char).append(1, ')');
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)");
104 return _("(未定義)", "(undefined)");
109 return std::string();
113 *! @brief nestのモンスターリストをソートするための関数 /
114 * Comp function for sorting nest monster information
115 * @param u ソート処理対象配列ポインタ
121 static bool ang_sort_comp_nest_mon_info(PlayerType *player_ptr, vptr u, vptr v, int a, int b)
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;
142 if (r1_ptr->level < r2_ptr->level) {
145 if (r1_ptr->level > r2_ptr->level) {
149 if (r1_ptr->mexp < r2_ptr->mexp) {
152 if (r1_ptr->mexp > r2_ptr->mexp) {
160 * @brief nestのモンスターリストをスワップするための関数 /
161 * Swap function for sorting nest monster information
162 * @param u スワップ処理対象配列ポインタ
164 * @param a スワップ対象参照ID1
165 * @param b スワップ対象参照ID2
168 static void ang_sort_swap_nest_mon_info(PlayerType *player_ptr, vptr u, vptr v, int a, int b)
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;
181 * @brief 生成するNestの情報テーブル
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 },
197 * @todo intをenumに変更する
199 static void add_cave_info(Grid *g_ptr, int cave_mask)
201 g_ptr->info |= cave_mask;
205 * @brief タイプ5の部屋…nestを生成する / Type 5 -- Monster nests
206 * @param player_ptr プレイヤーへの参照ポインタ
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
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
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
219 * Note that the "get_mon_num()" function may (rarely) fail, in which\n
220 * case the nest will be empty.\n
222 * Note that "monster nests" will never contain "unique" monsters.\n
224 bool build_type5(PlayerType *player_ptr, dun_data_type *dd_ptr)
226 POSITION y, x, y1, x1, y2, x2, xval, yval;
228 nest_mon_info_type nest_mon_info[NUM_NEST_MON_TYPE];
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;
238 /* No type available */
239 if (cur_nest_type < 0) {
243 n_ptr = &nest_types[cur_nest_type];
245 /* Process a preparation function if necessary */
246 if (n_ptr->prep_func) {
247 (*(n_ptr->prep_func))(player_ptr);
249 get_mon_num_prep(player_ptr, n_ptr->hook_func, nullptr);
251 align.sub_align = SUB_ALIGN_NEUTRAL;
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];
261 /* Decline incorrect alignment */
262 if (monster_has_hostile_align(player_ptr, &align, 0, 0, r_ptr)) {
266 /* Accept this monster */
267 if (MonsterRace(r_idx).is_valid()) {
277 const auto r_idx = select_r_idx();
278 if (!r_idx.has_value()) {
282 const auto *r_ptr = &monraces_info[r_idx.value()];
284 /* Note the alignment */
285 if (r_ptr->kind_flags.has(MonsterKindType::EVIL)) {
286 align.sub_align |= SUB_ALIGN_EVIL;
288 if (r_ptr->kind_flags.has(MonsterKindType::GOOD)) {
289 align.sub_align |= SUB_ALIGN_GOOD;
292 nest_mon_info[i].r_idx = r_idx.value();
293 nest_mon_info[i].used = false;
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)) {
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);
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);
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);
330 /* Advance to the center room */
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);
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);
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);
356 /* Place a secret door */
357 switch (randint1(4)) {
359 place_secret_door(player_ptr, y1 - 1, xval, DOOR_DEFAULT);
362 place_secret_door(player_ptr, y2 + 1, xval, DOOR_DEFAULT);
365 place_secret_door(player_ptr, yval, x1 - 1, DOOR_DEFAULT);
368 place_secret_door(player_ptr, yval, x2 + 1, DOOR_DEFAULT);
373 player_ptr, CHEAT_DUNGEON, _("モンスター部屋(nest)(%s%s)を生成します。", "Monster nest (%s%s)"), n_ptr->name, pit_subtype_string(cur_nest_type, true).data());
375 /* Place some monsters */
376 for (y = yval - 2; y <= yval + 2; y++) {
377 for (x = xval - 9; x <= xval + 9; x++) {
380 i = randint0(NUM_NEST_MON_TYPE);
381 r_idx = nest_mon_info[i].r_idx;
383 /* Place that "random" monster (no groups) */
384 (void)place_specific_monster(player_ptr, 0, y, x, r_idx, 0L);
386 nest_mon_info[i].used = true;
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);
393 /* Dump the entries (prevent multi-printing) */
394 for (i = 0; i < NUM_NEST_MON_TYPE; i++) {
395 if (!nest_mon_info[i].used) {
398 for (; i < NUM_NEST_MON_TYPE - 1; i++) {
399 if (nest_mon_info[i].r_idx != nest_mon_info[i + 1].r_idx) {
402 if (!nest_mon_info[i + 1].used) {
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());
416 * @brief 生成するPitの情報テーブル
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 },
432 * @brief タイプ6の部屋…pitを生成する / Type 6 -- Monster pits
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
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
440 * XXXXXXXXXXXXXXXXXXXXX\n
441 * X0000000000000000000X\n
442 * X0112233455543322110X\n
443 * X0112233467643322110X\n
444 * X0112233455543322110X\n
445 * X0000000000000000000X\n
446 * XXXXXXXXXXXXXXXXXXXXX\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
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
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
462 * Note that the "get_mon_num()" function may (rarely) fail, in which case\n
463 * the pit will be empty.\n
465 * Note that "monster pits" will never contain "unique" monsters.\n
467 bool build_type6(PlayerType *player_ptr, dun_data_type *dd_ptr)
469 POSITION y, x, y1, x1, y2, x2, xval, yval;
472 MonsterRaceId what[16];
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;
482 /* No type available */
483 if (cur_pit_type < 0) {
487 n_ptr = &pit_types[cur_pit_type];
489 /* Process a preparation function if necessary */
490 if (n_ptr->prep_func) {
491 (*(n_ptr->prep_func))(player_ptr);
493 get_mon_num_prep(player_ptr, n_ptr->hook_func, nullptr);
495 align.sub_align = SUB_ALIGN_NEUTRAL;
497 /* Pick some monster types */
498 for (i = 0; i < 16; i++) {
499 auto r_idx = MonsterRace::empty_id();
501 MonsterRaceInfo *r_ptr = nullptr;
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];
508 /* Decline incorrect alignment */
509 if (monster_has_hostile_align(player_ptr, &align, 0, 0, r_ptr)) {
513 /* Accept this monster */
518 if (!MonsterRace(r_idx).is_valid() || !attempts) {
522 /* Note the alignment */
523 if (r_ptr->kind_flags.has(MonsterKindType::EVIL)) {
524 align.sub_align |= SUB_ALIGN_EVIL;
526 if (r_ptr->kind_flags.has(MonsterKindType::GOOD)) {
527 align.sub_align |= SUB_ALIGN_GOOD;
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)) {
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);
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);
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);
567 /* Advance to the center room */
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);
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);
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);
592 /* Place a secret door */
593 switch (randint1(4)) {
595 place_secret_door(player_ptr, y1 - 1, xval, DOOR_DEFAULT);
598 place_secret_door(player_ptr, y2 + 1, xval, DOOR_DEFAULT);
601 place_secret_door(player_ptr, yval, x1 - 1, DOOR_DEFAULT);
604 place_secret_door(player_ptr, yval, x2 + 1, DOOR_DEFAULT);
608 /* Sort the entries */
609 for (i = 0; i < 16 - 1; i++) {
610 /* Sort the entries */
611 for (j = 0; j < 16 - 1; j++) {
615 int p1 = monraces_info[what[i1]].level;
616 int p2 = monraces_info[what[i2]].level;
620 MonsterRaceId tmp = what[i1];
628 player_ptr, CHEAT_DUNGEON, _("モンスター部屋(pit)(%s%s)を生成します。", "Monster pit (%s%s)"), n_ptr->name, pit_subtype_string(cur_pit_type, false).data());
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());
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
681 place_specific_monster(player_ptr, 0, yval, xval, what[7], PM_NO_KAGE);
688 * @brief 開門トラップのモンスター配置テーブル
690 * 中央からの相対座標(X,Y)、モンスターの強さ
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 },
710 * @brief 開門トラップに配置するモンスターの条件フィルタ
712 * 穴を掘るモンスター、壁を抜けるモンスターは却下
714 static bool vault_aux_trapped_pit(PlayerType *player_ptr, MonsterRaceId r_idx)
719 auto *r_ptr = &monraces_info[r_idx];
721 if (!vault_monster_okay(player_ptr, r_idx)) {
725 /* No wall passing monster */
726 if (r_ptr->feature_flags.has_any_of({ MonsterFeatureType::PASS_WALL, MonsterFeatureType::KILL_WALL })) {
734 * @brief タイプ13の部屋…開門トラップpitの生成 / Type 13 -- Trapped monster pits
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
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
744 * XXXXXXXXXXXXXXXXXXXXXXXXX\n
746 * XXXXXXXXXXXXXXXXXXXXXXX X\n
747 * XXXXX001123454321100XXX X\n
748 * XXX0012234567654322100X X\n
749 * XXXXXXXXXXXXXXXXXXXXXXX X\n
751 * X XXXXXXXXXXXXXXXXXXXXXXX\n
752 * X X0012234567654322100XXX\n
753 * X XXX001123454321100XXXXX\n
754 * X XXXXXXXXXXXXXXXXXXXXXXX\n
756 * XXXXXXXXXXXXXXXXXXXXXXXXX\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
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
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
772 * Note that the "get_mon_num()" function may (rarely) fail, in which case\n
773 * the pit will be empty.\n
775 * Note that "monster pits" will never contain "unique" monsters.\n
777 bool build_type13(PlayerType *player_ptr, dun_data_type *dd_ptr)
779 POSITION y, x, y1, x1, y2, x2, xval, yval;
782 MonsterRaceId what[16];
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;
792 /* Only in Angband */
793 if (floor_ptr->dungeon_idx != DUNGEON_ANGBAND) {
797 /* No type available */
798 if (cur_pit_type < 0) {
802 n_ptr = &pit_types[cur_pit_type];
804 /* Process a preparation function if necessary */
805 if (n_ptr->prep_func) {
806 (*(n_ptr->prep_func))(player_ptr);
808 get_mon_num_prep(player_ptr, n_ptr->hook_func, vault_aux_trapped_pit);
810 align.sub_align = SUB_ALIGN_NEUTRAL;
812 /* Pick some monster types */
813 for (i = 0; i < 16; i++) {
814 auto r_idx = MonsterRace::empty_id();
816 MonsterRaceInfo *r_ptr = nullptr;
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];
823 /* Decline incorrect alignment */
824 if (monster_has_hostile_align(player_ptr, &align, 0, 0, r_ptr)) {
828 /* Accept this monster */
833 if (!MonsterRace(r_idx).is_valid() || !attempts) {
837 /* Note the alignment */
838 if (r_ptr->kind_flags.has(MonsterKindType::EVIL)) {
839 align.sub_align |= SUB_ALIGN_EVIL;
841 if (r_ptr->kind_flags.has(MonsterKindType::GOOD)) {
842 align.sub_align |= SUB_ALIGN_GOOD;
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)) {
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);
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);
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);
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);
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);
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);
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);
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);
914 /* Random corridor */
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);
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);
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);
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);
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;
939 /* Sort the entries */
940 for (i = 0; i < 16 - 1; i++) {
941 /* Sort the entries */
942 for (j = 0; j < 16 - 1; j++) {
946 int p1 = monraces_info[what[i1]].level;
947 int p2 = monraces_info[what[i2]].level;
951 MonsterRaceId tmp = what[i1];
959 player_ptr, CHEAT_DUNGEON, _("%s%sの罠ピットが生成されました。", "Trapped monster pit (%s%s)"), n_ptr->name, pit_subtype_string(cur_pit_type, false).data());
961 /* Select the entries */
962 for (i = 0; i < 8; i++) {
963 /* Every other entry */
964 what[i] = what[i * 2];
967 msg_print(monraces_info[what[i]].name);
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);