OSDN Git Service

Merge pull request #2013 from sikabane-works/release/3.0.0Alpha51
[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-base/player-class.h"
42 #include "player-info/samurai-data-type.h"
43 #include "player/attack-defense-types.h"
44 #include "player/player-damage.h"
45 #include "player/player-skill.h"
46 #include "player/special-defense-types.h"
47 #include "spell-realm/spells-hex.h"
48 #include "status/action-setter.h"
49 #include "status/bad-status-setter.h"
50 #include "system/floor-type-definition.h"
51 #include "system/monster-race-definition.h"
52 #include "system/monster-type-definition.h"
53 #include "system/object-type-definition.h"
54 #include "system/player-type-definition.h"
55 #include "timed-effect/player-cut.h"
56 #include "timed-effect/player-stun.h"
57 #include "util/bit-flags-calculator.h"
58 #include "view/display-messages.h"
59
60 static bool check_no_blow(PlayerType *player_ptr, monap_type *monap_ptr)
61 {
62     auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
63     if (any_bits(r_ptr->flags1, RF1_NEVER_BLOW)) {
64         return false;
65     }
66
67     if (d_info[player_ptr->dungeon_idx].flags.has(DungeonFeatureType::NO_MELEE)) {
68         return false;
69     }
70
71     return is_hostile(monap_ptr->m_ptr);
72 }
73
74 /*!
75  * @brief プレイヤー死亡等でモンスターからプレイヤーへの直接攻撃処理を途中で打ち切るかどうかを判定する
76  * @param player_ptr プレイヤーへの参照ポインタ
77  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
78  * @return 攻撃続行ならばTRUE、打ち切りになったらFALSE
79  */
80 static bool check_monster_continuous_attack(PlayerType *player_ptr, monap_type *monap_ptr)
81 {
82     if (!monster_is_valid(monap_ptr->m_ptr) || (monap_ptr->method == RaceBlowMethodType::NONE)) {
83         return false;
84     }
85
86     auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
87     if (is_pet(monap_ptr->m_ptr) && (r_ptr->flags1 & RF1_UNIQUE) && (monap_ptr->method == RaceBlowMethodType::EXPLODE)) {
88         monap_ptr->method = RaceBlowMethodType::HIT;
89         monap_ptr->d_dice /= 10;
90     }
91
92     auto is_neighbor = distance(player_ptr->y, player_ptr->x, monap_ptr->m_ptr->fy, monap_ptr->m_ptr->fx) <= 1;
93     return player_ptr->playing && !player_ptr->is_dead && is_neighbor && !player_ptr->leaving;
94 }
95
96 /*!
97  * @brief 対邪悪結界が効いている状態で邪悪なモンスターから直接攻撃を受けた時の処理
98  * @param player_ptr プレイヤーへの参照ポインタ
99  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
100  * @return briefに書いた条件+確率が満たされたらTRUE、それ以外はFALSE
101  */
102 static bool effect_protecion_from_evil(PlayerType *player_ptr, monap_type *monap_ptr)
103 {
104     auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
105     if ((player_ptr->protevil <= 0) || none_bits(r_ptr->flags3, RF3_EVIL) || (player_ptr->lev < monap_ptr->rlev) || ((randint0(100) + player_ptr->lev) <= 50)) {
106         return false;
107     }
108
109     if (is_original_ap_and_seen(player_ptr, monap_ptr->m_ptr)) {
110         r_ptr->r_flags3 |= RF3_EVIL;
111     }
112
113 #ifdef JP
114     monap_ptr->abbreviate ? msg_format("撃退した。") : msg_format("%^sは撃退された。", monap_ptr->m_name);
115     monap_ptr->abbreviate = 1; /* 2回目以降は省略 */
116 #else
117     msg_format("%^s is repelled.", monap_ptr->m_name);
118 #endif
119
120     return true;
121 }
122
123 static void describe_silly_attacks(monap_type *monap_ptr)
124 {
125     if (monap_ptr->act == nullptr) {
126         return;
127     }
128
129     if (monap_ptr->do_silly_attack) {
130 #ifdef JP
131         monap_ptr->abbreviate = -1;
132 #endif
133         monap_ptr->act = silly_attacks[randint0(MAX_SILLY_ATTACK)];
134     }
135
136 #ifdef JP
137     if (monap_ptr->abbreviate == 0) {
138         msg_format("%^sに%s", monap_ptr->m_name, monap_ptr->act);
139     } else if (monap_ptr->abbreviate == 1) {
140         msg_format("%s", monap_ptr->act);
141     } else {
142        /* if (monap_ptr->abbreviate == -1) */
143        msg_format("%^s%s", monap_ptr->m_name, monap_ptr->act);
144     }
145
146     monap_ptr->abbreviate = 1; /*2回目以降は省略 */
147 #else
148     msg_format("%^s %s%s", monap_ptr->m_name, monap_ptr->act, monap_ptr->do_silly_attack ? " you." : "");
149 #endif
150 }
151
152 /*!
153  * @brief 切り傷と朦朧が同時に発生した時、片方を無効にする
154  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
155  */
156 static void select_cut_stun(monap_type *monap_ptr)
157 {
158     if ((monap_ptr->do_cut == 0) || (monap_ptr->do_stun == 0)) {
159         return;
160     }
161
162     if (randint0(100) < 50) {
163         monap_ptr->do_cut = 0;
164     } else {
165         monap_ptr->do_stun = 0;
166     }
167 }
168
169 static void calc_player_cut(PlayerType *player_ptr, monap_type *monap_ptr)
170 {
171     if (monap_ptr->do_cut == 0) {
172         return;
173     }
174
175     auto cut_plus = PlayerCut::get_accumulation(monap_ptr->d_dice * monap_ptr->d_side, monap_ptr->damage);
176     if (cut_plus > 0) {
177         (void)BadStatusSetter(player_ptr).mod_cut(cut_plus);
178     }
179 }
180
181 /*!
182  * @brief 能力値の実値を求める
183  * @param raw PlayerTypeに格納されている生値
184  * @return 実値
185  * @details AD&Dの記法に則り、19以上の値を取らなくしているので、格納方法が面倒
186  */
187 static int stat_value(const int raw)
188 {
189     if (raw <= 18) {
190         return raw;
191     }
192
193     return (raw - 18) / 10 + 18;
194 }
195
196 /*!
197  * @brief 朦朧を蓄積させる
198  * @param player_ptr プレイヤーへの参照ポインタ
199  * @param monap_ptr モンスター打撃への参照ポインタ
200  * @details
201  * 痛恨の一撃ならば朦朧蓄積ランクを1上げる.
202  * 2%の確率で朦朧蓄積ランクを1上げる.
203  * 肉体のパラメータが合計80を超える水準に強化されていたら朦朧蓄積ランクを1下げる.
204  */
205 static void process_player_stun(PlayerType *player_ptr, monap_type *monap_ptr)
206 {
207     if (monap_ptr->do_stun == 0) {
208         return;
209     }
210
211     auto total = monap_ptr->d_dice * monap_ptr->d_side;
212     auto accumulation_rank = PlayerStun::get_accumulation_rank(total, monap_ptr->damage);
213     if (accumulation_rank == 0) {
214         return;
215     }
216
217     if ((total < monap_ptr->damage) && (accumulation_rank <= 6)) {
218         accumulation_rank++;
219     }
220
221     if (one_in_(50)) {
222         accumulation_rank++;
223     }
224
225     auto str = stat_value(player_ptr->stat_cur[A_STR]);
226     auto dex = stat_value(player_ptr->stat_cur[A_DEX]);
227     auto con = stat_value(player_ptr->stat_cur[A_CON]);
228     auto is_powerful_body = str + dex + con > 80;
229     if (is_powerful_body) {
230         accumulation_rank--;
231     }
232
233     auto stun_plus = PlayerStun::get_accumulation(accumulation_rank);
234     if (stun_plus > 0) {
235         (void)BadStatusSetter(player_ptr).mod_stun(stun_plus);
236     }
237 }
238
239 static void monster_explode(PlayerType *player_ptr, monap_type *monap_ptr)
240 {
241     if (!monap_ptr->explode) {
242         return;
243     }
244
245     sound(SOUND_EXPLODE);
246     MonsterDamageProcessor mdp(player_ptr, monap_ptr->m_idx, monap_ptr->m_ptr->hp + 1, &monap_ptr->fear, AttributeType::NONE);
247     if (mdp.mon_take_hit(nullptr)) {
248         monap_ptr->blinked = false;
249         monap_ptr->alive = false;
250     }
251 }
252
253 static void describe_attack_evasion(PlayerType *player_ptr, monap_type *monap_ptr)
254 {
255     if (!monap_ptr->m_ptr->ml) {
256         return;
257     }
258
259     disturb(player_ptr, true, true);
260 #ifdef JP
261     auto is_suiken = any_bits(player_ptr->special_attack, ATTACK_SUIKEN);
262     if (monap_ptr->abbreviate) {
263         msg_format("%sかわした。", is_suiken ? "奇妙な動きで" : "");
264     } else {
265         msg_format("%s%^sの攻撃をかわした。", is_suiken ? "奇妙な動きで" : "", monap_ptr->m_name);
266     }
267
268     monap_ptr->abbreviate = 1; /* 2回目以降は省略 */
269 #else
270     msg_format("%^s misses you.", monap_ptr->m_name);
271 #endif
272 }
273
274 static void gain_armor_exp(PlayerType *player_ptr, monap_type *monap_ptr)
275 {
276     const auto o_ptr_mh = &player_ptr->inventory_list[INVEN_MAIN_HAND];
277     const auto o_ptr_sh = &player_ptr->inventory_list[INVEN_SUB_HAND];
278     if (!o_ptr_mh->is_armour() && !o_ptr_sh->is_armour()) {
279         return;
280     }
281
282     auto cur = player_ptr->skill_exp[PlayerSkillKindType::SHIELD];
283     auto max = s_info[enum2i(player_ptr->pclass)].s_max[PlayerSkillKindType::SHIELD];
284     if (cur >= max) {
285         return;
286     }
287
288     auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
289     auto target_level = r_ptr->level;
290     short increment = 0;
291     if ((cur / 100) < target_level) {
292         auto addition = (cur / 100 + 15) < target_level ? (target_level - (cur / 100 + 15)) : 0;
293         increment += 1 + addition;
294     }
295
296     player_ptr->skill_exp[PlayerSkillKindType::SHIELD] = std::min<short>(max, cur + increment);
297     player_ptr->update |= (PU_BONUS);
298 }
299
300 /*!
301  * @brief モンスターから直接攻撃を1回受けた時の処理
302  * @return 対邪悪結界により攻撃が当たらなかったらFALSE、それ以外はTRUE
303  * @param player_ptr プレイヤーへの参照ポインタ
304  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
305  * @details 最大4 回/モンスター/ターン、このルーチンを通る
306  */
307 static bool process_monster_attack_hit(PlayerType *player_ptr, monap_type *monap_ptr)
308 {
309     disturb(player_ptr, true, true);
310     if (effect_protecion_from_evil(player_ptr, monap_ptr)) {
311         return false;
312     }
313
314     monap_ptr->do_cut = 0;
315     monap_ptr->do_stun = 0;
316     describe_monster_attack_method(monap_ptr);
317     describe_silly_attacks(monap_ptr);
318     monap_ptr->obvious = true;
319     monap_ptr->damage = damroll(monap_ptr->d_dice, monap_ptr->d_side);
320     if (monap_ptr->explode) {
321         monap_ptr->damage = 0;
322     }
323
324     switch_monster_blow_to_player(player_ptr, monap_ptr);
325     select_cut_stun(monap_ptr);
326     calc_player_cut(player_ptr, monap_ptr);
327     process_player_stun(player_ptr, monap_ptr);
328     monster_explode(player_ptr, monap_ptr);
329     process_aura_counterattack(player_ptr, monap_ptr);
330     return true;
331 }
332
333 /*!
334  * @brief 一部の打撃種別の場合のみ、避けた旨のメッセージ表示と盾技能スキル向上を行う
335  * @param player_ptr プレイヤーへの参照ポインタ
336  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
337  */
338 static void process_monster_attack_evasion(PlayerType *player_ptr, monap_type *monap_ptr)
339 {
340     switch (monap_ptr->method) {
341     case RaceBlowMethodType::HIT:
342     case RaceBlowMethodType::TOUCH:
343     case RaceBlowMethodType::PUNCH:
344     case RaceBlowMethodType::KICK:
345     case RaceBlowMethodType::CLAW:
346     case RaceBlowMethodType::BITE:
347     case RaceBlowMethodType::STING:
348     case RaceBlowMethodType::SLASH:
349     case RaceBlowMethodType::BUTT:
350     case RaceBlowMethodType::CRUSH:
351     case RaceBlowMethodType::ENGULF:
352     case RaceBlowMethodType::CHARGE:
353         describe_attack_evasion(player_ptr, monap_ptr);
354         gain_armor_exp(player_ptr, monap_ptr);
355         monap_ptr->damage = 0;
356         return;
357     default:
358         return;
359     }
360 }
361
362 /*!
363  * @brief モンスターの打撃情報を蓄積させる
364  * @param player_ptr プレイヤーへの参照ポインタ
365  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
366  * @param ap_cnt モンスターの打撃 N回目
367  * @details どの敵が何をしてきたか正しく認識できていなければ情報を蓄積しない.
368  * 非自明な類の打撃については、そのダメージが 0 ならば基本的に知識が増えない.
369  * 但し、既に一定以上の知識があれば常に知識が増える(何をされたのか察知できる).
370  */
371 static void increase_blow_type_seen(PlayerType *player_ptr, monap_type *monap_ptr, const int ap_cnt)
372 {
373     if (!is_original_ap_and_seen(player_ptr, monap_ptr->m_ptr) || monap_ptr->do_silly_attack) {
374         return;
375     }
376
377     auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
378     if (!monap_ptr->obvious && (monap_ptr->damage == 0) && (r_ptr->r_blows[ap_cnt] <= 10)) {
379         return;
380     }
381
382     if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR) {
383         r_ptr->r_blows[ap_cnt]++;
384     }
385 }
386
387 /*!
388  * @brief モンスターからプレイヤーへの打撃処理本体
389  * @return 打撃に反応してプレイヤーがその場から離脱したかどうか
390  */
391 static bool process_monster_blows(PlayerType *player_ptr, monap_type *monap_ptr)
392 {
393     auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
394     for (auto ap_cnt = 0; ap_cnt < MAX_NUM_BLOWS; ap_cnt++) {
395         monap_ptr->obvious = false;
396         monap_ptr->damage = 0;
397         monap_ptr->act = nullptr;
398         monap_ptr->effect = r_ptr->blow[ap_cnt].effect;
399         monap_ptr->method = r_ptr->blow[ap_cnt].method;
400         monap_ptr->d_dice = r_ptr->blow[ap_cnt].d_dice;
401         monap_ptr->d_side = r_ptr->blow[ap_cnt].d_side;
402
403         if (!check_monster_continuous_attack(player_ptr, monap_ptr)) {
404             break;
405         }
406
407         // effect が RaceBlowEffectType::NONE (無効値)になることはあり得ないはずだが、万一そう
408         // なっていたら単に攻撃を打ち切る。
409         // r_info.txt の "B:" トークンに effect 以降を書き忘れた場合が該当する。
410         if (monap_ptr->effect == RaceBlowEffectType::NONE) {
411             plog("unexpected: monap_ptr->effect == RaceBlowEffectType::NONE");
412             break;
413         }
414
415         if (monap_ptr->method == RaceBlowMethodType::SHOOT) {
416             continue;
417         }
418
419         // フレーバーの打撃は必中扱い。それ以外は通常の命中判定を行う。
420         monap_ptr->ac = player_ptr->ac + player_ptr->to_a;
421         bool hit;
422         if (monap_ptr->effect == RaceBlowEffectType::FLAVOR) {
423             hit = true;
424         } else {
425             const int power = mbe_info[enum2i(monap_ptr->effect)].power;
426             hit = check_hit_from_monster_to_player(player_ptr, power, monap_ptr->rlev, monster_stunned_remaining(monap_ptr->m_ptr));
427         }
428
429         if (hit) {
430             // 命中した。命中処理と思い出処理を行う。
431             // 打撃そのものは対邪悪結界で撃退した可能性がある。
432             const bool protect = !process_monster_attack_hit(player_ptr, monap_ptr);
433             increase_blow_type_seen(player_ptr, monap_ptr, ap_cnt);
434
435             // 撃退成功時はそのまま次の打撃へ移行。
436             if (protect)
437                 continue;
438
439             // 撃退失敗時は落馬処理、変わり身のテレポート処理を行う。
440             check_fall_off_horse(player_ptr, monap_ptr);
441
442             // 変わり身のテレポートが成功したら攻撃を打ち切り、プレイヤーが離脱した旨を返す。
443             if (kawarimi(player_ptr, false)) {
444                 return true;
445             }
446         } else {
447             // 命中しなかった。回避時の処理、思い出処理を行う。
448             process_monster_attack_evasion(player_ptr, monap_ptr);
449             increase_blow_type_seen(player_ptr, monap_ptr, ap_cnt);
450         }
451     }
452
453     // 通常はプレイヤーはその場にとどまる。
454     return false;
455 }
456
457 static void postprocess_monster_blows(PlayerType *player_ptr, monap_type *monap_ptr)
458 {
459     SpellHex spell_hex(player_ptr, monap_ptr);
460     spell_hex.store_vengeful_damage(monap_ptr->get_damage);
461     spell_hex.eyes_on_eyes();
462     musou_counterattack(player_ptr, monap_ptr);
463     spell_hex.thief_teleport();
464     auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
465     if (player_ptr->is_dead && (r_ptr->r_deaths < MAX_SHORT) && !player_ptr->current_floor_ptr->inside_arena) {
466         r_ptr->r_deaths++;
467     }
468
469     if (monap_ptr->m_ptr->ml && monap_ptr->fear && monap_ptr->alive && !player_ptr->is_dead) {
470         sound(SOUND_FLEE);
471         msg_format(_("%^sは恐怖で逃げ出した!", "%^s flees in terror!"), monap_ptr->m_name);
472     }
473
474     PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::IAI });
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(PlayerType *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 (PlayerClass(player_ptr).samurai_stance_is(SamuraiStanceType::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 }