OSDN Git Service

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