OSDN Git Service

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