OSDN Git Service

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