2 * @brief モンスターからプレイヤーへの直接攻撃処理
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"
58 static bool check_no_blow(player_type *player_ptr, monap_type *monap_ptr)
60 auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
61 if (any_bits(r_ptr->flags1, RF1_NEVER_BLOW)) {
65 if (d_info[player_ptr->dungeon_idx].flags.has(DF::NO_MELEE)) {
69 return is_hostile(monap_ptr->m_ptr);
73 * @brief プレイヤー死亡等でモンスターからプレイヤーへの直接攻撃処理を途中で打ち切るかどうかを判定する
74 * @param player_ptr プレイヤーへの参照ポインタ
75 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
76 * @return 攻撃続行ならばTRUE、打ち切りになったらFALSE
78 static bool check_monster_continuous_attack(player_type *player_ptr, monap_type *monap_ptr)
80 if (!monster_is_valid(monap_ptr->m_ptr) || (monap_ptr->method == RBM_NONE)) {
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;
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;
95 * @brief 対邪悪結界が効いている状態で邪悪なモンスターから直接攻撃を受けた時の処理
96 * @param player_ptr プレイヤーへの参照ポインタ
97 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
98 * @return briefに書いた条件+確率が満たされたらTRUE、それ以外はFALSE
100 static bool effect_protecion_from_evil(player_type *player_ptr, monap_type *monap_ptr)
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)) {
107 if (is_original_ap_and_seen(player_ptr, monap_ptr->m_ptr)) {
108 r_ptr->r_flags3 |= RF3_EVIL;
112 monap_ptr->abbreviate ? msg_format("撃退した。") : msg_format("%^sは撃退された。", monap_ptr->m_name);
113 monap_ptr->abbreviate = 1; /* 2回目以降は省略 */
115 msg_format("%^s is repelled.", monap_ptr->m_name);
121 static void describe_silly_attacks(monap_type *monap_ptr)
123 if (monap_ptr->act == nullptr) {
127 if (monap_ptr->do_silly_attack) {
129 monap_ptr->abbreviate = -1;
131 monap_ptr->act = silly_attacks[randint0(MAX_SILLY_ATTACK)];
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);
140 /* if (monap_ptr->abbreviate == -1) */
141 msg_format("%^s%s", monap_ptr->m_name, monap_ptr->act);
144 monap_ptr->abbreviate = 1; /*2回目以降は省略 */
146 msg_format("%^s %s%s", monap_ptr->m_name, monap_ptr->act, monap_ptr->do_silly_attack ? " you." : "");
151 * @brief 切り傷と朦朧が同時に発生した時、片方を無効にする
152 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
154 static void select_cut_stun(monap_type *monap_ptr)
156 if ((monap_ptr->do_cut == 0) || (monap_ptr->do_stun == 0)) {
160 if (randint0(100) < 50) {
161 monap_ptr->do_cut = 0;
163 monap_ptr->do_stun = 0;
167 static void calc_player_cut(player_type *player_ptr, monap_type *monap_ptr)
169 if (monap_ptr->do_cut == 0) {
173 auto cut_plus = PlayerCut::get_accumulation(monap_ptr->d_dice * monap_ptr->d_side, monap_ptr->damage);
175 (void)BadStatusSetter(player_ptr).mod_cut(cut_plus);
181 * @param raw player_typeに格納されている生値
183 * @details AD&Dの記法に則り、19以上の値を取らなくしているので、格納方法が面倒
185 static int stat_value(const int raw)
191 return (raw - 18) / 10 + 18;
196 * @param player_ptr プレイヤーへの参照ポインタ
197 * @param monap_ptr モンスター打撃への参照ポインタ
199 * 痛恨の一撃ならば朦朧蓄積ランクを1上げる.
200 * 2%の確率で朦朧蓄積ランクを1上げる.
201 * 肉体のパラメータが合計80を超える水準に強化されていたら朦朧蓄積ランクを1下げる.
203 static void process_player_stun(player_type *player_ptr, monap_type *monap_ptr)
205 if (monap_ptr->do_stun == 0) {
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) {
215 if ((total < monap_ptr->damage) && (accumulation_rank <= 6)) {
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) {
231 auto stun_plus = PlayerStun::get_accumulation(accumulation_rank);
233 (void)BadStatusSetter(player_ptr).mod_stun(stun_plus);
237 static void monster_explode(player_type *player_ptr, monap_type *monap_ptr)
239 if (!monap_ptr->explode) {
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;
251 static void describe_attack_evasion(player_type *player_ptr, monap_type *monap_ptr)
253 if (!monap_ptr->m_ptr->ml) {
257 disturb(player_ptr, true, true);
259 auto is_suiken = any_bits(player_ptr->special_attack, ATTACK_SUIKEN);
260 if (monap_ptr->abbreviate) {
261 msg_format("%sかわした。", is_suiken ? "奇妙な動きで" : "");
263 msg_format("%s%^sの攻撃をかわした。", is_suiken ? "奇妙な動きで" : "", monap_ptr->m_name);
266 monap_ptr->abbreviate = 1; /* 2回目以降は省略 */
268 msg_format("%^s misses you.", monap_ptr->m_name);
272 static void gain_armor_exp(player_type *player_ptr, monap_type *monap_ptr)
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()) {
280 auto cur = player_ptr->skill_exp[SKILL_SHIELD];
281 auto max = s_info[player_ptr->pclass].s_max[SKILL_SHIELD];
286 auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
287 auto target_level = r_ptr->level;
289 if ((cur / 100) < target_level) {
290 auto addition = (cur / 100 + 15) < target_level ? (target_level - (cur / 100 + 15)) : 0;
291 increment += 1 + addition;
294 player_ptr->skill_exp[SKILL_SHIELD] = MIN(max, cur + increment);
295 player_ptr->update |= (PU_BONUS);
299 * @brief モンスターから直接攻撃を1回受けた時の処理
300 * @return 対邪悪結界により攻撃が当たらなかったらFALSE、それ以外はTRUE
301 * @param player_ptr プレイヤーへの参照ポインタ
302 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
303 * @details 最大4 回/モンスター/ターン、このルーチンを通る
305 static bool process_monster_attack_hit(player_type *player_ptr, monap_type *monap_ptr)
307 disturb(player_ptr, true, true);
308 if (effect_protecion_from_evil(player_ptr, monap_ptr)) {
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;
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);
332 * @brief 一部の打撃種別の場合のみ、避けた旨のメッセージ表示と盾技能スキル向上を行う
333 * @param player_ptr プレイヤーへの参照ポインタ
334 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
336 static void process_monster_attack_evasion(player_type *player_ptr, monap_type *monap_ptr)
338 switch (monap_ptr->method) {
351 describe_attack_evasion(player_ptr, monap_ptr);
352 gain_armor_exp(player_ptr, monap_ptr);
353 monap_ptr->damage = 0;
361 * @brief モンスターの打撃情報を蓄積させる
362 * @param player_ptr プレイヤーへの参照ポインタ
363 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
364 * @param ap_cnt モンスターの打撃 N回目
365 * @details どの敵が何をしてきたか正しく認識できていなければ情報を蓄積しない.
366 * 非自明な類の打撃については、そのダメージが 0 ならば基本的に知識が増えない.
367 * 但し、既に一定以上の知識があれば常に知識が増える(何をされたのか察知できる).
369 static void increase_blow_type_seen(player_type *player_ptr, monap_type *monap_ptr, const int ap_cnt)
371 if (!is_original_ap_and_seen(player_ptr, monap_ptr->m_ptr) || monap_ptr->do_silly_attack) {
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)) {
380 if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR) {
381 r_ptr->r_blows[ap_cnt]++;
386 * @brief モンスターからプレイヤーへの打撃処理本体
387 * @return 打撃に反応してプレイヤーがその場から離脱したかどうか
389 static bool process_monster_blows(player_type *player_ptr, monap_type *monap_ptr)
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;
401 if (!check_monster_continuous_attack(player_ptr, monap_ptr)) {
405 // effect が RBE_NONE (無効値)になることはあり得ないはずだが、万一そう
407 // r_info.txt の "B:" トークンに effect 以降を書き忘れた場合が該当する。
408 if (monap_ptr->effect == RBE_NONE) {
409 plog("unexpected: monap_ptr->effect == RBE_NONE");
413 if (monap_ptr->method == RBM_SHOOT) {
417 // フレーバーの打撃は必中扱い。それ以外は通常の命中判定を行う。
418 monap_ptr->ac = player_ptr->ac + player_ptr->to_a;
420 if (monap_ptr->effect == RBE_FLAVOR) {
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));
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);
433 // 撃退成功時はそのまま次の打撃へ移行。
437 // 撃退失敗時は落馬処理、変わり身のテレポート処理を行う。
438 check_fall_off_horse(player_ptr, monap_ptr);
440 // 変わり身のテレポートが成功したら攻撃を打ち切り、プレイヤーが離脱した旨を返す。
441 if (kawarimi(player_ptr, false)) {
445 // 命中しなかった。回避時の処理、思い出処理を行う。
446 process_monster_attack_evasion(player_ptr, monap_ptr);
447 increase_blow_type_seen(player_ptr, monap_ptr, ap_cnt);
451 // 通常はプレイヤーはその場にとどまる。
455 static void postprocess_monster_blows(player_type *player_ptr, monap_type *monap_ptr)
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) {
467 if (monap_ptr->m_ptr->ml && monap_ptr->fear && monap_ptr->alive && !player_ptr->is_dead) {
469 msg_format(_("%^sは恐怖で逃げ出した!", "%^s flees in terror!"), monap_ptr->m_name);
472 if (player_ptr->special_defense & KATA_IAI) {
473 set_action(player_ptr, ACTION_NONE);
478 * @brief モンスターからプレイヤーへの打撃処理 / Attack the player via physical attacks.
479 * @param m_idx 打撃を行うモンスターのID
480 * @return 実際に攻撃処理を行った場合TRUEを返す
482 bool make_attack_normal(player_type *player_ptr, MONSTER_IDX m_idx)
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)) {
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)) {
501 auto can_activate_kawarimi = randint0(55) < (player_ptr->lev * 3 / 5 + 20);
502 if (can_activate_kawarimi && kawarimi(player_ptr, true)) {
506 monap_ptr->blinked = false;
507 if (process_monster_blows(player_ptr, monap_ptr)) {
511 postprocess_monster_blows(player_ptr, monap_ptr);