OSDN Git Service

[Refactor] #929 Separated change_virtue_good_animal() from mon_take_hit()
[hengbandforosx/hengbandosx.git] / src / monster / monster-damage.cpp
index 28afd14..12cc30e 100644 (file)
@@ -29,7 +29,6 @@
 #include "monster-race/race-flags3.h"
 #include "monster-race/race-flags7.h"
 #include "monster-race/race-flags8.h"
-#include "monster-race/race-indice-types.h"
 #include "monster/monster-describer.h"
 #include "monster/monster-description-types.h"
 #include "monster/monster-info.h"
@@ -48,6 +47,7 @@
 #include "util/bit-flags-calculator.h"
 #include "view/display-messages.h"
 #include "world/world.h"
+#include <algorithm>
 
 /*
  * @brief コンストラクタ
@@ -66,473 +66,625 @@ MonsterDamageProcessor::MonsterDamageProcessor(player_type *target_ptr, MONSTER_
 }
 
 /*!
- * @brief モンスターに与えたダメージを元に経験値を加算する /
- * Calculate experience point to be get
- * @param m_ptr ダメージを与えたモンスターの構造体参照ポインタ
- * @details
- * <pre>
- * Even the 64 bit operation is not big enough to avoid overflaw
- * unless we carefully choose orders of ENERGY_MULTIPLICATION and ENERGY_DIVISION.
- * Get the coefficient first, and multiply (potentially huge) base
- * experience point of a monster later.
- * </pre>
+ * @brief モンスターのHPをダメージに応じて減算する /
+ * @return モンスターが生きていればfalse、死んだらtrue
  */
