OSDN Git Service

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