OSDN Git Service

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