-void MonsterDamageProcessor::get_exp_from_mon(monster_type *m_ptr, HIT_POINT exp_dam)
+bool MonsterDamageProcessor::mon_take_hit(concptr note)
 {
-    auto *r_ptr = &r_info[m_ptr->r_idx];
-    if (!monster_is_valid(m_ptr) || is_pet(m_ptr) || this->target_ptr->phase_out) {
-        return;
-    }
+    auto *m_ptr = &this->target_ptr->current_floor_ptr->m_list[this->m_idx];
+    monster_type exp_mon;
+    (void)COPY(&exp_mon, m_ptr, monster_type);
 
-    /*
-     * - Ratio of monster's level to player's level effects
-     * - Varying speed effects
-     * - Get a fraction in proportion of damage point
-     */
-    auto new_exp = r_ptr->level * SPEED_TO_ENERGY(m_ptr->mspeed) * exp_dam;
-    auto new_exp_frac = 0U;
-    auto div_h = 0;
-    auto div_l = (uint)((this->target_ptr->max_plv + 2) * SPEED_TO_ENERGY(r_ptr->speed));
+    auto exp_dam = (m_ptr->hp > this->dam) ? this->dam : m_ptr->hp;
 
-    /* Use (average maxhp * 2) as a denominator */
-    auto compensation = any_bits(r_ptr->flags1, RF1_FORCE_MAXHP) ? r_ptr->hside * 2 : r_ptr->hside + 1;
-    s64b_mul(&div_h, &div_l, 0, r_ptr->hdice * (ironman_nightmare ? 2 : 1) * compensation);
+    this->get_exp_from_mon(&exp_mon, exp_dam);
+    if (this->genocide_chaos_patron()) {
+        return true;
+    }
 
-    /* Special penalty in the wilderness */
-    if (!this->target_ptr->current_floor_ptr->dun_level && (none_bits(r_ptr->flags8, RF8_WILD_ONLY) || none_bits(r_ptr->flags1, RF1_UNIQUE))) {
-        s64b_mul(&div_h, &div_l, 0, 5);
+    m_ptr->hp -= this->dam;
+    m_ptr->dealt_damage += this->dam;
+    if (m_ptr->dealt_damage > m_ptr->max_maxhp * 100) {
+        m_ptr->dealt_damage = m_ptr->max_maxhp * 100;
     }
 
-    /* Do ENERGY_DIVISION first to prevent overflaw */
-    s64b_div(&new_exp, &new_exp_frac, div_h, div_l);
+    if (current_world_ptr->wizard) {
+        msg_format(_("合計%d/%dのダメージを与えた。", "You do %d (out of %d) damage."), m_ptr->dealt_damage, m_ptr->maxhp);
+    }
 
-    /* Special penalty for mutiply-monster */
-    if (any_bits(r_ptr->flags2, RF2_MULTIPLY) || (m_ptr->r_idx == MON_DAWN)) {
-        int monnum_penarty = r_ptr->r_akills / 400;
-        if (monnum_penarty > 8) {
-            monnum_penarty = 8;
+    auto *r_ptr = &r_info[m_ptr->r_idx];
+    if (m_ptr->hp < 0) {
+        this->death_special_flag_monster();
+        if (r_ptr->r_akills < MAX_SHORT) {
+            r_ptr->r_akills++;
         }
 
-        while (monnum_penarty--) {
-            /* Divide by 4 */
-            s64b_rshift(&new_exp, &new_exp_frac, 2);
+        this->increase_kill_numbers();
+        GAME_TEXT m_name[MAX_NLEN];
+        monster_desc(this->target_ptr, m_name, m_ptr, MD_TRUE_NAME);
+        this->death_amberites(m_name);
+        this->dying_scream(m_name);
+        this->change_virtue_non_beginner();
+        this->change_virtue_unique();
+        if (m_ptr->r_idx == MON_BEGGAR || m_ptr->r_idx == MON_LEPER) {
+            chg_virtue(this->target_ptr, V_COMPASSION, -1);
+        }
+
+        this->change_virtue_good_evil();
+        if (any_bits(r_ptr->flags3, RF3_UNDEAD) && any_bits(r_ptr->flags1, RF1_UNIQUE)) {
+            chg_virtue(this->target_ptr, V_VITALITY, 2);
         }
+
+        this->change_virtue_revenge();
+        if (any_bits(r_ptr->flags2, RF2_MULTIPLY) && (r_ptr->r_akills > 1000) && one_in_(10)) {
+            chg_virtue(this->target_ptr, V_VALOUR, -1);
+        }
+
+        this->change_virtue_wild_thief();
+        this->change_virtue_good_animal();
+        if (any_bits(r_ptr->flags1, RF1_UNIQUE) && record_destroy_uniq) {
+            char note_buf[160];
+            sprintf(note_buf, "%s%s", r_ptr->name.c_str(), m_ptr->mflag2.has(MFLAG2::CLONED) ? _("(クローン)", "(Clone)") : "");
+            exe_write_diary(this->target_ptr, DIARY_UNIQUE, 0, note_buf);
+        }
+
+        sound(SOUND_KILL);
+        if (note != nullptr) {
+            msg_format("%^s%s", m_name, note);
+        } else if (!m_ptr->ml) {
+            auto mes = is_echizen(this->target_ptr) ? _("せっかくだから%sを殺した。", "Because it's time, you have killed %s.")
+                                                    : _("%sを殺した。", "You have killed %s.");
+            msg_format(mes, m_name);
+        } else if (!monster_living(m_ptr->r_idx)) {
+            bool explode = false;
+            for (auto i = 0; i < 4; i++) {
+                if (r_ptr->blow[i].method == RBM_EXPLODE) {
+                    explode = true;
+                }
+            }
+
+            if (explode) {
+                msg_format(_("%sは爆発して粉々になった。", "%^s explodes into tiny shreds."), m_name);
+            } else {
+                auto mes = is_echizen(this->target_ptr) ? _("せっかくだから%sを殺した。", "Because it's time, you have destroyed %s.")
+                                                        : _("%sを殺した。", "You have destoryed %s.");
+                msg_format(mes, m_name);
+            }
+        } else {
+            auto mes = is_echizen(this->target_ptr) ? _("せっかくだから%sを殺した。", "Because it's time, you have slained %s.")
+                                                    : _("%sを殺した。", "You have slained %s.");
+            msg_format(mes, m_name);
+        }
+
+        if (any_bits(r_ptr->flags1, RF1_UNIQUE) && m_ptr->mflag2.has_not(MFLAG2::CLONED) && !vanilla_town) {
+            for (auto i = 0; i < MAX_BOUNTY; i++) {
+                if ((current_world_ptr->bounty_r_idx[i] == m_ptr->r_idx) && m_ptr->mflag2.has_not(MFLAG2::CHAMELEON)) {
+                    msg_format(_("%sの首には賞金がかかっている。", "There is a price on %s's head."), m_name);
+                    break;
+                }
+            }
+        }
+
+        monster_death(this->target_ptr, this->m_idx, true);
+        this->summon_special_unique();
+        this->get_exp_from_mon(&exp_mon, exp_mon.max_maxhp * 2);
+        *this->fear = false;
+        return true;
     }
 
-    /* Special penalty for rest_and_shoot exp scum */
-    if ((m_ptr->dealt_damage > m_ptr->max_maxhp) && (m_ptr->hp >= 0)) {
-        int over_damage = m_ptr->dealt_damage / m_ptr->max_maxhp;
-        if (over_damage > 32) {
-            over_damage = 32;
+    if (monster_fear_remaining(m_ptr) && (this->dam > 0)) {
+        auto fear_remining = monster_fear_remaining(m_ptr) - randint1(this->dam);
+        if (set_monster_monfear(this->target_ptr, this->m_idx, fear_remining)) {
+            *this->fear = false;
         }
+    }
 
-        while (over_damage--) {
-            /* 9/10 for once */
-            s64b_mul(&new_exp, &new_exp_frac, 0, 9);
-            s64b_div(&new_exp, &new_exp_frac, 0, 10);
+    // 恐怖の更なる加算.
+    if (!monster_fear_remaining(m_ptr) && none_bits(r_ptr->flags3, RF3_NO_FEAR)) {
+        int percentage = (100L * m_ptr->hp) / m_ptr->maxhp;
+        if ((randint1(10) >= percentage) || ((this->dam >= m_ptr->hp) && (randint0(100) < 80))) {
+            *this->fear = true;
+            (void)set_monster_monfear(
+                this->target_ptr, this->m_idx, (randint1(10) + (((this->dam >= m_ptr->hp) && (percentage > 7)) ? 20 : ((11 - percentage) * 5))));
         }
     }
 
-    s64b_mul(&new_exp, &new_exp_frac, 0, r_ptr->mexp);
-    gain_exp_64(this->target_ptr, new_exp, new_exp_frac);
+    return false;
 }
 
-/*!
- * @brief モンスターのHPをダメージに応じて減算する /
- * @return モンスターが生きていればfalse、死んだらtrue
- */
-bool MonsterDamageProcessor::mon_take_hit(concptr note)
+bool MonsterDamageProcessor::genocide_chaos_patron()
 {
     auto *m_ptr = &this->target_ptr->current_floor_ptr->m_list[this->m_idx];
-    auto *r_ptr = &r_info[m_ptr->r_idx];
+    if (!monster_is_valid(m_ptr)) {
+        this->m_idx = 0;
+    }
 
-    /* Innocent until proven otherwise */
-    auto innocent = true;
-    auto thief = false;
-    int i;
+    this->set_redraw();
+    (void)set_monster_csleep(this->target_ptr, this->m_idx, 0);
+    if (this->target_ptr->special_defense & NINJA_S_STEALTH) {
+        set_superstealth(this->target_ptr, false);
+    }
 
-    monster_type exp_mon;
-    (void)COPY(&exp_mon, m_ptr, monster_type);
+    return this->m_idx == 0;
+}
 
-    auto exp_dam = (m_ptr->hp > this->dam) ? this->dam : m_ptr->hp;
+/*
+ * @brief たぬき、カメレオン、ナズグル、ユニークの死亡時処理
+ * @param m_ptr ダメージを与えたモンスターの構造体参照ポインタ
+ */
+void MonsterDamageProcessor::death_special_flag_monster()
+{
+    auto *m_ptr = &this->target_ptr->current_floor_ptr->m_list[this->m_idx];
+    auto r_idx = m_ptr->r_idx;
+    auto *r_ptr = &r_info[r_idx];
+    if (any_bits(r_info[r_idx].flags7, RF7_TANUKI)) {
+        r_ptr = &r_info[r_idx];
+        m_ptr->ap_r_idx = r_idx;
+        if (r_ptr->r_sights < MAX_SHORT) {
+            r_ptr->r_sights++;
+        }
+    }
 
-    this->get_exp_from_mon(&exp_mon, exp_dam);
+    if (m_ptr->mflag2.has(MFLAG2::CHAMELEON)) {
+        r_ptr = real_r_ptr(m_ptr);
+        if (r_ptr->r_sights < MAX_SHORT) {
+            r_ptr->r_sights++;
+        }
+    }
 
-    /* Genocided by chaos patron */
-    if (!monster_is_valid(m_ptr)) {
-        this->m_idx = 0;
+    if (m_ptr->mflag2.has(MFLAG2::CLONED)) {
+        return;
     }
 
-    /* Redraw (later) if needed */
-    if (this->target_ptr->health_who == this->m_idx) {
-        this->target_ptr->redraw |= PR_HEALTH;
+    if (any_bits(r_ptr->flags7, RF7_NAZGUL)) {
+        r_ptr->max_num--;
+        return;
     }
 
-    if (this->target_ptr->riding == this->m_idx) {
-        this->target_ptr->redraw |= PR_UHEALTH;
+    if (none_bits(r_ptr->flags1, RF1_UNIQUE)) {
+        return;
     }
 
-    (void)set_monster_csleep(this->target_ptr, this->m_idx, 0);
+    this->death_unique_monster((monster_race_type)r_idx);
+}
 
-    /* Hack - Cancel any special player stealth magics. -LM- */
-    if (this->target_ptr->special_defense & NINJA_S_STEALTH) {
-        set_superstealth(this->target_ptr, false);
+/*
+ * @brief ユニークの死亡処理
+ * @param r_idx 死亡したユニークの種族番号
+ */
+void MonsterDamageProcessor::death_unique_monster(monster_race_type r_idx)
+{
+    r_info[r_idx].max_num = 0;
+    std::vector<monster_race_type> combined_uniques;
+    if (!check_combined_unique(r_idx, &combined_uniques)) {
+        return;
     }
 
-    /* Genocided by chaos patron */
-    if (this->m_idx == 0) {
-        return true;
+    std::vector<std::tuple<monster_race_type, monster_race_type, monster_race_type>> uniques;
+    const int one_unit = 3;
+    for (auto i = 0U; i < combined_uniques.size(); i += one_unit) {
+        auto unique = std::make_tuple(combined_uniques[i], combined_uniques[i + 1], combined_uniques[i + 2]);
+        uniques.push_back(unique);
     }
 
-    m_ptr->hp -= this->dam;
-    m_ptr->dealt_damage += this->dam;
+    this->death_combined_uniques(r_idx, &uniques);
+}
 
-    if (m_ptr->dealt_damage > m_ptr->max_maxhp * 100) {
-        m_ptr->dealt_damage = m_ptr->max_maxhp * 100;
-    }
+/*
+ * @brief 死亡したモンスターが分裂/合体を行う特殊ユニークか否かの判定処理
+ * @param r_idx 死亡したモンスターの種族番号
+ * @param united_uniques 分裂/合体を行う特殊ユニーク
+ * @details 合体後、合体前1、合体前2 の順にpush_backすること
+ */
+bool MonsterDamageProcessor::check_combined_unique(const monster_race_type r_idx, std::vector<monster_race_type> *combined_uniques)
+{
+    combined_uniques->push_back(MON_BANORLUPART);
+    combined_uniques->push_back(MON_BANOR);
+    combined_uniques->push_back(MON_LUPART);
 
-    if (current_world_ptr->wizard) {
-        msg_format(_("合計%d/%dのダメージを与えた。", "You do %d (out of %d) damage."), m_ptr->dealt_damage, m_ptr->maxhp);
+    for (const auto &unique : *combined_uniques) {
+        if (r_idx == unique) {
+            return true;
+        }
     }
 
-    /* It is dead now */
-    if (m_ptr->hp < 0) {
-        GAME_TEXT m_name[MAX_NLEN];
+    return false;
+}
 
-        if (r_info[m_ptr->r_idx].flags7 & RF7_TANUKI) {
-            /* You might have unmasked Tanuki first time */
-            r_ptr = &r_info[m_ptr->r_idx];
-            m_ptr->ap_r_idx = m_ptr->r_idx;
-            if (r_ptr->r_sights < MAX_SHORT)
-                r_ptr->r_sights++;
+/*
+ * @brief 分裂/合体を行う特殊ユニークの死亡処理
+ * @param m_ptr ダメージを与えたモンスターの構造体参照ポインタ
+ * @uniques 分裂/合体を行う特殊ユニークのリスト
+ */
+void MonsterDamageProcessor::death_combined_uniques(
+    const monster_race_type r_idx, std::vector<std::tuple<monster_race_type, monster_race_type, monster_race_type>> *combined_uniques)
+{
+    for (const auto &unique : *combined_uniques) {
+        auto united = (monster_race_type)0;
+        auto split1 = (monster_race_type)0;
+        auto split2 = (monster_race_type)0;
+        std::tie(united, split1, split2) = unique;
+        if ((r_idx == split1) || (r_idx == split2)) {
+            r_info[united].max_num = 0;
+            r_info[united].r_pkills++;
+            r_info[united].r_akills++;
+            if (r_info[united].r_tkills < MAX_SHORT) {
+                r_info[united].r_tkills++;
+            }
+
+            continue;
         }
 
-        if (m_ptr->mflag2.has(MFLAG2::CHAMELEON)) {
-            /* You might have unmasked Chameleon first time */
-            r_ptr = real_r_ptr(m_ptr);
-            if (r_ptr->r_sights < MAX_SHORT)
-                r_ptr->r_sights++;
+        if (r_idx != united) {
+            continue;
         }
 
-        if (m_ptr->mflag2.has_not(MFLAG2::CLONED)) {
-            /* When the player kills a Unique, it stays dead */
-            if (r_ptr->flags1 & RF1_UNIQUE) {
-                r_ptr->max_num = 0;
-
-                /* Mega-Hack -- Banor & Lupart */
-                if ((m_ptr->r_idx == MON_BANOR) || (m_ptr->r_idx == MON_LUPART)) {
-                    r_info[MON_BANORLUPART].max_num = 0;
-                    r_info[MON_BANORLUPART].r_pkills++;
-                    r_info[MON_BANORLUPART].r_akills++;
-                    if (r_info[MON_BANORLUPART].r_tkills < MAX_SHORT)
-                        r_info[MON_BANORLUPART].r_tkills++;
-                } else if (m_ptr->r_idx == MON_BANORLUPART) {
-                    r_info[MON_BANOR].max_num = 0;
-                    r_info[MON_BANOR].r_pkills++;
-                    r_info[MON_BANOR].r_akills++;
-                    if (r_info[MON_BANOR].r_tkills < MAX_SHORT)
-                        r_info[MON_BANOR].r_tkills++;
-                    r_info[MON_LUPART].max_num = 0;
-                    r_info[MON_LUPART].r_pkills++;
-                    r_info[MON_LUPART].r_akills++;
-                    if (r_info[MON_LUPART].r_tkills < MAX_SHORT)
-                        r_info[MON_LUPART].r_tkills++;
-                }
-            }
+        r_info[split1].max_num = 0;
+        r_info[split1].r_pkills++;
+        r_info[split1].r_akills++;
+        if (r_info[split1].r_tkills < MAX_SHORT) {
+            r_info[split1].r_tkills++;
+        }
 
-            /* When the player kills a Nazgul, it stays dead */
-            else if (r_ptr->flags7 & RF7_NAZGUL)
-                r_ptr->max_num--;
+        r_info[split2].max_num = 0;
+        r_info[split2].r_pkills++;
+        r_info[split2].r_akills++;
+        if (r_info[split2].r_tkills < MAX_SHORT) {
+            r_info[split2].r_tkills++;
         }
+    }
+}
 
-        /* Count all monsters killed */
-        if (r_ptr->r_akills < MAX_SHORT)
-            r_ptr->r_akills++;
+void MonsterDamageProcessor::increase_kill_numbers()
+{
+    auto *m_ptr = &this->target_ptr->current_floor_ptr->m_list[this->m_idx];
+    auto *r_ptr = &r_info[m_ptr->r_idx];
+    if (((m_ptr->ml == 0) || this->target_ptr->image) && none_bits(r_ptr->flags1, RF1_UNIQUE)) {
+        return;
+    }
 
-        /* Recall even invisible uniques or winners */
-        if ((m_ptr->ml && !this->target_ptr->image) || (r_ptr->flags1 & RF1_UNIQUE)) {
-            /* Count kills this life */
-            if (m_ptr->mflag2.has(MFLAG2::KAGE) && (r_info[MON_KAGE].r_pkills < MAX_SHORT))
-                r_info[MON_KAGE].r_pkills++;
-            else if (r_ptr->r_pkills < MAX_SHORT)
-                r_ptr->r_pkills++;
-
-            /* Count kills in all lives */
-            if (m_ptr->mflag2.has(MFLAG2::KAGE) && (r_info[MON_KAGE].r_tkills < MAX_SHORT))
-                r_info[MON_KAGE].r_tkills++;
-            else if (r_ptr->r_tkills < MAX_SHORT)
-                r_ptr->r_tkills++;
-
-            /* Hack -- Auto-recall */
-            monster_race_track(this->target_ptr, m_ptr->ap_r_idx);
-        }
+    if (m_ptr->mflag2.has(MFLAG2::KAGE) && (r_info[MON_KAGE].r_pkills < MAX_SHORT)) {
+        r_info[MON_KAGE].r_pkills++;
+    } else if (r_ptr->r_pkills < MAX_SHORT) {
+        r_ptr->r_pkills++;
+    }
 
-        monster_desc(this->target_ptr, m_name, m_ptr, MD_TRUE_NAME);
+    if (m_ptr->mflag2.has(MFLAG2::KAGE) && (r_info[MON_KAGE].r_tkills < MAX_SHORT)) {
+        r_info[MON_KAGE].r_tkills++;
+    } else if (r_ptr->r_tkills < MAX_SHORT) {
+        r_ptr->r_tkills++;
+    }
+
+    monster_race_track(this->target_ptr, m_ptr->ap_r_idx);
+}
 
-        /* Don't kill Amberites */
-        if ((r_ptr->flags3 & RF3_AMBERITE) && one_in_(2)) {
-            int curses = 1 + randint1(3);
-            bool stop_ty = false;
-            int count = 0;
+void MonsterDamageProcessor::death_amberites(GAME_TEXT *m_name)
+{
+    auto *m_ptr = &this->target_ptr->current_floor_ptr->m_list[this->m_idx];
+    auto *r_ptr = &r_info[m_ptr->r_idx];
+    if (none_bits(r_ptr->flags3, RF3_AMBERITE) || one_in_(2)) {
+        return;
+    }
 
-            msg_format(_("%^sは恐ろしい血の呪いをあなたにかけた!", "%^s puts a terrible blood curse on you!"), m_name);
-            curse_equipment(this->target_ptr, 100, 50);
+    auto curses = 1 + randint1(3);
+    auto stop_ty = false;
+    auto count = 0;
+    msg_format(_("%^sは恐ろしい血の呪いをあなたにかけた!", "%^s puts a terrible blood curse on you!"), m_name);
+    curse_equipment(this->target_ptr, 100, 50);
+    do {
+        stop_ty = activate_ty_curse(this->target_ptr, stop_ty, &count);
+    } while (--curses);
+}
 
-            do {
-                stop_ty = activate_ty_curse(this->target_ptr, stop_ty, &count);
-            } while (--curses);
-        }
+void MonsterDamageProcessor::dying_scream(GAME_TEXT *m_name)
+{
+    auto *m_ptr = &this->target_ptr->current_floor_ptr->m_list[this->m_idx];
+    auto *r_ptr = &r_info[m_ptr->r_idx];
+    if (none_bits(r_ptr->flags2, RF2_CAN_SPEAK)) {
+        return;
+    }
 
-        if (r_ptr->flags2 & RF2_CAN_SPEAK) {
-            char line_got[1024];
-            if (!get_rnd_line(_("mondeath_j.txt", "mondeath.txt"), m_ptr->r_idx, line_got)) {
-                msg_format("%^s %s", m_name, line_got);
-            }
+    char line_got[1024];
+    if (!get_rnd_line(_("mondeath_j.txt", "mondeath.txt"), m_ptr->r_idx, line_got)) {
+        msg_format("%^s %s", m_name, line_got);
+    }
 
 #ifdef WORLD_SCORE
-            if (m_ptr->r_idx == MON_SERPENT) {
-                screen_dump = make_screen_dump(this->target_ptr);
-            }
+    if (m_ptr->r_idx == MON_SERPENT) {
+        screen_dump = make_screen_dump(this->target_ptr);
+    }
 #endif
-        }
+}
 
-        if (d_info[this->target_ptr->dungeon_idx].flags.has_not(DF::BEGINNER)) {
-            if (!this->target_ptr->current_floor_ptr->dun_level && !this->target_ptr->ambush_flag && !this->target_ptr->current_floor_ptr->inside_arena) {
-                chg_virtue(this->target_ptr, V_VALOUR, -1);
-            } else if (r_ptr->level > this->target_ptr->current_floor_ptr->dun_level) {
-                if (randint1(10) <= (r_ptr->level - this->target_ptr->current_floor_ptr->dun_level))
-                    chg_virtue(this->target_ptr, V_VALOUR, 1);
-            }
-            if (r_ptr->level > 60) {
-                chg_virtue(this->target_ptr, V_VALOUR, 1);
-            }
-            if (r_ptr->level >= 2 * (this->target_ptr->lev + 1))
-                chg_virtue(this->target_ptr, V_VALOUR, 2);
+void MonsterDamageProcessor::change_virtue_non_beginner()
+{
+    auto *floor_ptr = this->target_ptr->current_floor_ptr;
+    auto *m_ptr = &floor_ptr->m_list[this->m_idx];
+    auto *r_ptr = &r_info[m_ptr->r_idx];
+    if (d_info[this->target_ptr->dungeon_idx].flags.has(DF::BEGINNER)) {
+        return;
+    }
+
+    if ((floor_ptr->dun_level == 0) && !this->target_ptr->ambush_flag && !floor_ptr->inside_arena) {
+        chg_virtue(this->target_ptr, V_VALOUR, -1);
+    } else if (r_ptr->level > floor_ptr->dun_level) {
+        if (randint1(10) <= (r_ptr->level - floor_ptr->dun_level)) {
+            chg_virtue(this->target_ptr, V_VALOUR, 1);
         }
+    }
 
-        if (r_ptr->flags1 & RF1_UNIQUE) {
-            if (r_ptr->flags3 & (RF3_EVIL | RF3_GOOD))
-                chg_virtue(this->target_ptr, V_HARMONY, 2);
+    if (r_ptr->level > 60) {
+        chg_virtue(this->target_ptr, V_VALOUR, 1);
+    }
 
-            if (r_ptr->flags3 & RF3_GOOD) {
-                chg_virtue(this->target_ptr, V_UNLIFE, 2);
-                chg_virtue(this->target_ptr, V_VITALITY, -2);
-            }
+    if (r_ptr->level >= 2 * (this->target_ptr->lev + 1)) {
+        chg_virtue(this->target_ptr, V_VALOUR, 2);
+    }
+}
 
-            if (one_in_(3))
-                chg_virtue(this->target_ptr, V_INDIVIDUALISM, -1);
-        }
+void MonsterDamageProcessor::change_virtue_unique()
+{
+    auto *floor_ptr = this->target_ptr->current_floor_ptr;
+    auto *m_ptr = &floor_ptr->m_list[this->m_idx];
+    auto *r_ptr = &r_info[m_ptr->r_idx];
+    if (none_bits(r_ptr->flags1, RF1_UNIQUE)) {
+        return;
+    }
 
-        if (m_ptr->r_idx == MON_BEGGAR || m_ptr->r_idx == MON_LEPER) {
-            chg_virtue(this->target_ptr, V_COMPASSION, -1);
-        }
+    if (any_bits(r_ptr->flags3, RF3_EVIL | RF3_GOOD)) {
+        chg_virtue(this->target_ptr, V_HARMONY, 2);
+    }
 
-        if ((r_ptr->flags3 & RF3_GOOD) && ((r_ptr->level) / 10 + (3 * this->target_ptr->current_floor_ptr->dun_level) >= randint1(100)))
-            chg_virtue(this->target_ptr, V_UNLIFE, 1);
-
-        if (r_ptr->d_char == 'A') {
-            if (r_ptr->flags1 & RF1_UNIQUE)
-                chg_virtue(this->target_ptr, V_FAITH, -2);
-            else if ((r_ptr->level) / 10 + (3 * this->target_ptr->current_floor_ptr->dun_level) >= randint1(100)) {
-                if (r_ptr->flags3 & RF3_GOOD)
-                    chg_virtue(this->target_ptr, V_FAITH, -1);
-                else
-                    chg_virtue(this->target_ptr, V_FAITH, 1);
-            }
-        } else if (r_ptr->flags3 & RF3_DEMON) {
-            if (r_ptr->flags1 & RF1_UNIQUE)
-                chg_virtue(this->target_ptr, V_FAITH, 2);
-            else if ((r_ptr->level) / 10 + (3 * this->target_ptr->current_floor_ptr->dun_level) >= randint1(100))
-                chg_virtue(this->target_ptr, V_FAITH, 1);
-        }
+    if (any_bits(r_ptr->flags3, RF3_GOOD)) {
+        chg_virtue(this->target_ptr, V_UNLIFE, 2);
+        chg_virtue(this->target_ptr, V_VITALITY, -2);
+    }
 
-        if ((r_ptr->flags3 & RF3_UNDEAD) && (r_ptr->flags1 & RF1_UNIQUE))
-            chg_virtue(this->target_ptr, V_VITALITY, 2);
+    if (one_in_(3)) {
+        chg_virtue(this->target_ptr, V_INDIVIDUALISM, -1);
+    }
+}
 
-        if (r_ptr->r_deaths) {
-            if (r_ptr->flags1 & RF1_UNIQUE) {
-                chg_virtue(this->target_ptr, V_HONOUR, 10);
-            } else if ((r_ptr->level) / 10 + (2 * this->target_ptr->current_floor_ptr->dun_level) >= randint1(100)) {
-                chg_virtue(this->target_ptr, V_HONOUR, 1);
-            }
+/*
+ * @brief 攻撃を与えたモンスターが天使か悪魔だった場合、徳を変化させる
+ * @details 天使かつ悪魔だった場合、天使であることが優先される
+ */
+void MonsterDamageProcessor::change_virtue_good_evil()
+{
+    auto *floor_ptr = this->target_ptr->current_floor_ptr;
+    auto *m_ptr = &floor_ptr->m_list[this->m_idx];
+    auto *r_ptr = &r_info[m_ptr->r_idx];
+    if (any_bits(r_ptr->flags3, RF3_GOOD) && ((r_ptr->level) / 10 + (3 * floor_ptr->dun_level) >= randint1(100))) {
+        chg_virtue(this->target_ptr, V_UNLIFE, 1);
+    }
+
+    if (any_bits(r_ptr->flags3, RF3_ANGEL)) {
+        if (any_bits(r_ptr->flags1, RF1_UNIQUE)) {
+            chg_virtue(this->target_ptr, V_FAITH, -2);
+        } else if ((r_ptr->level) / 10 + (3 * floor_ptr->dun_level) >= randint1(100)) {
+            auto change_value = any_bits(r_ptr->flags3, RF3_GOOD) ? -1 : 1;
+            chg_virtue(this->target_ptr, V_FAITH, change_value);
         }
-        if ((r_ptr->flags2 & RF2_MULTIPLY) && (r_ptr->r_akills > 1000) && one_in_(10)) {
-            chg_virtue(this->target_ptr, V_VALOUR, -1);
+
+        return;
+    }
+
+    if (any_bits(r_ptr->flags3, RF3_DEMON)) {
+        if (any_bits(r_ptr->flags1, RF1_UNIQUE)) {
+            chg_virtue(this->target_ptr, V_FAITH, 2);
+        } else if ((r_ptr->level) / 10 + (3 * floor_ptr->dun_level) >= randint1(100)) {
+            chg_virtue(this->target_ptr, V_FAITH, 1);
         }
+    }
+}
 
-        for (i = 0; i < 4; i++) {
-            if (r_ptr->blow[i].d_dice != 0)
-                innocent = false; /* Murderer! */
+/*
+ * @brief 過去に@を殺したことがあるユニークにリゾンべを果たせたら徳を上げる
+ */
+void MonsterDamageProcessor::change_virtue_revenge()
+{
+    auto *floor_ptr = this->target_ptr->current_floor_ptr;
+    auto *m_ptr = &floor_ptr->m_list[this->m_idx];
+    auto *r_ptr = &r_info[m_ptr->r_idx];
+    if (r_ptr->r_deaths == 0) {
+        return;
+    }
 
-            if ((r_ptr->blow[i].effect == RBE_EAT_ITEM) || (r_ptr->blow[i].effect == RBE_EAT_GOLD))
+    if (any_bits(r_ptr->flags1, RF1_UNIQUE)) {
+        chg_virtue(this->target_ptr, V_HONOUR, 10);
+        return;
+    }
 
-                thief = true; /* Thief! */
-        }
+    if ((r_ptr->level) / 10 + (2 * floor_ptr->dun_level) >= randint1(100)) {
+        chg_virtue(this->target_ptr, V_HONOUR, 1);
+    }
+}
 
-        /* The new law says it is illegal to live in the dungeon */
-        if (r_ptr->level != 0)
+/*
+ * @brief 盗み逃げをするモンスター及び地上のモンスターを攻撃した際に徳を変化させる
+ */
+void MonsterDamageProcessor::change_virtue_wild_thief()
+{
+    auto *floor_ptr = this->target_ptr->current_floor_ptr;
+    auto *m_ptr = &floor_ptr->m_list[this->m_idx];
+    auto *r_ptr = &r_info[m_ptr->r_idx];
+    auto innocent = true;
+    auto thief = false;
+    for (auto i = 0; i < MAX_NUM_BLOWS; i++) {
+        if (r_ptr->blow[i].d_dice != 0) {
             innocent = false;
+        }
 
-        if (thief) {
-            if (r_ptr->flags1 & RF1_UNIQUE)
-                chg_virtue(this->target_ptr, V_JUSTICE, 3);
-            else if (1 + ((r_ptr->level) / 10 + (2 * this->target_ptr->current_floor_ptr->dun_level)) >= randint1(100))
-                chg_virtue(this->target_ptr, V_JUSTICE, 1);
-        } else if (innocent) {
-            chg_virtue(this->target_ptr, V_JUSTICE, -1);
+        if ((r_ptr->blow[i].effect == RBE_EAT_ITEM) || (r_ptr->blow[i].effect == RBE_EAT_GOLD)) {
+            thief = true;
         }
+    }
+
+    if (r_ptr->level > 0) {
+        innocent = false;
+    }
 
-        auto magic_ability_flags = r_ptr->ability_flags;
-        magic_ability_flags.reset(RF_ABILITY_NOMAGIC_MASK);
-        if ((r_ptr->flags3 & RF3_ANIMAL) && !(r_ptr->flags3 & RF3_EVIL) && magic_ability_flags.none()) {
-            if (one_in_(4))
-                chg_virtue(this->target_ptr, V_NATURE, -1);
+    if (thief) {
+        if (any_bits(r_ptr->flags1, RF1_UNIQUE)) {
+            chg_virtue(this->target_ptr, V_JUSTICE, 3);
+            return;
         }
 
-        if ((r_ptr->flags1 & RF1_UNIQUE) && record_destroy_uniq) {
-            char note_buf[160];
-            sprintf(note_buf, "%s%s", r_ptr->name.c_str(), m_ptr->mflag2.has(MFLAG2::CLONED) ? _("(クローン)", "(Clone)") : "");
-            exe_write_diary(this->target_ptr, DIARY_UNIQUE, 0, note_buf);
+        if (1 + ((r_ptr->level) / 10 + (2 * floor_ptr->dun_level)) >= randint1(100)) {
+            chg_virtue(this->target_ptr, V_JUSTICE, 1);
         }
 
-        /* Make a sound */
-        sound(SOUND_KILL);
+        return;
+    }
 
-        /* Death by Missile/Spell attack */
-        if (note) {
-            msg_format("%^s%s", m_name, note);
-        }
+    if (innocent) {
+        chg_virtue(this->target_ptr, V_JUSTICE, -1);
+    }
+}
 
-        /* Death by physical attack -- invisible monster */
-        else if (!m_ptr->ml) {
-#ifdef JP
-            if (is_echizen(this->target_ptr))
-                msg_format("せっかくだから%sを殺した。", m_name);
-            else
-                msg_format("%sを殺した。", m_name);
-#else
-            msg_format("You have killed %s.", m_name);
-#endif
+/*
+ * @brief 邪悪でなく、魔法も持たない動物を攻撃した時に徳を下げる
+ */
+void MonsterDamageProcessor::change_virtue_good_animal()
+{
+    auto *floor_ptr = this->target_ptr->current_floor_ptr;
+    auto *m_ptr = &floor_ptr->m_list[this->m_idx];
+    auto *r_ptr = &r_info[m_ptr->r_idx];
+    auto magic_ability_flags = r_ptr->ability_flags;
+    magic_ability_flags.reset(RF_ABILITY_NOMAGIC_MASK);
+    if (none_bits(r_ptr->flags3, RF3_ANIMAL) || any_bits(r_ptr->flags3, RF3_EVIL) || magic_ability_flags.any()) {
+        return;
+    }
 
-        }
+    if (one_in_(4)) {
+        chg_virtue(this->target_ptr, V_NATURE, -1);
+    }
+}
 
-        /* Death by Physical attack -- non-living monster */
-        else if (!monster_living(m_ptr->r_idx)) {
-            bool explode = false;
+/*!
+ * @brief モンスターに与えたダメージを元に経験値を加算する /
+ * Calculate experience point to be get
+ * @param m_ptr ダメージを与えたモンスターの構造体参照ポインタ
+ * @details
+ * <pre>
+ * Even the 64 bit operation is not big enough to avoid overflaw
+ * unless we carefully choose orders of ENERGY_MULTIPLICATION and ENERGY_DIVISION.
+ * Get the coefficient first, and multiply (potentially huge) base
+ * experience point of a monster later.
+ * </pre>
+ */
+void MonsterDamageProcessor::get_exp_from_mon(monster_type *m_ptr, HIT_POINT exp_dam)
+{
+    auto *r_ptr = &r_info[m_ptr->r_idx];
+    if (!monster_is_valid(m_ptr) || is_pet(m_ptr) || this->target_ptr->phase_out) {
+        return;
+    }
 
-            for (i = 0; i < 4; i++) {
-                if (r_ptr->blow[i].method == RBM_EXPLODE)
-                    explode = true;
-            }
+    /*
+     * - Ratio of monster's level to player's level effects
+     * - Varying speed effects
+     * - Get a fraction in proportion of damage point
+     */
+    auto new_exp = r_ptr->level * SPEED_TO_ENERGY(m_ptr->mspeed) * exp_dam;
+    auto new_exp_frac = 0U;
+    auto div_h = 0;
+    auto div_l = (uint)((this->target_ptr->max_plv + 2) * SPEED_TO_ENERGY(r_ptr->speed));
 
-            /* Special note at death */
-            if (explode)
-                msg_format(_("%sは爆発して粉々になった。", "%^s explodes into tiny shreds."), m_name);
-            else {
-#ifdef JP
-                if (is_echizen(this->target_ptr))
-                    msg_format("せっかくだから%sを倒した。", m_name);
-                else
-                    msg_format("%sを倒した。", m_name);
-#else
-                msg_format("You have destroyed %s.", m_name);
-#endif
-            }
-        }
+    /* Use (average maxhp * 2) as a denominator */
+    auto compensation = any_bits(r_ptr->flags1, RF1_FORCE_MAXHP) ? r_ptr->hside * 2 : r_ptr->hside + 1;
+    s64b_mul(&div_h, &div_l, 0, r_ptr->hdice * (ironman_nightmare ? 2 : 1) * compensation);
 
-        /* Death by Physical attack -- living monster */
-        else {
-#ifdef JP
-            if (is_echizen(this->target_ptr))
-                msg_format("せっかくだから%sを葬り去った。", m_name);
-            else
-                msg_format("%sを葬り去った。", m_name);
-#else
-            msg_format("You have slain %s.", m_name);
-#endif
-        }
-        if ((r_ptr->flags1 & RF1_UNIQUE) && m_ptr->mflag2.has_not(MFLAG2::CLONED) && !vanilla_town) {
-            for (i = 0; i < MAX_BOUNTY; i++) {
-                if ((current_world_ptr->bounty_r_idx[i] == m_ptr->r_idx) && m_ptr->mflag2.has_not(MFLAG2::CHAMELEON)) {
-                    msg_format(_("%sの首には賞金がかかっている。", "There is a price on %s's head."), m_name);
-                    break;
-                }
-            }
-        }
+    /* Special penalty in the wilderness */
+    if (!this->target_ptr->current_floor_ptr->dun_level && (none_bits(r_ptr->flags8, RF8_WILD_ONLY) || none_bits(r_ptr->flags1, RF1_UNIQUE))) {
+        s64b_mul(&div_h, &div_l, 0, 5);
+    }
 
-        /* Generate treasure */
-        monster_death(this->target_ptr, this->m_idx, true);
+    /* Do ENERGY_DIVISION first to prevent overflaw */
+    s64b_div(&new_exp, &new_exp_frac, div_h, div_l);
 
-        // @todo デッドアタック扱いにしてここから削除したい.
-        bool is_special_summon = m_ptr->r_idx == MON_IKETA;
-        is_special_summon |= m_ptr->r_idx == MON_DOPPIO;
-        if (is_special_summon && !(this->target_ptr->current_floor_ptr->inside_arena || this->target_ptr->phase_out)) {
-            POSITION dummy_y = m_ptr->fy;
-            POSITION dummy_x = m_ptr->fx;
-            BIT_FLAGS mode = 0L;
-            if (is_pet(m_ptr))
-                mode |= PM_FORCE_PET;
-
-            MONRACE_IDX new_unique_idx;
-            concptr mes;
-            switch (m_ptr->r_idx) {
-            case MON_IKETA:
-                new_unique_idx = MON_BIKETAL;
-                mes = _("「ハァッハッハッハ!!私がバイケタルだ!!」", "Uwa-hahaha!  *I* am Biketal!");
-                break;
-            case MON_DOPPIO:
-                new_unique_idx = MON_DIAVOLO;
-                mes = _("「これは『試練』だ 過去に打ち勝てという『試練』とオレは受けとった」",
-                    "This is a 'trial'. I took it as a 'trial' to overcome in the past.");
-                break;
-            default: // バグでなければ入らない.
-                new_unique_idx = 0;
-                mes = "";
-                break;
-            }
+    /* Special penalty for mutiply-monster */
+    if (any_bits(r_ptr->flags2, RF2_MULTIPLY) || (m_ptr->r_idx == MON_DAWN)) {
+        int monnum_penarty = r_ptr->r_akills / 400;
+        if (monnum_penarty > 8) {
+            monnum_penarty = 8;
+        }
 
-            delete_monster_idx(this->target_ptr, this->m_idx);
-            if (summon_named_creature(this->target_ptr, 0, dummy_y, dummy_x, new_unique_idx, mode))
-                msg_print(mes);
-        } else {
-            delete_monster_idx(this->target_ptr, this->m_idx);
+        while (monnum_penarty--) {
+            /* Divide by 4 */
+            s64b_rshift(&new_exp, &new_exp_frac, 2);
         }
+    }
 
-        this->get_exp_from_mon(&exp_mon, exp_mon.max_maxhp * 2);
+    /* Special penalty for rest_and_shoot exp scum */
+    if ((m_ptr->dealt_damage > m_ptr->max_maxhp) && (m_ptr->hp >= 0)) {
+        int over_damage = m_ptr->dealt_damage / m_ptr->max_maxhp;
+        if (over_damage > 32) {
+            over_damage = 32;
+        }
 
-        /* Not afraid */
-        *this->fear = false;
+        while (over_damage--) {
+            /* 9/10 for once */
+            s64b_mul(&new_exp, &new_exp_frac, 0, 9);
+            s64b_div(&new_exp, &new_exp_frac, 0, 10);
+        }
+    }
 
-        /* Monster is dead */
-        return true;
+    s64b_mul(&new_exp, &new_exp_frac, 0, r_ptr->mexp);
+    gain_exp_64(this->target_ptr, new_exp, new_exp_frac);
+}
+
+void MonsterDamageProcessor::set_redraw()
+{
+    if (this->target_ptr->health_who == this->m_idx) {
+        this->target_ptr->redraw |= PR_HEALTH;
     }
-    /* Mega-Hack -- Pain cancels fear */
-    if (monster_fear_remaining(m_ptr) && (this->dam > 0)) {
-        /* Cure fear */
-        if (set_monster_monfear(this->target_ptr, this->m_idx, monster_fear_remaining(m_ptr) - randint1(this->dam))) {
-            /* No more fear */
-            *this->fear = false;
-        }
+
+    if (this->target_ptr->riding == this->m_idx) {
+        this->target_ptr->redraw |= PR_UHEALTH;
     }
+}
 
-    /* Sometimes a monster gets scared by damage */
-    if (!monster_fear_remaining(m_ptr) && none_bits(r_ptr->flags3, RF3_NO_FEAR)) {
-        /* Percentage of fully healthy */
-        int percentage = (100L * m_ptr->hp) / m_ptr->maxhp;
+/*
+ * @brief 特定ユニークを倒した時に更にユニークを特殊召喚する処理
+ * @param m_ptr ダメージを与えた特定ユニークの構造体参照ポインタ
+ */
+void MonsterDamageProcessor::summon_special_unique()
+{
+    auto *m_ptr = &this->target_ptr->current_floor_ptr->m_list[this->m_idx];
+    auto *r_ptr = &r_info[m_ptr->r_idx];
+    bool is_special_summon = m_ptr->r_idx == MON_IKETA;
+    is_special_summon |= m_ptr->r_idx == MON_DOPPIO;
+    if (!is_special_summon || this->target_ptr->current_floor_ptr->inside_arena || this->target_ptr->phase_out) {
+        delete_monster_idx(this->target_ptr, this->m_idx);
+        return;
+    }
 
-        /*
-         * Run (sometimes) if at 10% or less of max hit points,
-         * or (usually) when hit for half its current hit points
-         */
-        if ((randint1(10) >= percentage) || ((this->dam >= m_ptr->hp) && (randint0(100) < 80))) {
-            /* Hack -- note fear */
-            *this->fear = true;
+    auto dummy_y = m_ptr->fy;
+    auto dummy_x = m_ptr->fx;
+    auto mode = (BIT_FLAGS)0;
+    if (is_pet(m_ptr)) {
+        mode |= PM_FORCE_PET;
+    }
 
-            /* Hack -- Add some timed fear */
-            (void)set_monster_monfear(this->target_ptr, this->m_idx, (randint1(10) + (((this->dam >= m_ptr->hp) && (percentage > 7)) ? 20 : ((11 - percentage) * 5))));
-        }
+    MONRACE_IDX new_unique_idx;
+    concptr mes;
+    switch (m_ptr->r_idx) {
+    case MON_IKETA:
+        new_unique_idx = MON_BIKETAL;
+        mes = _("「ハァッハッハッハ!!私がバイケタルだ!!」", "Uwa-hahaha!  *I* am Biketal!");
+        break;
+    case MON_DOPPIO:
+        new_unique_idx = MON_DIAVOLO;
+        mes = _("「これは『試練』だ 過去に打ち勝てという『試練』とオレは受けとった」", "This is a 'trial'. I took it as a 'trial' to overcome in the past.");
+        break;
+    default: // バグでなければ入らない.
+        new_unique_idx = 0;
+        mes = "";
+        break;
     }
 
-    /* Not dead yet */
-    return false;
+    delete_monster_idx(this->target_ptr, this->m_idx);
+    if (summon_named_creature(this->target_ptr, 0, dummy_y, dummy_x, new_unique_idx, mode)) {
+        msg_print(mes);
+    }
 }