OSDN Git Service

[Refactor] race-flags#.h のインクルードを削除
[hengbandforosx/hengbandosx.git] / src / mspell / mspell-floor.cpp
1 /*!
2  * @brief フロアの一定範囲に効果を及ぼす (悲鳴、テレポート等)スペルの効果
3  * @date 2020/05/16
4  * @author Hourier
5  */
6
7 #include "mspell/mspell-floor.h"
8 #include "blue-magic/blue-magic-checker.h"
9 #include "core/disturbance.h"
10 #include "effect/attribute-types.h"
11 #include "effect/effect-characteristics.h"
12 #include "effect/effect-processor.h"
13 #include "mind/drs-types.h"
14 #include "monster-race/monster-race.h"
15 #include "monster-race/race-ability-flags.h"
16 #include "monster-race/race-brightness-mask.h"
17 #include "monster-race/race-flags-resistance.h"
18 #include "monster-race/race-indice-types.h"
19 #include "monster-race/race-resistance-mask.h"
20 #include "monster/monster-info.h"
21 #include "monster/monster-status-setter.h"
22 #include "monster/monster-status.h"
23 #include "monster/monster-update.h"
24 #include "mspell/mspell-result.h"
25 #include "mspell/mspell-status.h"
26 #include "mspell/mspell-util.h"
27 #include "player-base/player-class.h"
28 #include "player/player-personality-types.h"
29 #include "player/player-status-flags.h"
30 #include "player/player-status.h"
31 #include "spell-kind/spells-lite.h"
32 #include "spell-kind/spells-neighbor.h"
33 #include "spell-kind/spells-sight.h"
34 #include "spell-kind/spells-teleport.h"
35 #include "spell-kind/spells-world.h"
36 #include "spell-realm/spells-hex.h"
37 #include "system/floor-type-definition.h"
38 #include "system/monster-entity.h"
39 #include "system/monster-race-info.h"
40 #include "system/player-type-definition.h"
41 #include "system/redrawing-flags-updater.h"
42 #include "timed-effect/player-blindness.h"
43 #include "timed-effect/timed-effects.h"
44 #include "util/bit-flags-calculator.h"
45 #include "view/display-messages.h"
46
47 /*!
48  * @brief RF4_SHRIEKの処理。叫び。 /
49  * @param m_idx 呪文を唱えるモンスターID
50  * @param player_ptr プレイヤーへの参照ポインタ
51  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
52  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
53  *
54  * ラーニング不可。
55  */
56 MonsterSpellResult spell_RF4_SHRIEK(MONSTER_IDX m_idx, PlayerType *player_ptr, MONSTER_IDX t_idx, int target_type)
57 {
58     mspell_cast_msg_simple msg(_("%s^がかん高い金切り声をあげた。", "%s^ makes a high pitched shriek."),
59         _("%s^が%sに向かって叫んだ。", "%s^ shrieks at %s."));
60
61     simple_monspell_message(player_ptr, m_idx, t_idx, msg, target_type);
62
63     auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
64     auto result = MonsterSpellResult::make_valid();
65     if (m_ptr->r_idx == MonsterRaceId::LEE_QIEZI) {
66         msg_print(_("しかし、その声は誰の心にも響かなかった…。", "However, that voice didn't touch anyone's heart..."));
67         return result;
68     }
69
70     if (target_type == MONSTER_TO_PLAYER) {
71         aggravate_monsters(player_ptr, m_idx);
72     } else if (target_type == MONSTER_TO_MONSTER) {
73         set_monster_csleep(player_ptr, t_idx, 0);
74     }
75
76     return result;
77 }
78
79 /*!
80  * @brief RF6_WORLDの処理。時を止める。 /
81  * @param player_ptr プレイヤーへの参照ポインタ
82  * @param m_idx 呪文を唱えるモンスターID
83  *
84  * ラーニング不可。
85  */
86 MonsterSpellResult spell_RF6_WORLD(PlayerType *player_ptr, MONSTER_IDX m_idx)
87 {
88     auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
89     disturb(player_ptr, true, true);
90     (void)set_monster_timewalk(player_ptr, randint1(2) + 2, m_ptr->r_idx, true);
91
92     return MonsterSpellResult::make_valid();
93 }
94
95 /*!
96  * @brief RF6_BLINKの処理。ショート・テレポート。 /
97  * @param player_ptr プレイヤーへの参照ポインタ
98  * @param m_idx 呪文を唱えるモンスターID
99  * @param is_quantum_effect 量子的効果によるショート・テレポートの場合時TRUE
100  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
101  *
102  * ラーニング不可。
103  */
104 MonsterSpellResult spell_RF6_BLINK(PlayerType *player_ptr, MONSTER_IDX m_idx, int target_type, bool is_quantum_effect)
105 {
106     const auto res = MonsterSpellResult::make_valid();
107     const auto m_name = monster_name(player_ptr, m_idx);
108
109     if (target_type == MONSTER_TO_PLAYER) {
110         disturb(player_ptr, true, true);
111     }
112
113     if (!is_quantum_effect && SpellHex(player_ptr).check_hex_barrier(m_idx, HEX_ANTI_TELE)) {
114         if (see_monster(player_ptr, m_idx)) {
115             msg_format(_("魔法のバリアが%s^のテレポートを邪魔した。", "Magic barrier obstructs teleporting of %s^."), m_name.data());
116         }
117         return res;
118     }
119
120     if (see_monster(player_ptr, m_idx)) {
121         msg_format(_("%s^が瞬時に消えた。", "%s^ blinks away."), m_name.data());
122     }
123
124     teleport_away(player_ptr, m_idx, 10, TELEPORT_SPONTANEOUS);
125
126     if (target_type == MONSTER_TO_PLAYER) {
127         RedrawingFlagsUpdater::get_instance().set_flag(StatusRecalculatingFlag::MONSTER_STATUSES);
128     }
129
130     return res;
131 }
132
133 /*!
134  * @brief RF6_TPORTの処理。テレポート。 /
135  * @param player_ptr プレイヤーへの参照ポインタ
136  * @param m_idx 呪文を唱えるモンスターID
137  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
138  *
139  * ラーニング不可。
140  */
141 MonsterSpellResult spell_RF6_TPORT(PlayerType *player_ptr, MONSTER_IDX m_idx, int target_type)
142 {
143     const auto res = MonsterSpellResult::make_valid();
144     const auto m_name = monster_name(player_ptr, m_idx);
145
146     if (target_type == MONSTER_TO_PLAYER) {
147         disturb(player_ptr, true, true);
148     }
149     if (SpellHex(player_ptr).check_hex_barrier(m_idx, HEX_ANTI_TELE)) {
150         if (see_monster(player_ptr, m_idx)) {
151             msg_format(_("魔法のバリアが%s^のテレポートを邪魔した。", "Magic barrier obstructs teleporting of %s^."), m_name.data());
152         }
153         return res;
154     }
155
156     if (see_monster(player_ptr, m_idx)) {
157         msg_format(_("%s^がテレポートした。", "%s^ teleports away."), m_name.data());
158     }
159
160     teleport_away_followable(player_ptr, m_idx);
161
162     return res;
163 }
164
165 /*!
166  * @brief RF6_TELE_TOの処理。テレポート・バック。 /
167  * @param player_ptr プレイヤーへの参照ポインタ
168  * @param m_idx 呪文を唱えるモンスターID
169  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
170  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
171  *
172  * プレイヤーが対象ならラーニング可。
173  */
174 MonsterSpellResult spell_RF6_TELE_TO(PlayerType *player_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int target_type)
175 {
176     auto res = MonsterSpellResult::make_valid();
177     res.learnable = target_type == MONSTER_TO_PLAYER;
178
179     auto *floor_ptr = player_ptr->current_floor_ptr;
180     auto *m_ptr = &floor_ptr->m_list[m_idx];
181     auto *t_ptr = &floor_ptr->m_list[t_idx];
182     auto *tr_ptr = &t_ptr->get_monrace();
183
184     mspell_cast_msg_simple msg(_("%s^があなたを引き戻した。", "%s^ commands you to return."),
185         _("%s^が%sを引き戻した。", "%s^ commands %s to return."));
186
187     simple_monspell_message(player_ptr, m_idx, t_idx, msg, target_type);
188
189     if (target_type == MONSTER_TO_PLAYER) {
190         teleport_player_to(player_ptr, m_ptr->fy, m_ptr->fx, TELEPORT_PASSIVE);
191         return res;
192     }
193
194     if (target_type != MONSTER_TO_MONSTER) {
195         return res;
196     }
197
198     bool resists_tele = false;
199     const auto t_name = monster_name(player_ptr, t_idx);
200
201     if (tr_ptr->resistance_flags.has(MonsterResistanceType::RESIST_TELEPORT)) {
202         if (tr_ptr->kind_flags.has(MonsterKindType::UNIQUE) || tr_ptr->resistance_flags.has(MonsterResistanceType::RESIST_ALL)) {
203             if (is_original_ap_and_seen(player_ptr, t_ptr)) {
204                 tr_ptr->r_resistance_flags.set(MonsterResistanceType::RESIST_TELEPORT);
205             }
206             if (see_monster(player_ptr, t_idx)) {
207                 msg_format(_("%s^には効果がなかった。", "%s^ is unaffected!"), t_name.data());
208             }
209             resists_tele = true;
210         } else if (tr_ptr->level > randint1(100)) {
211             if (is_original_ap_and_seen(player_ptr, t_ptr)) {
212                 tr_ptr->r_resistance_flags.set(MonsterResistanceType::RESIST_TELEPORT);
213             }
214             if (see_monster(player_ptr, t_idx)) {
215                 msg_format(_("%s^は耐性を持っている!", "%s^ resists!"), t_name.data());
216             }
217             resists_tele = true;
218         }
219     }
220
221     if (resists_tele) {
222         set_monster_csleep(player_ptr, t_idx, 0);
223         return res;
224     }
225
226     if (t_idx == player_ptr->riding) {
227         teleport_player_to(player_ptr, m_ptr->fy, m_ptr->fx, TELEPORT_PASSIVE);
228     } else {
229         teleport_monster_to(player_ptr, t_idx, m_ptr->fy, m_ptr->fx, 100, TELEPORT_PASSIVE);
230     }
231     set_monster_csleep(player_ptr, t_idx, 0);
232
233     return res;
234 }
235
236 /*!
237  * @brief RF6_TELE_AWAYの処理。テレポート・アウェイ。 /
238  * @param player_ptr プレイヤーへの参照ポインタ
239  * @param m_idx 呪文を唱えるモンスターID
240  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
241  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
242  *
243  * プレイヤーが対象ならラーニング可。
244  */
245 MonsterSpellResult spell_RF6_TELE_AWAY(PlayerType *player_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int target_type)
246 {
247     auto res = MonsterSpellResult::make_valid();
248     res.learnable = target_type == MONSTER_TO_PLAYER;
249
250     auto *floor_ptr = player_ptr->current_floor_ptr;
251     MonsterEntity *t_ptr = &floor_ptr->m_list[t_idx];
252     MonsterRaceInfo *tr_ptr = &t_ptr->get_monrace();
253
254     mspell_cast_msg_simple msg(_("%s^にテレポートさせられた。", "%s^ teleports you away."),
255         _("%s^は%sをテレポートさせた。", "%s^ teleports %s away."));
256
257     simple_monspell_message(player_ptr, m_idx, t_idx, msg, target_type);
258
259     if (target_type == MONSTER_TO_PLAYER) {
260         if (is_echizen(player_ptr)) {
261             msg_print(_("くっそ~", ""));
262         } else if (is_chargeman(player_ptr)) {
263             if (randint0(2) == 0) {
264                 msg_print(_("ジュラル星人め!", ""));
265             } else {
266                 msg_print(_("弱い者いじめは止めるんだ!", ""));
267             }
268         }
269
270         teleport_player_away(m_idx, player_ptr, 100, false);
271         return res;
272     }
273
274     if (target_type != MONSTER_TO_MONSTER) {
275         return res;
276     }
277
278     bool resists_tele = false;
279     const auto t_name = monster_name(player_ptr, t_idx);
280
281     if (tr_ptr->resistance_flags.has(MonsterResistanceType::RESIST_TELEPORT)) {
282         if (tr_ptr->kind_flags.has(MonsterKindType::UNIQUE) || tr_ptr->resistance_flags.has(MonsterResistanceType::RESIST_ALL)) {
283             if (is_original_ap_and_seen(player_ptr, t_ptr)) {
284                 tr_ptr->r_resistance_flags.set(MonsterResistanceType::RESIST_TELEPORT);
285             }
286             if (see_monster(player_ptr, t_idx)) {
287                 msg_format(_("%s^には効果がなかった。", "%s^ is unaffected!"), t_name.data());
288             }
289             resists_tele = true;
290         } else if (tr_ptr->level > randint1(100)) {
291             if (is_original_ap_and_seen(player_ptr, t_ptr)) {
292                 tr_ptr->r_resistance_flags.set(MonsterResistanceType::RESIST_TELEPORT);
293             }
294             if (see_monster(player_ptr, t_idx)) {
295                 msg_format(_("%s^は耐性を持っている!", "%s^ resists!"), t_name.data());
296             }
297             resists_tele = true;
298         }
299     }
300
301     if (resists_tele) {
302         set_monster_csleep(player_ptr, t_idx, 0);
303         return res;
304     }
305
306     if (t_idx == player_ptr->riding) {
307         teleport_player_away(m_idx, player_ptr, MAX_PLAYER_SIGHT * 2 + 5, false);
308     } else {
309         teleport_away(player_ptr, t_idx, MAX_PLAYER_SIGHT * 2 + 5, TELEPORT_PASSIVE);
310     }
311     set_monster_csleep(player_ptr, t_idx, 0);
312
313     return res;
314 }
315
316 /*!
317  * @brief RF6_TELE_LEVELの処理。テレポート・レベル。 /
318  * @param player_ptr プレイヤーへの参照ポインタ
319  * @param m_idx 呪文を唱えるモンスターID
320  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
321  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
322  *
323  * ラーニング不可。
324  */
325 MonsterSpellResult spell_RF6_TELE_LEVEL(PlayerType *player_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int target_type)
326 {
327     const auto res = MonsterSpellResult::make_valid();
328
329     auto *floor_ptr = player_ptr->current_floor_ptr;
330     MonsterEntity *t_ptr = &floor_ptr->m_list[t_idx];
331     MonsterRaceInfo *tr_ptr = &t_ptr->get_monrace();
332     DEPTH rlev = monster_level_idx(floor_ptr, m_idx);
333     bool resist, saving_throw;
334
335     if (target_type == MONSTER_TO_PLAYER) {
336         resist = (has_resist_nexus(player_ptr) != 0);
337         saving_throw = (randint0(100 + rlev / 2) < player_ptr->skill_sav);
338
339         mspell_cast_msg_bad_status_to_player msg(_("%s^が何か奇妙な言葉をつぶやいた。", "%s^ mumbles strangely."),
340             _("%s^があなたの足を指さした。", "%s^ gestures at your feet."), _("しかし効果がなかった!", "You are unaffected!"),
341             _("しかし効力を跳ね返した!", "You resist the effects!"));
342
343         spell_badstatus_message_to_player(player_ptr, m_idx, msg, resist, saving_throw);
344
345         if (!resist && !saving_throw) {
346             teleport_level(player_ptr, 0);
347         }
348
349         update_smart_learn(player_ptr, m_idx, DRS_NEXUS);
350         return res;
351     }
352
353     if (target_type != MONSTER_TO_MONSTER) {
354         return res;
355     }
356
357     resist = tr_ptr->resistance_flags.has_any_of(RFR_EFF_RESIST_NEXUS_MASK) || tr_ptr->resistance_flags.has(MonsterResistanceType::RESIST_TELEPORT);
358     saving_throw = (tr_ptr->misc_flags.has(MonsterMiscType::QUESTOR)) || (tr_ptr->level > randint1((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10);
359
360     mspell_cast_msg_bad_status_to_monster msg(_("%s^が%sの足を指さした。", "%s^ gestures at %s's feet."),
361         _("%s^には効果がなかった。", "%s^ is unaffected!"), _("%s^は効力を跳ね返した!", "%s^ resist the effects!"), "");
362
363     spell_badstatus_message_to_mons(player_ptr, m_idx, t_idx, msg, resist, saving_throw);
364
365     if (!resist && !saving_throw) {
366         teleport_level(player_ptr, (t_idx == player_ptr->riding) ? 0 : t_idx);
367     }
368
369     return res;
370 }
371
372 /*!
373  * @brief RF6_DARKNESSの処理。暗闇or閃光。 /
374  * @param target_type プレイヤーへの参照ポインタ
375  * @param y 対象の地点のy座標
376  * @param x 対象の地点のx座標
377  * @param m_idx 呪文を唱えるモンスターID
378  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
379  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
380  *
381  * プレイヤーが対象かつ暗闇ならラーニング可。
382  */
383 MonsterSpellResult spell_RF6_DARKNESS(PlayerType *player_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int target_type)
384 {
385     mspell_cast_msg_blind msg;
386     concptr msg_done;
387     auto *floor_ptr = player_ptr->current_floor_ptr;
388     auto *m_ptr = &floor_ptr->m_list[m_idx];
389     auto *r_ptr = &m_ptr->get_monrace();
390     bool can_use_lite_area = false;
391     bool monster_to_monster = target_type == MONSTER_TO_MONSTER;
392     bool monster_to_player = target_type == MONSTER_TO_PLAYER;
393     const auto t_name = monster_name(player_ptr, t_idx);
394
395     const auto is_ninja = PlayerClass(player_ptr).equals(PlayerClassType::NINJA);
396     const auto is_living_monster = r_ptr->kind_flags.has_not(MonsterKindType::UNDEAD);
397     const auto is_not_weak_lite = r_ptr->resistance_flags.has_not(MonsterResistanceType::HURT_LITE);
398     if (is_ninja && is_living_monster && is_not_weak_lite && r_ptr->brightness_flags.has_none_of(dark_mask)) {
399         can_use_lite_area = true;
400     }
401
402     const auto &t_ref = floor_ptr->m_list[t_idx];
403     if (monster_to_monster && !t_ref.is_hostile()) {
404         can_use_lite_area = false;
405     }
406
407     auto res = MonsterSpellResult::make_valid();
408     res.learnable = monster_to_player && !can_use_lite_area;
409
410     if (can_use_lite_area) {
411         msg.blind = _("%s^が何かをつぶやいた。", "%s^ mumbles.");
412         msg.to_player = _("%s^が辺りを明るく照らした。", "%s^ cast a spell to light up.");
413         msg.to_mons = _("%s^が辺りを明るく照らした。", "%s^ cast a spell to light up.");
414
415         msg_done = _("%s^は白い光に包まれた。", "%s^ is surrounded by a white light.");
416     } else {
417         msg.blind = _("%s^が何かをつぶやいた。", "%s^ mumbles.");
418         msg.to_player = _("%s^が暗闇の中で手を振った。", "%s^ gestures in shadow.");
419         msg.to_mons = _("%s^が暗闇の中で手を振った。", "%s^ gestures in shadow.");
420
421         msg_done = _("%s^は暗闇に包まれた。", "%s^ is surrounded by darkness.");
422     }
423
424     monspell_message(player_ptr, m_idx, t_idx, msg, target_type);
425
426     if (see_monster(player_ptr, t_idx) && monster_to_monster) {
427         msg_format(msg_done, t_name.data());
428     }
429
430     if (monster_to_player) {
431         if (can_use_lite_area) {
432             (void)lite_area(player_ptr, 0, 3);
433         } else {
434             (void)unlite_area(player_ptr, 0, 3);
435         }
436     } else if (monster_to_monster) {
437         if (can_use_lite_area) {
438             (void)project(player_ptr, m_idx, 3, y, x, 0, AttributeType::LITE_WEAK, PROJECT_GRID | PROJECT_KILL);
439             lite_room(player_ptr, y, x);
440         } else {
441             (void)project(player_ptr, m_idx, 3, y, x, 0, AttributeType::DARK_WEAK, PROJECT_GRID | PROJECT_KILL);
442             unlite_room(player_ptr, y, x);
443         }
444     }
445
446     return res;
447 }
448
449 /*!
450  * @brief RF6_TRAPSの処理。トラップ。 /
451  * @param player_ptr プレイヤーへの参照ポインタ
452  * @param y 対象の地点のy座標
453  * @param x 対象の地点のx座標
454  * @param m_idx 呪文を唱えるモンスターID
455  *
456  * ラーニング可。
457  */
458 MonsterSpellResult spell_RF6_TRAPS(PlayerType *player_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx)
459 {
460     const auto m_name = monster_name(player_ptr, m_idx);
461     disturb(player_ptr, true, true);
462
463     if (player_ptr->effects()->blindness()->is_blind()) {
464         msg_format(_("%s^が何かをつぶやいて邪悪に微笑んだ。", "%s^ mumbles, and then cackles evilly."), m_name.data());
465     } else {
466         msg_format(_("%s^が呪文を唱えて邪悪に微笑んだ。", "%s^ casts a spell and cackles evilly."), m_name.data());
467     }
468
469     (void)trap_creation(player_ptr, y, x);
470
471     auto res = MonsterSpellResult::make_valid();
472     res.learnable = true;
473
474     return res;
475 }
476
477 /*!
478  * @brief RF6_RAISE_DEADの処理。死者復活。 /
479  * @param player_ptr プレイヤーへの参照ポインタ
480  * @param m_idx 呪文を唱えるモンスターID
481  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
482  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
483  *
484  * ラーニング不可。
485  */
486 MonsterSpellResult spell_RF6_RAISE_DEAD(PlayerType *player_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int target_type)
487 {
488     auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
489     mspell_cast_msg_blind msg(_("%s^が何かをつぶやいた。", "%s^ mumbles."),
490         _("%s^が死者復活の呪文を唱えた。", "%s^ casts a spell to revive corpses."), _("%s^が死者復活の呪文を唱えた。", "%s^ casts a spell to revive corpses."));
491
492     monspell_message(player_ptr, m_idx, t_idx, msg, target_type);
493
494     animate_dead(player_ptr, m_idx, m_ptr->fy, m_ptr->fx);
495
496     return MonsterSpellResult::make_valid();
497 }