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-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"
60 static bool check_no_blow(PlayerType *player_ptr, monap_type *monap_ptr)
62 auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
63 if (any_bits(r_ptr->flags1, RF1_NEVER_BLOW)) {
67 if (d_info[player_ptr->dungeon_idx].flags.has(DungeonFeatureType::NO_MELEE)) {
71 return is_hostile(monap_ptr->m_ptr);
75 * @brief プレイヤー死亡等でモンスターからプレイヤーへの直接攻撃処理を途中で打ち切るかどうかを判定する
76 * @param player_ptr プレイヤーへの参照ポインタ
77 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
78 * @return 攻撃続行ならばTRUE、打ち切りになったらFALSE
80 static bool check_monster_continuous_attack(PlayerType *player_ptr, monap_type *monap_ptr)
82 if (!monster_is_valid(monap_ptr->m_ptr) || (monap_ptr->method == RaceBlowMethodType::NONE)) {
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;
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;
97 * @brief 対邪悪結界が効いている状態で邪悪なモンスターから直接攻撃を受けた時の処理
98 * @param player_ptr プレイヤーへの参照ポインタ
99 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
100 * @return briefに書いた条件+確率が満たされたらTRUE、それ以外はFALSE
102 static bool effect_protecion_from_evil(PlayerType *player_ptr, monap_type *monap_ptr)
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)) {
109 if (is_original_ap_and_seen(player_ptr, monap_ptr->m_ptr)) {
110 r_ptr->r_flags3 |= RF3_EVIL;
114 monap_ptr->abbreviate ? msg_format("撃退した。") : msg_format("%^sは撃退された。", monap_ptr->m_name);
115 monap_ptr->abbreviate = 1; /* 2回目以降は省略 */
117 msg_format("%^s is repelled.", monap_ptr->m_name);
123 static void describe_silly_attacks(monap_type *monap_ptr)
125 if (monap_ptr->act == nullptr) {
129 if (monap_ptr->do_silly_attack) {
131 monap_ptr->abbreviate = -1;
133 monap_ptr->act = silly_attacks[randint0(MAX_SILLY_ATTACK)];
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);
142 /* if (monap_ptr->abbreviate == -1) */
143 msg_format("%^s%s", monap_ptr->m_name, monap_ptr->act);
146 monap_ptr->abbreviate = 1; /*2回目以降は省略 */
148 msg_format("%^s %s%s", monap_ptr->m_name, monap_ptr->act, monap_ptr->do_silly_attack ? " you." : "");
153 * @brief 切り傷と朦朧が同時に発生した時、片方を無効にする
154 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
156 static void select_cut_stun(monap_type *monap_ptr)
158 if ((monap_ptr->do_cut == 0) || (monap_ptr->do_stun == 0)) {
162 if (randint0(100) < 50) {
163 monap_ptr->do_cut = 0;
165 monap_ptr->do_stun = 0;
169 static void calc_player_cut(PlayerType *player_ptr, monap_type *monap_ptr)
171 if (monap_ptr->do_cut == 0) {
175 auto cut_plus = PlayerCut::get_accumulation(monap_ptr->d_dice * monap_ptr->d_side, monap_ptr->damage);
177 (void)BadStatusSetter(player_ptr).mod_cut(cut_plus);
183 * @param raw PlayerTypeに格納されている生値
185 * @details AD&Dの記法に則り、19以上の値を取らなくしているので、格納方法が面倒
187 static int stat_value(const int raw)
193 return (raw - 18) / 10 + 18;
198 * @param player_ptr プレイヤーへの参照ポインタ
199 * @param monap_ptr モンスター打撃への参照ポインタ
201 * 痛恨の一撃ならば朦朧蓄積ランクを1上げる.
202 * 2%の確率で朦朧蓄積ランクを1上げる.
203 * 肉体のパラメータが合計80を超える水準に強化されていたら朦朧蓄積ランクを1下げる.
205 static void process_player_stun(PlayerType *player_ptr, monap_type *monap_ptr)
207 if (monap_ptr->do_stun == 0) {
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) {
217 if ((total < monap_ptr->damage) && (accumulation_rank <= 6)) {
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) {
233 auto stun_plus = PlayerStun::get_accumulation(accumulation_rank);
235 (void)BadStatusSetter(player_ptr).mod_stun(stun_plus);
239 static void monster_explode(PlayerType *player_ptr, monap_type *monap_ptr)
241 if (!monap_ptr->explode) {
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;
253 static void describe_attack_evasion(PlayerType *player_ptr, monap_type *monap_ptr)
255 if (!monap_ptr->m_ptr->ml) {
259 disturb(player_ptr, true, true);
261 auto is_suiken = any_bits(player_ptr->special_attack, ATTACK_SUIKEN);
262 if (monap_ptr->abbreviate) {
263 msg_format("%sかわした。", is_suiken ? "奇妙な動きで" : "");
265 msg_format("%s%^sの攻撃をかわした。", is_suiken ? "奇妙な動きで" : "", monap_ptr->m_name);
268 monap_ptr->abbreviate = 1; /* 2回目以降は省略 */
270 msg_format("%^s misses you.", monap_ptr->m_name);
274 static void gain_armor_exp(PlayerType *player_ptr, monap_type *monap_ptr)
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()) {
282 auto cur = player_ptr->skill_exp[PlayerSkillKindType::SHIELD];
283 auto max = s_info[enum2i(player_ptr->pclass)].s_max[PlayerSkillKindType::SHIELD];
288 auto *r_ptr = &r_info[monap_ptr->m_ptr->r_idx];
289 auto target_level = r_ptr->level;
291 if ((cur / 100) < target_level) {
292 auto addition = (cur / 100 + 15) < target_level ? (target_level - (cur / 100 + 15)) : 0;
293 increment += 1 + addition;
296 player_ptr->skill_exp[PlayerSkillKindType::SHIELD] = std::min<short>(max, cur + increment);
297 player_ptr->update |= (PU_BONUS);
301 * @brief モンスターから直接攻撃を1回受けた時の処理
302 * @return 対邪悪結界により攻撃が当たらなかったらFALSE、それ以外はTRUE
303 * @param player_ptr プレイヤーへの参照ポインタ
304 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
305 * @details 最大4 回/モンスター/ターン、このルーチンを通る
307 static bool process_monster_attack_hit(PlayerType *player_ptr, monap_type *monap_ptr)
309 disturb(player_ptr, true, true);
310 if (effect_protecion_from_evil(player_ptr, monap_ptr)) {
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;
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);
334 * @brief 一部の打撃種別の場合のみ、避けた旨のメッセージ表示と盾技能スキル向上を行う
335 * @param player_ptr プレイヤーへの参照ポインタ
336 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
338 static void process_monster_attack_evasion(PlayerType *player_ptr, monap_type *monap_ptr)
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;
363 * @brief モンスターの打撃情報を蓄積させる
364 * @param player_ptr プレイヤーへの参照ポインタ
365 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
366 * @param ap_cnt モンスターの打撃 N回目
367 * @details どの敵が何をしてきたか正しく認識できていなければ情報を蓄積しない.
368 * 非自明な類の打撃については、そのダメージが 0 ならば基本的に知識が増えない.
369 * 但し、既に一定以上の知識があれば常に知識が増える(何をされたのか察知できる).
371 static void increase_blow_type_seen(PlayerType *player_ptr, monap_type *monap_ptr, const int ap_cnt)
373 if (!is_original_ap_and_seen(player_ptr, monap_ptr->m_ptr) || monap_ptr->do_silly_attack) {
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)) {
382 if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR) {
383 r_ptr->r_blows[ap_cnt]++;
388 * @brief モンスターからプレイヤーへの打撃処理本体
389 * @return 打撃に反応してプレイヤーがその場から離脱したかどうか
391 static bool process_monster_blows(PlayerType *player_ptr, monap_type *monap_ptr)
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;
403 if (!check_monster_continuous_attack(player_ptr, monap_ptr)) {
407 // effect が RaceBlowEffectType::NONE (無効値)になることはあり得ないはずだが、万一そう
409 // r_info.txt の "B:" トークンに effect 以降を書き忘れた場合が該当する。
410 if (monap_ptr->effect == RaceBlowEffectType::NONE) {
411 plog("unexpected: monap_ptr->effect == RaceBlowEffectType::NONE");
415 if (monap_ptr->method == RaceBlowMethodType::SHOOT) {
419 // フレーバーの打撃は必中扱い。それ以外は通常の命中判定を行う。
420 monap_ptr->ac = player_ptr->ac + player_ptr->to_a;
422 if (monap_ptr->effect == RaceBlowEffectType::FLAVOR) {
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));
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);
435 // 撃退成功時はそのまま次の打撃へ移行。
439 // 撃退失敗時は落馬処理、変わり身のテレポート処理を行う。
440 check_fall_off_horse(player_ptr, monap_ptr);
442 // 変わり身のテレポートが成功したら攻撃を打ち切り、プレイヤーが離脱した旨を返す。
443 if (kawarimi(player_ptr, false)) {
447 // 命中しなかった。回避時の処理、思い出処理を行う。
448 process_monster_attack_evasion(player_ptr, monap_ptr);
449 increase_blow_type_seen(player_ptr, monap_ptr, ap_cnt);
453 // 通常はプレイヤーはその場にとどまる。
457 static void postprocess_monster_blows(PlayerType *player_ptr, monap_type *monap_ptr)
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) {
469 if (monap_ptr->m_ptr->ml && monap_ptr->fear && monap_ptr->alive && !player_ptr->is_dead) {
471 msg_format(_("%^sは恐怖で逃げ出した!", "%^s flees in terror!"), monap_ptr->m_name);
474 PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::IAI });
478 * @brief モンスターからプレイヤーへの打撃処理 / Attack the player via physical attacks.
479 * @param m_idx 打撃を行うモンスターのID
480 * @return 実際に攻撃処理を行った場合TRUEを返す
482 bool make_attack_normal(PlayerType *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 (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)) {
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);