OSDN Git Service

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