OSDN Git Service

Reworded English description for sniper's exploding arrow ability.
[hengband/hengband.git] / src / mind / monk-attack.c
1 /*!
2  * @brief 素手で攻撃することに補正のある職業 (修行僧、狂戦士、練気術師)の打撃処理
3  * @date 2020/05/23
4  * @author Hourier
5  * @details 練気術師は騎乗していない時
6  */
7
8 #include "mind/monk-attack.h"
9 #include "cmd-action/cmd-attack.h"
10 #include "combat/attack-criticality.h"
11 #include "core/speed-table.h"
12 #include "core/stuff-handler.h"
13 #include "game-option/cheat-options.h"
14 #include "main/sound-definitions-table.h"
15 #include "main/sound-of-music.h"
16 #include "mind/mind-force-trainer.h"
17 #include "monster-race/monster-race.h"
18 #include "monster-race/race-flags1.h"
19 #include "monster-race/race-flags3.h"
20 #include "monster/monster-status-setter.h"
21 #include "monster/monster-status.h"
22 #include "player/attack-defense-types.h"
23 #include "player/special-defense-types.h"
24 #include "system/floor-type-definition.h"
25 #include "target/target-getter.h"
26 #include "util/string-processor.h"
27 #include "view/display-messages.h"
28 #include "world/world.h"
29
30 /*!
31  * @brief 朦朧への抵抗値を計算する
32  * @param pa_ptr 直接攻撃構造体への参照ポインタ
33  * @return 朦朧への抵抗値
34  */
35 static int calc_stun_resistance(player_attack_type *pa_ptr)
36 {
37     monster_race *r_ptr = &r_info[pa_ptr->m_ptr->r_idx];
38     int resist_stun = 0;
39     if (r_ptr->flags1 & RF1_UNIQUE)
40         resist_stun += 88;
41
42     if (r_ptr->flags3 & RF3_NO_STUN)
43         resist_stun += 66;
44
45     if (r_ptr->flags3 & RF3_NO_CONF)
46         resist_stun += 33;
47
48     if (r_ptr->flags3 & RF3_NO_SLEEP)
49         resist_stun += 33;
50
51     if ((r_ptr->flags3 & RF3_UNDEAD) || (r_ptr->flags3 & RF3_NONLIVING))
52         resist_stun += 66;
53
54     return resist_stun;
55 }
56
57 /*!
58  * @brief 技のランダム選択回数を決定する
59  * @param attacker_ptr プレーヤーへの参照ポインタ
60  * @return 技のランダム選択回数
61  * @details ランダム選択は一番強い技が最終的に選択されるので、回数が多いほど有利
62  */
63 static int calc_max_blow_selection_times(player_type *attacker_ptr)
64 {
65     if (attacker_ptr->special_defense & KAMAE_BYAKKO)
66         return (attacker_ptr->lev < 3 ? 1 : attacker_ptr->lev / 3);
67
68     if (attacker_ptr->special_defense & KAMAE_SUZAKU)
69         return 1;
70
71     if (attacker_ptr->special_defense & KAMAE_GENBU)
72         return 1;
73
74     return attacker_ptr->lev < 7 ? 1 : attacker_ptr->lev / 7;
75 }
76
77 /*!
78  * @brief プレーヤーのレベルと技の難度を加味しつつ、確率で一番強い技を選ぶ
79  * @param attacker_ptr プレーヤーへの参照ポインタ
80  * @return 技のランダム選択回数
81  * @return 技の行使に必要な最低レベル
82  */
83 static int select_blow(player_type *attacker_ptr, player_attack_type *pa_ptr, int max_blow_selection_times)
84 {
85     int min_level = 1;
86     const martial_arts *old_ptr = &ma_blows[0];
87     for (int times = 0; times < max_blow_selection_times; times++) {
88         do {
89             pa_ptr->ma_ptr = &ma_blows[randint0(MAX_MA)];
90             if ((attacker_ptr->pclass == CLASS_FORCETRAINER) && (pa_ptr->ma_ptr->min_level > 1))
91                 min_level = pa_ptr->ma_ptr->min_level + 3;
92             else
93                 min_level = pa_ptr->ma_ptr->min_level;
94         } while ((min_level > attacker_ptr->lev) || (randint1(attacker_ptr->lev) < pa_ptr->ma_ptr->chance));
95
96         if ((pa_ptr->ma_ptr->min_level <= old_ptr->min_level) || attacker_ptr->stun || attacker_ptr->confused) {
97             pa_ptr->ma_ptr = old_ptr;
98             continue;
99         }
100
101         old_ptr = pa_ptr->ma_ptr;
102         if (current_world_ptr->wizard && cheat_xtra)
103             msg_print(_("攻撃を再選択しました。", "Attack re-selected."));
104     }
105
106     if (attacker_ptr->pclass == CLASS_FORCETRAINER)
107         min_level = MAX(1, pa_ptr->ma_ptr->min_level - 3);
108     else
109         min_level = pa_ptr->ma_ptr->min_level;
110
111     return min_level;
112 }
113
114 static int process_monk_additional_effect(player_attack_type *pa_ptr, int *stun_effect)
115 {
116     int special_effect = 0;
117     monster_race *r_ptr = &r_info[pa_ptr->m_ptr->r_idx];
118     if (pa_ptr->ma_ptr->effect == MA_KNEE) {
119         if (r_ptr->flags1 & RF1_MALE) {
120             msg_format(_("%sに金的膝蹴りをくらわした!", "You hit %s in the groin with your knee!"), pa_ptr->m_name);
121             sound(SOUND_PAIN);
122             special_effect = MA_KNEE;
123         } else
124             msg_format(pa_ptr->ma_ptr->desc, pa_ptr->m_name);
125     }
126
127     else if (pa_ptr->ma_ptr->effect == MA_SLOW) {
128         if (!((r_ptr->flags1 & RF1_NEVER_MOVE) || angband_strchr("~#{}.UjmeEv$,DdsbBFIJQSXclnw!=?", r_ptr->d_char))) {
129             msg_format(_("%sの足首に関節蹴りをくらわした!", "You kick %s in the ankle."), pa_ptr->m_name);
130             special_effect = MA_SLOW;
131         } else
132             msg_format(pa_ptr->ma_ptr->desc, pa_ptr->m_name);
133     } else {
134         if (pa_ptr->ma_ptr->effect) {
135             *stun_effect = (pa_ptr->ma_ptr->effect / 2) + randint1(pa_ptr->ma_ptr->effect / 2);
136         }
137
138         msg_format(pa_ptr->ma_ptr->desc, pa_ptr->m_name);
139     }
140
141     return special_effect;
142 }
143
144 /*!
145  * @brief 攻撃の重さ (修行僧と練気術師における武器重量)を決定する
146  * @param attacker_ptr プレーヤーへの参照ポインタ
147  * @return 重さ
148  */
149 static WEIGHT calc_monk_attack_weight(player_type *attacker_ptr)
150 {
151     WEIGHT weight = 8;
152     if (attacker_ptr->special_defense & KAMAE_SUZAKU)
153         weight = 4;
154
155     if ((attacker_ptr->pclass == CLASS_FORCETRAINER) && (get_current_ki(attacker_ptr) != 0)) {
156         weight += (get_current_ki(attacker_ptr) / 30);
157         if (weight > 20)
158             weight = 20;
159     }
160
161     return weight;
162 }
163
164 /*!
165  * @brief 急所攻撃による追加効果を与える
166  * @param attacker_ptr プレーヤーへの参照ポインタ
167  * @param pa_ptr 直接攻撃構造体への参照ポインタ
168  * @param stun_effect 朦朧の残りターン
169  * @param resist_stun 朦朧への抵抗値
170  * @param special_effect 技を繰り出した時の追加効果
171  * @return なし
172  */
173 static void process_attack_vital_spot(player_type *attacker_ptr, player_attack_type *pa_ptr, int *stun_effect, int *resist_stun, const int special_effect)
174 {
175     monster_race *r_ptr = &r_info[pa_ptr->m_ptr->r_idx];
176     if ((special_effect == MA_KNEE) && ((pa_ptr->attack_damage + attacker_ptr->to_d[pa_ptr->hand]) < pa_ptr->m_ptr->hp)) {
177         msg_format(_("%^sは苦痛にうめいている!", "%^s moans in agony!"), pa_ptr->m_name);
178         *stun_effect = 7 + randint1(13);
179         *resist_stun /= 3;
180         return;
181     }
182
183     if ((special_effect == MA_SLOW) && ((pa_ptr->attack_damage + attacker_ptr->to_d[pa_ptr->hand]) < pa_ptr->m_ptr->hp)) {
184         if (!(r_ptr->flags1 & RF1_UNIQUE) && (randint1(attacker_ptr->lev) > r_ptr->level) && pa_ptr->m_ptr->mspeed > 60) {
185             msg_format(_("%^sは足をひきずり始めた。", "%^s starts limping slower."), pa_ptr->m_name);
186             pa_ptr->m_ptr->mspeed -= 10;
187         }
188     }
189 }
190
191 /*!
192  * @brief 朦朧効果を受けたモンスターのステータス表示
193  * @param attacker_ptr プレーヤーの参照ポインタ
194  * @param pa_ptr 直接攻撃構造体への参照ポインタ
195  * @param g_ptr グリッドへの参照ポインタ
196  * @param stun_effect 朦朧の残りターン
197  * @param resist_stun 朦朧への抵抗値
198  * @return なし
199  */
200 static void print_stun_effect(player_type *attacker_ptr, player_attack_type *pa_ptr, const int stun_effect, const int resist_stun)
201 {
202     monster_race *r_ptr = &r_info[pa_ptr->m_ptr->r_idx];
203     if (stun_effect && ((pa_ptr->attack_damage + attacker_ptr->to_d[pa_ptr->hand]) < pa_ptr->m_ptr->hp)) {
204         if (attacker_ptr->lev > randint1(r_ptr->level + resist_stun + 10)) {
205             if (set_monster_stunned(attacker_ptr, pa_ptr->g_ptr->m_idx, stun_effect + monster_stunned_remaining(pa_ptr->m_ptr))) {
206                 msg_format(_("%^sはフラフラになった。", "%^s is stunned."), pa_ptr->m_name);
207             } else {
208                 msg_format(_("%^sはさらにフラフラになった。", "%^s is more stunned."), pa_ptr->m_name);
209             }
210         }
211     }
212 }
213
214 /*!
215  * @brief 強力な素手攻撃ができる職業 (修行僧、狂戦士、練気術師)の素手攻撃処理メインルーチン
216  * @param attacker_ptr プレーヤーの参照ポインタ
217  * @param pa_ptr 直接攻撃構造体への参照ポインタ
218  * @param g_ptr グリッドへの参照ポインタ
219  * @return なし
220  */
221 void process_monk_attack(player_type *attacker_ptr, player_attack_type *pa_ptr)
222 {
223     int resist_stun = calc_stun_resistance(pa_ptr);
224     int max_blow_selection_times = calc_max_blow_selection_times(attacker_ptr);
225     int min_level = select_blow(attacker_ptr, pa_ptr, max_blow_selection_times);
226
227     pa_ptr->attack_damage = damroll(pa_ptr->ma_ptr->dd + attacker_ptr->to_dd[pa_ptr->hand], pa_ptr->ma_ptr->ds + attacker_ptr->to_ds[pa_ptr->hand]);
228     if (attacker_ptr->special_attack & ATTACK_SUIKEN)
229         pa_ptr->attack_damage *= 2;
230
231     int stun_effect = 0;
232     int special_effect = process_monk_additional_effect(pa_ptr, &stun_effect);
233     WEIGHT weight = calc_monk_attack_weight(attacker_ptr);
234     pa_ptr->attack_damage = critical_norm(attacker_ptr, attacker_ptr->lev * weight, min_level, pa_ptr->attack_damage, attacker_ptr->to_h[0], 0);
235     process_attack_vital_spot(attacker_ptr, pa_ptr, &stun_effect, &resist_stun, special_effect);
236     print_stun_effect(attacker_ptr, pa_ptr, stun_effect, resist_stun);
237 }
238
239 bool double_attack(player_type *creature_ptr)
240 {
241     DIRECTION dir;
242     if (!get_rep_dir(creature_ptr, &dir, FALSE))
243         return FALSE;
244     POSITION y = creature_ptr->y + ddy[dir];
245     POSITION x = creature_ptr->x + ddx[dir];
246     if (!creature_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
247         msg_print(_("その方向にはモンスターはいません。", "You don't see any monster in this direction"));
248         msg_print(NULL);
249         return TRUE;
250     }
251
252     if (one_in_(3))
253         msg_print(_("あーたたたたたたたたたたたたたたたたたたたたたた!!!", "Ahhhtatatatatatatatatatatatatatataatatatatattaaaaa!!!!"));
254     else if (one_in_(2))
255         msg_print(_("無駄無駄無駄無駄無駄無駄無駄無駄無駄無駄無駄無駄!!!", "Mudamudamudamudamudamudamudamudamudamudamudamudamuda!!!!"));
256     else
257         msg_print(_("オラオラオラオラオラオラオラオラオラオラオラオラ!!!", "Oraoraoraoraoraoraoraoraoraoraoraoraoraoraoraoraora!!!!"));
258
259     do_cmd_attack(creature_ptr, y, x, 0);
260     if (creature_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
261         handle_stuff(creature_ptr);
262         do_cmd_attack(creature_ptr, y, x, 0);
263     }
264
265     creature_ptr->energy_need += ENERGY_NEED();
266     return TRUE;
267 }