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"
39 #include "monster/monster-description-types.h"
42 static void set_no_magic_mask(msa_type *msa_ptr)
44 if (!msa_ptr->no_inate)
47 msa_ptr->ability_flags.reset(RF_ABILITY_NOMAGIC_MASK);
50 static void check_mspell_stupid(player_type *target_ptr, msa_type *msa_ptr)
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))
58 msa_ptr->ability_flags &= RF_ABILITY_NOMAGIC_MASK;
61 static void check_mspell_smart(player_type *target_ptr, msa_type *msa_ptr)
63 if ((msa_ptr->r_ptr->flags2 & RF2_SMART) == 0)
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;
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);
75 static void check_mspell_arena(player_type *target_ptr, msa_type *msa_ptr)
77 if (!target_ptr->current_floor_ptr->inside_arena && !target_ptr->phase_out)
80 msa_ptr->ability_flags.reset(RF_ABILITY_SUMMON_MASK).reset(RF_ABILITY::TELE_LEVEL);
82 if (msa_ptr->m_ptr->r_idx == MON_ROLENTO)
83 msa_ptr->ability_flags.reset(RF_ABILITY::SPECIAL);
86 static bool check_mspell_non_stupid(player_type *target_ptr, msa_type *msa_ptr)
88 if ((msa_ptr->r_ptr->flags2 & RF2_STUPID) != 0)
92 msa_ptr->ability_flags.reset(RF_ABILITY::DRAIN_MANA);
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);
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);
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);
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);
110 return msa_ptr->ability_flags.any();
113 static void set_mspell_list(msa_type *msa_ptr)
115 EnumClassFlagGroup<RF_ABILITY>::get_flags(msa_ptr->ability_flags, std::back_inserter(msa_ptr->mspells));
118 static void describe_mspell_monster(player_type *target_ptr, msa_type *msa_ptr)
120 monster_desc(target_ptr, msa_ptr->m_name, msa_ptr->m_ptr, 0x00);
124 /* Get the monster possessive ("his"/"her"/"its") */
126 monster_desc(target_ptr, m_poss, msa_ptr->m_ptr, MD_PRON_VISIBLE | MD_POSSESSIVE);
130 static bool switch_do_spell(player_type *target_ptr, msa_type *msa_ptr)
132 switch (msa_ptr->do_spell) {
133 case DO_SPELL_NONE: {
136 msa_ptr->thrown_spell = choose_attack_spell(target_ptr, msa_ptr);
137 if (msa_ptr->thrown_spell != RF_ABILITY::MAX)
143 case DO_SPELL_BR_LITE:
144 msa_ptr->thrown_spell = RF_ABILITY::BR_LITE;
146 case DO_SPELL_BR_DISI:
147 msa_ptr->thrown_spell = RF_ABILITY::BR_DISI;
149 case DO_SPELL_BA_LITE:
150 msa_ptr->thrown_spell = RF_ABILITY::BA_LITE;
157 static bool check_mspell_continuation(player_type *target_ptr, msa_type *msa_ptr)
159 if (msa_ptr->ability_flags.none())
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))
167 set_mspell_list(msa_ptr);
168 if (msa_ptr->mspells.empty() || !target_ptr->playing || target_ptr->is_dead || target_ptr->leaving)
171 describe_mspell_monster(target_ptr, msa_ptr);
172 if (!switch_do_spell(target_ptr, msa_ptr) || (msa_ptr->thrown_spell == RF_ABILITY::MAX))
178 static bool check_mspell_unexploded(player_type *target_ptr, msa_type *msa_ptr)
180 PERCENTAGE fail_rate = 25 - (msa_ptr->rlev + 3) / 4;
181 if (msa_ptr->r_ptr->flags2 & RF2_STUPID)
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);
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);
200 * @brief モンスターが使おうとする特技がプレイヤーに届くかどうかを返す
202 * ターゲット (msa_ptr->y, msa_ptr->x) は設定済みとする。
204 static bool check_thrown_mspell(player_type *target_ptr, msa_type *msa_ptr)
206 // プレイヤーがモンスターを正しく視認できていれば思い出に残る。
207 // FIXME: ここで処理するのはおかしいような?
208 msa_ptr->can_remember = is_original_ap_and_seen(target_ptr, msa_ptr->m_ptr);
210 // ターゲットがプレイヤー位置なら直接射線が通っているので常に届く。
211 bool direct = player_bold(target_ptr, msa_ptr->y, msa_ptr->x);
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:
255 static void check_mspell_imitation(player_type *target_ptr, msa_type *msa_ptr)
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))
262 /* Not RF_ABILITY::SPECIAL */
263 if (msa_ptr->thrown_spell == RF_ABILITY::SPECIAL)
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];
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;
281 static void remember_mspell(msa_type *msa_ptr)
283 if (!msa_ptr->can_remember)
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++;
292 * @brief モンスターの特殊技能メインルーチン /
293 * Creatures can cast spells, shoot missiles, and breathe.
294 * @param target_ptr プレーヤーへの参照ポインタ
295 * @param m_idx モンスター構造体配列のID
296 * @return 実際に特殊技能を利用したらTRUEを返す
298 bool make_attack_spell(player_type *target_ptr, MONSTER_IDX m_idx)
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);
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))
311 decide_lite_range(target_ptr, msa_ptr);
312 if (!decide_lite_projection(target_ptr, msa_ptr))
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))
324 if (check_mspell_unexploded(target_ptr, msa_ptr))
327 // 特技がプレイヤーに届かないなら使わない。
328 if (!check_thrown_mspell(target_ptr, msa_ptr))
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)
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++;