OSDN Git Service

[Refactor] #2807 Renamed monster-race-definition.h to monster-race-info.h
[hengbandforosx/hengbandosx.git] / src / mspell / mspell-judgement.cpp
1 /*!
2  * @brief モンスター魔法の実装(対モンスター処理) / Monster spells (attack monster)
3  * @date 2014/01/17
4  * @author
5  * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
6  * This software may be copied and distributed for educational, research,
7  * and not for profit purposes provided that this copyright and statement
8  * are included in all such copies.  Other copyrights may also apply.
9  * 2014 Deskull rearranged comment for Doxygen.
10  */
11
12 #include "mspell/mspell-judgement.h"
13 #include "effect/attribute-types.h"
14 #include "effect/effect-characteristics.h"
15 #include "floor/cave.h"
16 #include "floor/geometry.h"
17 #include "floor/line-of-sight.h"
18 #include "grid/feature-flag-types.h"
19 #include "main/sound-definitions-table.h"
20 #include "monster-race/monster-race.h"
21 #include "monster-race/race-flags-resistance.h"
22 #include "monster/monster-info.h"
23 #include "monster/monster-status.h"
24 #include "player-base/player-class.h"
25 #include "player-base/player-race.h"
26 #include "player-info/race-info.h"
27 #include "player/attack-defense-types.h"
28 #include "player/player-status-flags.h"
29 #include "player/player-status.h"
30 #include "player/special-defense-types.h"
31 #include "realm/realm-song-numbers.h"
32 #include "spell-realm/spells-song.h"
33 #include "spell/range-calc.h"
34 #include "system/dungeon-info.h"
35 #include "system/floor-type-definition.h"
36 #include "system/grid-type-definition.h"
37 #include "system/monster-race-info.h"
38 #include "system/monster-type-definition.h"
39 #include "system/player-type-definition.h"
40 #include "target/projection-path-calculator.h"
41
42 /*!
43  * @brief モンスターが敵対モンスターにビームを当てること可能かを判定する /
44  * Determine if a beam spell will hit the target.
45  * @param player_ptr プレイヤーへの参照ポインタ
46  * @param y1 始点のY座標
47  * @param x1 始点のX座標
48  * @param y2 目標のY座標
49  * @param x2 目標のX座標
50  * @param m_ptr 使用するモンスターの構造体参照ポインタ
51  * @return ビームが到達可能ならばTRUEを返す
52  */
53 bool direct_beam(PlayerType *player_ptr, POSITION y1, POSITION x1, POSITION y2, POSITION x2, MonsterEntity *m_ptr)
54 {
55     auto *floor_ptr = player_ptr->current_floor_ptr;
56     projection_path grid_g(player_ptr, get_max_range(player_ptr), y1, x1, y2, x2, PROJECT_THRU);
57     if (grid_g.path_num()) {
58         return false;
59     }
60
61     bool hit2 = false;
62     bool is_friend = m_ptr->is_pet();
63     for (const auto &[y, x] : grid_g) {
64         const auto &g_ref = floor_ptr->grid_array[y][x];
65         if (y == y2 && x == x2) {
66             hit2 = true;
67         } else if (is_friend && g_ref.m_idx > 0 && !are_enemies(player_ptr, *m_ptr, floor_ptr->m_list[g_ref.m_idx])) {
68             return false;
69         }
70
71         if (is_friend && player_bold(player_ptr, y, x)) {
72             return false;
73         }
74     }
75
76     if (!hit2) {
77         return false;
78     }
79     return true;
80 }
81
82 /*!
83  * @brief モンスターが敵対モンスターに直接ブレスを当てることが可能かを判定する /
84  * Determine if a breath will hit the target.
85  * @param y1 始点のY座標
86  * @param x1 始点のX座標
87  * @param y2 目標のY座標
88  * @param x2 目標のX座標
89  * @param rad 半径
90  * @param typ 効果属性ID
91  * @param is_friend TRUEならば、プレイヤーを巻き込む時にブレスの判定をFALSEにする。
92  * @return ブレスを直接当てられるならばTRUEを返す
93  */
94 bool breath_direct(PlayerType *player_ptr, POSITION y1, POSITION x1, POSITION y2, POSITION x2, POSITION rad, AttributeType typ, bool is_friend)
95 {
96     BIT_FLAGS flg;
97     switch (typ) {
98     case AttributeType::LITE:
99     case AttributeType::LITE_WEAK:
100         flg = PROJECT_LOS;
101         break;
102     case AttributeType::DISINTEGRATE:
103         flg = PROJECT_DISI;
104         break;
105     default:
106         flg = 0;
107         break;
108     }
109
110     projection_path grid_g(player_ptr, get_max_range(player_ptr), y1, x1, y2, x2, flg);
111     int i = 0;
112     POSITION y = y1;
113     POSITION x = x1;
114     for (const auto &[ny, nx] : grid_g) {
115         if (flg & PROJECT_DISI) {
116             if (cave_stop_disintegration(player_ptr->current_floor_ptr, ny, nx)) {
117                 break;
118             }
119         } else if (flg & PROJECT_LOS) {
120             if (!cave_los_bold(player_ptr->current_floor_ptr, ny, nx)) {
121                 break;
122             }
123         } else {
124             if (!cave_has_flag_bold(player_ptr->current_floor_ptr, ny, nx, TerrainCharacteristics::PROJECT)) {
125                 break;
126             }
127         }
128
129         y = ny;
130         x = nx;
131         i++;
132     }
133
134     bool hit2 = false;
135     bool hityou = false;
136     if (i == 0) {
137         if (flg & PROJECT_DISI) {
138             if (in_disintegration_range(player_ptr->current_floor_ptr, y1, x1, y2, x2) && (distance(y1, x1, y2, x2) <= rad)) {
139                 hit2 = true;
140             }
141             if (in_disintegration_range(player_ptr->current_floor_ptr, y1, x1, player_ptr->y, player_ptr->x) && (distance(y1, x1, player_ptr->y, player_ptr->x) <= rad)) {
142                 hityou = true;
143             }
144         } else if (flg & PROJECT_LOS) {
145             if (los(player_ptr, y1, x1, y2, x2) && (distance(y1, x1, y2, x2) <= rad)) {
146                 hit2 = true;
147             }
148             if (los(player_ptr, y1, x1, player_ptr->y, player_ptr->x) && (distance(y1, x1, player_ptr->y, player_ptr->x) <= rad)) {
149                 hityou = true;
150             }
151         } else {
152             if (projectable(player_ptr, y1, x1, y2, x2) && (distance(y1, x1, y2, x2) <= rad)) {
153                 hit2 = true;
154             }
155             if (projectable(player_ptr, y1, x1, player_ptr->y, player_ptr->x) && (distance(y1, x1, player_ptr->y, player_ptr->x) <= rad)) {
156                 hityou = true;
157             }
158         }
159     } else {
160         int grids = 0;
161         POSITION gx[1024], gy[1024];
162         POSITION gm[32];
163         POSITION gm_rad = rad;
164         breath_shape(player_ptr, grid_g, grid_g.path_num(), &grids, gx, gy, gm, &gm_rad, rad, y1, x1, y, x, typ);
165         for (i = 0; i < grids; i++) {
166             y = gy[i];
167             x = gx[i];
168             if ((y == y2) && (x == x2)) {
169                 hit2 = true;
170             }
171             if (player_bold(player_ptr, y, x)) {
172                 hityou = true;
173             }
174         }
175     }
176
177     if (!hit2) {
178         return false;
179     }
180     if (is_friend && hityou) {
181         return false;
182     }
183
184     return true;
185 }
186
187 /*!
188  * @brief モンスターが特殊能力の目標地点を決める処理 /
189  * Get the actual center point of ball spells (rad > 1) (originally from TOband)
190  * @param player_ptr プレイヤーへの参照ポインタ
191  * @param sy 始点のY座標
192  * @param sx 始点のX座標
193  * @param ty 目標Y座標を返す参照ポインタ
194  * @param tx 目標X座標を返す参照ポインタ
195  * @param flg 判定のフラグ配列
196  */
197 void get_project_point(PlayerType *player_ptr, POSITION sy, POSITION sx, POSITION *ty, POSITION *tx, BIT_FLAGS flg)
198 {
199     projection_path path_g(player_ptr, get_max_range(player_ptr), sy, sx, *ty, *tx, flg);
200     *ty = sy;
201     *tx = sx;
202     for (const auto &[y, x] : path_g) {
203         if (!cave_has_flag_bold(player_ptr->current_floor_ptr, y, x, TerrainCharacteristics::PROJECT)) {
204             break;
205         }
206
207         *ty = y;
208         *tx = x;
209     }
210 }
211
212 /*!
213  * @brief モンスターが敵モンスターに魔力消去を使うかどうかを返す /
214  * Check should monster cast dispel spell at other monster.
215  * @param player_ptr プレイヤーへの参照ポインタ
216  * @param m_idx 術者のモンスターID
217  * @param t_idx 目標のモンスターID
218  * @return 魔力消去を使うべきならばTRUEを変えす。
219  */
220 bool dispel_check_monster(PlayerType *player_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx)
221 {
222     const auto &t_ref = player_ptr->current_floor_ptr->m_list[t_idx];
223     if (t_ref.is_invulnerable()) {
224         return true;
225     }
226
227     constexpr auto threshold = 25;
228     if ((t_ref.mspeed < (STANDARD_SPEED + threshold)) && t_ref.is_accelerated()) {
229         return true;
230     }
231
232     if ((t_idx == player_ptr->riding) && dispel_check(player_ptr, m_idx)) {
233         return true;
234     }
235
236     return false;
237 }
238
239 /*!
240  * @brief モンスターがプレイヤーに魔力消去を与えるべきかを判定するルーチン
241  * Check should monster cast dispel spell.
242  * @param m_idx モンスターの構造体配列ID
243  * @return 魔力消去をかけるべきならTRUEを返す。
244  */
245 bool dispel_check(PlayerType *player_ptr, MONSTER_IDX m_idx)
246 {
247     if (is_invuln(player_ptr)) {
248         return true;
249     }
250
251     if (player_ptr->wraith_form) {
252         return true;
253     }
254
255     if (player_ptr->shield) {
256         return true;
257     }
258
259     if (player_ptr->magicdef) {
260         return true;
261     }
262
263     if (player_ptr->multishadow) {
264         return true;
265     }
266
267     if (player_ptr->dustrobe) {
268         return true;
269     }
270
271     PlayerClass pc(player_ptr);
272     if (player_ptr->shero && !pc.equals(PlayerClassType::BERSERKER)) {
273         return true;
274     }
275
276     if (player_ptr->mimic_form == MimicKindType::DEMON_LORD) {
277         return true;
278     }
279
280     const auto &floor_ref = *player_ptr->current_floor_ptr;
281     auto *m_ptr = &floor_ref.m_list[m_idx];
282     auto *r_ptr = &monraces_info[m_ptr->r_idx];
283     if (r_ptr->ability_flags.has(MonsterAbilityType::BR_ACID)) {
284         if (!has_immune_acid(player_ptr) && (player_ptr->oppose_acid || music_singing(player_ptr, MUSIC_RESIST))) {
285             return true;
286         }
287
288         if (player_ptr->special_defense & DEFENSE_ACID) {
289             return true;
290         }
291     }
292
293     if (r_ptr->ability_flags.has(MonsterAbilityType::BR_FIRE)) {
294         if (!(PlayerRace(player_ptr).equals(PlayerRaceType::BALROG) && player_ptr->lev > 44)) {
295             if (!has_immune_fire(player_ptr) && (player_ptr->oppose_fire || music_singing(player_ptr, MUSIC_RESIST))) {
296                 return true;
297             }
298
299             if (player_ptr->special_defense & DEFENSE_FIRE) {
300                 return true;
301             }
302         }
303     }
304
305     if (r_ptr->ability_flags.has(MonsterAbilityType::BR_ELEC)) {
306         if (!has_immune_elec(player_ptr) && (player_ptr->oppose_elec || music_singing(player_ptr, MUSIC_RESIST))) {
307             return true;
308         }
309
310         if (player_ptr->special_defense & DEFENSE_ELEC) {
311             return true;
312         }
313     }
314
315     if (r_ptr->ability_flags.has(MonsterAbilityType::BR_COLD)) {
316         if (!has_immune_cold(player_ptr) && (player_ptr->oppose_cold || music_singing(player_ptr, MUSIC_RESIST))) {
317             return true;
318         }
319
320         if (player_ptr->special_defense & DEFENSE_COLD) {
321             return true;
322         }
323     }
324
325     if (r_ptr->ability_flags.has_any_of({ MonsterAbilityType::BR_POIS, MonsterAbilityType::BR_NUKE }) && !(pc.equals(PlayerClassType::NINJA) && (player_ptr->lev > 44))) {
326         if (player_ptr->oppose_pois || music_singing(player_ptr, MUSIC_RESIST)) {
327             return true;
328         }
329
330         if (player_ptr->special_defense & DEFENSE_POIS) {
331             return true;
332         }
333     }
334
335     if (player_ptr->ult_res) {
336         return true;
337     }
338
339     if (player_ptr->tsuyoshi) {
340         return true;
341     }
342
343     if ((player_ptr->special_attack & ATTACK_ACID) && r_ptr->resistance_flags.has_none_of(RFR_EFF_IM_ACID_MASK)) {
344         return true;
345     }
346
347     if ((player_ptr->special_attack & ATTACK_FIRE) && r_ptr->resistance_flags.has_none_of(RFR_EFF_IM_FIRE_MASK)) {
348         return true;
349     }
350
351     if ((player_ptr->special_attack & ATTACK_ELEC) && r_ptr->resistance_flags.has_none_of(RFR_EFF_IM_ELEC_MASK)) {
352         return true;
353     }
354
355     if ((player_ptr->special_attack & ATTACK_COLD) && r_ptr->resistance_flags.has_none_of(RFR_EFF_IM_COLD_MASK)) {
356         return true;
357     }
358
359     if ((player_ptr->special_attack & ATTACK_POIS) && r_ptr->resistance_flags.has_none_of(RFR_EFF_IM_POISON_MASK)) {
360         return true;
361     }
362
363     if ((player_ptr->pspeed < 145) && is_fast(player_ptr)) {
364         return true;
365     }
366
367     constexpr auto threshold = 25;
368     const auto threshold_speed = STANDARD_SPEED + threshold;
369     if (player_ptr->lightspeed && (m_ptr->mspeed <= threshold_speed)) {
370         return true;
371     }
372
373     const auto &m_ref = floor_ref.m_list[player_ptr->riding];
374     if (player_ptr->riding == 0) {
375         return false;
376     }
377
378     return (floor_ref.m_list[player_ptr->riding].mspeed < threshold_speed) && m_ref.is_accelerated();
379 }