OSDN Git Service

Merge pull request #3740 from hengband/release/3.0.1.1-Beta
[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 "floor/cave.h"
10 #include "floor/geometry.h"
11 #include "floor/line-of-sight.h"
12 #include "monster-race/monster-race.h"
13 #include "monster-race/race-ability-mask.h"
14 #include "monster-race/race-brightness-mask.h"
15 #include "monster-race/race-flags2.h"
16 #include "monster-race/race-flags3.h"
17 #include "monster-race/race-flags7.h"
18 #include "mspell/mspell-attack-util.h"
19 #include "mspell/mspell-judgement.h"
20 #include "player-base/player-class.h"
21 #include "spell/range-calc.h"
22 #include "system/angband-system.h"
23 #include "system/dungeon-info.h"
24 #include "system/floor-type-definition.h"
25 #include "system/grid-type-definition.h"
26 #include "system/monster-entity.h"
27 #include "system/monster-race-info.h"
28 #include "system/player-type-definition.h"
29 #include "system/terrain-type-definition.h"
30 #include "target/projection-path-calculator.h"
31 #include "util/bit-flags-calculator.h"
32
33 /*!
34  * @brief モンスターがプレイヤーにダメージを与えるための最適な座標を算出する /
35  * @param player_ptr プレイヤーへの参照ポインタ
36  * @param m_ptr 技能を使用するモンスター構造体の参照ポインタ
37  * @param yp 最適な目標地点のY座標を返す参照ポインタ
38  * @param xp 最適な目標地点のX座標を返す参照ポインタ
39  * @param f_flag 射線に入れるのを避ける地形の所持フラグ
40  * @param path_check 射線を判定するための関数ポインタ
41  * @return 有効な座標があった場合TRUEを返す
42  */
43 bool adjacent_grid_check(PlayerType *player_ptr, MonsterEntity *m_ptr, POSITION *yp, POSITION *xp, TerrainCharacteristics f_flag, path_check_pf path_check)
44 {
45     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 } };
46     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 } };
47
48     int next;
49     if (m_ptr->fy < player_ptr->y && m_ptr->fx < player_ptr->x) {
50         next = 0;
51     } else if (m_ptr->fy < player_ptr->y) {
52         next = 1;
53     } else if (m_ptr->fx < player_ptr->x) {
54         next = 2;
55     } else {
56         next = 3;
57     }
58
59     for (int i = 0; i < 8; i++) {
60         int next_x = *xp + tonari_x[next][i];
61         int next_y = *yp + tonari_y[next][i];
62         Grid *g_ptr;
63         g_ptr = &player_ptr->current_floor_ptr->grid_array[next_y][next_x];
64         if (!g_ptr->cave_has_flag(f_flag)) {
65             continue;
66         }
67
68         if (path_check(player_ptr, m_ptr->fy, m_ptr->fx, next_y, next_x)) {
69             *yp = next_y;
70             *xp = next_x;
71             return true;
72         }
73     }
74
75     return false;
76 }
77
78 void decide_lite_range(PlayerType *player_ptr, msa_type *msa_ptr)
79 {
80     if (msa_ptr->ability_flags.has_not(MonsterAbilityType::BR_LITE)) {
81         return;
82     }
83
84     msa_ptr->y_br_lite = msa_ptr->y;
85     msa_ptr->x_br_lite = msa_ptr->x;
86     if (los(player_ptr, msa_ptr->m_ptr->fy, msa_ptr->m_ptr->fx, msa_ptr->y_br_lite, msa_ptr->x_br_lite)) {
87         const Pos2D pos(msa_ptr->y_br_lite, msa_ptr->x_br_lite);
88         const auto &terrain = player_ptr->current_floor_ptr->get_grid(pos).get_terrain();
89         if (terrain.flags.has_not(TerrainCharacteristics::LOS) && terrain.flags.has(TerrainCharacteristics::PROJECT) && one_in_(2)) {
90             msa_ptr->ability_flags.reset(MonsterAbilityType::BR_LITE);
91         }
92     } else if (!adjacent_grid_check(player_ptr, msa_ptr->m_ptr, &msa_ptr->y_br_lite, &msa_ptr->x_br_lite, TerrainCharacteristics::LOS, los)) {
93         msa_ptr->ability_flags.reset(MonsterAbilityType::BR_LITE);
94     }
95
96     if (msa_ptr->ability_flags.has(MonsterAbilityType::BR_LITE)) {
97         return;
98     }
99
100     msa_ptr->y_br_lite = 0;
101     msa_ptr->x_br_lite = 0;
102 }
103
104 static void feature_projection(const FloorType &floor, msa_type *msa_ptr)
105 {
106     const Pos2D pos(msa_ptr->y, msa_ptr->x);
107     const auto &terrain = floor.get_grid(pos).get_terrain();
108     if (terrain.flags.has(TerrainCharacteristics::PROJECT)) {
109         return;
110     }
111
112     if (msa_ptr->ability_flags.has(MonsterAbilityType::BR_DISI) && terrain.flags.has(TerrainCharacteristics::HURT_DISI) && one_in_(2)) {
113         msa_ptr->do_spell = DO_SPELL_BR_DISI;
114         return;
115     }
116
117     if (msa_ptr->ability_flags.has(MonsterAbilityType::BR_LITE) && terrain.flags.has(TerrainCharacteristics::LOS) && one_in_(2)) {
118         msa_ptr->do_spell = DO_SPELL_BR_LITE;
119     }
120 }
121
122 static void check_lite_area_by_mspell(PlayerType *player_ptr, msa_type *msa_ptr)
123 {
124     const auto &system = AngbandSystem::get_instance();
125     auto light_by_disintegration = msa_ptr->ability_flags.has(MonsterAbilityType::BR_DISI);
126     light_by_disintegration &= msa_ptr->m_ptr->cdis < system.get_max_range() / 2;
127     light_by_disintegration &= in_disintegration_range(player_ptr->current_floor_ptr, msa_ptr->m_ptr->fy, msa_ptr->m_ptr->fx, msa_ptr->y, msa_ptr->x);
128     light_by_disintegration &= 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));
129     if (light_by_disintegration) {
130         msa_ptr->do_spell = DO_SPELL_BR_DISI;
131         msa_ptr->success = true;
132         return;
133     }
134
135     auto light_by_lite = msa_ptr->ability_flags.has(MonsterAbilityType::BR_LITE);
136     light_by_lite &= msa_ptr->m_ptr->cdis < system.get_max_range() / 2;
137     light_by_lite &= los(player_ptr, msa_ptr->m_ptr->fy, msa_ptr->m_ptr->fx, msa_ptr->y, msa_ptr->x);
138     light_by_lite &= one_in_(5);
139     if (light_by_lite) {
140         msa_ptr->do_spell = DO_SPELL_BR_LITE;
141         msa_ptr->success = true;
142         return;
143     }
144
145     if (msa_ptr->ability_flags.has_not(MonsterAbilityType::BA_LITE) || (msa_ptr->m_ptr->cdis > system.get_max_range())) {
146         return;
147     }
148
149     auto by = msa_ptr->y;
150     auto bx = msa_ptr->x;
151     get_project_point(player_ptr, msa_ptr->m_ptr->fy, msa_ptr->m_ptr->fx, &by, &bx, 0L);
152     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)) {
153         msa_ptr->do_spell = DO_SPELL_BA_LITE;
154         msa_ptr->success = true;
155     }
156 }
157
158 static void decide_lite_breath(msa_type *msa_ptr)
159 {
160     if (msa_ptr->success) {
161         return;
162     }
163
164     if (msa_ptr->m_ptr->target_y && msa_ptr->m_ptr->target_x) {
165         msa_ptr->y = msa_ptr->m_ptr->target_y;
166         msa_ptr->x = msa_ptr->m_ptr->target_x;
167         msa_ptr->ability_flags &= RF_ABILITY_INDIRECT_MASK;
168         msa_ptr->success = true;
169     }
170
171     auto should_set = msa_ptr->y_br_lite == 0;
172     should_set |= msa_ptr->x_br_lite == 0;
173     should_set |= msa_ptr->m_ptr->cdis > AngbandSystem::get_instance().get_max_range() / 2;
174     should_set |= !one_in_(5);
175     if (should_set) {
176         return;
177     }
178
179     if (msa_ptr->success) {
180         msa_ptr->ability_flags.set(MonsterAbilityType::BR_LITE);
181         return;
182     }
183
184     msa_ptr->y = msa_ptr->y_br_lite;
185     msa_ptr->x = msa_ptr->x_br_lite;
186     msa_ptr->do_spell = DO_SPELL_BR_LITE;
187     msa_ptr->success = true;
188 }
189
190 bool decide_lite_projection(PlayerType *player_ptr, msa_type *msa_ptr)
191 {
192     if (projectable(player_ptr, msa_ptr->m_ptr->fy, msa_ptr->m_ptr->fx, msa_ptr->y, msa_ptr->x)) {
193         feature_projection(*player_ptr->current_floor_ptr, msa_ptr);
194         return true;
195     }
196
197     msa_ptr->success = false;
198     check_lite_area_by_mspell(player_ptr, msa_ptr);
199     if (!msa_ptr->success) {
200         msa_ptr->success = adjacent_grid_check(player_ptr, msa_ptr->m_ptr, &msa_ptr->y, &msa_ptr->x, TerrainCharacteristics::PROJECT, projectable);
201     }
202
203     decide_lite_breath(msa_ptr);
204     return msa_ptr->success;
205 }
206
207 void decide_lite_area(PlayerType *player_ptr, msa_type *msa_ptr)
208 {
209     if (msa_ptr->ability_flags.has_not(MonsterAbilityType::DARKNESS)) {
210         return;
211     }
212
213     PlayerClass pc(player_ptr);
214     auto can_use_lite_area = pc.equals(PlayerClassType::NINJA);
215     can_use_lite_area &= msa_ptr->r_ptr->kind_flags.has_not(MonsterKindType::UNDEAD);
216     can_use_lite_area &= msa_ptr->r_ptr->resistance_flags.has_not(MonsterResistanceType::HURT_LITE);
217     can_use_lite_area &= (msa_ptr->r_ptr->brightness_flags.has_none_of(dark_mask));
218
219     if (msa_ptr->r_ptr->behavior_flags.has(MonsterBehaviorType::STUPID)) {
220         return;
221     }
222
223     if (player_ptr->current_floor_ptr->get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
224         msa_ptr->ability_flags.reset(MonsterAbilityType::DARKNESS);
225         return;
226     }
227
228     if (pc.equals(PlayerClassType::NINJA) && !can_use_lite_area) {
229         msa_ptr->ability_flags.reset(MonsterAbilityType::DARKNESS);
230     }
231 }