OSDN Git Service

Merge pull request #3532 from sikabane-works/release/3.0.0.87-alpha
[hengbandforosx/hengbandosx.git] / src / melee / melee-spell-flags-checker.cpp
1 #include "melee/melee-spell-flags-checker.h"
2 #include "dungeon/dungeon-flag-types.h"
3 #include "effect/effect-characteristics.h"
4 #include "floor/geometry.h"
5 #include "floor/line-of-sight.h"
6 #include "melee/melee-spell-util.h"
7 #include "monster-floor/monster-move.h"
8 #include "monster-race/monster-race.h"
9 #include "monster-race/race-ability-mask.h"
10 #include "monster-race/race-brightness-mask.h"
11 #include "monster-race/race-flags2.h"
12 #include "monster-race/race-flags3.h"
13 #include "monster-race/race-flags7.h"
14 #include "monster-race/race-indice-types.h"
15 #include "monster/monster-info.h"
16 #include "monster/monster-status.h"
17 #include "mspell/mspell-checker.h"
18 #include "mspell/mspell-judgement.h"
19 #include "mspell/mspell-util.h"
20 #include "pet/pet-util.h"
21 #include "player-base/player-class.h"
22 #include "spell-kind/spells-world.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 "target/projection-path-calculator.h"
30 #include "util/bit-flags-calculator.h"
31
32 #include <iterator>
33
34 static void decide_melee_spell_target(PlayerType *player_ptr, melee_spell_type *ms_ptr)
35 {
36     if ((player_ptr->pet_t_m_idx == 0) || !ms_ptr->pet) {
37         return;
38     }
39
40     ms_ptr->target_idx = player_ptr->pet_t_m_idx;
41     ms_ptr->t_ptr = &player_ptr->current_floor_ptr->m_list[ms_ptr->target_idx];
42     if ((ms_ptr->m_idx == ms_ptr->target_idx) || !projectable(player_ptr, ms_ptr->m_ptr->fy, ms_ptr->m_ptr->fx, ms_ptr->t_ptr->fy, ms_ptr->t_ptr->fx)) {
43         ms_ptr->target_idx = 0;
44     }
45 }
46
47 static void decide_indirection_melee_spell(PlayerType *player_ptr, melee_spell_type *ms_ptr)
48 {
49     const auto &m_ref = *ms_ptr->m_ptr;
50     if ((ms_ptr->target_idx != 0) || (m_ref.target_y == 0)) {
51         return;
52     }
53
54     auto *floor_ptr = player_ptr->current_floor_ptr;
55     ms_ptr->target_idx = floor_ptr->grid_array[m_ref.target_y][m_ref.target_x].m_idx;
56     if (ms_ptr->target_idx == 0) {
57         return;
58     }
59
60     ms_ptr->t_ptr = &floor_ptr->m_list[ms_ptr->target_idx];
61     const auto &t_ref = *ms_ptr->t_ptr;
62     if ((ms_ptr->m_idx == ms_ptr->target_idx) || ((ms_ptr->target_idx != player_ptr->pet_t_m_idx) && !are_enemies(player_ptr, m_ref, t_ref))) {
63         ms_ptr->target_idx = 0;
64         return;
65     }
66
67     if (projectable(player_ptr, m_ref.fy, m_ref.fx, t_ref.fy, t_ref.fx)) {
68         return;
69     }
70
71     ms_ptr->ability_flags &= RF_ABILITY_INDIRECT_MASK;
72 }
73
74 static bool check_melee_spell_projection(PlayerType *player_ptr, melee_spell_type *ms_ptr)
75 {
76     if (ms_ptr->target_idx != 0) {
77         return true;
78     }
79
80     int start;
81     int plus = 1;
82     auto *floor_ptr = player_ptr->current_floor_ptr;
83     if (player_ptr->phase_out) {
84         start = randint1(floor_ptr->m_max - 1) + floor_ptr->m_max;
85         if (randint0(2)) {
86             plus = -1;
87         }
88     } else {
89         start = floor_ptr->m_max + 1;
90     }
91
92     for (int i = start; ((i < start + floor_ptr->m_max) && (i > start - floor_ptr->m_max)); i += plus) {
93         MONSTER_IDX dummy = (i % floor_ptr->m_max);
94         if (!dummy) {
95             continue;
96         }
97
98         ms_ptr->target_idx = dummy;
99         ms_ptr->t_ptr = &floor_ptr->m_list[ms_ptr->target_idx];
100         const auto &m_ref = *ms_ptr->m_ptr;
101         const auto &t_ref = *ms_ptr->t_ptr;
102         const auto is_enemies = are_enemies(player_ptr, m_ref, t_ref);
103         const auto is_projectable = projectable(player_ptr, m_ref.fy, m_ref.fx, t_ref.fy, t_ref.fx);
104         if (!t_ref.is_valid() || (ms_ptr->m_idx == ms_ptr->target_idx) || !is_enemies || !is_projectable) {
105             continue;
106         }
107
108         return true;
109     }
110
111     return false;
112 }
113
114 static void check_darkness(PlayerType *player_ptr, melee_spell_type *ms_ptr)
115 {
116     if (ms_ptr->ability_flags.has_not(MonsterAbilityType::DARKNESS)) {
117         return;
118     }
119
120     const auto vs_ninja = PlayerClass(player_ptr).equals(PlayerClassType::NINJA) && !ms_ptr->t_ptr->is_hostile();
121     auto can_use_lite_area = vs_ninja && ms_ptr->r_ptr->kind_flags.has_not(MonsterKindType::UNDEAD);
122     can_use_lite_area &= ms_ptr->r_ptr->resistance_flags.has_not(MonsterResistanceType::HURT_LITE);
123     can_use_lite_area &= ms_ptr->r_ptr->brightness_flags.has_none_of(dark_mask);
124     if (ms_ptr->r_ptr->behavior_flags.has(MonsterBehaviorType::STUPID)) {
125         return;
126     }
127
128     if (player_ptr->current_floor_ptr->get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
129         ms_ptr->ability_flags.reset(MonsterAbilityType::DARKNESS);
130         return;
131     }
132
133     if (vs_ninja && !can_use_lite_area) {
134         ms_ptr->ability_flags.reset(MonsterAbilityType::DARKNESS);
135     }
136 }
137
138 static void check_stupid(melee_spell_type *ms_ptr)
139 {
140     if (!ms_ptr->in_no_magic_dungeon || ms_ptr->r_ptr->behavior_flags.has(MonsterBehaviorType::STUPID)) {
141         return;
142     }
143
144     ms_ptr->ability_flags &= RF_ABILITY_NOMAGIC_MASK;
145 }
146
147 static void check_arena(PlayerType *player_ptr, melee_spell_type *ms_ptr)
148 {
149     if (!player_ptr->current_floor_ptr->inside_arena && !player_ptr->phase_out) {
150         return;
151     }
152
153     ms_ptr->ability_flags.reset(RF_ABILITY_SUMMON_MASK).reset(MonsterAbilityType::TELE_LEVEL);
154     if (ms_ptr->m_ptr->r_idx == MonsterRaceId::ROLENTO) {
155         ms_ptr->ability_flags.reset(MonsterAbilityType::SPECIAL);
156     }
157 }
158
159 static void check_melee_spell_distance(PlayerType *player_ptr, melee_spell_type *ms_ptr)
160 {
161     auto ball_mask_except_rocket = RF_ABILITY_BALL_MASK;
162     ball_mask_except_rocket.reset(MonsterAbilityType::ROCKET);
163     if (ms_ptr->ability_flags.has_none_of(ball_mask_except_rocket)) {
164         return;
165     }
166
167     POSITION real_y = ms_ptr->y;
168     POSITION real_x = ms_ptr->x;
169     get_project_point(player_ptr, ms_ptr->m_ptr->fy, ms_ptr->m_ptr->fx, &real_y, &real_x, 0L);
170     if (!projectable(player_ptr, real_y, real_x, player_ptr->y, player_ptr->x) && ms_ptr->ability_flags.has(MonsterAbilityType::BA_LITE) && (distance(real_y, real_x, player_ptr->y, player_ptr->x) <= 4) && los(player_ptr, real_y, real_x, player_ptr->y, player_ptr->x)) {
171         ms_ptr->ability_flags.reset(MonsterAbilityType::BA_LITE);
172
173         return;
174     }
175
176     int dist = distance(real_y, real_x, player_ptr->y, player_ptr->x);
177     if (dist <= 2) {
178         ms_ptr->ability_flags.reset(ball_mask_except_rocket);
179         return;
180     }
181
182     if (dist > 4) {
183         return;
184     }
185
186     ms_ptr->ability_flags.reset(RF_ABILITY_BIG_BALL_MASK);
187 }
188
189 static void check_melee_spell_rocket(PlayerType *player_ptr, melee_spell_type *ms_ptr)
190 {
191     if (ms_ptr->ability_flags.has_not(MonsterAbilityType::ROCKET)) {
192         return;
193     }
194
195     POSITION real_y = ms_ptr->y;
196     POSITION real_x = ms_ptr->x;
197     get_project_point(player_ptr, ms_ptr->m_ptr->fy, ms_ptr->m_ptr->fx, &real_y, &real_x, PROJECT_STOP);
198     if (projectable(player_ptr, real_y, real_x, player_ptr->y, player_ptr->x) && (distance(real_y, real_x, player_ptr->y, player_ptr->x) <= 2)) {
199         ms_ptr->ability_flags.reset(MonsterAbilityType::ROCKET);
200     }
201 }
202
203 static void check_melee_spell_beam(PlayerType *player_ptr, melee_spell_type *ms_ptr)
204 {
205     if (ms_ptr->ability_flags.has_none_of(RF_ABILITY_BEAM_MASK) || direct_beam(player_ptr, ms_ptr->m_ptr->fy, ms_ptr->m_ptr->fx, ms_ptr->t_ptr->fy, ms_ptr->t_ptr->fx, ms_ptr->m_ptr)) {
206         return;
207     }
208
209     ms_ptr->ability_flags.reset(RF_ABILITY_BEAM_MASK);
210 }
211
212 static void check_melee_spell_breath(PlayerType *player_ptr, melee_spell_type *ms_ptr)
213 {
214     if (ms_ptr->ability_flags.has_none_of(RF_ABILITY_BREATH_MASK)) {
215         return;
216     }
217
218     POSITION rad = (ms_ptr->r_ptr->flags2 & RF2_POWERFUL) ? 3 : 2;
219     if (!breath_direct(player_ptr, ms_ptr->m_ptr->fy, ms_ptr->m_ptr->fx, ms_ptr->t_ptr->fy, ms_ptr->t_ptr->fx, rad, AttributeType::NONE, true)) {
220         ms_ptr->ability_flags.reset(RF_ABILITY_BREATH_MASK);
221         return;
222     }
223
224     if (ms_ptr->ability_flags.has(MonsterAbilityType::BR_LITE) && !breath_direct(player_ptr, ms_ptr->m_ptr->fy, ms_ptr->m_ptr->fx,
225                                                                       ms_ptr->t_ptr->fy, ms_ptr->t_ptr->fx, rad, AttributeType::LITE, true)) {
226         ms_ptr->ability_flags.reset(MonsterAbilityType::BR_LITE);
227         return;
228     }
229
230     if (ms_ptr->ability_flags.has(MonsterAbilityType::BR_DISI) && !breath_direct(player_ptr, ms_ptr->m_ptr->fy, ms_ptr->m_ptr->fx,
231                                                                       ms_ptr->t_ptr->fy, ms_ptr->t_ptr->fx, rad, AttributeType::DISINTEGRATE, true)) {
232         ms_ptr->ability_flags.reset(MonsterAbilityType::BR_DISI);
233     }
234 }
235
236 static void check_melee_spell_special(PlayerType *player_ptr, melee_spell_type *ms_ptr)
237 {
238     if (ms_ptr->ability_flags.has_not(MonsterAbilityType::SPECIAL)) {
239         return;
240     }
241
242     if (ms_ptr->m_ptr->r_idx == MonsterRaceId::ROLENTO) {
243         if ((player_ptr->pet_extra_flags & (PF_ATTACK_SPELL | PF_SUMMON_SPELL)) != (PF_ATTACK_SPELL | PF_SUMMON_SPELL)) {
244             ms_ptr->ability_flags.reset(MonsterAbilityType::SPECIAL);
245         }
246
247         return;
248     }
249
250     if (ms_ptr->r_ptr->d_char == 'B') {
251         if ((player_ptr->pet_extra_flags & (PF_ATTACK_SPELL | PF_TELEPORT)) != (PF_ATTACK_SPELL | PF_TELEPORT)) {
252             ms_ptr->ability_flags.reset(MonsterAbilityType::SPECIAL);
253         }
254
255         return;
256     }
257
258     ms_ptr->ability_flags.reset(MonsterAbilityType::SPECIAL);
259 }
260
261 static void check_riding(PlayerType *player_ptr, melee_spell_type *ms_ptr)
262 {
263     if (ms_ptr->m_idx != player_ptr->riding) {
264         return;
265     }
266
267     ms_ptr->ability_flags.reset(RF_ABILITY_RIDING_MASK);
268 }
269
270 static void check_pet(PlayerType *player_ptr, melee_spell_type *ms_ptr)
271 {
272     if (!ms_ptr->pet) {
273         return;
274     }
275
276     ms_ptr->ability_flags.reset({ MonsterAbilityType::SHRIEK, MonsterAbilityType::DARKNESS, MonsterAbilityType::TRAPS });
277     if (!(player_ptr->pet_extra_flags & PF_TELEPORT)) {
278         ms_ptr->ability_flags.reset({ MonsterAbilityType::BLINK, MonsterAbilityType::TPORT, MonsterAbilityType::TELE_TO, MonsterAbilityType::TELE_AWAY, MonsterAbilityType::TELE_LEVEL });
279     }
280
281     if (!(player_ptr->pet_extra_flags & PF_ATTACK_SPELL)) {
282         ms_ptr->ability_flags.reset(RF_ABILITY_ATTACK_MASK);
283     }
284
285     if (!(player_ptr->pet_extra_flags & PF_SUMMON_SPELL)) {
286         ms_ptr->ability_flags.reset(RF_ABILITY_SUMMON_MASK);
287     }
288
289     if (!(player_ptr->pet_extra_flags & PF_BALL_SPELL) && (ms_ptr->m_idx != player_ptr->riding)) {
290         check_melee_spell_distance(player_ptr, ms_ptr);
291         check_melee_spell_rocket(player_ptr, ms_ptr);
292         check_melee_spell_beam(player_ptr, ms_ptr);
293         check_melee_spell_breath(player_ptr, ms_ptr);
294     }
295
296     check_melee_spell_special(player_ptr, ms_ptr);
297 }
298
299 static void check_non_stupid(PlayerType *player_ptr, melee_spell_type *ms_ptr)
300 {
301     if (ms_ptr->r_ptr->behavior_flags.has(MonsterBehaviorType::STUPID)) {
302         return;
303     }
304
305     if (ms_ptr->ability_flags.has_any_of(RF_ABILITY_BOLT_MASK) && !clean_shot(player_ptr, ms_ptr->m_ptr->fy, ms_ptr->m_ptr->fx, ms_ptr->t_ptr->fy, ms_ptr->t_ptr->fx, ms_ptr->pet)) {
306         ms_ptr->ability_flags.reset(RF_ABILITY_BOLT_MASK);
307     }
308
309     if (ms_ptr->ability_flags.has_any_of(RF_ABILITY_SUMMON_MASK) && !(summon_possible(player_ptr, ms_ptr->t_ptr->fy, ms_ptr->t_ptr->fx))) {
310         ms_ptr->ability_flags.reset(RF_ABILITY_SUMMON_MASK);
311     }
312
313     if (ms_ptr->ability_flags.has(MonsterAbilityType::DISPEL) && !dispel_check_monster(player_ptr, ms_ptr->m_idx, ms_ptr->target_idx)) {
314         ms_ptr->ability_flags.reset(MonsterAbilityType::DISPEL);
315     }
316
317     if (ms_ptr->ability_flags.has(MonsterAbilityType::RAISE_DEAD) && !raise_possible(player_ptr, ms_ptr->m_ptr)) {
318         ms_ptr->ability_flags.reset(MonsterAbilityType::RAISE_DEAD);
319     }
320
321     if (ms_ptr->ability_flags.has(MonsterAbilityType::SPECIAL) && (ms_ptr->m_ptr->r_idx == MonsterRaceId::ROLENTO) && !summon_possible(player_ptr, ms_ptr->t_ptr->fy, ms_ptr->t_ptr->fx)) {
322         ms_ptr->ability_flags.reset(MonsterAbilityType::SPECIAL);
323     }
324 }
325
326 static void check_smart(PlayerType *player_ptr, melee_spell_type *ms_ptr)
327 {
328     if (ms_ptr->r_ptr->behavior_flags.has_not(MonsterBehaviorType::SMART)) {
329         return;
330     }
331
332     if ((ms_ptr->m_ptr->hp < ms_ptr->m_ptr->maxhp / 10) && (randint0(100) < 50)) {
333         ms_ptr->ability_flags &= RF_ABILITY_INT_MASK;
334     }
335
336     if (ms_ptr->ability_flags.has(MonsterAbilityType::TELE_LEVEL) && is_teleport_level_ineffective(player_ptr, (ms_ptr->target_idx == player_ptr->riding) ? 0 : ms_ptr->target_idx)) {
337         ms_ptr->ability_flags.reset(MonsterAbilityType::TELE_LEVEL);
338     }
339 }
340
341 static bool set_melee_spell_set(PlayerType *player_ptr, melee_spell_type *ms_ptr)
342 {
343     if (ms_ptr->ability_flags.none()) {
344         return false;
345     }
346
347     EnumClassFlagGroup<MonsterAbilityType>::get_flags(ms_ptr->ability_flags, std::back_inserter(ms_ptr->spells));
348
349     return !ms_ptr->spells.empty() && player_ptr->playing && !player_ptr->is_dead && !player_ptr->leaving;
350 }
351
352 bool check_melee_spell_set(PlayerType *player_ptr, melee_spell_type *ms_ptr)
353 {
354     if (ms_ptr->m_ptr->is_confused()) {
355         return false;
356     }
357
358     ms_ptr->ability_flags = ms_ptr->r_ptr->ability_flags;
359     decide_melee_spell_target(player_ptr, ms_ptr);
360     decide_indirection_melee_spell(player_ptr, ms_ptr);
361     if (!check_melee_spell_projection(player_ptr, ms_ptr)) {
362         return false;
363     }
364
365     ms_ptr->y = ms_ptr->t_ptr->fy;
366     ms_ptr->x = ms_ptr->t_ptr->fx;
367     reset_target(ms_ptr->m_ptr);
368     ms_ptr->ability_flags.reset({ MonsterAbilityType::WORLD, MonsterAbilityType::TRAPS, MonsterAbilityType::FORGET });
369     if (ms_ptr->ability_flags.has(MonsterAbilityType::BR_LITE) && !los(player_ptr, ms_ptr->m_ptr->fy, ms_ptr->m_ptr->fx, ms_ptr->t_ptr->fy, ms_ptr->t_ptr->fx)) {
370         ms_ptr->ability_flags.reset(MonsterAbilityType::BR_LITE);
371     }
372
373     if (ms_ptr->ability_flags.has(MonsterAbilityType::SPECIAL) && (ms_ptr->m_ptr->r_idx != MonsterRaceId::ROLENTO) && (ms_ptr->r_ptr->d_char != 'B')) {
374         ms_ptr->ability_flags.reset(MonsterAbilityType::SPECIAL);
375     }
376
377     check_darkness(player_ptr, ms_ptr);
378     check_stupid(ms_ptr);
379     check_arena(player_ptr, ms_ptr);
380     if (player_ptr->phase_out && !one_in_(3)) {
381         ms_ptr->ability_flags.reset(MonsterAbilityType::HEAL);
382     }
383
384     check_riding(player_ptr, ms_ptr);
385     check_pet(player_ptr, ms_ptr);
386     check_non_stupid(player_ptr, ms_ptr);
387     check_smart(player_ptr, ms_ptr);
388     return set_melee_spell_set(player_ptr, ms_ptr);
389 }