OSDN Git Service

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