1 #include "smith/object-smith.h"
2 #include "object-enchant/special-object-flags.h"
3 #include "object-enchant/tr-flags.h"
4 #include "object-enchant/tr-types.h"
5 #include "object/item-tester-hooker.h"
6 #include "object/object-flags.h"
7 #include "perception/object-perception.h"
8 #include "player-base/player-class.h"
9 #include "player-info/smith-data-type.h"
10 #include "smith/smith-info.h"
11 #include "smith/smith-tables.h"
12 #include "smith/smith-types.h"
13 #include "system/object-type-definition.h"
14 #include "system/player-type-definition.h"
19 #include <unordered_map>
25 * @brief 所持しているエッセンスで付与可能な回数を取得する
27 * @param essence_list 使用するエッセンスのリスト
28 * @param consumption 1種あたりに使用する消費量
29 * @return 所持しているエッセンスで付与可能な回数を返す
31 int addable_count(smith_data_type *smith_data, std::vector<SmithEssenceType> essence_list, int consumption)
33 if (consumption <= 0) {
37 std::vector<int> addable_count;
38 for (auto essence : essence_list) {
39 int own_amount = smith_data->essences[essence];
40 addable_count.push_back(own_amount / consumption);
42 return *std::min_element(addable_count.begin(), addable_count.end());
50 Smith::Smith(PlayerType *player_ptr)
51 : player_ptr(player_ptr)
52 , smith_data(PlayerClass(player_ptr).get_specific_data<smith_data_type>())
57 * @brief 引数で指定した鍛冶効果の鍛冶情報を得る
59 * @param effect 情報を得る鍛冶効果
60 * @return 鍛冶情報構造体へのポインタを保持する std::optional オブジェクトを返す
62 std::optional<const ISmithInfo *> Smith::find_smith_info(SmithEffectType effect)
64 // 何度も呼ぶので線形探索を避けるため鍛冶効果から鍛冶情報のテーブルを引けるmapを作成しておく。
65 static std::unordered_map<SmithEffectType, const ISmithInfo *> search_map;
66 if (search_map.empty()) {
67 for (const auto &info : smith_info_table) {
68 search_map.emplace(info->effect, info.get());
72 auto it = search_map.find(effect);
73 if (it == search_map.end()) {
81 * @brief 指定したエッセンスの表記名を取得する
83 * @param essence 表記名を取得するエッセンス
84 * @return 表記名を表す文字列へのポインタ
86 concptr Smith::get_essence_name(SmithEssenceType essence)
88 auto it = essence_to_name.find(essence);
89 auto essence_name = it != essence_to_name.end() ? it->second : _("不明", "Unknown");
94 * @brief 指定した鍛冶効果の表記名を取得する
96 * @param effect 表記名を取得する鍛冶効果
97 * @return 表記名を表す文字列へのポインタ
99 concptr Smith::get_effect_name(SmithEffectType effect)
101 auto info = find_smith_info(effect);
102 if (!info.has_value()) {
103 return _("不明", "Unknown");
106 return info.value()->name;
110 * @brief 指定した鍛冶効果の付与に必要なエッセンスの表記を取得する。複数のエッセンスが必要な場合は "+" で連結されたものとなる。
113 * @return 鍛冶効果の付与に必要なエッセンスを表記する std::string オブジェクト
115 std::string Smith::get_need_essences_desc(SmithEffectType effect)
117 auto info = find_smith_info(effect);
118 if (!info.has_value() || info.value()->need_essences.empty()) {
119 return _("不明", "Unknown");
122 const auto &need_essences = info.value()->need_essences;
123 std::stringstream ss;
124 for (auto i = 0U; i < need_essences.size(); i++) {
125 ss << Smith::get_essence_name(need_essences[i]);
126 if (i < need_essences.size() - 1) {
135 * @brief 鍛冶効果を付与するのに必要なエッセンスのリストを返す
138 * @return 鍛冶効果を付与するのに必要なエッセンスのリスト
140 std::vector<SmithEssenceType> Smith::get_need_essences(SmithEffectType effect)
142 auto info = find_smith_info(effect);
143 if (!info.has_value()) {
147 return info.value()->need_essences;
151 * @brief 鍛冶効果を付与する時のエッセンス消費量を取得する
152 * 複数のエッセンスを消費する鍛冶効果の場合は全てのエッセンスからこの量が消費される
153 * アイテムが複数ある場合はアイテムの個数倍の消費量となる
156 * @param o_ptr 鍛冶効果を付与するアイテムへのポインタ。nullptrの場合はデフォルトの消費量が返される。
157 * @return 鍛冶効果を付与する時のエッセンス消費量
159 int Smith::get_essence_consumption(SmithEffectType effect, const object_type *o_ptr)
161 auto info = find_smith_info(effect);
162 if (!info.has_value()) {
166 auto consumption = info.value()->consumption;
167 if (o_ptr == nullptr) {
171 if ((o_ptr->tval >= ItemKindType::SHOT) && (o_ptr->tval <= ItemKindType::BOLT)) {
172 consumption = (consumption + 9) / 10;
175 consumption *= o_ptr->number;
181 * @brief 鍛冶効果の対象となるアイテムを絞り込むための ItemTester クラスを取得する
184 * @return ItemTesterクラスのオブジェクトをstd::unique_ptrに格納したものを返す
186 std::unique_ptr<ItemTester> Smith::get_item_tester(SmithEffectType effect)
188 auto info = find_smith_info(effect);
189 if (!info.has_value()) {
190 return std::make_unique<TvalItemTester>(ItemKindType::NONE);
193 auto tester_func = [i = info.value()](const object_type *o_ptr) {
194 return i->can_give_smith_effect(o_ptr);
196 return std::make_unique<FuncItemTester>(tester_func);
200 * @brief 鍛冶効果により得られる特性フラグを取得する
203 * @return 鍛冶効果により得られる特性フラグがONになったTrFlagsオブジェクト
205 TrFlags Smith::get_effect_tr_flags(SmithEffectType effect)
207 auto info = find_smith_info(effect);
208 if (!info.has_value()) {
212 return info.value()->tr_flags();
216 * @brief アイテムに付与されている発動効果の発動IDを得る
218 * @param o_ptr アイテム構造体へのポインタ
219 * @return アイテムに付与されている発動効果の発動ID(random_art_activation_type型)
220 * 付与されている発動効果が無い場合は std::nullopt
222 std::optional<RandomArtActType> Smith::object_activation(const object_type *o_ptr)
224 return o_ptr->smith_act_idx;
228 * @brief アイテムに付与されている鍛冶効果を取得する
230 * @param o_ptr アイテム構造体へのポインタ
231 * @return アイテムに付与されている鍛冶効果を保持する std::optional オブジェクト返す。
232 * 鍛冶効果が付与できないアイテムか、何も付与されていなければ std::nullopt を返す。
234 std::optional<SmithEffectType> Smith::object_effect(const object_type *o_ptr)
236 return o_ptr->smith_effect;
240 * @brief 指定した鍛冶カテゴリの鍛冶効果のリストを取得する
242 * @param category 鍛冶カテゴリ
243 * @return 指定した鍛冶カテゴリの鍛冶効果のリスト
245 std::vector<SmithEffectType> Smith::get_effect_list(SmithCategoryType category)
247 std::vector<SmithEffectType> result;
249 for (const auto &info : smith_info_table) {
250 if (info->category == category) {
251 result.push_back(info->effect);
259 * @brief 指定した鍛冶効果のエッセンスを付与できる回数を取得する
262 * @param o_ptr 鍛冶効果を付与するアイテムへのポインタ。nullptrの場合はデフォルトの消費量での回数が返される。
263 * @return エッセンスを付与できる回数を返す
265 int Smith::get_addable_count(SmithEffectType effect, const object_type *o_ptr) const
267 auto info = find_smith_info(effect);
268 if (!info.has_value()) {
272 auto consumption = Smith::get_essence_consumption(effect, o_ptr);
274 return addable_count(this->smith_data.get(), info.value()->need_essences, consumption);
278 * @brief 鍛冶エッセンスの全リストを取得する
280 * @return 鍛冶エッセンスの全リスト
282 const std::vector<SmithEssenceType> &Smith::get_essence_list()
284 return essence_list_order;
288 * @brief 指定したエッセンスの所持量を取得する
290 * @param essence 所持量を取得するエッセンス
293 int Smith::get_essence_num_of_posessions(SmithEssenceType essence) const
295 return this->smith_data->essences[essence];
301 * @param o_ptr エッセンスの抽出を行うアイテムへのポインタ
302 * @return 抽出したエッセンスと抽出した量のタプルのリストを返す
304 Smith::DrainEssenceResult Smith::drain_essence(object_type *o_ptr)
306 // 抽出量を揃えるためKILLフラグのみ付いている場合はSLAYフラグも付ける
307 auto old_flgs = object_flags(o_ptr);
308 if (old_flgs.has(TR_KILL_DRAGON))
309 old_flgs.set(TR_SLAY_DRAGON);
310 if (old_flgs.has(TR_KILL_ANIMAL))
311 old_flgs.set(TR_SLAY_ANIMAL);
312 if (old_flgs.has(TR_KILL_EVIL))
313 old_flgs.set(TR_SLAY_EVIL);
314 if (old_flgs.has(TR_KILL_UNDEAD))
315 old_flgs.set(TR_SLAY_UNDEAD);
316 if (old_flgs.has(TR_KILL_DEMON))
317 old_flgs.set(TR_SLAY_DEMON);
318 if (old_flgs.has(TR_KILL_ORC))
319 old_flgs.set(TR_SLAY_ORC);
320 if (old_flgs.has(TR_KILL_TROLL))
321 old_flgs.set(TR_SLAY_TROLL);
322 if (old_flgs.has(TR_KILL_GIANT))
323 old_flgs.set(TR_SLAY_GIANT);
324 if (old_flgs.has(TR_KILL_HUMAN))
325 old_flgs.set(TR_SLAY_HUMAN);
326 if (old_flgs.has(TR_KILL_GOOD))
327 old_flgs.set(TR_SLAY_GOOD);
329 // マイナス効果のあるアイテムから抽出する時のペナルティを計算
331 if (o_ptr->curse_flags.has_any_of({ CurseTraitType::CURSED, CurseTraitType::HEAVY_CURSE, CurseTraitType::PERMA_CURSE }))
334 for (auto &&info : essence_drain_info_table) {
335 if (info.amount < 0 && old_flgs.has(info.tr_flag)) {
340 const auto is_artifact = o_ptr->is_artifact();
342 // アイテムをエッセンス抽出後の状態にする
343 const object_type old_o = *o_ptr;
344 o_ptr->prep(o_ptr->k_idx);
346 o_ptr->iy = old_o.iy;
347 o_ptr->ix = old_o.ix;
348 o_ptr->marked = old_o.marked;
349 o_ptr->number = old_o.number;
350 o_ptr->discount = old_o.discount;
352 if (o_ptr->tval == ItemKindType::DRAG_ARMOR)
353 o_ptr->timeout = old_o.timeout;
354 o_ptr->ident |= (IDENT_FULL_KNOWN);
355 object_aware(player_ptr, o_ptr);
358 auto new_flgs = object_flags(o_ptr);
360 std::unordered_map<SmithEssenceType, int> drain_values;
363 for (auto &&info : essence_drain_info_table) {
365 if (TR_PVAL_FLAG_MASK.has(info.tr_flag) && old_o.pval > 0) {
366 pval = new_flgs.has(info.tr_flag) ? old_o.pval - o_ptr->pval : old_o.pval;
369 if ((new_flgs.has_not(info.tr_flag) || pval) && old_flgs.has(info.tr_flag)) {
370 for (auto &&essence : info.essences) {
371 drain_values[essence] += info.amount * std::max(pval, 1);
377 drain_values[SmithEssenceType::UNIQUE] += 10;
380 // ダイス/命中/ダメージ/ACからの抽出
381 auto diff = [](int o, int n) { return std::max(o - n, 0); };
383 if (o_ptr->is_weapon_ammo()) {
384 drain_values[SmithEssenceType::ATTACK] += diff(old_o.ds, o_ptr->ds) * 10;
385 drain_values[SmithEssenceType::ATTACK] += diff(old_o.dd, o_ptr->dd) * 10;
388 drain_values[SmithEssenceType::ATTACK] += diff(old_o.to_h, o_ptr->to_h) * 10;
389 drain_values[SmithEssenceType::ATTACK] += diff(old_o.to_d, o_ptr->to_d) * 10;
390 drain_values[SmithEssenceType::AC] += diff(old_o.ac, o_ptr->ac) * 10;
391 drain_values[SmithEssenceType::AC] += diff(old_o.to_a, o_ptr->to_a) * 10;
393 // 個数/矢弾/マイナス効果のペナルティによる抽出量の調整
394 for (auto &&[unuse, val] : drain_values) {
395 val *= o_ptr->number;
397 val = std::max(val, 0);
398 if (o_ptr->is_ammo()) {
404 std::vector<std::tuple<SmithEssenceType, int>> result;
406 for (auto essence : essence_list_order) {
407 auto drain_value = drain_values[essence];
408 if (drain_value <= 0) {
412 this->smith_data->essences[essence] = std::min<int16_t>(Smith::ESSENCE_AMOUNT_MAX, this->smith_data->essences[essence] + drain_value);
413 result.emplace_back(essence, drain_value);
422 * @param effect 付与する鍛冶効果
423 * @param o_ptr 鍛冶効果を付与するアイテムへのポインタ
424 * @param number エッセンス付与数
425 * @return 鍛冶効果の付与に成功したら ture、失敗したら false を返す
427 bool Smith::add_essence(SmithEffectType effect, object_type *o_ptr, int number)
429 auto info = find_smith_info(effect);
430 if (!info.has_value()) {
434 const auto total_consumption = this->get_essence_consumption(effect, o_ptr) * number;
435 for (auto &&essence : info.value()->need_essences) {
436 this->smith_data->essences[essence] -= static_cast<int16_t>(total_consumption);
439 return info.value()->add_essence(this->player_ptr, o_ptr, number);
445 * @param o_ptr 鍛冶効果を消去するアイテムへのポインタ
447 void Smith::erase_essence(object_type *o_ptr) const
449 o_ptr->smith_act_idx = std::nullopt;
451 auto effect = Smith::object_effect(o_ptr);
452 if (!effect.has_value()) {
455 auto info = find_smith_info(effect.value());
456 if (!info.has_value()) {
460 info.value()->erase_essence(o_ptr);