OSDN Git Service

[Refactor] #3286 Removed player-redraw-types.h
[hengbandforosx/hengbandosx.git] / src / status / bad-status-setter.cpp
1 #include "status/bad-status-setter.h"
2 #include "avatar/avatar.h"
3 #include "core/disturbance.h"
4 #include "core/stuff-handler.h"
5 #include "core/window-redrawer.h"
6 #include "game-option/disturbance-options.h"
7 #include "mind/mind-sniper.h"
8 #include "player-base/player-class.h"
9 #include "player-base/player-race.h"
10 #include "player-info/bluemage-data-type.h"
11 #include "player-info/monk-data-type.h"
12 #include "player/attack-defense-types.h"
13 #include "player/player-status-flags.h"
14 #include "player/player-status.h"
15 #include "player/special-defense-types.h"
16 #include "spell-realm/spells-hex.h"
17 #include "status/base-status.h"
18 #include "status/buff-setter.h"
19 #include "system/player-type-definition.h"
20 #include "system/redrawing-flags-updater.h"
21 #include "timed-effect/player-blindness.h"
22 #include "timed-effect/player-confusion.h"
23 #include "timed-effect/player-cut.h"
24 #include "timed-effect/player-deceleration.h"
25 #include "timed-effect/player-fear.h"
26 #include "timed-effect/player-hallucination.h"
27 #include "timed-effect/player-paralysis.h"
28 #include "timed-effect/player-poison.h"
29 #include "timed-effect/player-stun.h"
30 #include "timed-effect/timed-effects.h"
31 #include "view/display-messages.h"
32 #include <algorithm>
33
34 BadStatusSetter::BadStatusSetter(PlayerType *player_ptr)
35     : player_ptr(player_ptr)
36     , player_confusion(player_ptr->effects()->confusion())
37 {
38 }
39
40 /*!
41  * @brief 盲目の継続時間をセットする / Set "blind", notice observable changes
42  * @param v 継続時間
43  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
44  * @details
45  * Note the use of "PU_UN_LITE" and "PU_UN_VIEW", which is needed to\n
46  * memorize any terrain features which suddenly become "visible".\n
47  * Note that blindness is currently the only thing which can affect\n
48  * "player_can_see_bold()".\n
49  */
50 bool BadStatusSetter::set_blindness(const TIME_EFFECT tmp_v)
51 {
52     auto notice = false;
53     auto v = std::clamp<short>(tmp_v, 0, 10000);
54     if (this->player_ptr->is_dead) {
55         return false;
56     }
57
58     PlayerRace pr(this->player_ptr);
59     const auto blindness = this->player_ptr->effects()->blindness();
60     const auto is_blind = blindness->is_blind();
61     if (v > 0) {
62         if (!is_blind) {
63             if (pr.equals(PlayerRaceType::ANDROID)) {
64                 msg_print(_("センサーをやられた!", "The sensor broke!"));
65             } else {
66                 msg_print(_("目が見えなくなってしまった!", "You are blind!"));
67             }
68
69             notice = true;
70             chg_virtue(this->player_ptr, Virtue::ENLIGHTEN, -1);
71         }
72     } else {
73         if (is_blind) {
74             if (pr.equals(PlayerRaceType::ANDROID)) {
75                 msg_print(_("センサーが復旧した。", "The sensor has been restored."));
76             } else {
77                 msg_print(_("やっと目が見えるようになった。", "You can see again."));
78             }
79
80             notice = true;
81         }
82     }
83
84     blindness->set(v);
85     auto &rfu = RedrawingFlagsUpdater::get_instance();
86     rfu.set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
87     if (!notice) {
88         return false;
89     }
90
91     if (disturb_state) {
92         disturb(this->player_ptr, false, false);
93     }
94
95     const auto flags_srf = {
96         StatusRedrawingFlag::UN_VIEW,
97         StatusRedrawingFlag::UN_LITE,
98         StatusRedrawingFlag::VIEW,
99         StatusRedrawingFlag::LITE,
100         StatusRedrawingFlag::MONSTER_STATUSES,
101         StatusRedrawingFlag::MONSTER_LITE,
102     };
103     rfu.set_flags(flags_srf);
104     rfu.set_flag(MainWindowRedrawingFlag::MAP);
105     this->player_ptr->window_flags |= PW_OVERHEAD | PW_DUNGEON;
106     handle_stuff(this->player_ptr);
107     return true;
108 }
109
110 bool BadStatusSetter::mod_blindness(const TIME_EFFECT tmp_v)
111 {
112     return this->set_blindness(this->player_ptr->effects()->blindness()->current() + tmp_v);
113 }
114
115 /*!
116  * @brief 混乱の継続時間をセットする / Set "confused", notice observable changes
117  * @param v 継続時間
118  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
119  */
120 bool BadStatusSetter::set_confusion(const TIME_EFFECT tmp_v)
121 {
122     auto notice = false;
123     auto v = std::clamp<short>(tmp_v, 0, 10000);
124     if (this->player_ptr->is_dead) {
125         return false;
126     }
127
128     auto &rfu = RedrawingFlagsUpdater::get_instance();
129     auto is_confused = this->player_confusion->is_confused();
130     if (v > 0) {
131         if (!is_confused) {
132             msg_print(_("あなたは混乱した!", "You are confused!"));
133             if (this->player_ptr->action == ACTION_LEARN) {
134                 msg_print(_("学習が続けられない!", "You cannot continue learning!"));
135                 auto bluemage_data = PlayerClass(player_ptr).get_specific_data<bluemage_data_type>();
136                 bluemage_data->new_magic_learned = false;
137                 rfu.set_flag(MainWindowRedrawingFlag::ACTION);
138                 this->player_ptr->action = ACTION_NONE;
139             }
140             if (this->player_ptr->action == ACTION_MONK_STANCE) {
141                 msg_print(_("構えがとけた。", "You lose your stance."));
142                 PlayerClass(player_ptr).set_monk_stance(MonkStanceType::NONE);
143                 rfu.set_flag(StatusRedrawingFlag::BONUS);
144                 rfu.set_flag(MainWindowRedrawingFlag::ACTION);
145                 this->player_ptr->action = ACTION_NONE;
146             } else if (this->player_ptr->action == ACTION_SAMURAI_STANCE) {
147                 msg_print(_("型が崩れた。", "You lose your stance."));
148                 PlayerClass(player_ptr).lose_balance();
149             }
150
151             /* Sniper */
152             reset_concentration(this->player_ptr, true);
153
154             SpellHex spell_hex(this->player_ptr);
155             if (spell_hex.is_spelling_any()) {
156                 (void)spell_hex.stop_all_spells();
157             }
158
159             notice = true;
160             this->player_ptr->counter = false;
161             chg_virtue(this->player_ptr, Virtue::HARMONY, -1);
162         }
163     } else {
164         if (is_confused) {
165             msg_print(_("やっと混乱がおさまった。", "You feel less confused now."));
166             this->player_ptr->special_attack &= ~(ATTACK_SUIKEN);
167             notice = true;
168         }
169     }
170
171     this->player_confusion->set(v);
172     rfu.set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
173     if (!notice) {
174         return false;
175     }
176
177     if (disturb_state) {
178         disturb(this->player_ptr, false, false);
179     }
180
181     handle_stuff(this->player_ptr);
182     return true;
183 }
184
185 bool BadStatusSetter::mod_confusion(const TIME_EFFECT tmp_v)
186 {
187     return this->set_confusion(this->player_confusion->current() + tmp_v);
188 }
189
190 /*!
191  * @brief 毒の継続時間をセットする / Set "poisoned", notice observable changes
192  * @param v 継続時間
193  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
194  */
195 bool BadStatusSetter::set_poison(const TIME_EFFECT tmp_v)
196 {
197     auto notice = false;
198     auto v = std::clamp<short>(tmp_v, 0, 10000);
199     if (this->player_ptr->is_dead) {
200         return false;
201     }
202
203     const auto player_poison = this->player_ptr->effects()->poison();
204     const auto is_poisoned = player_poison->is_poisoned();
205     if (v > 0) {
206         if (!is_poisoned) {
207             msg_print(_("毒に侵されてしまった!", "You are poisoned!"));
208             notice = true;
209         }
210     } else {
211         if (is_poisoned) {
212             msg_print(_("やっと毒の痛みがなくなった。", "You are no longer poisoned."));
213             notice = true;
214         }
215     }
216
217     player_poison->set(v);
218     RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
219     if (!notice) {
220         return false;
221     }
222
223     if (disturb_state) {
224         disturb(this->player_ptr, false, false);
225     }
226
227     handle_stuff(this->player_ptr);
228     return true;
229 }
230
231 bool BadStatusSetter::mod_poison(const TIME_EFFECT tmp_v)
232 {
233     return this->set_poison(this->player_ptr->effects()->poison()->current() + tmp_v);
234 }
235
236 /*!
237  * @brief 恐怖の継続時間をセットする / Set "fearful", notice observable changes
238  * @param v 継続時間
239  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
240  */
241 bool BadStatusSetter::set_fear(const TIME_EFFECT tmp_v)
242 {
243     auto notice = false;
244     auto v = std::clamp<short>(tmp_v, 0, 10000);
245     if (this->player_ptr->is_dead) {
246         return false;
247     }
248
249     auto fear = this->player_ptr->effects()->fear();
250     if (v > 0) {
251         if (!fear->is_fearful()) {
252             msg_print(_("何もかも恐くなってきた!", "You are terrified!"));
253             if (PlayerClass(this->player_ptr).lose_balance()) {
254                 msg_print(_("型が崩れた。", "You lose your stance."));
255             }
256
257             notice = true;
258             this->player_ptr->counter = false;
259             chg_virtue(this->player_ptr, Virtue::VALOUR, -1);
260         }
261     } else {
262         if (fear->is_fearful()) {
263             msg_print(_("やっと恐怖を振り払った。", "You feel bolder now."));
264             notice = true;
265         }
266     }
267
268     fear->set(v);
269     RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
270     if (!notice) {
271         return false;
272     }
273
274     if (disturb_state) {
275         disturb(this->player_ptr, false, false);
276     }
277
278     handle_stuff(this->player_ptr);
279     return true;
280 }
281
282 bool BadStatusSetter::mod_fear(const TIME_EFFECT tmp_v)
283 {
284     return this->set_fear(this->player_ptr->effects()->fear()->current() + tmp_v);
285 }
286
287 /*!
288  * @brief 麻痺の継続時間をセットする / Set "paralyzed", notice observable changes
289  * @param v 継続時間
290  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
291  */
292 bool BadStatusSetter::set_paralysis(const TIME_EFFECT tmp_v)
293 {
294     auto notice = false;
295     auto v = std::clamp<short>(tmp_v, 0, 10000);
296     if (this->player_ptr->is_dead) {
297         return false;
298     }
299
300     auto paralysis = this->player_ptr->effects()->paralysis();
301     if (v > 0) {
302         if (!paralysis->is_paralyzed()) {
303             msg_print(_("体が麻痺してしまった!", "You are paralyzed!"));
304             reset_concentration(this->player_ptr, true);
305
306             SpellHex spell_hex(this->player_ptr);
307             if (spell_hex.is_spelling_any()) {
308                 (void)spell_hex.stop_all_spells();
309             }
310
311             this->player_ptr->counter = false;
312             notice = true;
313         }
314     } else {
315         if (paralysis->is_paralyzed()) {
316             msg_print(_("やっと動けるようになった。", "You can move again."));
317             notice = true;
318         }
319     }
320
321     paralysis->set(v);
322     RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
323     if (!notice) {
324         return false;
325     }
326
327     if (disturb_state) {
328         disturb(this->player_ptr, false, false);
329     }
330
331     RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::ACTION);
332     handle_stuff(this->player_ptr);
333     return true;
334 }
335
336 bool BadStatusSetter::mod_paralysis(const TIME_EFFECT tmp_v)
337 {
338     return this->set_paralysis(this->player_ptr->effects()->paralysis()->current() + tmp_v);
339 }
340
341 /*!
342  * @brief 幻覚の継続時間をセットする / Set "image", notice observable changes
343  * @param v 継続時間
344  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
345  * @details Note that we must redraw the map when hallucination changes.
346  */
347 bool BadStatusSetter::hallucination(const TIME_EFFECT tmp_v)
348 {
349     auto notice = false;
350     auto v = std::clamp<short>(tmp_v, 0, 10000);
351     if (this->player_ptr->is_dead) {
352         return false;
353     }
354
355     if (is_chargeman(this->player_ptr)) {
356         v = 0;
357     }
358
359     auto hallucination = this->player_ptr->effects()->hallucination();
360     if (v > 0) {
361         set_tsuyoshi(this->player_ptr, 0, true);
362         if (!hallucination->is_hallucinated()) {
363             msg_print(_("ワーオ!何もかも虹色に見える!", "Oh, wow! Everything looks so cosmic now!"));
364             reset_concentration(this->player_ptr, true);
365
366             this->player_ptr->counter = false;
367             notice = true;
368         }
369     } else {
370         if (hallucination->is_hallucinated()) {
371             msg_print(_("やっとはっきりと物が見えるようになった。", "You can see clearly again."));
372             notice = true;
373         }
374     }
375
376     hallucination->set(v);
377     auto &rfu = RedrawingFlagsUpdater::get_instance();
378     rfu.set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
379     if (!notice) {
380         return false;
381     }
382
383     if (disturb_state) {
384         disturb(this->player_ptr, false, true);
385     }
386
387     const auto flags_mwrf = {
388         MainWindowRedrawingFlag::MAP,
389         MainWindowRedrawingFlag::HEALTH,
390         MainWindowRedrawingFlag::UHEALTH,
391     };
392     rfu.set_flags(flags_mwrf);
393     rfu.set_flag(StatusRedrawingFlag::MONSTER_STATUSES);
394     this->player_ptr->window_flags |= PW_OVERHEAD | PW_DUNGEON;
395     handle_stuff(this->player_ptr);
396     return true;
397 }
398
399 bool BadStatusSetter::mod_hallucination(const TIME_EFFECT tmp_v)
400 {
401     return this->hallucination(this->player_ptr->effects()->hallucination()->current() + tmp_v);
402 }
403
404 /*!
405  * @brief 減速の継続時間をセットする / Set "slow", notice observable changes
406  * @param v 継続時間
407  * @param do_dec 現在の継続時間より長い値のみ上書きする
408  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
409  */
410 bool BadStatusSetter::set_deceleration(const TIME_EFFECT tmp_v, bool do_dec)
411 {
412     auto notice = false;
413     auto v = std::clamp<short>(tmp_v, 0, 10000);
414     if (this->player_ptr->is_dead) {
415         return false;
416     }
417
418     auto deceleration = this->player_ptr->effects()->deceleration();
419     auto is_slow = deceleration->is_slow();
420     if (v > 0) {
421         if (is_slow && !do_dec) {
422             if (deceleration->current() > v) {
423                 return false;
424             }
425         } else if (!is_slow) {
426             msg_print(_("体の動きが遅くなってしまった!", "You feel yourself moving slower!"));
427             notice = true;
428         }
429     } else {
430         if (is_slow) {
431             msg_print(_("動きの遅さがなくなったようだ。", "You feel yourself speed up."));
432             notice = true;
433         }
434     }
435
436     deceleration->set(v);
437     if (!notice) {
438         return false;
439     }
440
441     if (disturb_state) {
442         disturb(this->player_ptr, false, false);
443     }
444
445     RedrawingFlagsUpdater::get_instance().set_flag(StatusRedrawingFlag::BONUS);
446     handle_stuff(this->player_ptr);
447     return true;
448 }
449
450 bool BadStatusSetter::mod_deceleration(const TIME_EFFECT tmp_v, bool do_dec)
451 {
452     return this->set_deceleration(this->player_ptr->effects()->deceleration()->current() + tmp_v, do_dec);
453 }
454
455 /*!
456  * @brief 朦朧の継続時間をセットする / Set "stun", notice observable changes
457  * @param v 継続時間
458  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
459  * @details
460  * Note the special code to only notice "range" changes.
461  */
462 bool BadStatusSetter::set_stun(const TIME_EFFECT tmp_v)
463 {
464     auto v = std::clamp<short>(tmp_v, 0, 10000);
465     if (this->player_ptr->is_dead) {
466         return false;
467     }
468
469     if (PlayerRace(this->player_ptr).has_stun_immunity() || PlayerClass(this->player_ptr).has_stun_immunity()) {
470         v = 0;
471     }
472
473     auto notice = this->process_stun_effect(v);
474     this->player_ptr->effects()->stun()->set(v);
475     if (!notice) {
476         return false;
477     }
478
479     if (disturb_state) {
480         disturb(this->player_ptr, false, false);
481     }
482
483     auto &rfu = RedrawingFlagsUpdater::get_instance();
484     rfu.set_flag(StatusRedrawingFlag::BONUS);
485     rfu.set_flag(MainWindowRedrawingFlag::STUN);
486     handle_stuff(this->player_ptr);
487     return true;
488 }
489
490 bool BadStatusSetter::mod_stun(const TIME_EFFECT tmp_v)
491 {
492     return this->set_stun(this->player_ptr->effects()->stun()->current() + tmp_v);
493 }
494
495 /*!
496  * @brief 出血の継続時間をセットする / Set "cut", notice observable changes
497  * @param v 継続時間
498  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
499  * @details
500  * Note the special code to only notice "range" changes.
501  */
502 bool BadStatusSetter::set_cut(const TIME_EFFECT tmp_v)
503 {
504     auto v = std::clamp<short>(tmp_v, 0, 10000);
505     if (this->player_ptr->is_dead) {
506         return false;
507     }
508
509     if (PlayerRace(this->player_ptr).has_cut_immunity()) {
510         v = 0;
511     }
512
513     auto notice = this->process_cut_effect(v);
514     this->player_ptr->effects()->cut()->set(v);
515     if (!notice) {
516         return false;
517     }
518
519     if (disturb_state) {
520         disturb(this->player_ptr, false, false);
521     }
522
523     auto &rfu = RedrawingFlagsUpdater::get_instance();
524     rfu.set_flag(StatusRedrawingFlag::BONUS);
525     rfu.set_flag(MainWindowRedrawingFlag::CUT);
526     handle_stuff(this->player_ptr);
527     return true;
528 }
529
530 bool BadStatusSetter::mod_cut(const TIME_EFFECT tmp_v)
531 {
532     return this->set_cut(this->player_ptr->effects()->cut()->current() + tmp_v);
533 }
534
535 bool BadStatusSetter::process_stun_effect(const short v)
536 {
537     auto old_rank = this->player_ptr->effects()->stun()->get_rank();
538     auto new_rank = PlayerStun::get_rank(v);
539     if (new_rank > old_rank) {
540         this->process_stun_status(new_rank, v);
541         return true;
542     }
543
544     if (new_rank < old_rank) {
545         this->clear_head();
546         return true;
547     }
548
549     return false;
550 }
551
552 void BadStatusSetter::process_stun_status(const PlayerStunRank new_rank, const short v)
553 {
554     auto stun_mes = PlayerStun::get_stun_mes(new_rank);
555     msg_print(stun_mes);
556     this->decrease_int_wis(v);
557     if (PlayerClass(this->player_ptr).lose_balance()) {
558         msg_print(_("型が崩れた。", "You lose your stance."));
559     }
560
561     reset_concentration(this->player_ptr, true);
562
563     SpellHex spell_hex(this->player_ptr);
564     if (spell_hex.is_spelling_any()) {
565         (void)spell_hex.stop_all_spells();
566     }
567 }
568
569 void BadStatusSetter::clear_head()
570 {
571     if (this->player_ptr->effects()->stun()->is_stunned()) {
572         return;
573     }
574
575     msg_print(_("やっと朦朧状態から回復した。", "You are no longer stunned."));
576     if (disturb_state) {
577         disturb(this->player_ptr, false, false);
578     }
579 }
580
581 /*!
582  * @todo 後で知能と賢さが両方減る確率を減らす.
583  */
584 void BadStatusSetter::decrease_int_wis(const short v)
585 {
586     if ((v <= randint1(1000)) && !one_in_(16)) {
587         return;
588     }
589
590     msg_print(_("割れるような頭痛がする。", "A vicious blow hits your head."));
591     auto rand = randint0(5);
592     switch (rand) {
593     case 0:
594         if (has_sustain_int(this->player_ptr) == 0) {
595             (void)do_dec_stat(this->player_ptr, A_INT);
596         }
597
598         if (has_sustain_wis(this->player_ptr) == 0) {
599             (void)do_dec_stat(this->player_ptr, A_WIS);
600         }
601
602         return;
603     case 1:
604     case 2:
605         if (has_sustain_int(this->player_ptr) == 0) {
606             (void)do_dec_stat(this->player_ptr, A_INT);
607         }
608
609         return;
610     case 3:
611     case 4:
612         if (has_sustain_wis(this->player_ptr) == 0) {
613             (void)do_dec_stat(this->player_ptr, A_WIS);
614         }
615
616         return;
617     default:
618         throw("Invalid random number is specified!");
619     }
620 }
621
622 bool BadStatusSetter::process_cut_effect(const short v)
623 {
624     auto player_cut = this->player_ptr->effects()->cut();
625     auto old_rank = player_cut->get_rank();
626     auto new_rank = player_cut->get_rank(v);
627     if (new_rank > old_rank) {
628         this->decrease_charisma(new_rank, v);
629         return true;
630     }
631
632     if (new_rank < old_rank) {
633         this->stop_blooding(new_rank);
634         return true;
635     }
636
637     return false;
638 }
639
640 void BadStatusSetter::decrease_charisma(const PlayerCutRank new_rank, const short v)
641 {
642     auto player_cut = this->player_ptr->effects()->cut();
643     auto cut_mes = player_cut->get_cut_mes(new_rank);
644     msg_print(cut_mes);
645     if (v <= randint1(1000) && !one_in_(16)) {
646         return;
647     }
648
649     if (has_sustain_chr(this->player_ptr)) {
650         return;
651     }
652
653     msg_print(_("ひどい傷跡が残ってしまった。", "You have been horribly scarred."));
654     do_dec_stat(this->player_ptr, A_CHR);
655 }
656
657 void BadStatusSetter::stop_blooding(const PlayerCutRank new_rank)
658 {
659     if (new_rank >= PlayerCutRank::GRAZING) {
660         return;
661     }
662
663     auto blood_stop_mes = PlayerRace(this->player_ptr).equals(PlayerRaceType::ANDROID)
664                               ? _("怪我が直った", "leaking fluid")
665                               : _("出血が止まった", "bleeding");
666     msg_format(_("やっと%s。", "You are no longer %s."), blood_stop_mes);
667     if (disturb_state) {
668         disturb(this->player_ptr, false, false);
669     }
670 }