OSDN Git Service

Merge pull request #3172 from Hourier/Remove-Nickname-Command
[hengbandforosx/hengbandosx.git] / src / monster-floor / monster-summon.cpp
1 #include "monster-floor/monster-summon.h"
2 #include "dungeon/dungeon-flag-types.h"
3 #include "floor/geometry.h"
4 #include "floor/wild.h"
5 #include "main/sound-definitions-table.h"
6 #include "main/sound-of-music.h"
7 #include "monster-floor/monster-generator.h"
8 #include "monster-floor/place-monster-types.h"
9 #include "monster-race/monster-race-hook.h"
10 #include "monster-race/monster-race.h"
11 #include "monster-race/race-flags1.h"
12 #include "monster-race/race-flags7.h"
13 #include "monster-race/race-indice-types.h"
14 #include "monster/monster-info.h"
15 #include "monster/monster-list.h"
16 #include "monster/monster-util.h"
17 #include "mspell/summon-checker.h"
18 #include "spell/summon-types.h"
19 #include "system/dungeon-info.h"
20 #include "system/floor-type-definition.h"
21 #include "system/monster-entity.h"
22 #include "system/monster-race-info.h"
23 #include "system/player-type-definition.h"
24
25 /*!
26  * @var summon_specific_who
27  * @brief 召喚を行ったプレイヤーあるいはモンスターのIDを示すグローバル変数 / Hack -- the index of the summoning monster
28  * @todo summon_specific_who グローバル変数の除去と関数引数への代替を行う
29  */
30 int summon_specific_who = -1;
31
32 /*!
33  * @var summon_unique_okay
34  * @brief 召喚対象にユニークを含めるかを示すグローバル変数 / summoning unique enable
35  * @todo summon_unique_okay グローバル変数の除去と関数引数への代替を行う
36  */
37 bool summon_unique_okay = false;
38
39 /*!
40  * @brief モンスターが召喚の基本条件に合っているかをチェックする / Hack -- help decide if a monster race is "okay" to summon
41  * @param r_idx チェックするモンスター種族ID
42  * @return 召喚対象にできるならばTRUE
43  */
44 static bool summon_specific_okay(PlayerType *player_ptr, MonsterRaceId r_idx)
45 {
46     auto *r_ptr = &monraces_info[r_idx];
47     if (!mon_hook_dungeon(player_ptr, r_idx)) {
48         return false;
49     }
50
51     auto &floor = *player_ptr->current_floor_ptr;
52     if (summon_specific_who > 0) {
53         auto *m_ptr = &floor.m_list[summon_specific_who];
54         if (monster_has_hostile_align(player_ptr, m_ptr, 0, 0, r_ptr)) {
55             return false;
56         }
57     } else if (summon_specific_who < 0) {
58         if (monster_has_hostile_align(player_ptr, nullptr, 10, -10, r_ptr) && !one_in_(std::abs(player_ptr->alignment) / 2 + 1)) {
59             return false;
60         }
61     }
62
63     if (!summon_unique_okay && (r_ptr->kind_flags.has(MonsterKindType::UNIQUE) || (r_ptr->population_flags.has(MonsterPopulationType::NAZGUL)))) {
64         return false;
65     }
66
67     if (!summon_specific_type) {
68         return true;
69     }
70
71     const auto is_like_unique = r_ptr->kind_flags.has(MonsterKindType::UNIQUE) || (r_ptr->population_flags.has(MonsterPopulationType::NAZGUL));
72     if ((summon_specific_who < 0) && is_like_unique && monster_has_hostile_align(player_ptr, nullptr, 10, -10, r_ptr)) {
73         return false;
74     }
75
76     if ((r_ptr->flags7 & RF7_CHAMELEON) && floor.get_dungeon_definition().flags.has(DungeonFeatureType::CHAMELEON)) {
77         return true;
78     }
79
80     if (summon_specific_who > 0) {
81         auto *m_ptr = &floor.m_list[summon_specific_who];
82         return check_summon_specific(player_ptr, m_ptr->r_idx, r_idx);
83     } else {
84         return check_summon_specific(player_ptr, MonsterRaceId::PLAYER, r_idx);
85     }
86 }
87
88 /*!
89  * @brief モンスター死亡時に召喚されうるモンスターかどうかの判定
90  * @param type モンスター種族ID
91  * @return 召喚されうるならばTRUE (あやしい影として生成されない)
92  */
93 static bool is_dead_summoning(summon_type type)
94 {
95     bool summoning = type == SUMMON_BLUE_HORROR;
96     summoning |= type == SUMMON_DAWN;
97     summoning |= type == SUMMON_TOTEM_MOAI;
98     return summoning;
99 }
100
101 /*!
102  * @brief 荒野のレベルを含めた階層レベルを返す
103  * @param player_ptr プレイヤーへの参照ポインタ
104  * @return 階層レベル
105  * @details
106  * ダンジョン及びクエストはdun_level>0となる。
107  * 荒野はdun_level==0なので、その場合荒野レベルを返す。
108  */
109 DEPTH get_dungeon_or_wilderness_level(PlayerType *player_ptr)
110 {
111     auto *floor_ptr = player_ptr->current_floor_ptr;
112     if (floor_ptr->dun_level > 0) {
113         return floor_ptr->dun_level;
114     }
115
116     return wilderness[player_ptr->wilderness_y][player_ptr->wilderness_x].level;
117 }
118
119 /*!
120  * @brief モンスターを召喚により配置する / Place a monster (of the specified "type") near the given location. Return TRUE if a monster was actually summoned.
121  * @param player_ptr プレイヤーへの参照ポインタ
122  * @param who 召喚主のモンスター情報ID
123  * @param y1 目標地点y座標
124  * @param x1 目標地点x座標
125  * @param lev 相当生成階
126  * @param type 召喚種別
127  * @param mode 生成オプション
128  * @return 召喚できたらtrueを返す
129  */
130 bool summon_specific(PlayerType *player_ptr, MONSTER_IDX who, POSITION y1, POSITION x1, DEPTH lev, summon_type type, BIT_FLAGS mode)
131 {
132     auto *floor_ptr = player_ptr->current_floor_ptr;
133     if (floor_ptr->inside_arena) {
134         return false;
135     }
136
137     POSITION x, y;
138     if (!mon_scatter(player_ptr, MonsterRace::empty_id(), &y, &x, y1, x1, 2)) {
139         return false;
140     }
141
142     summon_specific_who = who;
143     summon_specific_type = type;
144     summon_unique_okay = (mode & PM_ALLOW_UNIQUE) != 0;
145     get_mon_num_prep(player_ptr, summon_specific_okay, get_monster_hook2(player_ptr, y, x));
146
147     DEPTH dlev = get_dungeon_or_wilderness_level(player_ptr);
148     MonsterRaceId r_idx = get_mon_num(player_ptr, 0, (dlev + lev) / 2 + 5, 0);
149     if (!MonsterRace(r_idx).is_valid()) {
150         summon_specific_type = SUMMON_NONE;
151         return false;
152     }
153
154     if (is_dead_summoning(type)) {
155         mode |= PM_NO_KAGE;
156     }
157
158     if (!place_monster_aux(player_ptr, who, y, x, r_idx, mode)) {
159         summon_specific_type = SUMMON_NONE;
160         return false;
161     }
162
163     summon_specific_type = SUMMON_NONE;
164
165     bool notice = false;
166     if (who <= 0) {
167         notice = true;
168     } else {
169         auto *m_ptr = &player_ptr->current_floor_ptr->m_list[who];
170         if (m_ptr->is_pet()) {
171             notice = true;
172         } else if (is_seen(player_ptr, m_ptr)) {
173             notice = true;
174         } else if (player_can_see_bold(player_ptr, y, x)) {
175             notice = true;
176         }
177     }
178
179     if (notice) {
180         sound(SOUND_SUMMON);
181     }
182
183     return true;
184 }
185
186 /*!
187  * @brief 特定モンスター種族を召喚により生成する / A "dangerous" function, creates a pet of the specified type
188  * @param player_ptr プレイヤーへの参照ポインタ
189  * @param who 召喚主のモンスター情報ID
190  * @param oy 目標地点y座標
191  * @param ox 目標地点x座標
192  * @param r_idx 生成するモンスター種族ID
193  * @param mode 生成オプション
194  * @return 召喚できたらtrueを返す
195  */
196 bool summon_named_creature(PlayerType *player_ptr, MONSTER_IDX who, POSITION oy, POSITION ox, MonsterRaceId r_idx, BIT_FLAGS mode)
197 {
198     if (!MonsterRace(r_idx).is_valid() || (r_idx >= static_cast<MonsterRaceId>(monraces_info.size()))) {
199         return false;
200     }
201
202     POSITION x, y;
203     if (player_ptr->current_floor_ptr->inside_arena || !mon_scatter(player_ptr, r_idx, &y, &x, oy, ox, 2)) {
204         return false;
205     }
206
207     return place_monster_aux(player_ptr, who, y, x, r_idx, (mode | PM_NO_KAGE));
208 }