OSDN Git Service

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