OSDN Git Service

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