OSDN Git Service

Merge pull request #2164 from Slimebreath6078/feature/Refactor_mflag_kind
[hengbandforosx/hengbandosx.git] / src / mspell / mspell-lite.cpp
1 /*!
2  * @brief モンスターの魔法によってフロアを明るくする処理及びその判定
3  * @date 2020/07/23
4  * @author Hourier
5  */
6
7 #include "mspell/mspell-lite.h"
8 #include "dungeon/dungeon-flag-types.h"
9 #include "dungeon/dungeon.h"
10 #include "floor/cave.h"
11 #include "floor/geometry.h"
12 #include "floor/line-of-sight.h"
13 #include "grid/feature.h"
14 #include "monster-race/monster-race.h"
15 #include "monster-race/race-ability-mask.h"
16 #include "monster-race/race-flags2.h"
17 #include "monster-race/race-flags3.h"
18 #include "monster-race/race-flags7.h"
19 #include "mspell/mspell-attack-util.h"
20 #include "mspell/mspell-judgement.h"
21 #include "player-base/player-class.h"
22 #include "spell/range-calc.h"
23 #include "system/floor-type-definition.h"
24 #include "system/grid-type-definition.h"
25 #include "system/monster-race-definition.h"
26 #include "system/monster-type-definition.h"
27 #include "system/player-type-definition.h"
28 #include "target/projection-path-calculator.h"
29 #include "util/bit-flags-calculator.h"
30
31 /*!
32  * @brief モンスターがプレイヤーにダメージを与えるための最適な座標を算出する /
33  * @param player_ptr プレイヤーへの参照ポインタ
34  * @param m_ptr 技能を使用するモンスター構造体の参照ポインタ
35  * @param yp 最適な目標地点のY座標を返す参照ポインタ
36  * @param xp 最適な目標地点のX座標を返す参照ポインタ
37  * @param f_flag 射線に入れるのを避ける地形の所持フラグ
38  * @param path_check 射線を判定するための関数ポインタ
39  * @return 有効な座標があった場合TRUEを返す
40  */
41 bool adjacent_grid_check(PlayerType *player_ptr, monster_type *m_ptr, POSITION *yp, POSITION *xp, FloorFeatureType f_flag, path_check_pf path_check)
42 {
43     static int tonari_y[4][8] = { { -1, -1, -1, 0, 0, 1, 1, 1 }, { -1, -1, -1, 0, 0, 1, 1, 1 }, { 1, 1, 1, 0, 0, -1, -1, -1 }, { 1, 1, 1, 0, 0, -1, -1, -1 } };
44     static int tonari_x[4][8] = { { -1, 0, 1, -1, 1, -1, 0, 1 }, { 1, 0, -1, 1, -1, 1, 0, -1 }, { -1, 0, 1, -1, 1, -1, 0, 1 }, { 1, 0, -1, 1, -1, 1, 0, -1 } };
45
46     int next;
47     if (m_ptr->fy < player_ptr->y && m_ptr->fx < player_ptr->x)
48         next = 0;
49     else if (m_ptr->fy < player_ptr->y)
50         next = 1;
51     else if (m_ptr->fx < player_ptr->x)
52         next = 2;
53     else
54         next = 3;
55
56     for (int i = 0; i < 8; i++) {
57         int next_x = *xp + tonari_x[next][i];
58         int next_y = *yp + tonari_y[next][i];
59         grid_type *g_ptr;
60         g_ptr = &player_ptr->current_floor_ptr->grid_array[next_y][next_x];
61         if (!g_ptr->cave_has_flag(f_flag))
62             continue;
63
64         if (path_check(player_ptr, m_ptr->fy, m_ptr->fx, next_y, next_x)) {
65             *yp = next_y;
66             *xp = next_x;
67             return true;
68         }
69     }
70
71     return false;
72 }
73
74 void decide_lite_range(PlayerType *player_ptr, msa_type *msa_ptr)
75 {
76     if (msa_ptr->ability_flags.has_not(MonsterAbilityType::BR_LITE))
77         return;
78
79     msa_ptr->y_br_lite = msa_ptr->y;
80     msa_ptr->x_br_lite = msa_ptr->x;
81     if (los(player_ptr, msa_ptr->m_ptr->fy, msa_ptr->m_ptr->fx, msa_ptr->y_br_lite, msa_ptr->x_br_lite)) {
82         auto *f_ptr = &f_info[player_ptr->current_floor_ptr->grid_array[msa_ptr->y_br_lite][msa_ptr->x_br_lite].feat];
83         if (f_ptr->flags.has_not(FloorFeatureType::LOS) && f_ptr->flags.has(FloorFeatureType::PROJECT) && one_in_(2))
84             msa_ptr->ability_flags.reset(MonsterAbilityType::BR_LITE);
85     } else if (!adjacent_grid_check(player_ptr, msa_ptr->m_ptr, &msa_ptr->y_br_lite, &msa_ptr->x_br_lite, FloorFeatureType::LOS, los))
86         msa_ptr->ability_flags.reset(MonsterAbilityType::BR_LITE);
87
88     if (msa_ptr->ability_flags.has(MonsterAbilityType::BR_LITE))
89         return;
90
91     msa_ptr->y_br_lite = 0;
92     msa_ptr->x_br_lite = 0;
93 }
94
95 static void feature_projection(floor_type *floor_ptr, msa_type *msa_ptr)
96 {
97     auto *f_ptr = &f_info[floor_ptr->grid_array[msa_ptr->y][msa_ptr->x].feat];
98     if (f_ptr->flags.has(FloorFeatureType::PROJECT))
99         return;
100
101     if (msa_ptr->ability_flags.has(MonsterAbilityType::BR_DISI) && f_ptr->flags.has(FloorFeatureType::HURT_DISI) && one_in_(2)) {
102         msa_ptr->do_spell = DO_SPELL_BR_DISI;
103         return;
104     }
105
106     if (msa_ptr->ability_flags.has(MonsterAbilityType::BR_LITE) && f_ptr->flags.has(FloorFeatureType::LOS) && one_in_(2))
107         msa_ptr->do_spell = DO_SPELL_BR_LITE;
108 }
109
110 static void check_lite_area_by_mspell(PlayerType *player_ptr, msa_type *msa_ptr)
111 {
112     if (msa_ptr->ability_flags.has(MonsterAbilityType::BR_DISI) && (msa_ptr->m_ptr->cdis < get_max_range(player_ptr) / 2) && in_disintegration_range(player_ptr->current_floor_ptr, msa_ptr->m_ptr->fy, msa_ptr->m_ptr->fx, msa_ptr->y, msa_ptr->x) && (one_in_(10) || (projectable(player_ptr, msa_ptr->y, msa_ptr->x, msa_ptr->m_ptr->fy, msa_ptr->m_ptr->fx) && one_in_(2)))) {
113         msa_ptr->do_spell = DO_SPELL_BR_DISI;
114         msa_ptr->success = true;
115         return;
116     }
117
118     if (msa_ptr->ability_flags.has(MonsterAbilityType::BR_LITE) && (msa_ptr->m_ptr->cdis < get_max_range(player_ptr) / 2) && los(player_ptr, msa_ptr->m_ptr->fy, msa_ptr->m_ptr->fx, msa_ptr->y, msa_ptr->x) && one_in_(5)) {
119         msa_ptr->do_spell = DO_SPELL_BR_LITE;
120         msa_ptr->success = true;
121         return;
122     }
123
124     if (msa_ptr->ability_flags.has_not(MonsterAbilityType::BA_LITE) || (msa_ptr->m_ptr->cdis > get_max_range(player_ptr)))
125         return;
126
127     POSITION by = msa_ptr->y, bx = msa_ptr->x;
128     get_project_point(player_ptr, msa_ptr->m_ptr->fy, msa_ptr->m_ptr->fx, &by, &bx, 0L);
129     if ((distance(by, bx, msa_ptr->y, msa_ptr->x) <= 3) && los(player_ptr, by, bx, msa_ptr->y, msa_ptr->x) && one_in_(5)) {
130         msa_ptr->do_spell = DO_SPELL_BA_LITE;
131         msa_ptr->success = true;
132     }
133 }
134
135 static void decide_lite_breath(PlayerType *player_ptr, msa_type *msa_ptr)
136 {
137     if (msa_ptr->success)
138         return;
139
140     if (msa_ptr->m_ptr->target_y && msa_ptr->m_ptr->target_x) {
141         msa_ptr->y = msa_ptr->m_ptr->target_y;
142         msa_ptr->x = msa_ptr->m_ptr->target_x;
143         msa_ptr->ability_flags &= RF_ABILITY_INDIRECT_MASK;
144         msa_ptr->success = true;
145     }
146
147     if ((msa_ptr->y_br_lite == 0) || (msa_ptr->x_br_lite == 0) || (msa_ptr->m_ptr->cdis > get_max_range(player_ptr) / 2) || !one_in_(5))
148         return;
149
150     if (msa_ptr->success) {
151         msa_ptr->ability_flags.set(MonsterAbilityType::BR_LITE);
152         return;
153     }
154
155     msa_ptr->y = msa_ptr->y_br_lite;
156     msa_ptr->x = msa_ptr->x_br_lite;
157     msa_ptr->do_spell = DO_SPELL_BR_LITE;
158     msa_ptr->success = true;
159 }
160
161 bool decide_lite_projection(PlayerType *player_ptr, msa_type *msa_ptr)
162 {
163     if (projectable(player_ptr, msa_ptr->m_ptr->fy, msa_ptr->m_ptr->fx, msa_ptr->y, msa_ptr->x)) {
164         feature_projection(player_ptr->current_floor_ptr, msa_ptr);
165         return true;
166     }
167
168     msa_ptr->success = false;
169     check_lite_area_by_mspell(player_ptr, msa_ptr);
170     if (!msa_ptr->success)
171         msa_ptr->success = adjacent_grid_check(player_ptr, msa_ptr->m_ptr, &msa_ptr->y, &msa_ptr->x, FloorFeatureType::PROJECT, projectable);
172
173     decide_lite_breath(player_ptr, msa_ptr);
174     return msa_ptr->success;
175 }
176
177 void decide_lite_area(PlayerType *player_ptr, msa_type *msa_ptr)
178 {
179     if (msa_ptr->ability_flags.has_not(MonsterAbilityType::DARKNESS))
180         return;
181
182     PlayerClass pc(player_ptr);
183     bool can_use_lite_area = pc.equals(PlayerClassType::NINJA) && msa_ptr->r_ptr->kind_flags.has_not(MonsterKindType::UNDEAD) && none_bits(msa_ptr->r_ptr->flags3, RF3_HURT_LITE) && ((msa_ptr->r_ptr->flags7 & RF7_DARK_MASK) == 0);
184
185     if (msa_ptr->r_ptr->behavior_flags.has(MonsterBehaviorType::STUPID))
186         return;
187
188     if (d_info[player_ptr->dungeon_idx].flags.has(DungeonFeatureType::DARKNESS)) {
189         msa_ptr->ability_flags.reset(MonsterAbilityType::DARKNESS);
190         return;
191     }
192
193     if (pc.equals(PlayerClassType::NINJA) && !can_use_lite_area)
194         msa_ptr->ability_flags.reset(MonsterAbilityType::DARKNESS);
195 }