OSDN Git Service

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