OSDN Git Service

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