OSDN Git Service

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