/*!
- * @brief 防具系のアイテムを強化して(恐らく床に)生成する処理
- * @date 2020/06/02
+ * @brief 武器でも防具でもアクセサリでもない、その他のアイテム群を生成・強化する処理
+ * @date 2022/02/23
* @author Hourier
- * @todo ちょっと長い。要分割
+ * @details 他との兼ね合いでEnchanterとなっているが、油つぼ・人形・死体・像は生成のみで強化はしない
*/
#include "object-enchant/others/apply-magic-others.h"
#include "artifact/random-art-generator.h"
#include "game-option/cheat-options.h"
#include "inventory/inventory-slot-types.h"
+#include "monster-floor/place-monster-types.h"
#include "monster-race/monster-race-hook.h"
#include "monster-race/monster-race.h"
-#include "monster-race/race-flags9.h"
#include "monster-race/race-indice-types.h"
#include "monster/monster-list.h"
#include "monster/monster-util.h"
#include "object-enchant/object-ego.h"
#include "object-enchant/tr-types.h"
#include "object-enchant/trc-types.h"
-#include "object/object-kind.h"
+#include "object/tval-types.h"
#include "perception/object-perception.h"
#include "sv-definition/sv-lite-types.h"
#include "sv-definition/sv-other-types.h"
+#include "system/baseitem-info.h"
#include "system/floor-type-definition.h"
-#include "system/monster-race-definition.h"
-#include "system/object-type-definition.h"
+#include "system/item-entity.h"
+#include "system/monster-race-info.h"
#include "system/player-type-definition.h"
#include "util/bit-flags-calculator.h"
#include "view/display-messages.h"
+#include <unordered_map>
/*!
- * @brief その他雑多のオブジェクトに生成ランクごとの強化を与えるサブルーチン
- * Apply magic to an item known to be "boring"
+ * @brief コンストラクタ
* @param player_ptr プレイヤーへの参照ポインタ
- * @param o_ptr 強化を与えたいオブジェクトの構造体参照ポインタ
+ * @param o_ptr 強化を与えたい/生成したいオブジェクトの構造体参照ポインタ
* @param power 生成ランク
* @details power > 2はデバッグ専用.
*/
-void apply_magic_others(PlayerType *player_ptr, ObjectType *o_ptr, int power)
+OtherItemsEnchanter::OtherItemsEnchanter(PlayerType *player_ptr, ItemEntity *o_ptr)
+ : player_ptr(player_ptr)
+ , o_ptr(o_ptr)
{
- object_kind *k_ptr = &k_info[o_ptr->k_idx];
+}
- floor_type *floor_ptr = player_ptr->current_floor_ptr;
- switch (o_ptr->tval) {
- case ItemKindType::WHISTLE: {
+/*!
+ * @brief その他雑多のオブジェクトに生成ランクごとの強化を与える
+ * @details power > 2はデバッグ専用.
+ */
+void OtherItemsEnchanter::apply_magic()
+{
+ const auto tval = this->o_ptr->bi_key.tval();
+ switch (tval) {
+ case ItemKindType::FLASK:
+ this->o_ptr->fuel = this->o_ptr->pval;
+ this->o_ptr->pval = 0;
break;
- }
- case ItemKindType::FLASK: {
- o_ptr->xtra4 = o_ptr->pval;
- o_ptr->pval = 0;
+ case ItemKindType::WAND:
+ case ItemKindType::STAFF:
+ this->enchant_wand_staff();
break;
- }
- case ItemKindType::LITE: {
- if (o_ptr->sval == SV_LITE_TORCH) {
- if (o_ptr->pval > 0)
- o_ptr->xtra4 = randint1(o_ptr->pval);
- o_ptr->pval = 0;
- }
-
- if (o_ptr->sval == SV_LITE_LANTERN) {
- if (o_ptr->pval > 0)
- o_ptr->xtra4 = randint1(o_ptr->pval);
- o_ptr->pval = 0;
- }
-
- if (power > 2) {
- become_random_artifact(player_ptr, o_ptr, false);
- } else if ((power == 2) || ((power == 1) && one_in_(3))) {
- while (!o_ptr->name2) {
- while (true) {
- bool okay_flag = true;
-
- o_ptr->name2 = get_random_ego(INVEN_LITE, true);
-
- switch (o_ptr->name2) {
- case EGO_LITE_LONG:
- if (o_ptr->sval == SV_LITE_FEANOR)
- okay_flag = false;
- }
-
- if (okay_flag)
- break;
- }
- }
- } else if (power == -2) {
- o_ptr->name2 = get_random_ego(INVEN_LITE, false);
- switch (o_ptr->name2) {
- case EGO_LITE_DARKNESS:
- o_ptr->xtra4 = 0;
-
- if (o_ptr->sval == SV_LITE_TORCH) {
- o_ptr->art_flags.set(TR_LITE_M1);
- } else if (o_ptr->sval == SV_LITE_LANTERN) {
- o_ptr->art_flags.set(TR_LITE_M2);
- } else if (o_ptr->sval == SV_LITE_FEANOR) {
- o_ptr->art_flags.set(TR_LITE_M3);
- }
- break;
- }
- }
-
+ case ItemKindType::ROD:
+ this->o_ptr->pval = this->o_ptr->get_baseitem().pval;
break;
- }
- case ItemKindType::WAND:
- case ItemKindType::STAFF: {
- /* The wand or staff gets a number of initial charges equal
- * to between 1/2 (+1) and the full object kind's pval. -LM-
- */
- o_ptr->pval = k_ptr->pval / 2 + randint1((k_ptr->pval + 1) / 2);
+ case ItemKindType::CAPTURE:
+ this->o_ptr->pval = 0;
+ object_aware(this->player_ptr, this->o_ptr);
+ object_known(this->o_ptr);
break;
- }
- case ItemKindType::ROD: {
- o_ptr->pval = k_ptr->pval;
+ case ItemKindType::FIGURINE:
+ this->generate_figurine();
break;
- }
- case ItemKindType::CAPTURE: {
- o_ptr->pval = 0;
- object_aware(player_ptr, o_ptr);
- object_known(o_ptr);
+ case ItemKindType::CORPSE:
+ this->generate_corpse();
+ break;
+ case ItemKindType::STATUE:
+ this->generate_statue();
+ break;
+ case ItemKindType::CHEST:
+ this->generate_chest();
+ break;
+ default:
break;
}
- case ItemKindType::FIGURINE: {
- PARAMETER_VALUE i = 1;
- int check;
- monster_race *r_ptr;
- while (true) {
- i = randint1(r_info.size() - 1);
-
- if (!item_monster_okay(player_ptr, i))
- continue;
- if (i == MON_TSUCHINOKO)
- continue;
-
- r_ptr = &r_info[i];
- check = (floor_ptr->dun_level < r_ptr->level) ? (r_ptr->level - floor_ptr->dun_level) : 0;
- if (!r_ptr->rarity)
- continue;
- if (r_ptr->rarity > 100)
- continue;
- if (randint0(check))
- continue;
-
- break;
+}
+
+/*
+ * @brief 杖を強化する
+ * The wand or staff gets a number of initial charges equal
+ * to between 1/2 (+1) and the full object kind's pval.
+ */
+void OtherItemsEnchanter::enchant_wand_staff()
+{
+ const auto &baseitem = this->o_ptr->get_baseitem();
+ this->o_ptr->pval = baseitem.pval / 2 + randint1((baseitem.pval + 1) / 2);
+}
+
+/*
+ * @brief ランダムに選択したモンスター種族IDからその人形を作る
+ * @details
+ * ツチノコの人形は作らない
+ * レアリティが1~100のものだけ生成対象になる
+ * レベルの高い人形ほど生成されにくい
+ * たまに呪われる
+ */
+void OtherItemsEnchanter::generate_figurine()
+{
+ auto *floor_ptr = this->player_ptr->current_floor_ptr;
+ MonsterRaceId r_idx;
+ while (true) {
+ r_idx = MonsterRace::pick_one_at_random();
+ if (!item_monster_okay(this->player_ptr, r_idx) || (r_idx == MonsterRaceId::TSUCHINOKO)) {
+ continue;
}
- o_ptr->pval = i;
- if (one_in_(6))
- o_ptr->curse_flags.set(CurseTraitType::CURSED);
+ auto *r_ptr = &monraces_info[r_idx];
+ auto check = (floor_ptr->dun_level < r_ptr->level) ? (r_ptr->level - floor_ptr->dun_level) : 0;
+ if ((r_ptr->rarity == 0) || (r_ptr->rarity > 100) || (randint0(check) > 0)) {
+ continue;
+ }
break;
}
- case ItemKindType::CORPSE: {
- PARAMETER_VALUE i = 1;
- int check;
- uint32_t match = 0;
- monster_race *r_ptr;
- if (o_ptr->sval == SV_SKELETON) {
- match = RF9_DROP_SKELETON;
- } else if (o_ptr->sval == SV_CORPSE) {
- match = RF9_DROP_CORPSE;
+
+ this->o_ptr->pval = enum2i(r_idx);
+ if (one_in_(6)) {
+ this->o_ptr->curse_flags.set(CurseTraitType::CURSED);
+ }
+}
+
+/*
+ * @brief ランダムに選択したモンスター種族IDからその死体/骨を作る
+ * @details
+ * そもそも死体も骨も落とさないモンスターは対象外
+ * ユニークやあやしい影等、そこらに落ちている死体としてふさわしくないものは弾く
+ * レアリティが1~100のものだけ生成対象になる (はず)
+ * レベルの高い死体/骨ほど生成されにくい
+ */
+void OtherItemsEnchanter::generate_corpse()
+{
+ const std::unordered_map<int, MonsterDropType> match = {
+ { SV_SKELETON, MonsterDropType::DROP_SKELETON },
+ { SV_CORPSE, MonsterDropType::DROP_CORPSE },
+ };
+
+ get_mon_num_prep(this->player_ptr, item_monster_okay, nullptr);
+ auto *floor_ptr = this->player_ptr->current_floor_ptr;
+ MonsterRaceId r_idx;
+ while (true) {
+ r_idx = get_mon_num(this->player_ptr, 0, floor_ptr->dun_level, PM_NONE);
+ auto &r_ref = monraces_info[r_idx];
+ auto check = (floor_ptr->dun_level < r_ref.level) ? (r_ref.level - floor_ptr->dun_level) : 0;
+ const auto sval = this->o_ptr->bi_key.sval();
+ if (!sval.has_value()) {
+ continue;
}
- get_mon_num_prep(player_ptr, item_monster_okay, nullptr);
- while (true) {
- i = get_mon_num(player_ptr, 0, floor_ptr->dun_level, 0);
- r_ptr = &r_info[i];
- check = (floor_ptr->dun_level < r_ptr->level) ? (r_ptr->level - floor_ptr->dun_level) : 0;
- if (!r_ptr->rarity)
- continue;
- if (!(r_ptr->flags9 & match))
- continue;
- if (randint0(check))
- continue;
-
- break;
+ if ((r_ref.rarity == 0) || (match.find(sval.value()) != match.end() && r_ref.drop_flags.has_not(match.at(sval.value()))) || (randint0(check) > 0)) {
+ continue;
}
- o_ptr->pval = i;
- object_aware(player_ptr, o_ptr);
- object_known(o_ptr);
break;
}
- case ItemKindType::STATUE: {
- PARAMETER_VALUE i = 1;
- monster_race *r_ptr;
- while (true) {
- i = randint1(r_info.size() - 1);
- r_ptr = &r_info[i];
- if (!r_ptr->rarity)
- continue;
- break;
- }
+ this->o_ptr->pval = enum2i(r_idx);
+ object_aware(this->player_ptr, this->o_ptr);
+ object_known(this->o_ptr);
+}
- o_ptr->pval = i;
- if (cheat_peek) {
- msg_format(_("%sの像", "Statue of %s"), r_ptr->name.c_str());
+/*
+ * @brief ランダムに選択したモンスター種族IDからその像を作る
+ * @details レアリティが1以上のものだけ生成対象になる
+ */
+void OtherItemsEnchanter::generate_statue()
+{
+ auto pick_r_idx_for_statue = [] {
+ while (true) {
+ auto r_idx = MonsterRace::pick_one_at_random();
+ if (monraces_info[r_idx].rarity > 0) {
+ return r_idx;
+ }
}
+ };
+ auto r_idx = pick_r_idx_for_statue();
+ auto *r_ptr = &monraces_info[r_idx];
- object_aware(player_ptr, o_ptr);
- object_known(o_ptr);
- break;
+ this->o_ptr->pval = enum2i(r_idx);
+ if (cheat_peek) {
+ msg_format(_("%sの像", "Statue of %s"), r_ptr->name.data());
}
- case ItemKindType::CHEST: {
- DEPTH obj_level = k_info[o_ptr->k_idx].level;
- if (obj_level <= 0)
- break;
- o_ptr->pval = randint1(obj_level);
- if (o_ptr->sval == SV_CHEST_KANDUME)
- o_ptr->pval = 6;
+ object_aware(this->player_ptr, this->o_ptr);
+ object_known(this->o_ptr);
+}
- o_ptr->xtra3 = floor_ptr->dun_level + 5;
- if (o_ptr->pval > 55)
- o_ptr->pval = 55 + (byte)randint0(5);
+/*
+ * @brief 箱を生成する
+ * @details 箱にはレベルがあり、箱の召喚トラップが発動すると箱レベルと同等のモンスターが召喚される
+ */
+void OtherItemsEnchanter::generate_chest()
+{
+ auto obj_level = this->o_ptr->get_baseitem().level;
+ if (obj_level <= 0) {
+ return;
+ }
- break;
+ this->o_ptr->pval = randint1(obj_level);
+ if (this->o_ptr->bi_key.sval() == SV_CHEST_KANDUME) {
+ this->o_ptr->pval = 6;
}
- default:
- break;
+ this->o_ptr->chest_level = this->player_ptr->current_floor_ptr->dun_level + 5;
+ if (this->o_ptr->pval > 55) {
+ this->o_ptr->pval = 55 + randint0(5);
}
}