OSDN Git Service

[Fix] 忍者以外でも変わり身で攻撃を回避する場合がある不具合を解消した / Resolved the issue that the attack was avoide...
[hengbandforosx/hengbandosx.git] / src / monster-attack / monster-attack-player.cpp
1 /*!
2  * @brief モンスターからプレイヤーへの直接攻撃処理
3  * @date 2020/05/23
4  * @author Hourier
5  */
6
7 #include "monster-attack/monster-attack-player.h"
8 #include "cmd-action/cmd-attack.h"
9 #include "cmd-action/cmd-pet.h"
10 #include "combat/attack-accuracy.h"
11 #include "combat/attack-criticality.h"
12 #include "combat/aura-counterattack.h"
13 #include "combat/combat-options-type.h"
14 #include "combat/hallucination-attacks-table.h"
15 #include "core/disturbance.h"
16 #include "core/player-update-types.h"
17 #include "dungeon/dungeon-flag-types.h"
18 #include "dungeon/dungeon.h"
19 #include "floor/geometry.h"
20 #include "inventory/inventory-slot-types.h"
21 #include "main/sound-definitions-table.h"
22 #include "main/sound-of-music.h"
23 #include "mind/mind-ninja.h"
24 #include "mind/mind-samurai.h"
25 #include "monster-attack/monster-attack-describer.h"
26 #include "monster-attack/monster-attack-effect.h"
27 #include "monster-attack/monster-attack-switcher.h"
28 #include "monster-attack/monster-attack-util.h"
29 #include "monster-race/monster-race.h"
30 #include "monster-race/race-flags1.h"
31 #include "monster-race/race-flags3.h"
32 #include "monster/monster-damage.h"
33 #include "monster/monster-describer.h"
34 #include "monster/monster-description-types.h"
35 #include "monster/monster-info.h"
36 #include "monster/monster-status.h"
37 #include "monster/smart-learn-types.h"
38 #include "object-hook/hook-armor.h"
39 #include "object/item-tester-hooker.h"
40 #include "pet/pet-fall-off.h"
41 #include "player/attack-defense-types.h"
42 #include "player/player-damage.h"
43 #include "player/player-skill.h"
44 #include "player/special-defense-types.h"
45 #include "spell-realm/spells-hex.h"
46 #include "status/action-setter.h"
47 #include "status/bad-status-setter.h"
48 #include "system/floor-type-definition.h"
49 #include "system/monster-race-definition.h"
50 #include "system/monster-type-definition.h"
51 #include "system/object-type-definition.h"
52 #include "system/player-type-definition.h"
53 #include "timed-effect/player-cut.h"
54 #include "timed-effect/player-stun.h"
55 #include "util/bit-flags-calculator.h"
56 #include "view/display-messages.h"
57
58 static bool check_no_blow(player_type *player_ptr, monap_type *monap_ptr)
59 {
60     auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
61     if (any_bits(r_ptr->flags1, RF1_NEVER_BLOW)) {
62         return false;
63     }
64
65     if (d_info[player_ptr->dungeon_idx].flags.has(DF::NO_MELEE)) {
66         return false;
67     }
68
69     return is_hostile(monap_ptr->m_ptr);
70 }
71
72 /*!
73  * @brief プレイヤー死亡等でモンスターからプレイヤーへの直接攻撃処理を途中で打ち切るかどうかを判定する
74  * @param player_ptr プレイヤーへの参照ポインタ
75  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
76  * @return 攻撃続行ならばTRUE、打ち切りになったらFALSE
77  */
78 static bool check_monster_continuous_attack(player_type *player_ptr, monap_type *monap_ptr)
79 {
80     if (!monster_is_valid(monap_ptr->m_ptr) || (monap_ptr->method == RBM_NONE)) {
81         return false;
82     }
83
84     auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
85     if (is_pet(monap_ptr->m_ptr) && (r_ptr->flags1 & RF1_UNIQUE) && (monap_ptr->method == RBM_EXPLODE)) {
86         monap_ptr->method = RBM_HIT;
87         monap_ptr->d_dice /= 10;
88     }
89
90     auto is_neighbor = distance(player_ptr->y, player_ptr->x, monap_ptr->m_ptr->fy, monap_ptr->m_ptr->fx) <= 1;
91     return player_ptr->playing && !player_ptr->is_dead && is_neighbor && !player_ptr->leaving;
92 }
93
94 /*!
95  * @brief 対邪悪結界が効いている状態で邪悪なモンスターから直接攻撃を受けた時の処理
96  * @param player_ptr プレイヤーへの参照ポインタ
97  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
98  * @return briefに書いた条件+確率が満たされたらTRUE、それ以外はFALSE
99  */
100 static bool effect_protecion_from_evil(player_type *player_ptr, monap_type *monap_ptr)
101 {
102     auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
103     if ((player_ptr->protevil <= 0) || none_bits(r_ptr->flags3, RF3_EVIL) || (player_ptr->lev < monap_ptr->rlev) || ((randint0(100) + player_ptr->lev) <= 50)) {
104         return false;
105     }
106
107     if (is_original_ap_and_seen(player_ptr, monap_ptr->m_ptr)) {
108         r_ptr->r_flags3 |= RF3_EVIL;
109     }
110
111 #ifdef JP
112     monap_ptr->abbreviate ? msg_format("撃退した。") : msg_format("%^sは撃退された。", monap_ptr->m_name);
113     monap_ptr->abbreviate = 1; /* 2回目以降は省略 */
114 #else
115     msg_format("%^s is repelled.", monap_ptr->m_name);
116 #endif
117
118     return true;
119 }
120
121 static void describe_silly_attacks(monap_type *monap_ptr)
122 {
123     if (monap_ptr->act == nullptr) {
124         return;
125     }
126
127     if (monap_ptr->do_silly_attack) {
128 #ifdef JP
129         monap_ptr->abbreviate = -1;
130 #endif
131         monap_ptr->act = silly_attacks[randint0(MAX_SILLY_ATTACK)];
132     }
133
134 #ifdef JP
135     if (monap_ptr->abbreviate == 0) {
136         msg_format("%^sに%s", monap_ptr->m_name, monap_ptr->act);
137     } else if (monap_ptr->abbreviate == 1) {
138         msg_format("%s", monap_ptr->act);
139     } else {
140        /* if (monap_ptr->abbreviate == -1) */
141        msg_format("%^s%s", monap_ptr->m_name, monap_ptr->act);
142     }
143
144     monap_ptr->abbreviate = 1; /*2回目以降は省略 */
145 #else
146     msg_format("%^s %s%s", monap_ptr->m_name, monap_ptr->act, monap_ptr->do_silly_attack ? " you." : "");
147 #endif
148 }
149
150 /*!
151  * @brief 切り傷と朦朧が同時に発生した時、片方を無効にする
152  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
153  */
154 static void select_cut_stun(monap_type *monap_ptr)
155 {
156     if ((monap_ptr->do_cut == 0) || (monap_ptr->do_stun == 0)) {
157         return;
158     }
159
160     if (randint0(100) < 50) {
161         monap_ptr->do_cut = 0;
162     } else {
163         monap_ptr->do_stun = 0;
164     }
165 }
166
167 static void calc_player_cut(player_type *player_ptr, monap_type *monap_ptr)
168 {
169     if (monap_ptr->do_cut == 0) {
170         return;
171     }
172
173     auto cut_plus = PlayerCut::get_accumulation(monap_ptr->d_dice * monap_ptr->d_side, monap_ptr->damage);
174     if (cut_plus > 0) {
175         (void)BadStatusSetter(player_ptr).mod_cut(cut_plus);
176     }
177 }
178
179 /*!
180  * @brief 能力値の実値を求める
181  * @param raw player_typeに格納されている生値
182  * @return 実値
183  * @details AD&Dの記法に則り、19以上の値を取らなくしているので、格納方法が面倒
184  */
185 static int stat_value(const int raw)
186 {
187     if (raw <= 18) {
188         return raw;
189     }
190
191     return (raw - 18) / 10 + 18;
192 }
193
194 /*!
195  * @brief 朦朧を蓄積させる
196  * @param player_ptr プレイヤーへの参照ポインタ
197  * @param monap_ptr モンスター打撃への参照ポインタ
198  * @details
199  * 痛恨の一撃ならば朦朧蓄積ランクを1上げる.
200  * 2%の確率で朦朧蓄積ランクを1上げる.
201  * 肉体のパラメータが合計80を超える水準に強化されていたら朦朧蓄積ランクを1下げる.
202  */
203 static void process_player_stun(player_type *player_ptr, monap_type *monap_ptr)
204 {
205     if (monap_ptr->do_stun == 0) {
206         return;
207     }
208
209     auto total = monap_ptr->d_dice * monap_ptr->d_side;
210     auto accumulation_rank = PlayerStun::get_accumulation_rank(total, monap_ptr->damage);
211     if (accumulation_rank == 0) {
212         return;
213     }
214
215     if ((total < monap_ptr->damage) && (accumulation_rank <= 6)) {
216         accumulation_rank++;
217     }
218
219     if (one_in_(50)) {
220         accumulation_rank++;
221     }
222
223     auto str = stat_value(player_ptr->stat_cur[A_STR]);
224     auto dex = stat_value(player_ptr->stat_cur[A_DEX]);
225     auto con = stat_value(player_ptr->stat_cur[A_CON]);
226     auto is_powerful_body = str + dex + con > 80;
227     if (is_powerful_body) {
228         accumulation_rank--;
229     }
230
231     auto stun_plus = PlayerStun::get_accumulation(accumulation_rank);
232     if (stun_plus > 0) {
233         (void)BadStatusSetter(player_ptr).mod_stun(stun_plus);
234     }
235 }
236
237 static void monster_explode(player_type *player_ptr, monap_type *monap_ptr)
238 {
239     if (!monap_ptr->explode) {
240         return;
241     }
242
243     sound(SOUND_EXPLODE);
244     MonsterDamageProcessor mdp(player_ptr, monap_ptr->m_idx, monap_ptr->m_ptr->hp + 1, &monap_ptr->fear);
245     if (mdp.mon_take_hit(nullptr)) {
246         monap_ptr->blinked = false;
247         monap_ptr->alive = false;
248     }
249 }
250
251 static void describe_attack_evasion(player_type *player_ptr, monap_type *monap_ptr)
252 {
253     if (!monap_ptr->m_ptr->ml) {
254         return;
255     }
256
257     disturb(player_ptr, true, true);
258 #ifdef JP
259     auto is_suiken = any_bits(player_ptr->special_attack, ATTACK_SUIKEN);
260     if (monap_ptr->abbreviate) {
261         msg_format("%sかわした。", is_suiken ? "奇妙な動きで" : "");
262     } else {
263         msg_format("%s%^sの攻撃をかわした。", is_suiken ? "奇妙な動きで" : "", monap_ptr->m_name);
264     }
265
266     monap_ptr->abbreviate = 1; /* 2回目以降は省略 */
267 #else
268     msg_format("%^s misses you.", monap_ptr->m_name);
269 #endif
270 }
271
272 static void gain_armor_exp(player_type *player_ptr, monap_type *monap_ptr)
273 {
274     const auto o_ptr_mh = &player_ptr->inventory_list[INVEN_MAIN_HAND];
275     const auto o_ptr_sh = &player_ptr->inventory_list[INVEN_SUB_HAND];
276     if (!o_ptr_mh->is_armour() && !o_ptr_sh->is_armour()) {
277         return;
278     }
279
280     auto cur = player_ptr->skill_exp[SKILL_SHIELD];
281     auto max = s_info[player_ptr->pclass].s_max[SKILL_SHIELD];
282     if (cur >= max) {
283         return;
284     }
285
286     auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
287     auto target_level = r_ptr->level;
288     auto increment = 0;
289     if ((cur / 100) < target_level) {
290         auto addition = (cur / 100 + 15) < target_level ? (target_level - (cur / 100 + 15)) : 0;
291         increment += 1 + addition;
292     }
293
294     player_ptr->skill_exp[SKILL_SHIELD] = MIN(max, cur + increment);
295     player_ptr->update |= (PU_BONUS);
296 }
297
298 /*!
299  * @brief モンスターから直接攻撃を1回受けた時の処理
300  * @return 対邪悪結界により攻撃が当たらなかったらFALSE、それ以外はTRUE
301  * @param player_ptr プレイヤーへの参照ポインタ
302  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
303  * @details 最大4 回/モンスター/ターン、このルーチンを通る
304  */
305 static bool process_monster_attack_hit(player_type *player_ptr, monap_type *monap_ptr)
306 {
307     disturb(player_ptr, true, true);
308     if (effect_protecion_from_evil(player_ptr, monap_ptr)) {
309         return false;
310     }
311
312     monap_ptr->do_cut = 0;
313     monap_ptr->do_stun = 0;
314     describe_monster_attack_method(monap_ptr);
315     describe_silly_attacks(monap_ptr);
316     monap_ptr->obvious = true;
317     monap_ptr->damage = damroll(monap_ptr->d_dice, monap_ptr->d_side);
318     if (monap_ptr->explode) {
319         monap_ptr->damage = 0;
320     }
321
322     switch_monster_blow_to_player(player_ptr, monap_ptr);
323     select_cut_stun(monap_ptr);
324     calc_player_cut(player_ptr, monap_ptr);
325     process_player_stun(player_ptr, monap_ptr);
326     monster_explode(player_ptr, monap_ptr);
327     process_aura_counterattack(player_ptr, monap_ptr);
328     return true;
329 }
330
331 /*!
332  * @brief 一部の打撃種別の場合のみ、避けた旨のメッセージ表示と盾技能スキル向上を行う
333  * @param player_ptr プレイヤーへの参照ポインタ
334  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
335  */
336 static void process_monster_attack_evasion(player_type *player_ptr, monap_type *monap_ptr)
337 {
338     switch (monap_ptr->method) {
339     case RBM_HIT:
340     case RBM_TOUCH:
341     case RBM_PUNCH:
342     case RBM_KICK:
343     case RBM_CLAW:
344     case RBM_BITE:
345     case RBM_STING:
346     case RBM_SLASH:
347     case RBM_BUTT:
348     case RBM_CRUSH:
349     case RBM_ENGULF:
350     case RBM_CHARGE:
351         describe_attack_evasion(player_ptr, monap_ptr);
352         gain_armor_exp(player_ptr, monap_ptr);
353         monap_ptr->damage = 0;
354         return;
355     default:
356         return;
357     }
358 }
359
360 /*!
361  * @brief モンスターの打撃情報を蓄積させる
362  * @param player_ptr プレイヤーへの参照ポインタ
363  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
364  * @param ap_cnt モンスターの打撃 N回目
365  * @details どの敵が何をしてきたか正しく認識できていなければ情報を蓄積しない.
366  * 非自明な類の打撃については、そのダメージが 0 ならば基本的に知識が増えない.
367  * 但し、既に一定以上の知識があれば常に知識が増える(何をされたのか察知できる).
368  */
369 static void increase_blow_type_seen(player_type *player_ptr, monap_type *monap_ptr, const int ap_cnt)
370 {
371     if (!is_original_ap_and_seen(player_ptr, monap_ptr->m_ptr) || monap_ptr->do_silly_attack) {
372         return;
373     }
374
375     auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
376     if (!monap_ptr->obvious && (monap_ptr->damage == 0) && (r_ptr->r_blows[ap_cnt] <= 10)) {
377         return;
378     }
379
380     if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR) {
381         r_ptr->r_blows[ap_cnt]++;
382     }
383 }
384
385 /*!
386  * @brief モンスターからプレイヤーへの打撃処理本体
387  * @return 打撃に反応してプレイヤーがその場から離脱したかどうか
388  */
389 static bool process_monster_blows(player_type *player_ptr, monap_type *monap_ptr)
390 {
391     auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
392     for (auto ap_cnt = 0; ap_cnt < MAX_NUM_BLOWS; ap_cnt++) {
393         monap_ptr->obvious = false;
394         monap_ptr->damage = 0;
395         monap_ptr->act = nullptr;
396         monap_ptr->effect = r_ptr->blow[ap_cnt].effect;
397         monap_ptr->method = r_ptr->blow[ap_cnt].method;
398         monap_ptr->d_dice = r_ptr->blow[ap_cnt].d_dice;
399         monap_ptr->d_side = r_ptr->blow[ap_cnt].d_side;
400
401         if (!check_monster_continuous_attack(player_ptr, monap_ptr)) {
402             break;
403         }
404
405         // effect が RBE_NONE (無効値)になることはあり得ないはずだが、万一そう
406         // なっていたら単に攻撃を打ち切る。
407         // r_info.txt の "B:" トークンに effect 以降を書き忘れた場合が該当する。
408         if (monap_ptr->effect == RBE_NONE) {
409             plog("unexpected: monap_ptr->effect == RBE_NONE");
410             break;
411         }
412
413         if (monap_ptr->method == RBM_SHOOT) {
414             continue;
415         }
416
417         // フレーバーの打撃は必中扱い。それ以外は通常の命中判定を行う。
418         monap_ptr->ac = player_ptr->ac + player_ptr->to_a;
419         bool hit;
420         if (monap_ptr->effect == RBE_FLAVOR) {
421             hit = true;
422         } else {
423             const int power = mbe_info[monap_ptr->effect].power;
424             hit = check_hit_from_monster_to_player(player_ptr, power, monap_ptr->rlev, monster_stunned_remaining(monap_ptr->m_ptr));
425         }
426
427         if (hit) {
428             // 命中した。命中処理と思い出処理を行う。
429             // 打撃そのものは対邪悪結界で撃退した可能性がある。
430             const bool protect = !process_monster_attack_hit(player_ptr, monap_ptr);
431             increase_blow_type_seen(player_ptr, monap_ptr, ap_cnt);
432
433             // 撃退成功時はそのまま次の打撃へ移行。
434             if (protect)
435                 continue;
436
437             // 撃退失敗時は落馬処理、変わり身のテレポート処理を行う。
438             check_fall_off_horse(player_ptr, monap_ptr);
439
440             // 変わり身のテレポートが成功したら攻撃を打ち切り、プレイヤーが離脱した旨を返す。
441             if (kawarimi(player_ptr, false)) {
442                 return true;
443             }
444         } else {
445             // 命中しなかった。回避時の処理、思い出処理を行う。
446             process_monster_attack_evasion(player_ptr, monap_ptr);
447             increase_blow_type_seen(player_ptr, monap_ptr, ap_cnt);
448         }
449     }
450
451     // 通常はプレイヤーはその場にとどまる。
452     return false;
453 }
454
455 static void postprocess_monster_blows(player_type *player_ptr, monap_type *monap_ptr)
456 {
457     SpellHex spell_hex(player_ptr, monap_ptr);
458     spell_hex.store_vengeful_damage(monap_ptr->get_damage);
459     spell_hex.eyes_on_eyes();
460     musou_counterattack(player_ptr, monap_ptr);
461     spell_hex.thief_teleport();
462     auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
463     if (player_ptr->is_dead && (r_ptr->r_deaths < MAX_SHORT) && !player_ptr->current_floor_ptr->inside_arena) {
464         r_ptr->r_deaths++;
465     }
466
467     if (monap_ptr->m_ptr->ml && monap_ptr->fear && monap_ptr->alive && !player_ptr->is_dead) {
468         sound(SOUND_FLEE);
469         msg_format(_("%^sは恐怖で逃げ出した!", "%^s flees in terror!"), monap_ptr->m_name);
470     }
471
472     if (player_ptr->special_defense & KATA_IAI) {
473         set_action(player_ptr, ACTION_NONE);
474     }
475 }
476
477 /*!
478  * @brief モンスターからプレイヤーへの打撃処理 / Attack the player via physical attacks.
479  * @param m_idx 打撃を行うモンスターのID
480  * @return 実際に攻撃処理を行った場合TRUEを返す
481  */
482 bool make_attack_normal(player_type *player_ptr, MONSTER_IDX m_idx)
483 {
484     monap_type tmp_monap;
485     monap_type *monap_ptr = initialize_monap_type(player_ptr, &tmp_monap, m_idx);
486     if (!check_no_blow(player_ptr, monap_ptr)) {
487         return false;
488     }
489
490     auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
491     monap_ptr->rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1);
492     monster_desc(player_ptr, monap_ptr->m_name, monap_ptr->m_ptr, 0);
493     monster_desc(player_ptr, monap_ptr->ddesc, monap_ptr->m_ptr, MD_WRONGDOER_NAME);
494     if (any_bits(player_ptr->special_defense, KATA_IAI)) {
495         msg_format(_("相手が襲いかかる前に素早く武器を振るった。", "You took sen, drew and cut in one motion before %s moved."), monap_ptr->m_name);
496         if (do_cmd_attack(player_ptr, monap_ptr->m_ptr->fy, monap_ptr->m_ptr->fx, HISSATSU_IAI)) {
497             return true;
498         }
499     }
500
501     auto can_activate_kawarimi = randint0(55) < (player_ptr->lev * 3 / 5 + 20);
502     if (can_activate_kawarimi && kawarimi(player_ptr, true)) {
503         return true;
504     }
505
506     monap_ptr->blinked = false;
507     if (process_monster_blows(player_ptr, monap_ptr)) {
508         return true;
509     }
510
511     postprocess_monster_blows(player_ptr, monap_ptr);
512     return true;
513 }