OSDN Git Service

Merge pull request #3569 from sikabane-works/release/3.0.0.88-alpha
[hengbandforosx/hengbandosx.git] / src / mind / mind-samurai.cpp
1 /*!
2  * @brief 剣術家のレイシャルパワー処理
3  * @date 2020/05/16
4  * @author Hourier
5  */
6
7 #include "mind/mind-samurai.h"
8 #include "action/action-limited.h"
9 #include "avatar/avatar.h"
10 #include "cmd-action/cmd-attack.h"
11 #include "inventory/inventory-slot-types.h"
12 #include "io/input-key-acceptor.h"
13 #include "mind/stances-table.h"
14 #include "monster-attack/monster-attack-player.h"
15 #include "monster-race/monster-race-hook.h"
16 #include "monster-race/monster-race.h"
17 #include "monster-race/race-flags-resistance.h"
18 #include "monster-race/race-flags3.h"
19 #include "monster-race/race-resistance-mask.h"
20 #include "monster/monster-describer.h"
21 #include "monster/monster-info.h"
22 #include "monster/monster-status-setter.h"
23 #include "monster/monster-status.h"
24 #include "object-enchant/tr-types.h"
25 #include "pet/pet-util.h"
26 #include "player-attack/player-attack.h"
27 #include "player-base/player-class.h"
28 #include "player-info/samurai-data-type.h"
29 #include "player/attack-defense-types.h"
30 #include "status/action-setter.h"
31 #include "system/grid-type-definition.h"
32 #include "system/item-entity.h"
33 #include "system/monster-entity.h"
34 #include "system/monster-race-info.h"
35 #include "system/player-type-definition.h"
36 #include "system/redrawing-flags-updater.h"
37 #include "term/screen-processor.h"
38 #include "term/z-form.h"
39 #include "timed-effect/player-cut.h"
40 #include "timed-effect/player-fear.h"
41 #include "timed-effect/player-stun.h"
42 #include "timed-effect/timed-effects.h"
43 #include "util/bit-flags-calculator.h"
44 #include "util/int-char-converter.h"
45 #include "view/display-messages.h"
46
47 struct samurai_slaying_type {
48     samurai_slaying_type(MULTIPLY mult, const TrFlags &flags, MonsterEntity *m_ptr, combat_options mode, MonsterRaceInfo *r_ptr);
49     MULTIPLY mult;
50     TrFlags flags;
51     MonsterEntity *m_ptr;
52     combat_options mode;
53     MonsterRaceInfo *r_ptr;
54 };
55
56 samurai_slaying_type::samurai_slaying_type(MULTIPLY mult, const TrFlags &flags, MonsterEntity *m_ptr, combat_options mode, MonsterRaceInfo *r_ptr)
57     : mult(mult)
58     , flags(flags)
59     , m_ptr(m_ptr)
60     , mode(mode)
61     , r_ptr(r_ptr)
62 {
63 }
64
65 /*!
66  * @nrief 焔霊 (焼棄スレイ)
67  * @param player_ptr プレイヤーへの参照ポインタ
68  * @param samurai_slaying_ptr スレイ計算に必要なパラメータ群への参照ポインタ
69  */
70 static void hissatsu_burning_strike(PlayerType *player_ptr, samurai_slaying_type *samurai_slaying_ptr)
71 {
72     if (samurai_slaying_ptr->mode != HISSATSU_FIRE) {
73         return;
74     }
75
76     /* Notice immunity */
77     if (samurai_slaying_ptr->r_ptr->resistance_flags.has_any_of(RFR_EFF_IM_FIRE_MASK)) {
78         if (is_original_ap_and_seen(player_ptr, samurai_slaying_ptr->m_ptr)) {
79             samurai_slaying_ptr->r_ptr->r_resistance_flags.set(samurai_slaying_ptr->r_ptr->resistance_flags & RFR_EFF_IM_FIRE_MASK);
80         }
81
82         return;
83     }
84
85     /* Otherwise, take the damage */
86     if (samurai_slaying_ptr->flags.has(TR_BRAND_FIRE)) {
87         if (samurai_slaying_ptr->r_ptr->resistance_flags.has(MonsterResistanceType::HURT_FIRE)) {
88             if (samurai_slaying_ptr->mult < 70) {
89                 samurai_slaying_ptr->mult = 70;
90             }
91
92             if (is_original_ap_and_seen(player_ptr, samurai_slaying_ptr->m_ptr)) {
93                 samurai_slaying_ptr->r_ptr->r_resistance_flags.set(MonsterResistanceType::HURT_FIRE);
94             }
95
96         } else if (samurai_slaying_ptr->mult < 35) {
97             samurai_slaying_ptr->mult = 35;
98         }
99
100         return;
101     }
102
103     if (samurai_slaying_ptr->r_ptr->resistance_flags.has(MonsterResistanceType::HURT_FIRE)) {
104         if (samurai_slaying_ptr->mult < 50) {
105             samurai_slaying_ptr->mult = 50;
106         }
107
108         if (is_original_ap_and_seen(player_ptr, samurai_slaying_ptr->m_ptr)) {
109             samurai_slaying_ptr->r_ptr->r_resistance_flags.set(MonsterResistanceType::HURT_FIRE);
110         }
111     } else if (samurai_slaying_ptr->mult < 25) {
112         samurai_slaying_ptr->mult = 25;
113     }
114 }
115
116 /*!
117  * @brief サーペンツタン (毒殺スレイ)
118  * @param player_ptr プレイヤーへの参照ポインタ
119  * @param samurai_slaying_ptr スレイ計算に必要なパラメータ群への参照ポインタ
120  */
121 static void hissatsu_serpent_tongue(PlayerType *player_ptr, samurai_slaying_type *samurai_slaying_ptr)
122 {
123     if (samurai_slaying_ptr->mode != HISSATSU_POISON) {
124         return;
125     }
126
127     /* Notice immunity */
128     if (samurai_slaying_ptr->r_ptr->resistance_flags.has_any_of(RFR_EFF_IM_POISON_MASK)) {
129         if (is_original_ap_and_seen(player_ptr, samurai_slaying_ptr->m_ptr)) {
130             samurai_slaying_ptr->r_ptr->r_resistance_flags.set(samurai_slaying_ptr->r_ptr->resistance_flags & RFR_EFF_IM_POISON_MASK);
131         }
132
133         return;
134     }
135
136     /* Otherwise, take the damage */
137     if (samurai_slaying_ptr->flags.has(TR_BRAND_POIS)) {
138         if (samurai_slaying_ptr->mult < 35) {
139             samurai_slaying_ptr->mult = 35;
140         }
141     } else if (samurai_slaying_ptr->mult < 25) {
142         samurai_slaying_ptr->mult = 25;
143     }
144 }
145
146 /*!
147  * @brief 二重の極み^h^h^h^h^h 斬魔剣弐の太刀 (邪悪無生命スレイ)
148  * @param samurai_slaying_ptr スレイ計算に必要なパラメータ群への参照ポインタ
149  */
150 static void hissatsu_zanma_ken(samurai_slaying_type *samurai_slaying_ptr)
151 {
152     if (samurai_slaying_ptr->mode != HISSATSU_ZANMA) {
153         return;
154     }
155
156     if (!samurai_slaying_ptr->m_ptr->has_living_flag() && samurai_slaying_ptr->r_ptr->kind_flags.has(MonsterKindType::EVIL)) {
157         if (samurai_slaying_ptr->mult < 15) {
158             samurai_slaying_ptr->mult = 25;
159         } else if (samurai_slaying_ptr->mult < 50) {
160             samurai_slaying_ptr->mult = std::min<short>(50, samurai_slaying_ptr->mult + 20);
161         }
162     }
163 }
164
165 /*!
166  * @brief 破岩斬 (岩石スレイ)
167  * @param player_ptr プレイヤーへの参照ポインタ
168  * @param samurai_slaying_ptr スレイ計算に必要なパラメータ群への参照ポインタ
169  */
170 static void hissatsu_rock_smash(PlayerType *player_ptr, samurai_slaying_type *samurai_slaying_ptr)
171 {
172     if (samurai_slaying_ptr->mode != HISSATSU_HAGAN) {
173         return;
174     }
175
176     if (samurai_slaying_ptr->r_ptr->resistance_flags.has(MonsterResistanceType::HURT_ROCK)) {
177         if (is_original_ap_and_seen(player_ptr, samurai_slaying_ptr->m_ptr)) {
178             samurai_slaying_ptr->r_ptr->r_resistance_flags.set(MonsterResistanceType::HURT_ROCK);
179         }
180
181         if (samurai_slaying_ptr->mult == 10) {
182             samurai_slaying_ptr->mult = 40;
183         } else if (samurai_slaying_ptr->mult < 60) {
184             samurai_slaying_ptr->mult = 60;
185         }
186     }
187 }
188
189 /*!
190  * @brief 乱れ雪月花 (冷気スレイ)
191  * @param player_ptr プレイヤーへの参照ポインタ
192  * @param samurai_slaying_ptr スレイ計算に必要なパラメータ群への参照ポインタ
193  */
194 static void hissatsu_midare_setsugetsuka(PlayerType *player_ptr, samurai_slaying_type *samurai_slaying_ptr)
195 {
196     if (samurai_slaying_ptr->mode != HISSATSU_COLD) {
197         return;
198     }
199
200     /* Notice immunity */
201     if (samurai_slaying_ptr->r_ptr->resistance_flags.has_any_of(RFR_EFF_IM_COLD_MASK)) {
202         if (is_original_ap_and_seen(player_ptr, samurai_slaying_ptr->m_ptr)) {
203             samurai_slaying_ptr->r_ptr->r_resistance_flags.set(samurai_slaying_ptr->r_ptr->resistance_flags & RFR_EFF_IM_COLD_MASK);
204         }
205
206         return;
207     }
208
209     /* Otherwise, take the damage */
210     if (samurai_slaying_ptr->flags.has(TR_BRAND_COLD)) {
211         if (samurai_slaying_ptr->r_ptr->resistance_flags.has(MonsterResistanceType::HURT_COLD)) {
212             if (samurai_slaying_ptr->mult < 70) {
213                 samurai_slaying_ptr->mult = 70;
214             }
215
216             if (is_original_ap_and_seen(player_ptr, samurai_slaying_ptr->m_ptr)) {
217                 samurai_slaying_ptr->r_ptr->r_resistance_flags.set(MonsterResistanceType::HURT_COLD);
218             }
219         } else if (samurai_slaying_ptr->mult < 35) {
220             samurai_slaying_ptr->mult = 35;
221         }
222
223         return;
224     }
225
226     if (samurai_slaying_ptr->r_ptr->resistance_flags.has(MonsterResistanceType::HURT_COLD)) {
227         if (samurai_slaying_ptr->mult < 50) {
228             samurai_slaying_ptr->mult = 50;
229         }
230
231         if (is_original_ap_and_seen(player_ptr, samurai_slaying_ptr->m_ptr)) {
232             samurai_slaying_ptr->r_ptr->r_resistance_flags.set(MonsterResistanceType::HURT_COLD);
233         }
234     } else if (samurai_slaying_ptr->mult < 25) {
235         samurai_slaying_ptr->mult = 25;
236     }
237 }
238
239 /*!
240  * @brief 雷撃鷲爪斬
241  * @param player_ptr プレイヤーへの参照ポインタ
242  * @param samurai_slaying_ptr スレイ計算に必要なパラメータ群への参照ポインタ
243  */
244 static void hissatsu_lightning_eagle(PlayerType *player_ptr, samurai_slaying_type *samurai_slaying_ptr)
245 {
246     if (samurai_slaying_ptr->mode != HISSATSU_ELEC) {
247         return;
248     }
249
250     /* Notice immunity */
251     if (samurai_slaying_ptr->r_ptr->resistance_flags.has_any_of(RFR_EFF_IM_ELEC_MASK)) {
252         if (is_original_ap_and_seen(player_ptr, samurai_slaying_ptr->m_ptr)) {
253             samurai_slaying_ptr->r_ptr->r_resistance_flags.set(samurai_slaying_ptr->r_ptr->resistance_flags & RFR_EFF_IM_ELEC_MASK);
254         }
255
256         return;
257     }
258
259     /* Otherwise, take the damage */
260     if (samurai_slaying_ptr->flags.has(TR_BRAND_ELEC)) {
261         if (samurai_slaying_ptr->mult < 70) {
262             samurai_slaying_ptr->mult = 70;
263         }
264     } else if (samurai_slaying_ptr->mult < 50) {
265         samurai_slaying_ptr->mult = 50;
266     }
267 }
268
269 /*!
270  * @brief 赤流渦 (ペインバッカー)
271  * @param player_ptr プレイヤーへの参照ポインタ
272  * @param samurai_slaying_ptr スレイ計算に必要なパラメータ群への参照ポインタ
273  */
274 static void hissatsu_bloody_maelstroem(PlayerType *player_ptr, samurai_slaying_type *samurai_slaying_ptr)
275 {
276     auto player_cut = player_ptr->effects()->cut();
277     if ((samurai_slaying_ptr->mode == HISSATSU_SEKIRYUKA) && player_cut->is_cut() && samurai_slaying_ptr->m_ptr->has_living_flag()) {
278         auto tmp = std::min<short>(100, std::max<short>(10, player_cut->current() / 10));
279         if (samurai_slaying_ptr->mult < tmp) {
280             samurai_slaying_ptr->mult = tmp;
281         }
282     }
283 }
284
285 /*!
286  * @brief 慶雲鬼忍剣 (アンデッドスレイ)
287  * @param player_ptr プレイヤーへの参照ポインタ
288  * @param samurai_slaying_ptr スレイ計算に必要なパラメータ群への参照ポインタ
289  */
290 static void hissatsu_keiun_kininken(PlayerType *player_ptr, samurai_slaying_type *samurai_slaying_ptr)
291 {
292     if (samurai_slaying_ptr->mode != HISSATSU_UNDEAD) {
293         return;
294     }
295
296     if (samurai_slaying_ptr->r_ptr->kind_flags.has(MonsterKindType::UNDEAD)) {
297         if (is_original_ap_and_seen(player_ptr, samurai_slaying_ptr->m_ptr)) {
298             samurai_slaying_ptr->r_ptr->r_kind_flags.set(MonsterKindType::UNDEAD);
299
300             if (samurai_slaying_ptr->mult == 10) {
301                 samurai_slaying_ptr->mult = 70;
302             } else if (samurai_slaying_ptr->mult < 140) {
303                 samurai_slaying_ptr->mult = std::min<short>(140, samurai_slaying_ptr->mult + 60);
304             }
305         }
306     }
307
308     if (samurai_slaying_ptr->mult == 10) {
309         samurai_slaying_ptr->mult = 40;
310     } else if (samurai_slaying_ptr->mult < 60) {
311         samurai_slaying_ptr->mult = std::min<short>(60, samurai_slaying_ptr->mult + 30);
312     }
313 }
314
315 /*!
316  * @brief 剣術のスレイ倍率計算を行う /
317  * Calcurate magnification of hissatsu technics
318  * @param mult 剣術のスレイ効果以前に算出している多要素の倍率(/10倍)
319  * @param flags 剣術に使用する武器のスレイフラグ配列
320  * @param m_ptr 目標となるモンスターの構造体参照ポインタ
321  * @param mode 剣術のスレイ型ID
322  * @return スレイの倍率(/10倍)
323  */
324 MULTIPLY mult_hissatsu(PlayerType *player_ptr, MULTIPLY mult, const TrFlags &flags, MonsterEntity *m_ptr, combat_options mode)
325 {
326     auto *r_ptr = &monraces_info[m_ptr->r_idx];
327     samurai_slaying_type tmp_slaying(mult, flags, m_ptr, mode, r_ptr);
328     samurai_slaying_type *samurai_slaying_ptr = &tmp_slaying;
329     hissatsu_burning_strike(player_ptr, samurai_slaying_ptr);
330     hissatsu_serpent_tongue(player_ptr, samurai_slaying_ptr);
331     hissatsu_zanma_ken(samurai_slaying_ptr);
332     hissatsu_rock_smash(player_ptr, samurai_slaying_ptr);
333     hissatsu_midare_setsugetsuka(player_ptr, samurai_slaying_ptr);
334     hissatsu_lightning_eagle(player_ptr, samurai_slaying_ptr);
335     hissatsu_bloody_maelstroem(player_ptr, samurai_slaying_ptr);
336     hissatsu_keiun_kininken(player_ptr, samurai_slaying_ptr);
337
338     if (samurai_slaying_ptr->mult > 150) {
339         samurai_slaying_ptr->mult = 150;
340     }
341
342     return samurai_slaying_ptr->mult;
343 }
344
345 void concentration(PlayerType *player_ptr)
346 {
347     int max_csp = std::max(player_ptr->msp * 4, player_ptr->lev * 5 + 5);
348
349     if (total_friends) {
350         msg_print(_("今はペットを操ることに集中していないと。", "Your pets demand all of your attention."));
351         return;
352     }
353
354     if (!PlayerClass(player_ptr).samurai_stance_is(SamuraiStanceType::NONE)) {
355         msg_print(_("今は構えに集中している。", "You're already concentrating on your stance."));
356         return;
357     }
358
359     msg_print(_("精神を集中して気合いを溜めた。", "You concentrate to charge your power."));
360
361     player_ptr->csp += player_ptr->msp / 2;
362     if (player_ptr->csp >= max_csp) {
363         player_ptr->csp = max_csp;
364         player_ptr->csp_frac = 0;
365     }
366
367     RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::MP);
368 }
369
370 /*!
371  * @brief 剣術家の型設定処理
372  * @return 型を変化させたらTRUE、型の構え不能かキャンセルしたらFALSEを返す。
373  */
374 bool choose_samurai_stance(PlayerType *player_ptr)
375 {
376     char choice;
377     char buf[80];
378
379     if (cmd_limit_confused(player_ptr)) {
380         return false;
381     }
382
383     auto effects = player_ptr->effects();
384     if (effects->stun()->is_stunned()) {
385         msg_print(_("意識がはっきりとしない。", "You are not clear-headed"));
386         return false;
387     }
388
389     if (effects->fear()->is_fearful()) {
390         msg_print(_("体が震えて構えられない!", "You are trembling with fear!"));
391         return false;
392     }
393
394     screen_save();
395     prt(_(" a) 型を崩す", " a) No Form"), 2, 20);
396     for (auto i = 0U; i < samurai_stances.size(); i++) {
397         if (player_ptr->lev >= samurai_stances[i].min_level) {
398             strnfmt(buf, sizeof(buf), _(" %c) %sの型    %s", " %c) Stance of %-12s  %s"), I2A(i + 1), samurai_stances[i].desc, samurai_stances[i].info);
399             prt(buf, 3 + i, 20);
400         }
401     }
402
403     prt("", 1, 0);
404     prt(_("        どの型で構えますか?", "        Choose Stance: "), 1, 14);
405
406     SamuraiStanceType new_stance = SamuraiStanceType::NONE;
407     while (true) {
408         choice = inkey();
409
410         if (choice == ESCAPE) {
411             screen_load();
412             return false;
413         } else if ((choice == 'a') || (choice == 'A')) {
414             if (player_ptr->action == ACTION_SAMURAI_STANCE) {
415                 set_action(player_ptr, ACTION_NONE);
416             } else {
417                 msg_print(_("もともと構えていない。", "You are not in a special stance."));
418             }
419             screen_load();
420             return true;
421         } else if ((choice == 'b') || (choice == 'B')) {
422             new_stance = SamuraiStanceType::IAI;
423             break;
424         } else if (((choice == 'c') || (choice == 'C')) && (player_ptr->lev > 29)) {
425             new_stance = SamuraiStanceType::FUUJIN;
426             break;
427         } else if (((choice == 'd') || (choice == 'D')) && (player_ptr->lev > 34)) {
428             new_stance = SamuraiStanceType::KOUKIJIN;
429             break;
430         } else if (((choice == 'e') || (choice == 'E')) && (player_ptr->lev > 39)) {
431             new_stance = SamuraiStanceType::MUSOU;
432             break;
433         }
434     }
435
436     set_action(player_ptr, ACTION_SAMURAI_STANCE);
437     auto &rfu = RedrawingFlagsUpdater::get_instance();
438     if (PlayerClass(player_ptr).samurai_stance_is(new_stance)) {
439         msg_print(_("構え直した。", "You reassume a stance."));
440     } else {
441         static constexpr auto flags_srf = {
442             StatusRecalculatingFlag::BONUS,
443             StatusRecalculatingFlag::MONSTER_STATUSES,
444         };
445         rfu.set_flags(flags_srf);
446         msg_format(_("%sの型で構えた。", "You assume the %s stance."), samurai_stances[enum2i(new_stance) - 1].desc);
447         PlayerClass(player_ptr).set_samurai_stance(new_stance);
448     }
449
450     static constexpr auto flags = {
451         MainWindowRedrawingFlag::ACTION,
452         MainWindowRedrawingFlag::TIMED_EFFECT,
453     };
454     rfu.set_flags(flags);
455     screen_load();
456     return true;
457 }
458
459 /*!
460  * @brief 剣術家限定で、型等に応じて命中率を高める
461  * @param player_ptr プレイヤーへの参照ポインタ
462  * @param pa_ptr 直接攻撃構造体への参照ポインタ
463  * @return 上昇後の命中率
464  */
465 int calc_attack_quality(PlayerType *player_ptr, player_attack_type *pa_ptr)
466 {
467     auto *o_ptr = &player_ptr->inventory_list[INVEN_MAIN_HAND + pa_ptr->hand];
468     int bonus = player_ptr->to_h[pa_ptr->hand] + o_ptr->to_h;
469     int chance = (player_ptr->skill_thn + (bonus * BTH_PLUS_ADJ));
470     if (pa_ptr->mode == HISSATSU_IAI) {
471         chance += 60;
472     }
473
474     if (PlayerClass(player_ptr).samurai_stance_is(SamuraiStanceType::KOUKIJIN)) {
475         chance += 150;
476     }
477
478     if (player_ptr->sutemi) {
479         chance = std::max(chance * 3 / 2, chance + 60);
480     }
481
482     int vir = virtue_number(player_ptr, Virtue::VALOUR);
483     if (vir != 0) {
484         chance += (player_ptr->virtues[vir - 1] / 10);
485     }
486
487     return chance;
488 }
489
490 /*!
491  * @brief 峰打ちの効果処理
492  * @param player_ptr プレイヤーへの参照ポインタ
493  * @param pa_ptr 直接攻撃構造体への参照ポインタ
494  */
495 void mineuchi(PlayerType *player_ptr, player_attack_type *pa_ptr)
496 {
497     if (pa_ptr->mode != HISSATSU_MINEUCHI) {
498         return;
499     }
500
501     pa_ptr->attack_damage = 0;
502     anger_monster(player_ptr, pa_ptr->m_ptr);
503
504     auto *r_ptr = &monraces_info[pa_ptr->m_ptr->r_idx];
505     if ((r_ptr->flags3 & (RF3_NO_STUN))) {
506         msg_format(_("%s には効果がなかった。", "%s is not effected."), pa_ptr->m_name);
507         return;
508     }
509
510     int tmp = (10 + randint1(15) + player_ptr->lev / 5);
511     if (pa_ptr->m_ptr->get_remaining_stun()) {
512         msg_format(_("%sはひどくもうろうとした。", "%s is more dazed."), pa_ptr->m_name);
513         tmp /= 2;
514     } else {
515         msg_format(_("%s はもうろうとした。", "%s is dazed."), pa_ptr->m_name);
516     }
517
518     (void)set_monster_stunned(player_ptr, pa_ptr->g_ptr->m_idx, pa_ptr->m_ptr->get_remaining_stun() + tmp);
519 }
520
521 /*!
522  * @brief 無想による反撃処理
523  * @param player_ptr プレイヤーへの参照ポインタ
524  * @param pa_ptr 直接攻撃構造体への参照ポインタ
525  */
526 void musou_counterattack(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
527 {
528     const auto is_musou = PlayerClass(player_ptr).samurai_stance_is(SamuraiStanceType::MUSOU);
529     if ((!player_ptr->counter && !is_musou) || !monap_ptr->alive || player_ptr->is_dead || !monap_ptr->m_ptr->ml || (player_ptr->csp <= 7)) {
530         return;
531     }
532
533     const auto m_target_name = monster_desc(player_ptr, monap_ptr->m_ptr, 0);
534     player_ptr->csp -= 7;
535     msg_format(_("%s^に反撃した!", "You counterattacked %s!"), m_target_name.data());
536     do_cmd_attack(player_ptr, monap_ptr->m_ptr->fy, monap_ptr->m_ptr->fx, HISSATSU_COUNTER);
537     monap_ptr->fear = false;
538     RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::MP);
539 }