OSDN Git Service

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