OSDN Git Service

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