#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"
#include "util/bit-flags-calculator.h"
#include "view/display-messages.h"
#include "world/world.h"
+#include <algorithm>
/*
* @brief コンストラクタ
}
/*!
- * @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);
+ }
}