OSDN Git Service

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