OSDN Git Service

9dda097f2fde0c54b441f483cea0e1f14ddbacf3
[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/line-of-sight.h"
6 #include "floor/geometry.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 "spell-kind/spells-world.h"
22 #include "spell/spell-types.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(player_type *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(player_type *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(player_type *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)
88             || !projectable(player_ptr, ms_ptr->m_ptr->fy, ms_ptr->m_ptr->fx, ms_ptr->t_ptr->fy, ms_ptr->t_ptr->fx))
89             continue;
90
91         return true;
92     }
93
94     return false;
95 }
96
97 static void check_darkness(player_type *player_ptr, melee_spell_type *ms_ptr)
98 {
99     if (ms_ptr->ability_flags.has_not(RF_ABILITY::DARKNESS))
100         return;
101
102     bool vs_ninja = (player_ptr->pclass == PlayerClassType::NINJA) && !is_hostile(ms_ptr->t_ptr);
103     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);
104     if ((ms_ptr->r_ptr->flags2 & RF2_STUPID) != 0)
105         return;
106
107     if (d_info[player_ptr->dungeon_idx].flags.has(DF::DARKNESS)) {
108         ms_ptr->ability_flags.reset(RF_ABILITY::DARKNESS);
109         return;
110     }
111
112     if (vs_ninja && !can_use_lite_area)
113         ms_ptr->ability_flags.reset(RF_ABILITY::DARKNESS);
114 }
115
116 static void check_stupid(melee_spell_type *ms_ptr)
117 {
118     if (!ms_ptr->in_no_magic_dungeon || ((ms_ptr->r_ptr->flags2 & RF2_STUPID) != 0))
119         return;
120
121     ms_ptr->ability_flags &= RF_ABILITY_NOMAGIC_MASK;
122 }
123
124 static void check_arena(player_type *player_ptr, melee_spell_type *ms_ptr)
125 {
126     if (!player_ptr->current_floor_ptr->inside_arena && !player_ptr->phase_out)
127         return;
128
129     ms_ptr->ability_flags.reset(RF_ABILITY_SUMMON_MASK).reset(RF_ABILITY::TELE_LEVEL);
130     if (ms_ptr->m_ptr->r_idx == MON_ROLENTO)
131         ms_ptr->ability_flags.reset(RF_ABILITY::SPECIAL);
132 }
133
134 static void check_melee_spell_distance(player_type *player_ptr, melee_spell_type *ms_ptr)
135 {
136     auto ball_mask_except_rocket = RF_ABILITY_BALL_MASK;
137     ball_mask_except_rocket.reset(RF_ABILITY::ROCKET);
138     if (ms_ptr->ability_flags.has_none_of(ball_mask_except_rocket))
139         return;
140
141     POSITION real_y = ms_ptr->y;
142     POSITION real_x = ms_ptr->x;
143     get_project_point(player_ptr, ms_ptr->m_ptr->fy, ms_ptr->m_ptr->fx, &real_y, &real_x, 0L);
144     if (!projectable(player_ptr, real_y, real_x, player_ptr->y, player_ptr->x) && ms_ptr->ability_flags.has(RF_ABILITY::BA_LITE)
145         && (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)) {
146         ms_ptr->ability_flags.reset(RF_ABILITY::BA_LITE);
147
148         return;
149     }
150
151     int dist = distance(real_y, real_x, player_ptr->y, player_ptr->x);
152     if (dist <= 2) {
153         ms_ptr->ability_flags.reset(ball_mask_except_rocket);
154         return;
155     }
156
157     if (dist > 4)
158         return;
159
160     ms_ptr->ability_flags.reset(RF_ABILITY_BIG_BALL_MASK);
161 }
162
163 static void check_melee_spell_rocket(player_type *player_ptr, melee_spell_type *ms_ptr)
164 {
165     if (ms_ptr->ability_flags.has_not(RF_ABILITY::ROCKET))
166         return;
167
168     POSITION real_y = ms_ptr->y;
169     POSITION real_x = ms_ptr->x;
170     get_project_point(player_ptr, ms_ptr->m_ptr->fy, ms_ptr->m_ptr->fx, &real_y, &real_x, PROJECT_STOP);
171     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))
172         ms_ptr->ability_flags.reset(RF_ABILITY::ROCKET);
173 }
174
175 static void check_melee_spell_beam(player_type *player_ptr, melee_spell_type *ms_ptr)
176 {
177     if (ms_ptr->ability_flags.has_none_of(RF_ABILITY_BEAM_MASK)
178         || 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))
179         return;
180
181     ms_ptr->ability_flags.reset(RF_ABILITY_BEAM_MASK);
182 }
183
184 static void check_melee_spell_breath(player_type *player_ptr, melee_spell_type *ms_ptr)
185 {
186     if (ms_ptr->ability_flags.has_none_of(RF_ABILITY_BREATH_MASK))
187         return;
188
189     POSITION rad = (ms_ptr->r_ptr->flags2 & RF2_POWERFUL) ? 3 : 2;
190     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, 0, true)) {
191         ms_ptr->ability_flags.reset(RF_ABILITY_BREATH_MASK);
192         return;
193     }
194
195     if (ms_ptr->ability_flags.has(RF_ABILITY::BR_LITE)
196         && !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, GF_LITE, true)) {
197         ms_ptr->ability_flags.reset(RF_ABILITY::BR_LITE);
198         return;
199     }
200
201     if (ms_ptr->ability_flags.has(RF_ABILITY::BR_DISI)
202         && !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, GF_DISINTEGRATE, true)) {
203         ms_ptr->ability_flags.reset(RF_ABILITY::BR_DISI);
204     }
205 }
206
207 static void check_melee_spell_special(player_type *player_ptr, melee_spell_type *ms_ptr)
208 {
209     if (ms_ptr->ability_flags.has_not(RF_ABILITY::SPECIAL))
210         return;
211
212     if (ms_ptr->m_ptr->r_idx == MON_ROLENTO) {
213         if ((player_ptr->pet_extra_flags & (PF_ATTACK_SPELL | PF_SUMMON_SPELL)) != (PF_ATTACK_SPELL | PF_SUMMON_SPELL))
214             ms_ptr->ability_flags.reset(RF_ABILITY::SPECIAL);
215
216         return;
217     }
218
219     if (ms_ptr->r_ptr->d_char == 'B') {
220         if ((player_ptr->pet_extra_flags & (PF_ATTACK_SPELL | PF_TELEPORT)) != (PF_ATTACK_SPELL | PF_TELEPORT))
221             ms_ptr->ability_flags.reset(RF_ABILITY::SPECIAL);
222
223         return;
224     }
225
226     ms_ptr->ability_flags.reset(RF_ABILITY::SPECIAL);
227 }
228
229 static void check_riding(player_type *player_ptr, melee_spell_type *ms_ptr)
230 {
231     if (ms_ptr->m_idx != player_ptr->riding)
232         return;
233
234     ms_ptr->ability_flags.reset(RF_ABILITY_RIDING_MASK);
235 }
236
237 static void check_pet(player_type *player_ptr, melee_spell_type *ms_ptr)
238 {
239     if (!ms_ptr->pet)
240         return;
241
242     ms_ptr->ability_flags.reset({ RF_ABILITY::SHRIEK, RF_ABILITY::DARKNESS, RF_ABILITY::TRAPS });
243     if (!(player_ptr->pet_extra_flags & PF_TELEPORT))
244         ms_ptr->ability_flags.reset({ RF_ABILITY::BLINK, RF_ABILITY::TPORT, RF_ABILITY::TELE_TO, RF_ABILITY::TELE_AWAY, RF_ABILITY::TELE_LEVEL });
245
246     if (!(player_ptr->pet_extra_flags & PF_ATTACK_SPELL)) {
247         ms_ptr->ability_flags.reset(RF_ABILITY_ATTACK_MASK);
248     }
249
250     if (!(player_ptr->pet_extra_flags & PF_SUMMON_SPELL)) {
251         ms_ptr->ability_flags.reset(RF_ABILITY_SUMMON_MASK);
252     }
253
254     if (!(player_ptr->pet_extra_flags & PF_BALL_SPELL) && (ms_ptr->m_idx != player_ptr->riding)) {
255         check_melee_spell_distance(player_ptr, ms_ptr);
256         check_melee_spell_rocket(player_ptr, ms_ptr);
257         check_melee_spell_beam(player_ptr, ms_ptr);
258         check_melee_spell_breath(player_ptr, ms_ptr);
259     }
260
261     check_melee_spell_special(player_ptr, ms_ptr);
262 }
263
264 static void check_non_stupid(player_type *player_ptr, melee_spell_type *ms_ptr)
265 {
266     if ((ms_ptr->r_ptr->flags2 & RF2_STUPID) != 0)
267         return;
268
269     if (ms_ptr->ability_flags.has_any_of(RF_ABILITY_BOLT_MASK)
270         && !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)) {
271         ms_ptr->ability_flags.reset(RF_ABILITY_BOLT_MASK);
272     }
273
274     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))) {
275         ms_ptr->ability_flags.reset(RF_ABILITY_SUMMON_MASK);
276     }
277
278     if (ms_ptr->ability_flags.has(RF_ABILITY::DISPEL) && !dispel_check_monster(player_ptr, ms_ptr->m_idx, ms_ptr->target_idx))
279         ms_ptr->ability_flags.reset(RF_ABILITY::DISPEL);
280
281     if (ms_ptr->ability_flags.has(RF_ABILITY::RAISE_DEAD) && !raise_possible(player_ptr, ms_ptr->m_ptr))
282         ms_ptr->ability_flags.reset(RF_ABILITY::RAISE_DEAD);
283
284     if (ms_ptr->ability_flags.has(RF_ABILITY::SPECIAL) && (ms_ptr->m_ptr->r_idx == MON_ROLENTO)
285         && !summon_possible(player_ptr, ms_ptr->t_ptr->fy, ms_ptr->t_ptr->fx))
286         ms_ptr->ability_flags.reset(RF_ABILITY::SPECIAL);
287 }
288
289 static void check_smart(player_type *player_ptr, melee_spell_type *ms_ptr)
290 {
291     if ((ms_ptr->r_ptr->flags2 & RF2_SMART) == 0)
292         return;
293
294     if ((ms_ptr->m_ptr->hp < ms_ptr->m_ptr->maxhp / 10) && (randint0(100) < 50)) {
295         ms_ptr->ability_flags &= RF_ABILITY_INT_MASK;
296     }
297
298     if (ms_ptr->ability_flags.has(RF_ABILITY::TELE_LEVEL)
299         && is_teleport_level_ineffective(player_ptr, (ms_ptr->target_idx == player_ptr->riding) ? 0 : ms_ptr->target_idx))
300         ms_ptr->ability_flags.reset(RF_ABILITY::TELE_LEVEL);
301 }
302
303 static bool set_melee_spell_set(player_type *player_ptr, melee_spell_type *ms_ptr)
304 {
305     if (ms_ptr->ability_flags.none())
306         return false;
307
308     EnumClassFlagGroup<RF_ABILITY>::get_flags(ms_ptr->ability_flags, std::back_inserter(ms_ptr->spells));
309
310     return !ms_ptr->spells.empty() && player_ptr->playing && !player_ptr->is_dead && !player_ptr->leaving;
311 }
312
313 bool check_melee_spell_set(player_type *player_ptr, melee_spell_type *ms_ptr)
314 {
315     if (monster_confused_remaining(ms_ptr->m_ptr))
316         return false;
317
318     ms_ptr->ability_flags = ms_ptr->r_ptr->ability_flags;
319     decide_melee_spell_target(player_ptr, ms_ptr);
320     decide_indirection_melee_spell(player_ptr, ms_ptr);
321     if (!check_melee_spell_projection(player_ptr, ms_ptr))
322         return false;
323
324     ms_ptr->y = ms_ptr->t_ptr->fy;
325     ms_ptr->x = ms_ptr->t_ptr->fx;
326     reset_target(ms_ptr->m_ptr);
327     ms_ptr->ability_flags.reset({ RF_ABILITY::WORLD, RF_ABILITY::TRAPS, RF_ABILITY::FORGET });
328     if (ms_ptr->ability_flags.has(RF_ABILITY::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))
329         ms_ptr->ability_flags.reset(RF_ABILITY::BR_LITE);
330
331     if (ms_ptr->ability_flags.has(RF_ABILITY::SPECIAL) && (ms_ptr->m_ptr->r_idx != MON_ROLENTO) && (ms_ptr->r_ptr->d_char != 'B'))
332         ms_ptr->ability_flags.reset(RF_ABILITY::SPECIAL);
333
334     check_darkness(player_ptr, ms_ptr);
335     check_stupid(ms_ptr);
336     check_arena(player_ptr, ms_ptr);
337     if (player_ptr->phase_out && !one_in_(3))
338         ms_ptr->ability_flags.reset(RF_ABILITY::HEAL);
339
340     check_riding(player_ptr, ms_ptr);
341     check_pet(player_ptr, ms_ptr);
342     check_non_stupid(player_ptr, ms_ptr);
343     check_smart(player_ptr, ms_ptr);
344     return set_melee_spell_set(player_ptr, ms_ptr);
345 }