OSDN Git Service

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