OSDN Git Service

Merge pull request #2133 from Hourier/Define-Player-Class-Equals
[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 "dungeon/dungeon.h"
4 #include "effect/effect-characteristics.h"
5 #include "floor/geometry.h"
6 #include "floor/line-of-sight.h"
7 #include "melee/melee-spell-util.h"
8 #include "monster-floor/monster-move.h"
9 #include "monster-race/monster-race.h"
10 #include "monster-race/race-ability-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/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
30 #include <iterator>
31
32 static void decide_melee_spell_target(PlayerType *player_ptr, melee_spell_type *ms_ptr)
33 {
34     if ((player_ptr->pet_t_m_idx == 0) || !ms_ptr->pet)
35         return;
36
37     ms_ptr->target_idx = player_ptr->pet_t_m_idx;
38     ms_ptr->t_ptr = &player_ptr->current_floor_ptr->m_list[ms_ptr->target_idx];
39     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))
40         ms_ptr->target_idx = 0;
41 }
42
43 static void decide_indirection_melee_spell(PlayerType *player_ptr, melee_spell_type *ms_ptr)
44 {
45     if ((ms_ptr->target_idx != 0) || (ms_ptr->m_ptr->target_y == 0))
46         return;
47
48     floor_type *floor_ptr = player_ptr->current_floor_ptr;
49     ms_ptr->target_idx = floor_ptr->grid_array[ms_ptr->m_ptr->target_y][ms_ptr->m_ptr->target_x].m_idx;
50     if (ms_ptr->target_idx == 0)
51         return;
52
53     ms_ptr->t_ptr = &floor_ptr->m_list[ms_ptr->target_idx];
54     if ((ms_ptr->m_idx == ms_ptr->target_idx) || ((ms_ptr->target_idx != player_ptr->pet_t_m_idx) && !are_enemies(player_ptr, ms_ptr->m_ptr, ms_ptr->t_ptr))) {
55         ms_ptr->target_idx = 0;
56         return;
57     }
58
59     if (projectable(player_ptr, ms_ptr->m_ptr->fy, ms_ptr->m_ptr->fx, ms_ptr->t_ptr->fy, ms_ptr->t_ptr->fx))
60         return;
61
62     ms_ptr->ability_flags &= RF_ABILITY_INDIRECT_MASK;
63 }
64
65 static bool check_melee_spell_projection(PlayerType *player_ptr, melee_spell_type *ms_ptr)
66 {
67     if (ms_ptr->target_idx != 0)
68         return true;
69
70     int start;
71     int plus = 1;
72     floor_type *floor_ptr = player_ptr->current_floor_ptr;
73     if (player_ptr->phase_out) {
74         start = randint1(floor_ptr->m_max - 1) + floor_ptr->m_max;
75         if (randint0(2))
76             plus = -1;
77     } else
78         start = floor_ptr->m_max + 1;
79
80     for (int i = start; ((i < start + floor_ptr->m_max) && (i > start - floor_ptr->m_max)); i += plus) {
81         MONSTER_IDX dummy = (i % floor_ptr->m_max);
82         if (!dummy)
83             continue;
84
85         ms_ptr->target_idx = dummy;
86         ms_ptr->t_ptr = &floor_ptr->m_list[ms_ptr->target_idx];
87         if (!monster_is_valid(ms_ptr->t_ptr) || (ms_ptr->m_idx == ms_ptr->target_idx) || !are_enemies(player_ptr, ms_ptr->m_ptr, ms_ptr->t_ptr) || !projectable(player_ptr, ms_ptr->m_ptr->fy, ms_ptr->m_ptr->fx, ms_ptr->t_ptr->fy, ms_ptr->t_ptr->fx))
88             continue;
89
90         return true;
91     }
92
93     return false;
94 }
95
96 static void check_darkness(PlayerType *player_ptr, melee_spell_type *ms_ptr)
97 {
98     if (ms_ptr->ability_flags.has_not(MonsterAbilityType::DARKNESS))
99         return;
100
101     bool vs_ninja = PlayerClass(player_ptr).equals(PlayerClassType::NINJA) && !is_hostile(ms_ptr->t_ptr);
102     bool can_use_lite_area = vs_ninja && !(ms_ptr->r_ptr->flags3 & (RF3_UNDEAD | RF3_HURT_LITE)) && !(ms_ptr->r_ptr->flags7 & RF7_DARK_MASK);
103     if (ms_ptr->r_ptr->behavior_flags.has(MonsterBehaviorType::STUPID))
104         return;
105
106     if (d_info[player_ptr->dungeon_idx].flags.has(DungeonFeatureType::DARKNESS)) {
107         ms_ptr->ability_flags.reset(MonsterAbilityType::DARKNESS);
108         return;
109     }
110
111     if (vs_ninja && !can_use_lite_area)
112         ms_ptr->ability_flags.reset(MonsterAbilityType::DARKNESS);
113 }
114
115 static void check_stupid(melee_spell_type *ms_ptr)
116 {
117     if (!ms_ptr->in_no_magic_dungeon || ms_ptr->r_ptr->behavior_flags.has(MonsterBehaviorType::STUPID))
118         return;
119
120     ms_ptr->ability_flags &= RF_ABILITY_NOMAGIC_MASK;
121 }
122
123 static void check_arena(PlayerType *player_ptr, melee_spell_type *ms_ptr)
124 {
125     if (!player_ptr->current_floor_ptr->inside_arena && !player_ptr->phase_out)
126         return;
127
128     ms_ptr->ability_flags.reset(RF_ABILITY_SUMMON_MASK).reset(MonsterAbilityType::TELE_LEVEL);
129     if (ms_ptr->m_ptr->r_idx == MON_ROLENTO)
130         ms_ptr->ability_flags.reset(MonsterAbilityType::SPECIAL);
131 }
132
133 static void check_melee_spell_distance(PlayerType *player_ptr, melee_spell_type *ms_ptr)
134 {
135     auto ball_mask_except_rocket = RF_ABILITY_BALL_MASK;
136     ball_mask_except_rocket.reset(MonsterAbilityType::ROCKET);
137     if (ms_ptr->ability_flags.has_none_of(ball_mask_except_rocket))
138         return;
139
140     POSITION real_y = ms_ptr->y;
141     POSITION real_x = ms_ptr->x;
142     get_project_point(player_ptr, ms_ptr->m_ptr->fy, ms_ptr->m_ptr->fx, &real_y, &real_x, 0L);
143     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)) {
144         ms_ptr->ability_flags.reset(MonsterAbilityType::BA_LITE);
145
146         return;
147     }
148
149     int dist = distance(real_y, real_x, player_ptr->y, player_ptr->x);
150     if (dist <= 2) {
151         ms_ptr->ability_flags.reset(ball_mask_except_rocket);
152         return;
153     }
154
155     if (dist > 4)
156         return;
157
158     ms_ptr->ability_flags.reset(RF_ABILITY_BIG_BALL_MASK);
159 }
160
161 static void check_melee_spell_rocket(PlayerType *player_ptr, melee_spell_type *ms_ptr)
162 {
163     if (ms_ptr->ability_flags.has_not(MonsterAbilityType::ROCKET))
164         return;
165
166     POSITION real_y = ms_ptr->y;
167     POSITION real_x = ms_ptr->x;
168     get_project_point(player_ptr, ms_ptr->m_ptr->fy, ms_ptr->m_ptr->fx, &real_y, &real_x, PROJECT_STOP);
169     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))
170         ms_ptr->ability_flags.reset(MonsterAbilityType::ROCKET);
171 }
172
173 static void check_melee_spell_beam(PlayerType *player_ptr, melee_spell_type *ms_ptr)
174 {
175     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))
176         return;
177
178     ms_ptr->ability_flags.reset(RF_ABILITY_BEAM_MASK);
179 }
180
181 static void check_melee_spell_breath(PlayerType *player_ptr, melee_spell_type *ms_ptr)
182 {
183     if (ms_ptr->ability_flags.has_none_of(RF_ABILITY_BREATH_MASK))
184         return;
185
186     POSITION rad = (ms_ptr->r_ptr->flags2 & RF2_POWERFUL) ? 3 : 2;
187     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)) {
188         ms_ptr->ability_flags.reset(RF_ABILITY_BREATH_MASK);
189         return;
190     }
191
192     if (ms_ptr->ability_flags.has(MonsterAbilityType::BR_LITE) && !breath_direct(player_ptr, ms_ptr->m_ptr->fy, ms_ptr->m_ptr->fx,
193                                                                       ms_ptr->t_ptr->fy, ms_ptr->t_ptr->fx, rad, AttributeType::LITE, true)) {
194         ms_ptr->ability_flags.reset(MonsterAbilityType::BR_LITE);
195         return;
196     }
197
198     if (ms_ptr->ability_flags.has(MonsterAbilityType::BR_DISI) && !breath_direct(player_ptr, ms_ptr->m_ptr->fy, ms_ptr->m_ptr->fx,
199                                                                       ms_ptr->t_ptr->fy, ms_ptr->t_ptr->fx, rad, AttributeType::DISINTEGRATE, true)) {
200         ms_ptr->ability_flags.reset(MonsterAbilityType::BR_DISI);
201     }
202 }
203
204 static void check_melee_spell_special(PlayerType *player_ptr, melee_spell_type *ms_ptr)
205 {
206     if (ms_ptr->ability_flags.has_not(MonsterAbilityType::SPECIAL))
207         return;
208
209     if (ms_ptr->m_ptr->r_idx == MON_ROLENTO) {
210         if ((player_ptr->pet_extra_flags & (PF_ATTACK_SPELL | PF_SUMMON_SPELL)) != (PF_ATTACK_SPELL | PF_SUMMON_SPELL))
211             ms_ptr->ability_flags.reset(MonsterAbilityType::SPECIAL);
212
213         return;
214     }
215
216     if (ms_ptr->r_ptr->d_char == 'B') {
217         if ((player_ptr->pet_extra_flags & (PF_ATTACK_SPELL | PF_TELEPORT)) != (PF_ATTACK_SPELL | PF_TELEPORT))
218             ms_ptr->ability_flags.reset(MonsterAbilityType::SPECIAL);
219
220         return;
221     }
222
223     ms_ptr->ability_flags.reset(MonsterAbilityType::SPECIAL);
224 }
225
226 static void check_riding(PlayerType *player_ptr, melee_spell_type *ms_ptr)
227 {
228     if (ms_ptr->m_idx != player_ptr->riding)
229         return;
230
231     ms_ptr->ability_flags.reset(RF_ABILITY_RIDING_MASK);
232 }
233
234 static void check_pet(PlayerType *player_ptr, melee_spell_type *ms_ptr)
235 {
236     if (!ms_ptr->pet)
237         return;
238
239     ms_ptr->ability_flags.reset({ MonsterAbilityType::SHRIEK, MonsterAbilityType::DARKNESS, MonsterAbilityType::TRAPS });
240     if (!(player_ptr->pet_extra_flags & PF_TELEPORT))
241         ms_ptr->ability_flags.reset({ MonsterAbilityType::BLINK, MonsterAbilityType::TPORT, MonsterAbilityType::TELE_TO, MonsterAbilityType::TELE_AWAY, MonsterAbilityType::TELE_LEVEL });
242
243     if (!(player_ptr->pet_extra_flags & PF_ATTACK_SPELL)) {
244         ms_ptr->ability_flags.reset(RF_ABILITY_ATTACK_MASK);
245     }
246
247     if (!(player_ptr->pet_extra_flags & PF_SUMMON_SPELL)) {
248         ms_ptr->ability_flags.reset(RF_ABILITY_SUMMON_MASK);
249     }
250
251     if (!(player_ptr->pet_extra_flags & PF_BALL_SPELL) && (ms_ptr->m_idx != player_ptr->riding)) {
252         check_melee_spell_distance(player_ptr, ms_ptr);
253         check_melee_spell_rocket(player_ptr, ms_ptr);
254         check_melee_spell_beam(player_ptr, ms_ptr);
255         check_melee_spell_breath(player_ptr, ms_ptr);
256     }
257
258     check_melee_spell_special(player_ptr, ms_ptr);
259 }
260
261 static void check_non_stupid(PlayerType *player_ptr, melee_spell_type *ms_ptr)
262 {
263     if (ms_ptr->r_ptr->behavior_flags.has(MonsterBehaviorType::STUPID))
264         return;
265
266     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)) {
267         ms_ptr->ability_flags.reset(RF_ABILITY_BOLT_MASK);
268     }
269
270     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))) {
271         ms_ptr->ability_flags.reset(RF_ABILITY_SUMMON_MASK);
272     }
273
274     if (ms_ptr->ability_flags.has(MonsterAbilityType::DISPEL) && !dispel_check_monster(player_ptr, ms_ptr->m_idx, ms_ptr->target_idx))
275         ms_ptr->ability_flags.reset(MonsterAbilityType::DISPEL);
276
277     if (ms_ptr->ability_flags.has(MonsterAbilityType::RAISE_DEAD) && !raise_possible(player_ptr, ms_ptr->m_ptr))
278         ms_ptr->ability_flags.reset(MonsterAbilityType::RAISE_DEAD);
279
280     if (ms_ptr->ability_flags.has(MonsterAbilityType::SPECIAL) && (ms_ptr->m_ptr->r_idx == MON_ROLENTO) && !summon_possible(player_ptr, ms_ptr->t_ptr->fy, ms_ptr->t_ptr->fx))
281         ms_ptr->ability_flags.reset(MonsterAbilityType::SPECIAL);
282 }
283
284 static void check_smart(PlayerType *player_ptr, melee_spell_type *ms_ptr)
285 {
286     if (ms_ptr->r_ptr->behavior_flags.has_not(MonsterBehaviorType::SMART))
287         return;
288
289     if ((ms_ptr->m_ptr->hp < ms_ptr->m_ptr->maxhp / 10) && (randint0(100) < 50)) {
290         ms_ptr->ability_flags &= RF_ABILITY_INT_MASK;
291     }
292
293     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))
294         ms_ptr->ability_flags.reset(MonsterAbilityType::TELE_LEVEL);
295 }
296
297 static bool set_melee_spell_set(PlayerType *player_ptr, melee_spell_type *ms_ptr)
298 {
299     if (ms_ptr->ability_flags.none())
300         return false;
301
302     EnumClassFlagGroup<MonsterAbilityType>::get_flags(ms_ptr->ability_flags, std::back_inserter(ms_ptr->spells));
303
304     return !ms_ptr->spells.empty() && player_ptr->playing && !player_ptr->is_dead && !player_ptr->leaving;
305 }
306
307 bool check_melee_spell_set(PlayerType *player_ptr, melee_spell_type *ms_ptr)
308 {
309     if (monster_confused_remaining(ms_ptr->m_ptr))
310         return false;
311
312     ms_ptr->ability_flags = ms_ptr->r_ptr->ability_flags;
313     decide_melee_spell_target(player_ptr, ms_ptr);
314     decide_indirection_melee_spell(player_ptr, ms_ptr);
315     if (!check_melee_spell_projection(player_ptr, ms_ptr))
316         return false;
317
318     ms_ptr->y = ms_ptr->t_ptr->fy;
319     ms_ptr->x = ms_ptr->t_ptr->fx;
320     reset_target(ms_ptr->m_ptr);
321     ms_ptr->ability_flags.reset({ MonsterAbilityType::WORLD, MonsterAbilityType::TRAPS, MonsterAbilityType::FORGET });
322     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))
323         ms_ptr->ability_flags.reset(MonsterAbilityType::BR_LITE);
324
325     if (ms_ptr->ability_flags.has(MonsterAbilityType::SPECIAL) && (ms_ptr->m_ptr->r_idx != MON_ROLENTO) && (ms_ptr->r_ptr->d_char != 'B'))
326         ms_ptr->ability_flags.reset(MonsterAbilityType::SPECIAL);
327
328     check_darkness(player_ptr, ms_ptr);
329     check_stupid(ms_ptr);
330     check_arena(player_ptr, ms_ptr);
331     if (player_ptr->phase_out && !one_in_(3))
332         ms_ptr->ability_flags.reset(MonsterAbilityType::HEAL);
333
334     check_riding(player_ptr, ms_ptr);
335     check_pet(player_ptr, ms_ptr);
336     check_non_stupid(player_ptr, ms_ptr);
337     check_smart(player_ptr, ms_ptr);
338     return set_melee_spell_set(player_ptr, ms_ptr);
339 }