OSDN Git Service

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