OSDN Git Service

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