OSDN Git Service

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