OSDN Git Service

405e90bc3cc87747e9d3dff8e7a453349e2b2a5a
[hengbandforosx/hengbandosx.git] / src / status / buff-setter.cpp
1 #include "status/buff-setter.h"
2 #include "avatar/avatar.h"
3 #include "core/disturbance.h"
4 #include "core/player-redraw-types.h"
5 #include "core/speed-table.h"
6 #include "core/stuff-handler.h"
7 #include "core/window-redrawer.h"
8 #include "game-option/disturbance-options.h"
9 #include "monster/monster-status-setter.h"
10 #include "player-base/player-class.h"
11 #include "player-info/class-info.h"
12 #include "player-info/race-info.h"
13 #include "player/attack-defense-types.h"
14 #include "player/player-status.h"
15 #include "realm/realm-song-numbers.h"
16 #include "spell-realm/spells-song.h"
17 #include "status/base-status.h"
18 #include "status/buff-setter.h"
19 #include "status/element-resistance.h"
20 #include "system/player-type-definition.h"
21 #include "system/redrawing-flags-updater.h"
22 #include "timed-effect/player-acceleration.h"
23 #include "timed-effect/player-blindness.h"
24 #include "timed-effect/player-confusion.h"
25 #include "timed-effect/player-cut.h"
26 #include "timed-effect/player-deceleration.h"
27 #include "timed-effect/player-fear.h"
28 #include "timed-effect/player-hallucination.h"
29 #include "timed-effect/player-paralysis.h"
30 #include "timed-effect/player-poison.h"
31 #include "timed-effect/player-stun.h"
32 #include "timed-effect/timed-effects.h"
33 #include "view/display-messages.h"
34
35 /*!
36  * @brief プレイヤーの全ての時限効果をリセットする。 / reset timed flags
37  */
38 void reset_tim_flags(PlayerType *player_ptr)
39 {
40     auto effects = player_ptr->effects();
41     effects->acceleration()->reset();
42     player_ptr->lightspeed = 0;
43     effects->deceleration()->reset();
44     effects->blindness()->reset();
45     effects->paralysis()->reset();
46     effects->confusion()->reset();
47     effects->fear()->reset();
48     effects->hallucination()->reset();
49     effects->poison()->reset();
50     effects->cut()->reset();
51     effects->stun()->reset();
52
53     player_ptr->protevil = 0; /* Timed -- Protection */
54     player_ptr->invuln = 0; /* Timed -- Invulnerable */
55     player_ptr->ult_res = 0;
56     player_ptr->hero = 0; /* Timed -- Heroism */
57     player_ptr->shero = 0; /* Timed -- Super Heroism */
58     player_ptr->shield = 0; /* Timed -- Shield Spell */
59     player_ptr->blessed = 0; /* Timed -- Blessed */
60     player_ptr->tim_invis = 0; /* Timed -- Invisibility */
61     player_ptr->tim_infra = 0; /* Timed -- Infra Vision */
62     player_ptr->tim_regen = 0; /* Timed -- Regeneration */
63     player_ptr->tim_stealth = 0; /* Timed -- Stealth */
64     player_ptr->tim_esp = 0;
65     player_ptr->wraith_form = 0; /* Timed -- Wraith Form */
66     player_ptr->tim_levitation = 0;
67     player_ptr->tim_sh_touki = 0;
68     player_ptr->tim_sh_fire = 0;
69     player_ptr->tim_sh_holy = 0;
70     player_ptr->tim_eyeeye = 0;
71     player_ptr->magicdef = 0;
72     player_ptr->resist_magic = 0;
73     player_ptr->tsuyoshi = 0;
74     player_ptr->tim_pass_wall = 0;
75     player_ptr->tim_res_nether = 0;
76     player_ptr->tim_res_time = 0;
77     player_ptr->tim_mimic = 0;
78     player_ptr->mimic_form = MimicKindType::NONE;
79     player_ptr->tim_reflect = 0;
80     player_ptr->multishadow = 0;
81     player_ptr->dustrobe = 0;
82     player_ptr->action = ACTION_NONE;
83
84     player_ptr->oppose_acid = 0; /* Timed -- oppose acid */
85     player_ptr->oppose_elec = 0; /* Timed -- oppose lightning */
86     player_ptr->oppose_fire = 0; /* Timed -- oppose heat */
87     player_ptr->oppose_cold = 0; /* Timed -- oppose cold */
88     player_ptr->oppose_pois = 0; /* Timed -- oppose poison */
89
90     player_ptr->word_recall = 0;
91     player_ptr->alter_reality = 0;
92     player_ptr->sutemi = false;
93     player_ptr->counter = false;
94     player_ptr->ele_attack = 0;
95     player_ptr->ele_immune = 0;
96     player_ptr->special_attack = 0L;
97     player_ptr->special_defense = 0L;
98
99     while (player_ptr->energy_need < 0) {
100         player_ptr->energy_need += ENERGY_NEED();
101     }
102
103     player_ptr->timewalk = false;
104
105     if (player_ptr->riding) {
106         (void)set_monster_fast(player_ptr, player_ptr->riding, 0);
107         (void)set_monster_slow(player_ptr, player_ptr->riding, 0);
108         (void)set_monster_invulner(player_ptr, player_ptr->riding, 0, false);
109     }
110
111     if (PlayerClass(player_ptr).equals(PlayerClassType::BARD)) {
112         set_singing_song_effect(player_ptr, MUSIC_NONE);
113         set_singing_song_id(player_ptr, 0);
114     }
115 }
116
117 /*!
118  * @brief 加速の継続時間をセットする / Set "fast", notice observable changes
119  * @param v 継続時間
120  * @param do_dec 現在の継続時間より長い値のみ上書きする
121  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
122  */
123 bool set_acceleration(PlayerType *player_ptr, TIME_EFFECT v, bool do_dec)
124 {
125     bool notice = false;
126     v = (v > 10000) ? 10000 : (v < 0) ? 0
127                                       : v;
128
129     if (player_ptr->is_dead) {
130         return false;
131     }
132
133     auto acceleration = player_ptr->effects()->acceleration();
134     if (v) {
135         if (acceleration->is_fast() && !do_dec) {
136             if (acceleration->current() > v) {
137                 return false;
138             }
139         } else if (!is_fast(player_ptr) && !player_ptr->lightspeed) {
140             msg_print(_("素早く動けるようになった!", "You feel yourself moving much faster!"));
141             notice = true;
142             chg_virtue(player_ptr, Virtue::PATIENCE, -1);
143             chg_virtue(player_ptr, Virtue::DILIGENCE, 1);
144         }
145     } else {
146         if (acceleration->is_fast() && !player_ptr->lightspeed) {
147             auto is_singing = music_singing(player_ptr, MUSIC_SPEED);
148             is_singing |= music_singing(player_ptr, MUSIC_SHERO);
149             if (!is_singing) {
150                 msg_print(_("動きの素早さがなくなったようだ。", "You feel yourself slow down."));
151                 notice = true;
152             }
153         }
154     }
155
156     acceleration->set(v);
157     if (!notice) {
158         return false;
159     }
160
161     if (disturb_state) {
162         disturb(player_ptr, false, false);
163     }
164     RedrawingFlagsUpdater::get_instance().set_flag(StatusRedrawingFlag::BONUS);
165     handle_stuff(player_ptr);
166     return true;
167 }
168
169 bool mod_acceleration(PlayerType *player_ptr, const TIME_EFFECT v, const bool do_dec)
170 {
171     return set_acceleration(player_ptr, player_ptr->effects()->acceleration()->current() + v, do_dec);
172 }
173
174 /*!
175  * @brief 肌石化の継続時間をセットする / Set "shield", notice observable changes
176  * @param v 継続時間
177  * @param do_dec 現在の継続時間より長い値のみ上書きする
178  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
179  */
180 bool set_shield(PlayerType *player_ptr, TIME_EFFECT v, bool do_dec)
181 {
182     bool notice = false;
183     v = (v > 10000) ? 10000 : (v < 0) ? 0
184                                       : v;
185
186     if (player_ptr->is_dead) {
187         return false;
188     }
189
190     if (v) {
191         if (player_ptr->shield && !do_dec) {
192             if (player_ptr->shield > v) {
193                 return false;
194             }
195         } else if (!player_ptr->shield) {
196             msg_print(_("肌が石になった。", "Your skin turns to stone."));
197             notice = true;
198         }
199     } else {
200         if (player_ptr->shield) {
201             msg_print(_("肌が元に戻った。", "Your skin returns to normal."));
202             notice = true;
203         }
204     }
205
206     player_ptr->shield = v;
207     auto &rfu = RedrawingFlagsUpdater::get_instance();
208     rfu.set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
209     if (!notice) {
210         return false;
211     }
212
213     if (disturb_state) {
214         disturb(player_ptr, false, false);
215     }
216
217     rfu.set_flag(StatusRedrawingFlag::BONUS);
218     handle_stuff(player_ptr);
219     return true;
220 }
221
222 /*!
223  * @brief 魔法の鎧の継続時間をセットする / Set "magicdef", notice observable changes
224  * @param v 継続時間
225  * @param do_dec 現在の継続時間より長い値のみ上書きする
226  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
227  */
228 bool set_magicdef(PlayerType *player_ptr, TIME_EFFECT v, bool do_dec)
229 {
230     bool notice = false;
231     v = (v > 10000) ? 10000 : (v < 0) ? 0
232                                       : v;
233
234     if (player_ptr->is_dead) {
235         return false;
236     }
237
238     if (v) {
239         if (player_ptr->magicdef && !do_dec) {
240             if (player_ptr->magicdef > v) {
241                 return false;
242             }
243         } else if (!player_ptr->magicdef) {
244             msg_print(_("魔法の防御力が増したような気がする。", "You feel more resistant to magic."));
245             notice = true;
246         }
247     } else {
248         if (player_ptr->magicdef) {
249             msg_print(_("魔法の防御力が元に戻った。", "You feel less resistant to magic."));
250             notice = true;
251         }
252     }
253
254     player_ptr->magicdef = v;
255     auto &rfu = RedrawingFlagsUpdater::get_instance();
256     rfu.set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
257     if (!notice) {
258         return false;
259     }
260
261     if (disturb_state) {
262         disturb(player_ptr, false, false);
263     }
264
265     rfu.set_flag(StatusRedrawingFlag::BONUS);
266     handle_stuff(player_ptr);
267     return true;
268 }
269
270 /*!
271  * @brief 祝福の継続時間をセットする / Set "blessed", notice observable changes
272  * @param v 継続時間
273  * @param do_dec 現在の継続時間より長い値のみ上書きする
274  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
275  */
276 bool set_blessed(PlayerType *player_ptr, TIME_EFFECT v, bool do_dec)
277 {
278     bool notice = false;
279     v = (v > 10000) ? 10000 : (v < 0) ? 0
280                                       : v;
281
282     if (player_ptr->is_dead) {
283         return false;
284     }
285
286     if (v) {
287         if (player_ptr->blessed && !do_dec) {
288             if (player_ptr->blessed > v) {
289                 return false;
290             }
291         } else if (!is_blessed(player_ptr)) {
292             msg_print(_("高潔な気分になった!", "You feel righteous!"));
293             notice = true;
294         }
295     } else {
296         if (player_ptr->blessed && !music_singing(player_ptr, MUSIC_BLESS)) {
297             msg_print(_("高潔な気分が消え失せた。", "The prayer has expired."));
298             notice = true;
299         }
300     }
301
302     player_ptr->blessed = v;
303     auto &rfu = RedrawingFlagsUpdater::get_instance();
304     rfu.set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
305     if (!notice) {
306         return false;
307     }
308
309     if (disturb_state) {
310         disturb(player_ptr, false, false);
311     }
312
313     rfu.set_flag(StatusRedrawingFlag::BONUS);
314     handle_stuff(player_ptr);
315     return true;
316 }
317
318 /*!
319  * @brief 士気高揚の継続時間をセットする / Set "hero", notice observable changes
320  * @param v 継続時間
321  * @param do_dec 現在の継続時間より長い値のみ上書きする
322  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
323  */
324 bool set_hero(PlayerType *player_ptr, TIME_EFFECT v, bool do_dec)
325 {
326     bool notice = false;
327     v = (v > 10000) ? 10000 : (v < 0) ? 0
328                                       : v;
329
330     if (player_ptr->is_dead) {
331         return false;
332     }
333
334     if (v) {
335         if (player_ptr->hero && !do_dec) {
336             if (player_ptr->hero > v) {
337                 return false;
338             }
339         } else if (!is_hero(player_ptr)) {
340             msg_print(_("ヒーローになった気がする!", "You feel like a hero!"));
341             notice = true;
342         }
343     } else {
344         if (player_ptr->hero && !music_singing(player_ptr, MUSIC_HERO) && !music_singing(player_ptr, MUSIC_SHERO)) {
345             msg_print(_("ヒーローの気分が消え失せた。", "The heroism wears off."));
346             notice = true;
347         }
348     }
349
350     player_ptr->hero = v;
351     auto &rfu = RedrawingFlagsUpdater::get_instance();
352     rfu.set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
353     if (!notice) {
354         return false;
355     }
356
357     if (disturb_state) {
358         disturb(player_ptr, false, false);
359     }
360
361     const auto flags = {
362         StatusRedrawingFlag::BONUS,
363         StatusRedrawingFlag::HP,
364     };
365     RedrawingFlagsUpdater::get_instance().set_flags(flags);
366     handle_stuff(player_ptr);
367     return true;
368 }
369
370 /*!
371  * @brief 変身効果の継続時間と変身先をセットする / Set "tim_mimic", and "mimic_form", notice observable changes
372  * @param v 継続時間
373  * @param mimic_race_idx 変身内容
374  * @param do_dec 現在の継続時間より長い値のみ上書きする
375  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
376  */
377 bool set_mimic(PlayerType *player_ptr, TIME_EFFECT v, MimicKindType mimic_race_idx, bool do_dec)
378 {
379     bool notice = false;
380     v = (v > 10000) ? 10000 : (v < 0) ? 0
381                                       : v;
382
383     if (player_ptr->is_dead) {
384         return false;
385     }
386
387     if (v) {
388         if (player_ptr->tim_mimic && (player_ptr->mimic_form == mimic_race_idx) && !do_dec) {
389             if (player_ptr->tim_mimic > v) {
390                 return false;
391             }
392         } else if ((!player_ptr->tim_mimic) || (player_ptr->mimic_form != mimic_race_idx)) {
393             msg_print(_("自分の体が変わってゆくのを感じた。", "You feel that your body changes."));
394             player_ptr->mimic_form = mimic_race_idx;
395             notice = true;
396         }
397     }
398
399     else {
400         if (player_ptr->tim_mimic) {
401             msg_print(_("変身が解けた。", "You are no longer transformed."));
402             if (player_ptr->mimic_form == MimicKindType::DEMON) {
403                 set_oppose_fire(player_ptr, 0, true);
404             }
405             player_ptr->mimic_form = MimicKindType::NONE;
406             notice = true;
407             mimic_race_idx = MimicKindType::NONE;
408         }
409     }
410
411     player_ptr->tim_mimic = v;
412     if (!notice) {
413         return false;
414     }
415
416     if (disturb_state) {
417         disturb(player_ptr, false, true);
418     }
419
420     auto &rfu = RedrawingFlagsUpdater::get_instance();
421     const auto flags_mwrf = {
422         MainWindowRedrawingFlag::BASIC,
423         MainWindowRedrawingFlag::TIMED_EFFECT,
424     };
425     rfu.set_flags(flags_mwrf);
426     const auto flags_srf = {
427         StatusRedrawingFlag::BONUS,
428         StatusRedrawingFlag::HP,
429     };
430     rfu.set_flags(flags_srf);
431     handle_stuff(player_ptr);
432     return true;
433 }
434
435 /*!
436  * @brief 狂戦士化の継続時間をセットする / Set "shero", notice observable changes
437  * @param v 継続時間/ 0ならば無条件にリセット
438  * @param do_dec FALSEの場合現在の継続時間より長い値のみ上書きする
439  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
440  */
441 bool set_shero(PlayerType *player_ptr, TIME_EFFECT v, bool do_dec)
442 {
443     bool notice = false;
444     v = (v > 10000) ? 10000 : (v < 0) ? 0
445                                       : v;
446
447     if (player_ptr->is_dead) {
448         return false;
449     }
450
451     if (PlayerClass(player_ptr).equals(PlayerClassType::BERSERKER)) {
452         v = 1;
453         return false;
454     }
455
456     if (v) {
457         if (player_ptr->shero && !do_dec) {
458             if (player_ptr->shero > v) {
459                 return false;
460             }
461         } else if (!player_ptr->shero) {
462             msg_print(_("殺戮マシーンになった気がする!", "You feel like a killing machine!"));
463             notice = true;
464         }
465     } else {
466         if (player_ptr->shero) {
467             msg_print(_("野蛮な気持ちが消え失せた。", "You feel less berserk."));
468             notice = true;
469         }
470     }
471
472     player_ptr->shero = v;
473     auto &rfu = RedrawingFlagsUpdater::get_instance();
474     rfu.set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
475     if (!notice) {
476         return false;
477     }
478
479     if (disturb_state) {
480         disturb(player_ptr, false, false);
481     }
482
483     const auto flags = {
484         StatusRedrawingFlag::BONUS,
485         StatusRedrawingFlag::HP,
486     };
487     RedrawingFlagsUpdater::get_instance().set_flags(flags);
488     handle_stuff(player_ptr);
489     return true;
490 }
491
492 /*!
493  * @brief 幽体化の継続時間をセットする / Set "wraith_form", notice observable changes
494  * @param v 継続時間
495  * @param do_dec 現在の継続時間より長い値のみ上書きする
496  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
497  */
498 bool set_wraith_form(PlayerType *player_ptr, TIME_EFFECT v, bool do_dec)
499 {
500     bool notice = false;
501     v = (v > 10000) ? 10000 : (v < 0) ? 0
502                                       : v;
503
504     if (player_ptr->is_dead) {
505         return false;
506     }
507
508     auto &rfu = RedrawingFlagsUpdater::get_instance();
509     if (v) {
510         if (player_ptr->wraith_form && !do_dec) {
511             if (player_ptr->wraith_form > v) {
512                 return false;
513             }
514         } else if (!player_ptr->wraith_form) {
515             msg_print(_("物質界を離れて幽鬼のような存在になった!", "You leave the physical world and turn into a wraith-being!"));
516             notice = true;
517             chg_virtue(player_ptr, Virtue::UNLIFE, 3);
518             chg_virtue(player_ptr, Virtue::HONOUR, -2);
519             chg_virtue(player_ptr, Virtue::SACRIFICE, -2);
520             chg_virtue(player_ptr, Virtue::VALOUR, -5);
521             rfu.set_flag(MainWindowRedrawingFlag::MAP);
522             rfu.set_flag(StatusRedrawingFlag::MONSTER_STATUSES);
523             player_ptr->window_flags |= (PW_OVERHEAD | PW_DUNGEON);
524         }
525     } else {
526         if (player_ptr->wraith_form) {
527             msg_print(_("不透明になった感じがする。", "You feel opaque."));
528             notice = true;
529             rfu.set_flag(MainWindowRedrawingFlag::MAP);
530             rfu.set_flag(StatusRedrawingFlag::MONSTER_STATUSES);
531             player_ptr->window_flags |= (PW_OVERHEAD | PW_DUNGEON);
532         }
533     }
534
535     player_ptr->wraith_form = v;
536     rfu.set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
537     if (!notice) {
538         return false;
539     }
540
541     if (disturb_state) {
542         disturb(player_ptr, false, false);
543     }
544
545     rfu.set_flag(StatusRedrawingFlag::BONUS);
546     handle_stuff(player_ptr);
547     return true;
548 }
549
550 /*!
551  * @brief オクレ兄さんの継続時間をセットする / Set "tsuyoshi", notice observable changes
552  * @param v 継続時間
553  * @param do_dec 現在の継続時間より長い値のみ上書きする
554  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
555  */
556 bool set_tsuyoshi(PlayerType *player_ptr, TIME_EFFECT v, bool do_dec)
557 {
558     bool notice = false;
559     v = (v > 10000) ? 10000 : (v < 0) ? 0
560                                       : v;
561
562     if (player_ptr->is_dead) {
563         return false;
564     }
565
566     if (v) {
567         if (player_ptr->tsuyoshi && !do_dec) {
568             if (player_ptr->tsuyoshi > v) {
569                 return false;
570             }
571         } else if (!player_ptr->tsuyoshi) {
572             msg_print(_("「オクレ兄さん!」", "Brother OKURE!"));
573             notice = true;
574             chg_virtue(player_ptr, Virtue::VITALITY, 2);
575         }
576     } else {
577         if (player_ptr->tsuyoshi) {
578             msg_print(_("肉体が急速にしぼんでいった。", "Your body has quickly shriveled."));
579
580             (void)dec_stat(player_ptr, A_CON, 20, true);
581             (void)dec_stat(player_ptr, A_STR, 20, true);
582
583             notice = true;
584             chg_virtue(player_ptr, Virtue::VITALITY, -3);
585         }
586     }
587
588     player_ptr->tsuyoshi = v;
589     auto &rfu = RedrawingFlagsUpdater::get_instance();
590     rfu.set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
591     if (!notice) {
592         return false;
593     }
594
595     if (disturb_state) {
596         disturb(player_ptr, false, false);
597     }
598
599     const auto flags = {
600         StatusRedrawingFlag::BONUS,
601         StatusRedrawingFlag::HP,
602     };
603     rfu.set_flags(flags);
604     handle_stuff(player_ptr);
605     return true;
606 }