OSDN Git Service

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