OSDN Git Service

Merge pull request #3814 from Slimebreath6078/feature/Add_Laffey_II
[hengbandforosx/hengbandosx.git] / src / mspell / mspell-attack.cpp
1 #include "mspell/mspell-attack.h"
2 #include "blue-magic/blue-magic-checker.h"
3 #include "core/disturbance.h"
4 #include "dungeon/dungeon-flag-types.h"
5 #include "dungeon/quest.h"
6 #include "floor/cave.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-indice-types.h"
11 #include "monster/monster-describer.h"
12 #include "monster/monster-flag-types.h"
13 #include "monster/monster-info.h"
14 #include "monster/monster-status.h"
15 #include "mspell/assign-monster-spell.h"
16 #include "mspell/improper-mspell-remover.h"
17 #include "mspell/mspell-attack-util.h"
18 #include "mspell/mspell-checker.h"
19 #include "mspell/mspell-learn-checker.h"
20 #include "mspell/mspell-lite.h"
21 #include "mspell/mspell-result.h"
22 #include "mspell/mspell-selector.h"
23 #include "mspell/mspell-util.h"
24 #include "player-base/player-class.h"
25 #include "player-info/mane-data-type.h"
26 #include "player/attack-defense-types.h"
27 #include "spell-kind/spells-world.h"
28 #include "spell-realm/spells-hex.h"
29 #include "system/angband-system.h"
30 #include "system/dungeon-info.h"
31 #include "system/floor-type-definition.h"
32 #include "system/monster-entity.h"
33 #include "system/monster-race-info.h"
34 #include "system/player-type-definition.h"
35 #include "system/redrawing-flags-updater.h"
36 #include "target/projection-path-calculator.h"
37 #include "timed-effect/player-blindness.h"
38 #include "timed-effect/timed-effects.h"
39 #include "util/string-processor.h"
40 #include "view/display-messages.h"
41 #include "world/world.h"
42 #include <iterator>
43 #ifdef JP
44 #else
45 #include "monster/monster-description-types.h"
46 #endif
47
48 static void set_no_magic_mask(msa_type *msa_ptr)
49 {
50     if (!msa_ptr->no_inate) {
51         return;
52     }
53
54     msa_ptr->ability_flags.reset(RF_ABILITY_NOMAGIC_MASK);
55 }
56
57 static void check_mspell_stupid(PlayerType *player_ptr, msa_type *msa_ptr)
58 {
59     auto *floor_ptr = player_ptr->current_floor_ptr;
60     auto is_in_no_magic_dungeon = floor_ptr->get_dungeon_definition().flags.has(DungeonFeatureType::NO_MAGIC);
61     is_in_no_magic_dungeon &= floor_ptr->is_in_dungeon();
62     is_in_no_magic_dungeon &= !floor_ptr->is_in_quest() || QuestType::is_fixed(floor_ptr->quest_number);
63     msa_ptr->in_no_magic_dungeon = is_in_no_magic_dungeon;
64     if (!msa_ptr->in_no_magic_dungeon || (msa_ptr->r_ptr->behavior_flags.has(MonsterBehaviorType::STUPID))) {
65         return;
66     }
67
68     msa_ptr->ability_flags &= RF_ABILITY_NOMAGIC_MASK;
69 }
70
71 static void check_mspell_smart(const FloorType &floor, msa_type *msa_ptr)
72 {
73     if (msa_ptr->r_ptr->behavior_flags.has_not(MonsterBehaviorType::SMART)) {
74         return;
75     }
76
77     if ((msa_ptr->m_ptr->hp < msa_ptr->m_ptr->maxhp / 10) && (randint0(100) < 50)) {
78         msa_ptr->ability_flags &= RF_ABILITY_INT_MASK;
79     }
80
81     if (msa_ptr->ability_flags.has(MonsterAbilityType::TELE_LEVEL) && floor.can_teleport_level()) {
82         msa_ptr->ability_flags.reset(MonsterAbilityType::TELE_LEVEL);
83     }
84 }
85
86 static void check_mspell_arena(const FloorType &floor, msa_type *msa_ptr)
87 {
88     if (!floor.inside_arena && !AngbandSystem::get_instance().is_phase_out()) {
89         return;
90     }
91
92     msa_ptr->ability_flags.reset(RF_ABILITY_SUMMON_MASK).reset(MonsterAbilityType::TELE_LEVEL);
93
94     if (msa_ptr->m_ptr->r_idx == MonsterRaceId::ROLENTO) {
95         msa_ptr->ability_flags.reset(MonsterAbilityType::SPECIAL);
96     }
97 }
98
99 static bool check_mspell_non_stupid(PlayerType *player_ptr, msa_type *msa_ptr)
100 {
101     if (msa_ptr->r_ptr->behavior_flags.has(MonsterBehaviorType::STUPID)) {
102         return true;
103     }
104
105     if (!player_ptr->csp) {
106         msa_ptr->ability_flags.reset(MonsterAbilityType::DRAIN_MANA);
107     }
108
109     if (msa_ptr->ability_flags.has_any_of(RF_ABILITY_BOLT_MASK) &&
110         !clean_shot(player_ptr, msa_ptr->m_ptr->fy, msa_ptr->m_ptr->fx, player_ptr->y, player_ptr->x, false)) {
111         msa_ptr->ability_flags.reset(RF_ABILITY_BOLT_MASK);
112     }
113
114     if (msa_ptr->ability_flags.has_any_of(RF_ABILITY_SUMMON_MASK) && !(summon_possible(player_ptr, msa_ptr->y, msa_ptr->x))) {
115         msa_ptr->ability_flags.reset(RF_ABILITY_SUMMON_MASK);
116     }
117
118     if (msa_ptr->ability_flags.has(MonsterAbilityType::RAISE_DEAD) && !raise_possible(player_ptr, msa_ptr->m_ptr)) {
119         msa_ptr->ability_flags.reset(MonsterAbilityType::RAISE_DEAD);
120     }
121
122     if (msa_ptr->ability_flags.has(MonsterAbilityType::SPECIAL) && (msa_ptr->m_ptr->r_idx == MonsterRaceId::ROLENTO) &&
123         !summon_possible(player_ptr, msa_ptr->y, msa_ptr->x)) {
124         msa_ptr->ability_flags.reset(MonsterAbilityType::SPECIAL);
125     }
126
127     return msa_ptr->ability_flags.any();
128 }
129
130 static void set_mspell_list(msa_type *msa_ptr)
131 {
132     EnumClassFlagGroup<MonsterAbilityType>::get_flags(msa_ptr->ability_flags, std::back_inserter(msa_ptr->mspells));
133 }
134
135 static bool switch_do_spell(PlayerType *player_ptr, msa_type *msa_ptr)
136 {
137     switch (msa_ptr->do_spell) {
138     case DO_SPELL_NONE: {
139         int attempt = 10;
140         while (attempt--) {
141             msa_ptr->thrown_spell = choose_attack_spell(player_ptr, msa_ptr);
142             if (msa_ptr->thrown_spell != MonsterAbilityType::MAX) {
143                 break;
144             }
145         }
146
147         return true;
148     }
149     case DO_SPELL_BR_LITE:
150         msa_ptr->thrown_spell = MonsterAbilityType::BR_LITE;
151         return true;
152     case DO_SPELL_BR_DISI:
153         msa_ptr->thrown_spell = MonsterAbilityType::BR_DISI;
154         return true;
155     case DO_SPELL_BA_LITE:
156         msa_ptr->thrown_spell = MonsterAbilityType::BA_LITE;
157         return true;
158     default:
159         return false;
160     }
161 }
162
163 static bool check_mspell_continuation(PlayerType *player_ptr, msa_type *msa_ptr)
164 {
165     if (msa_ptr->ability_flags.none()) {
166         return false;
167     }
168
169     remove_bad_spells(msa_ptr->m_idx, player_ptr, msa_ptr->ability_flags);
170     check_mspell_arena(*player_ptr->current_floor_ptr, msa_ptr);
171     if (msa_ptr->ability_flags.none() || !check_mspell_non_stupid(player_ptr, msa_ptr)) {
172         return false;
173     }
174
175     set_mspell_list(msa_ptr);
176     if (msa_ptr->mspells.empty() || !player_ptr->playing || player_ptr->is_dead || player_ptr->leaving) {
177         return false;
178     }
179
180     msa_ptr->m_name = monster_desc(player_ptr, msa_ptr->m_ptr, 0x00);
181     if (!switch_do_spell(player_ptr, msa_ptr) || (msa_ptr->thrown_spell == MonsterAbilityType::MAX)) {
182         return false;
183     }
184
185     return true;
186 }
187
188 static bool check_mspell_unexploded(PlayerType *player_ptr, msa_type *msa_ptr)
189 {
190     PERCENTAGE fail_rate = 25 - (msa_ptr->rlev + 3) / 4;
191     if (msa_ptr->r_ptr->behavior_flags.has(MonsterBehaviorType::STUPID)) {
192         fail_rate = 0;
193     }
194
195     if (!spell_is_inate(msa_ptr->thrown_spell) && (msa_ptr->in_no_magic_dungeon || (msa_ptr->m_ptr->get_remaining_stun() && one_in_(2)) || (randint0(100) < fail_rate))) {
196         disturb(player_ptr, true, true);
197         msg_format(_("%s^は呪文を唱えようとしたが失敗した。", "%s^ tries to cast a spell, but fails."), msa_ptr->m_name.data());
198         return true;
199     }
200
201     if (!spell_is_inate(msa_ptr->thrown_spell) && SpellHex(player_ptr).check_hex_barrier(msa_ptr->m_idx, HEX_ANTI_MAGIC)) {
202         msg_format(_("反魔法バリアが%s^の呪文をかき消した。", "Anti magic barrier cancels the spell which %s^ casts."), msa_ptr->m_name.data());
203         return true;
204     }
205
206     return false;
207 }
208
209 /*!
210  * @brief モンスターが使おうとする特技がプレイヤーに届くかどうかを返す
211  *
212  * ターゲット (msa_ptr->y, msa_ptr->x) は設定済みとする。
213  */
214 static bool check_thrown_mspell(PlayerType *player_ptr, msa_type *msa_ptr)
215 {
216     // プレイヤーがモンスターを正しく視認できていれば思い出に残る。
217     // FIXME: ここで処理するのはおかしいような?
218     msa_ptr->can_remember = is_original_ap_and_seen(player_ptr, msa_ptr->m_ptr);
219
220     // ターゲットがプレイヤー位置なら直接射線が通っているので常に届く。
221     if (player_ptr->is_located_at({ msa_ptr->y, msa_ptr->x })) {
222         return true;
223     }
224
225     // ターゲットがプレイヤー位置からずれているとき、直接の射線を必要とする特技
226     // (ボルト系など)は届かないものとみなす。
227     switch (msa_ptr->thrown_spell) {
228     case MonsterAbilityType::DISPEL:
229     case MonsterAbilityType::SHOOT:
230     case MonsterAbilityType::DRAIN_MANA:
231     case MonsterAbilityType::MIND_BLAST:
232     case MonsterAbilityType::BRAIN_SMASH:
233     case MonsterAbilityType::CAUSE_1:
234     case MonsterAbilityType::CAUSE_2:
235     case MonsterAbilityType::CAUSE_3:
236     case MonsterAbilityType::CAUSE_4:
237     case MonsterAbilityType::BO_ACID:
238     case MonsterAbilityType::BO_ELEC:
239     case MonsterAbilityType::BO_FIRE:
240     case MonsterAbilityType::BO_COLD:
241     case MonsterAbilityType::BO_NETH:
242     case MonsterAbilityType::BO_WATE:
243     case MonsterAbilityType::BO_MANA:
244     case MonsterAbilityType::BO_PLAS:
245     case MonsterAbilityType::BO_ICEE:
246     case MonsterAbilityType::BO_VOID:
247     case MonsterAbilityType::BO_ABYSS:
248     case MonsterAbilityType::MISSILE:
249     case MonsterAbilityType::SCARE:
250     case MonsterAbilityType::BLIND:
251     case MonsterAbilityType::CONF:
252     case MonsterAbilityType::SLOW:
253     case MonsterAbilityType::HOLD:
254     case MonsterAbilityType::HAND_DOOM:
255     case MonsterAbilityType::TELE_TO:
256     case MonsterAbilityType::TELE_AWAY:
257     case MonsterAbilityType::TELE_LEVEL:
258     case MonsterAbilityType::PSY_SPEAR:
259     case MonsterAbilityType::DARKNESS:
260     case MonsterAbilityType::FORGET:
261         return false;
262     default:
263         return true;
264     }
265 }
266
267 static void check_mspell_imitation(PlayerType *player_ptr, msa_type *msa_ptr)
268 {
269     const auto seen = (!player_ptr->effects()->blindness()->is_blind() && msa_ptr->m_ptr->ml);
270     const auto can_imitate = player_ptr->current_floor_ptr->has_los({ msa_ptr->m_ptr->fy, msa_ptr->m_ptr->fx });
271     PlayerClass pc(player_ptr);
272     if (!seen || !can_imitate || (w_ptr->timewalk_m_idx != 0) || !pc.equals(PlayerClassType::IMITATOR)) {
273         return;
274     }
275
276     /* Not RF_ABILITY::SPECIAL */
277     if (msa_ptr->thrown_spell == MonsterAbilityType::SPECIAL) {
278         return;
279     }
280
281     auto mane_data = pc.get_specific_data<mane_data_type>();
282
283     if (mane_data->mane_list.size() == MAX_MANE) {
284         mane_data->mane_list.pop_front();
285     }
286
287     mane_data->mane_list.push_back({ msa_ptr->thrown_spell, msa_ptr->dam });
288     mane_data->new_mane = true;
289     RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::IMITATION);
290 }
291
292 static void remember_mspell(msa_type *msa_ptr)
293 {
294     if (!msa_ptr->can_remember) {
295         return;
296     }
297
298     msa_ptr->r_ptr->r_ability_flags.set(msa_ptr->thrown_spell);
299     if (msa_ptr->r_ptr->r_cast_spell < MAX_UCHAR) {
300         msa_ptr->r_ptr->r_cast_spell++;
301     }
302 }
303
304 /*!
305  * @brief モンスターの特殊技能メインルーチン /
306  * Creatures can cast spells, shoot missiles, and breathe.
307  * @param player_ptr プレイヤーへの参照ポインタ
308  * @param m_idx モンスター構造体配列のID
309  * @return 実際に特殊技能を利用したらTRUEを返す
310  */
311 bool make_attack_spell(PlayerType *player_ptr, MONSTER_IDX m_idx)
312 {
313     msa_type tmp_msa(player_ptr, m_idx);
314     msa_type *msa_ptr = &tmp_msa;
315     if (msa_ptr->m_ptr->is_confused()) {
316         reset_target(msa_ptr->m_ptr);
317         return false;
318     }
319
320     const auto &m_ref = *msa_ptr->m_ptr;
321     auto should_prevent = m_ref.mflag.has(MonsterTemporaryFlagType::PREVENT_MAGIC);
322     should_prevent |= !m_ref.is_hostile();
323     should_prevent |= (m_ref.cdis > AngbandSystem::get_instance().get_max_range()) && !m_ref.target_y;
324     if (should_prevent) {
325         return false;
326     }
327
328     decide_lite_range(player_ptr, msa_ptr);
329     if (!decide_lite_projection(player_ptr, msa_ptr)) {
330         return false;
331     }
332
333     reset_target(msa_ptr->m_ptr);
334     msa_ptr->rlev = ((msa_ptr->r_ptr->level >= 1) ? msa_ptr->r_ptr->level : 1);
335     set_no_magic_mask(msa_ptr);
336     decide_lite_area(player_ptr, msa_ptr);
337     check_mspell_stupid(player_ptr, msa_ptr);
338     check_mspell_smart(*player_ptr->current_floor_ptr, msa_ptr);
339     if (!check_mspell_continuation(player_ptr, msa_ptr)) {
340         return false;
341     }
342
343     if (check_mspell_unexploded(player_ptr, msa_ptr)) {
344         return true;
345     }
346
347     // 特技がプレイヤーに届かないなら使わない。
348     if (!check_thrown_mspell(player_ptr, msa_ptr)) {
349         return false;
350     }
351
352     // 特技を使う。
353     const auto monspell_res = monspell_to_player(player_ptr, msa_ptr->thrown_spell, msa_ptr->y, msa_ptr->x, m_idx);
354     if (!monspell_res.valid) {
355         return false;
356     }
357
358     msa_ptr->dam = monspell_res.dam;
359     check_mspell_imitation(player_ptr, msa_ptr);
360     remember_mspell(msa_ptr);
361     if (player_ptr->is_dead && (msa_ptr->r_ptr->r_deaths < MAX_SHORT) && !player_ptr->current_floor_ptr->inside_arena) {
362         msa_ptr->r_ptr->r_deaths++;
363     }
364
365     return true;
366 }