OSDN Git Service

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