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 "util/bit-flags-calculator.h"
54 #include "view/display-messages.h"
56 static bool check_no_blow(player_type *player_ptr, monap_type *monap_ptr)
58 auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
59 if (any_bits(r_ptr->flags1, RF1_NEVER_BLOW)) {
63 if (d_info[player_ptr->dungeon_idx].flags.has(DF::NO_MELEE)) {
67 return is_hostile(monap_ptr->m_ptr);
71 * @brief プレイヤー死亡等でモンスターからプレイヤーへの直接攻撃処理を途中で打ち切るかどうかを判定する
72 * @param player_ptr プレイヤーへの参照ポインタ
73 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
74 * @return 攻撃続行ならばTRUE、打ち切りになったらFALSE
76 static bool check_monster_continuous_attack(player_type *player_ptr, monap_type *monap_ptr)
78 if (!monster_is_valid(monap_ptr->m_ptr) || (monap_ptr->method == RBM_NONE)) {
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;
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;
93 * @brief 対邪悪結界が効いている状態で邪悪なモンスターから直接攻撃を受けた時の処理
94 * @param player_ptr プレイヤーへの参照ポインタ
95 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
96 * @return briefに書いた条件+確率が満たされたらTRUE、それ以外はFALSE
98 static bool effect_protecion_from_evil(player_type *player_ptr, monap_type *monap_ptr)
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)) {
105 if (is_original_ap_and_seen(player_ptr, monap_ptr->m_ptr)) {
106 r_ptr->r_flags3 |= RF3_EVIL;
110 monap_ptr->abbreviate ? msg_format("撃退した。") : msg_format("%^sは撃退された。", monap_ptr->m_name);
111 monap_ptr->abbreviate = 1; /* 2回目以降は省略 */
113 msg_format("%^s is repelled.", monap_ptr->m_name);
119 static void describe_silly_attacks(monap_type *monap_ptr)
121 if (monap_ptr->act == nullptr) {
125 if (monap_ptr->do_silly_attack) {
127 monap_ptr->abbreviate = -1;
129 monap_ptr->act = silly_attacks[randint0(MAX_SILLY_ATTACK)];
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);
138 /* if (monap_ptr->abbreviate == -1) */
139 msg_format("%^s%s", monap_ptr->m_name, monap_ptr->act);
142 monap_ptr->abbreviate = 1; /*2回目以降は省略 */
144 msg_format("%^s %s%s", monap_ptr->m_name, monap_ptr->act, monap_ptr->do_silly_attack ? " you." : "");
149 * @brief 切り傷と朦朧が同時に発生した時、片方を無効にする
150 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
152 static void select_cut_stun(monap_type *monap_ptr)
154 if ((monap_ptr->do_cut == 0) || (monap_ptr->do_stun == 0)) {
158 if (randint0(100) < 50) {
159 monap_ptr->do_cut = 0;
161 monap_ptr->do_stun = 0;
165 static void calc_player_cut(player_type *player_ptr, monap_type *monap_ptr)
167 if (monap_ptr->do_cut == 0) {
172 auto criticality = calc_monster_critical(monap_ptr->d_dice, monap_ptr->d_side, monap_ptr->damage);
173 switch (criticality) {
178 cut_plus = randint1(5);
181 cut_plus = randint1(5) + 5;
184 cut_plus = randint1(20) + 20;
187 cut_plus = randint1(50) + 50;
190 cut_plus = randint1(100) + 100;
201 (void)set_cut(player_ptr, player_ptr->cut + cut_plus);
205 static void calc_player_stun(player_type *player_ptr, monap_type *monap_ptr)
207 if (monap_ptr->do_stun == 0) {
212 auto criticality = calc_monster_critical(monap_ptr->d_dice, monap_ptr->d_side, monap_ptr->damage);
213 switch (criticality) {
218 stun_plus = randint1(5);
221 stun_plus = randint1(5) + 10;
224 stun_plus = randint1(10) + 20;
227 stun_plus = randint1(15) + 30;
230 stun_plus = randint1(20) + 40;
241 (void)set_stun(player_ptr, player_ptr->stun + stun_plus);
245 static void monster_explode(player_type *player_ptr, monap_type *monap_ptr)
247 if (!monap_ptr->explode) {
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;
259 static void describe_attack_evasion(player_type *player_ptr, monap_type *monap_ptr)
261 if (!monap_ptr->m_ptr->ml) {
265 disturb(player_ptr, true, true);
267 auto is_suiken = any_bits(player_ptr->special_attack, ATTACK_SUIKEN);
268 if (monap_ptr->abbreviate) {
269 msg_format("%sかわした。", is_suiken ? "奇妙な動きで" : "");
271 msg_format("%s%^sの攻撃をかわした。", is_suiken ? "奇妙な動きで" : "", monap_ptr->m_name);
274 monap_ptr->abbreviate = 1; /* 2回目以降は省略 */
276 msg_format("%^s misses you.", monap_ptr->m_name);
280 static void gain_armor_exp(player_type *player_ptr, monap_type *monap_ptr)
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()) {
288 auto cur = player_ptr->skill_exp[SKILL_SHIELD];
289 auto max = s_info[player_ptr->pclass].s_max[SKILL_SHIELD];
294 auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
295 auto target_level = r_ptr->level;
297 if ((cur / 100) < target_level) {
298 auto addition = (cur / 100 + 15) < target_level ? (target_level - (cur / 100 + 15)) : 0;
299 increment += 1 + addition;
302 player_ptr->skill_exp[SKILL_SHIELD] = MIN(max, cur + increment);
303 player_ptr->update |= (PU_BONUS);
307 * @brief モンスターから直接攻撃を1回受けた時の処理
308 * @return 対邪悪結界により攻撃が当たらなかったらFALSE、それ以外はTRUE
309 * @param player_ptr プレイヤーへの参照ポインタ
310 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
311 * @details 最大4 回/モンスター/ターン、このルーチンを通る
313 static bool process_monster_attack_hit(player_type *player_ptr, monap_type *monap_ptr)
315 disturb(player_ptr, true, true);
316 if (effect_protecion_from_evil(player_ptr, monap_ptr)) {
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;
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);
340 * @brief 一部の打撃種別の場合のみ、避けた旨のメッセージ表示と盾技能スキル向上を行う
341 * @param player_ptr プレイヤーへの参照ポインタ
342 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
344 static void process_monster_attack_evasion(player_type *player_ptr, monap_type *monap_ptr)
346 switch (monap_ptr->method) {
359 describe_attack_evasion(player_ptr, monap_ptr);
360 gain_armor_exp(player_ptr, monap_ptr);
361 monap_ptr->damage = 0;
369 * @brief モンスターの打撃情報を蓄積させる
370 * @param player_ptr プレイヤーへの参照ポインタ
371 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
372 * @param ap_cnt モンスターの打撃 N回目
373 * @details どの敵が何をしてきたか正しく認識できていなければ情報を蓄積しない.
374 * 非自明な類の打撃については、そのダメージが 0 ならば基本的に知識が増えない.
375 * 但し、既に一定以上の知識があれば常に知識が増える(何をされたのか察知できる).
377 static void increase_blow_type_seen(player_type *player_ptr, monap_type *monap_ptr, const int ap_cnt)
379 if (!is_original_ap_and_seen(player_ptr, monap_ptr->m_ptr) || monap_ptr->do_silly_attack) {
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)) {
388 if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR) {
389 r_ptr->r_blows[ap_cnt]++;
394 * @brief モンスターからプレイヤーへの打撃処理本体
395 * @return 打撃に反応してプレイヤーがその場から離脱したかどうか
397 static bool process_monster_blows(player_type *player_ptr, monap_type *monap_ptr)
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;
409 if (!check_monster_continuous_attack(player_ptr, monap_ptr)) {
413 // effect が RBE_NONE (無効値)になることはあり得ないはずだが、万一そう
415 // r_info.txt の "B:" トークンに effect 以降を書き忘れた場合が該当する。
416 if (monap_ptr->effect == RBE_NONE) {
417 plog("unexpected: monap_ptr->effect == RBE_NONE");
421 if (monap_ptr->method == RBM_SHOOT) {
425 // フレーバーの打撃は必中扱い。それ以外は通常の命中判定を行う。
426 monap_ptr->ac = player_ptr->ac + player_ptr->to_a;
428 if (monap_ptr->effect == RBE_FLAVOR) {
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));
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);
441 // 撃退成功時はそのまま次の打撃へ移行。
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))
453 // 命中しなかった。回避時の処理、思い出処理を行う。
454 process_monster_attack_evasion(player_ptr, monap_ptr);
455 increase_blow_type_seen(player_ptr, monap_ptr, ap_cnt);
459 // 通常はプレイヤーはその場にとどまる。
463 static void postprocess_monster_blows(player_type *player_ptr, monap_type *monap_ptr)
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) {
475 if (monap_ptr->m_ptr->ml && monap_ptr->fear && monap_ptr->alive && !player_ptr->is_dead) {
477 msg_format(_("%^sは恐怖で逃げ出した!", "%^s flees in terror!"), monap_ptr->m_name);
480 if (player_ptr->special_defense & KATA_IAI) {
481 set_action(player_ptr, ACTION_NONE);
486 * @brief モンスターからプレイヤーへの打撃処理 / Attack the player via physical attacks.
487 * @param m_idx 打撃を行うモンスターのID
488 * @return 実際に攻撃処理を行った場合TRUEを返す
490 bool make_attack_normal(player_type *player_ptr, MONSTER_IDX m_idx)
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)) {
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)) {
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)) {
515 monap_ptr->blinked = false;
516 if (process_monster_blows(player_ptr, monap_ptr)) {
520 postprocess_monster_blows(player_ptr, monap_ptr);