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"
34 static void decide_melee_spell_target(PlayerType *player_ptr, melee_spell_type *ms_ptr)
36 if ((player_ptr->pet_t_m_idx == 0) || !ms_ptr->pet) {
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;
47 static void decide_indirection_melee_spell(PlayerType *player_ptr, melee_spell_type *ms_ptr)
49 const auto &m_ref = *ms_ptr->m_ptr;
50 if ((ms_ptr->target_idx != 0) || (m_ref.target_y == 0)) {
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) {
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;
67 if (projectable(player_ptr, m_ref.fy, m_ref.fx, t_ref.fy, t_ref.fx)) {
71 ms_ptr->ability_flags &= RF_ABILITY_INDIRECT_MASK;
74 static bool check_melee_spell_projection(PlayerType *player_ptr, melee_spell_type *ms_ptr)
76 if (ms_ptr->target_idx != 0) {
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;
89 start = floor_ptr->m_max + 1;
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);
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) {
114 static void check_darkness(PlayerType *player_ptr, melee_spell_type *ms_ptr)
116 if (ms_ptr->ability_flags.has_not(MonsterAbilityType::DARKNESS)) {
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)) {
128 if (player_ptr->current_floor_ptr->get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
129 ms_ptr->ability_flags.reset(MonsterAbilityType::DARKNESS);
133 if (vs_ninja && !can_use_lite_area) {
134 ms_ptr->ability_flags.reset(MonsterAbilityType::DARKNESS);
138 static void check_stupid(melee_spell_type *ms_ptr)
140 if (!ms_ptr->in_no_magic_dungeon || ms_ptr->r_ptr->behavior_flags.has(MonsterBehaviorType::STUPID)) {
144 ms_ptr->ability_flags &= RF_ABILITY_NOMAGIC_MASK;
147 static void check_arena(PlayerType *player_ptr, melee_spell_type *ms_ptr)
149 if (!player_ptr->current_floor_ptr->inside_arena && !player_ptr->phase_out) {
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);
159 static void check_melee_spell_distance(PlayerType *player_ptr, melee_spell_type *ms_ptr)
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)) {
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);
176 int dist = distance(real_y, real_x, player_ptr->y, player_ptr->x);
178 ms_ptr->ability_flags.reset(ball_mask_except_rocket);
186 ms_ptr->ability_flags.reset(RF_ABILITY_BIG_BALL_MASK);
189 static void check_melee_spell_rocket(PlayerType *player_ptr, melee_spell_type *ms_ptr)
191 if (ms_ptr->ability_flags.has_not(MonsterAbilityType::ROCKET)) {
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);
203 static void check_melee_spell_beam(PlayerType *player_ptr, melee_spell_type *ms_ptr)
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)) {
209 ms_ptr->ability_flags.reset(RF_ABILITY_BEAM_MASK);
212 static void check_melee_spell_breath(PlayerType *player_ptr, melee_spell_type *ms_ptr)
214 if (ms_ptr->ability_flags.has_none_of(RF_ABILITY_BREATH_MASK)) {
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);
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);
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);
236 static void check_melee_spell_special(PlayerType *player_ptr, melee_spell_type *ms_ptr)
238 if (ms_ptr->ability_flags.has_not(MonsterAbilityType::SPECIAL)) {
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);
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);
258 ms_ptr->ability_flags.reset(MonsterAbilityType::SPECIAL);
261 static void check_riding(PlayerType *player_ptr, melee_spell_type *ms_ptr)
263 if (ms_ptr->m_idx != player_ptr->riding) {
267 ms_ptr->ability_flags.reset(RF_ABILITY_RIDING_MASK);
270 static void check_pet(PlayerType *player_ptr, melee_spell_type *ms_ptr)
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 });
281 if (!(player_ptr->pet_extra_flags & PF_ATTACK_SPELL)) {
282 ms_ptr->ability_flags.reset(RF_ABILITY_ATTACK_MASK);
285 if (!(player_ptr->pet_extra_flags & PF_SUMMON_SPELL)) {
286 ms_ptr->ability_flags.reset(RF_ABILITY_SUMMON_MASK);
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);
296 check_melee_spell_special(player_ptr, ms_ptr);
299 static void check_non_stupid(PlayerType *player_ptr, melee_spell_type *ms_ptr)
301 if (ms_ptr->r_ptr->behavior_flags.has(MonsterBehaviorType::STUPID)) {
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);
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);
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);
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);
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);
326 static void check_smart(PlayerType *player_ptr, melee_spell_type *ms_ptr)
328 if (ms_ptr->r_ptr->behavior_flags.has_not(MonsterBehaviorType::SMART)) {
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;
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);
341 static bool set_melee_spell_set(PlayerType *player_ptr, melee_spell_type *ms_ptr)
343 if (ms_ptr->ability_flags.none()) {
347 EnumClassFlagGroup<MonsterAbilityType>::get_flags(ms_ptr->ability_flags, std::back_inserter(ms_ptr->spells));
349 return !ms_ptr->spells.empty() && player_ptr->playing && !player_ptr->is_dead && !player_ptr->leaving;
352 bool check_melee_spell_set(PlayerType *player_ptr, melee_spell_type *ms_ptr)
354 if (ms_ptr->m_ptr->is_confused()) {
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)) {
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);
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);
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);
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);