2 * @brief モンスター魔法の実装(対モンスター処理) / Monster spells (attack monster)
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.
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"
44 * @brief モンスターが敵対モンスターにビームを当てること可能かを判定する /
45 * Determine if a beam spell will hit the target.
46 * @param player_ptr プレイヤーへの参照ポインタ
51 * @param m_ptr 使用するモンスターの構造体参照ポインタ
52 * @return ビームが到達可能ならばTRUEを返す
54 bool direct_beam(PlayerType *player_ptr, POSITION y1, POSITION x1, POSITION y2, POSITION x2, MonsterEntity *m_ptr)
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()) {
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) {
69 } else if (is_friend && is_monster(grid.m_idx) && !m_ptr->is_hostile_to_melee(floor.m_list[grid.m_idx])) {
73 if (is_friend && player_ptr->is_located_at(pos)) {
82 * @brief モンスターが敵対モンスターに直接ブレスを当てることが可能かを判定する /
83 * Determine if a breath will hit the target.
90 * @param is_friend TRUEならば、プレイヤーを巻き込む時にブレスの判定をFALSEにする。
91 * @return ブレスを直接当てられるならばTRUEを返す
93 bool breath_direct(PlayerType *player_ptr, POSITION y1, POSITION x1, POSITION y2, POSITION x2, POSITION rad, AttributeType typ, bool is_friend)
97 case AttributeType::LITE:
98 case AttributeType::LITE_WEAK:
101 case AttributeType::DISINTEGRATE:
109 projection_path grid_g(player_ptr, AngbandSystem::get_instance().get_max_range(), y1, x1, y2, x2, flg);
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)) {
118 } else if (flg & PROJECT_LOS) {
119 if (!cave_los_bold(player_ptr->current_floor_ptr, ny, nx)) {
123 if (!cave_has_flag_bold(player_ptr->current_floor_ptr, ny, nx, TerrainCharacteristics::PROJECT)) {
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)) {
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)) {
143 } else if (flg & PROJECT_LOS) {
144 if (los(player_ptr, y1, x1, y2, x2) && (distance(y1, x1, y2, x2) <= rad)) {
147 if (los(player_ptr, y1, x1, player_ptr->y, player_ptr->x) && (distance(y1, x1, player_ptr->y, player_ptr->x) <= rad)) {
151 if (projectable(player_ptr, y1, x1, y2, x2) && (distance(y1, x1, y2, x2) <= rad)) {
154 if (projectable(player_ptr, y1, x1, player_ptr->y, player_ptr->x) && (distance(y1, x1, player_ptr->y, player_ptr->x) <= rad)) {
160 POSITION gx[1024], gy[1024];
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)) {
169 if (player_ptr->is_located_at(pos)) {
178 if (is_friend && hityou) {
186 * @brief モンスターが特殊能力の目標地点を決める処理 /
187 * Get the actual center point of ball spells (rad > 1) (originally from TOband)
188 * @param player_ptr プレイヤーへの参照ポインタ
191 * @param ty 目標Y座標を返す参照ポインタ
192 * @param tx 目標X座標を返す参照ポインタ
193 * @param flg 判定のフラグ配列
195 void get_project_point(PlayerType *player_ptr, POSITION sy, POSITION sx, POSITION *ty, POSITION *tx, BIT_FLAGS flg)
197 projection_path path_g(player_ptr, AngbandSystem::get_instance().get_max_range(), sy, sx, *ty, *tx, flg);
200 for (const auto &[y, x] : path_g) {
201 if (!cave_has_flag_bold(player_ptr->current_floor_ptr, y, x, TerrainCharacteristics::PROJECT)) {
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を変えす。
218 bool dispel_check_monster(PlayerType *player_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx)
220 const auto &t_ref = player_ptr->current_floor_ptr->m_list[t_idx];
221 if (t_ref.is_invulnerable()) {
225 constexpr auto threshold = 25;
226 if ((t_ref.mspeed < (STANDARD_SPEED + threshold)) && t_ref.is_accelerated()) {
230 if ((t_idx == player_ptr->riding) && dispel_check(player_ptr, m_idx)) {
238 * @brief モンスターがプレイヤーに魔力消去を与えるべきかを判定するルーチン
239 * Check should monster cast dispel spell.
240 * @param m_idx モンスターの構造体配列ID
241 * @return 魔力消去をかけるべきならTRUEを返す。
243 bool dispel_check(PlayerType *player_ptr, MONSTER_IDX m_idx)
245 if (is_invuln(player_ptr)) {
249 if (player_ptr->wraith_form) {
253 if (player_ptr->shield) {
257 if (player_ptr->magicdef) {
261 if (player_ptr->multishadow) {
265 if (player_ptr->dustrobe) {
269 PlayerClass pc(player_ptr);
270 if (player_ptr->shero && !pc.equals(PlayerClassType::BERSERKER)) {
274 if (player_ptr->mimic_form == MimicKindType::DEMON_LORD) {
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))) {
286 if (player_ptr->special_defense & DEFENSE_ACID) {
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))) {
297 if (player_ptr->special_defense & DEFENSE_FIRE) {
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))) {
308 if (player_ptr->special_defense & DEFENSE_ELEC) {
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))) {
318 if (player_ptr->special_defense & DEFENSE_COLD) {
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)) {
328 if (player_ptr->special_defense & DEFENSE_POIS) {
333 if (player_ptr->ult_res) {
337 if (player_ptr->tsuyoshi) {
341 if ((player_ptr->special_attack & ATTACK_ACID) && r_ptr->resistance_flags.has_none_of(RFR_EFF_IM_ACID_MASK)) {
345 if ((player_ptr->special_attack & ATTACK_FIRE) && r_ptr->resistance_flags.has_none_of(RFR_EFF_IM_FIRE_MASK)) {
349 if ((player_ptr->special_attack & ATTACK_ELEC) && r_ptr->resistance_flags.has_none_of(RFR_EFF_IM_ELEC_MASK)) {
353 if ((player_ptr->special_attack & ATTACK_COLD) && r_ptr->resistance_flags.has_none_of(RFR_EFF_IM_COLD_MASK)) {
357 if ((player_ptr->special_attack & ATTACK_POIS) && r_ptr->resistance_flags.has_none_of(RFR_EFF_IM_POISON_MASK)) {
361 if ((player_ptr->pspeed < 145) && is_fast(player_ptr)) {
365 constexpr auto threshold = 25;
366 const auto threshold_speed = STANDARD_SPEED + threshold;
367 if (player_ptr->lightspeed && (m_ptr->mspeed <= threshold_speed)) {
371 const auto &m_ref = floor_ref.m_list[player_ptr->riding];
372 if (player_ptr->riding == 0) {
376 return (floor_ref.m_list[player_ptr->riding].mspeed < threshold_speed) && m_ref.is_accelerated();