OSDN Git Service

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