OSDN Git Service

[Fix] C画面素手ダメージをクリティカル考慮に変更
[hengbandforosx/hengbandosx.git] / src / combat / attack-criticality.cpp
1 #include "combat/attack-criticality.h"
2 #include "combat/combat-options-type.h"
3 #include "inventory/inventory-slot-types.h"
4 #include "main/sound-of-music.h"
5 #include "monster-race/monster-race.h"
6 #include "monster-race/race-flags1.h"
7 #include "monster-race/race-flags7.h"
8 #include "object/tval-types.h"
9 #include "player-attack/player-attack.h"
10 #include "player-base/player-class.h"
11 #include "player-info/equipment-info.h"
12 #include "sv-definition/sv-weapon-types.h"
13 #include "system/item-entity.h"
14 #include "system/monster-entity.h"
15 #include "system/monster-race-info.h"
16 #include "system/player-type-definition.h"
17 #include "view/display-messages.h"
18
19 /*!
20  * @brief クリティカルダメージを適用する
21  *
22  * @param k クリティカルの強度を決定する値
23  * @param base_dam クリティカル適用前のダメージ
24  * @param mult 期待値計算時のdam倍率
25  * @return クリティカルを適用したダメージと、クリティカル発生時に表示するメッセージと、クリティカル効果音のタプルを返す
26  */
27 std::tuple<int, concptr, sound_type> apply_critical_norm_damage(int k, int base_dam, int mult)
28 {
29     if (k < 400) {
30         return { 2 * base_dam + 5 * mult, _("手ごたえがあった!", "It was a good hit!"), SOUND_GOOD_HIT };
31     }
32     if (k < 700) {
33         return { 2 * base_dam + 10 * mult, _("かなりの手ごたえがあった!", "It was a great hit!"), SOUND_GREAT_HIT };
34     }
35     if (k < 900) {
36         return { 3 * base_dam + 15 * mult, _("会心の一撃だ!", "It was a superb hit!"), SOUND_SUPERB_HIT };
37     }
38     if (k < 1300) {
39         return { 3 * base_dam + 20 * mult, _("最高の会心の一撃だ!", "It was a *GREAT* hit!"), SOUND_STAR_GREAT_HIT };
40     }
41     return { ((7 * base_dam) / 2) + 25 * mult, _("比類なき最高の会心の一撃だ!", "It was a *SUPERB* hit!"), SOUND_STAR_SUPERB_HIT };
42 }
43
44 /*!
45  * @brief プレイヤーからモンスターへの打撃クリティカル判定 /
46  * Critical hits (by player) Factor in weapon weight, total plusses, player melee bonus
47  * @param weight 矢弾の重量
48  * @param plus 武器の命中修正
49  * @param dam 現在算出中のダメージ値
50  * @param meichuu 打撃の基本命中力
51  * @param mode オプションフラグ
52  * @return クリティカル修正が入ったダメージ値
53  */
54 int critical_norm(PlayerType *player_ptr, WEIGHT weight, int plus, int dam, int16_t meichuu, combat_options mode, bool impact)
55 {
56     /* Extract "blow" power */
57     int i = (weight + (meichuu * 3 + plus * 5) + player_ptr->skill_thn);
58
59     /* Chance */
60     auto pow = PlayerClass(player_ptr).equals(PlayerClassType::NINJA) ? 4444 : 5000;
61     if (impact) {
62         pow /= 2;
63     }
64
65     bool is_special_option = randint1(pow) <= i;
66     is_special_option |= mode == HISSATSU_MAJIN;
67     is_special_option |= mode == HISSATSU_3DAN;
68     if (!is_special_option) {
69         return dam;
70     }
71
72     int k = weight + randint1(650);
73     if (impact || (mode == HISSATSU_MAJIN) || (mode == HISSATSU_3DAN)) {
74         k += randint1(650);
75     }
76
77     auto [critical_dam, msg, battle_sound] = apply_critical_norm_damage(k, dam);
78     sound(battle_sound);
79     msg_print(msg);
80     return critical_dam;
81 }
82
83 /*!
84  * @brief 忍者ヒットで急所を突く
85  * @param player_ptr プレイヤーへの参照ポインタ
86  * @param pa_ptr 直接攻撃構造体への参照ポインタ
87  * @details 闇討ち&追討ちを実施した後に致命傷チェックを行う
88  * チェックを通ったら、ユニークならば2倍ダメージ、それ以外は一撃死
89  * @todo 3つの処理をdetailsに書くよりは関数自体を分割すべきだが、一旦後回しにする。他の項目と一緒に処理する
90  */
91 static void ninja_critical(PlayerType *player_ptr, player_attack_type *pa_ptr)
92 {
93     auto *r_ptr = &pa_ptr->m_ptr->get_monrace();
94     int maxhp = pa_ptr->m_ptr->maxhp;
95     if (one_in_(pa_ptr->backstab ? 13 : (pa_ptr->stab_fleeing || pa_ptr->surprise_attack) ? 15
96                                                                                           : 27)) {
97         pa_ptr->attack_damage *= 5;
98         pa_ptr->drain_result *= 2;
99         msg_format(_("刃が%sに深々と突き刺さった!", "You critically injured %s!"), pa_ptr->m_name);
100         return;
101     }
102
103     const auto no_instantly_death = r_ptr->resistance_flags.has(MonsterResistanceType::NO_INSTANTLY_DEATH);
104     bool is_weaken = pa_ptr->m_ptr->hp < maxhp / 2;
105     bool is_unique = r_ptr->kind_flags.has(MonsterKindType::UNIQUE) || no_instantly_death;
106     bool is_critical = (is_weaken && one_in_((player_ptr->num_blow[0] + player_ptr->num_blow[1] + 1) * 10)) || ((one_in_(666) || ((pa_ptr->backstab || pa_ptr->surprise_attack) && one_in_(11))) && !is_unique);
107     if (!is_critical) {
108         return;
109     }
110
111     if (is_unique || !is_weaken) {
112         if (no_instantly_death) {
113             r_ptr->r_resistance_flags.set(MonsterResistanceType::NO_INSTANTLY_DEATH);
114         }
115         pa_ptr->attack_damage = std::max(pa_ptr->attack_damage * 5, pa_ptr->m_ptr->hp / 2);
116         pa_ptr->drain_result *= 2;
117         msg_format(_("%sに致命傷を負わせた!", "You fatally injured %s!"), pa_ptr->m_name);
118     } else {
119         pa_ptr->attack_damage = pa_ptr->m_ptr->hp + 1;
120         msg_format(_("刃が%sの急所を貫いた!", "You hit %s on a fatal spot!"), pa_ptr->m_name);
121     }
122 }
123
124 /*!
125  * @brief 急所を突く
126  * @param player_ptr プレイヤーへの参照ポインタ
127  * @param pa_ptr 直接攻撃構造体への参照ポインタ
128  */
129 void critical_attack(PlayerType *player_ptr, player_attack_type *pa_ptr)
130 {
131     auto *o_ptr = &player_ptr->inventory_list[enum2i(INVEN_MAIN_HAND) + pa_ptr->hand];
132     auto *r_ptr = &pa_ptr->m_ptr->get_monrace();
133     const auto no_instantly_death = r_ptr->resistance_flags.has(MonsterResistanceType::NO_INSTANTLY_DEATH);
134     if ((o_ptr->bi_key == BaseitemKey(ItemKindType::SWORD, SV_POISON_NEEDLE)) || (pa_ptr->mode == HISSATSU_KYUSHO)) {
135         if ((randint1(randint1(r_ptr->level / 7) + 5) == 1) && r_ptr->kind_flags.has_not(MonsterKindType::UNIQUE) && !no_instantly_death) {
136             pa_ptr->attack_damage = pa_ptr->m_ptr->hp + 1;
137             msg_format(_("%sの急所を突き刺した!", "You hit %s on a fatal spot!"), pa_ptr->m_name);
138         } else {
139             if (no_instantly_death) {
140                 r_ptr->r_resistance_flags.set(MonsterResistanceType::NO_INSTANTLY_DEATH);
141             }
142             pa_ptr->attack_damage = 1;
143         }
144
145         return;
146     }
147
148     if (!PlayerClass(player_ptr).equals(PlayerClassType::NINJA)) {
149         return;
150     }
151
152     const auto has_weapon = has_melee_weapon(player_ptr, enum2i(INVEN_MAIN_HAND) + pa_ptr->hand);
153     const auto is_ninja_hit = has_weapon && !player_ptr->is_icky_wield[pa_ptr->hand] && ((player_ptr->cur_lite <= 0) || one_in_(7));
154     if (is_ninja_hit) {
155         ninja_critical(player_ptr, pa_ptr);
156     }
157 }