2 * @file item-entity.cpp
3 * @brief アイテム定義の構造体とエンティティ処理実装
8 #include "system/item-entity.h"
9 #include "artifact/fixed-art-types.h"
10 #include "artifact/random-art-effects.h"
11 #include "monster-race/monster-race.h"
12 #include "object-enchant/object-curse.h"
13 #include "object-enchant/special-object-flags.h"
14 #include "object-enchant/tr-types.h"
15 #include "object-enchant/trc-types.h"
16 #include "object-enchant/trg-types.h"
17 #include "object/object-flags.h"
18 #include "object/object-value.h"
19 #include "object/tval-types.h"
20 #include "smith/object-smith.h"
21 #include "sv-definition/sv-armor-types.h"
22 #include "sv-definition/sv-lite-types.h"
23 #include "sv-definition/sv-other-types.h"
24 #include "sv-definition/sv-protector-types.h"
25 #include "sv-definition/sv-weapon-types.h"
26 #include "system/baseitem-info-definition.h"
27 #include "system/monster-race-definition.h"
28 #include "system/player-type-definition.h"
29 #include "term/term-color-types.h"
30 #include "util/bit-flags-calculator.h"
31 #include "util/string-processor.h"
33 #include <unordered_map>
35 ItemEntity::ItemEntity()
36 : fixed_artifact_idx(FixedArtifactId::NONE)
42 * Wipe an object clean.
44 void ItemEntity::wipe()
51 * Wipe an object clean.
52 * @param j_ptr 複製元のオブジェクトの構造体参照ポインタ
54 void ItemEntity::copy_from(const ItemEntity *j_ptr)
60 * @brief オブジェクト構造体にベースアイテムを作成する
61 * Prepare an object based on an object kind.
62 * @param player_ptr プレイヤーへの参照ポインタ
63 * @param k_idx 新たに作成したいベースアイテム情報のID
65 void ItemEntity::prep(KIND_OBJECT_IDX ko_idx)
67 auto *k_ptr = &baseitems_info[ko_idx];
68 auto old_stack_idx = this->stack_idx;
70 this->stack_idx = old_stack_idx;
72 this->tval = k_ptr->bi_key.tval();
73 this->sval = k_ptr->bi_key.sval().value();
74 this->pval = k_ptr->pval;
76 this->weight = k_ptr->weight;
77 this->to_h = k_ptr->to_h;
78 this->to_d = k_ptr->to_d;
79 this->to_a = k_ptr->to_a;
84 if (k_ptr->act_idx > RandomArtActType::NONE) {
85 this->activation_id = k_ptr->act_idx;
87 if (baseitems_info[this->k_idx].cost <= 0) {
88 this->ident |= (IDENT_BROKEN);
91 if (k_ptr->gen_flags.has(ItemGenerationTraitType::CURSED)) {
92 this->curse_flags.set(CurseTraitType::CURSED);
94 if (k_ptr->gen_flags.has(ItemGenerationTraitType::HEAVY_CURSE)) {
95 this->curse_flags.set(CurseTraitType::HEAVY_CURSE);
97 if (k_ptr->gen_flags.has(ItemGenerationTraitType::PERMA_CURSE)) {
98 this->curse_flags.set(CurseTraitType::PERMA_CURSE);
100 if (k_ptr->gen_flags.has(ItemGenerationTraitType::RANDOM_CURSE0)) {
101 this->curse_flags.set(get_curse(0, this));
103 if (k_ptr->gen_flags.has(ItemGenerationTraitType::RANDOM_CURSE1)) {
104 this->curse_flags.set(get_curse(1, this));
106 if (k_ptr->gen_flags.has(ItemGenerationTraitType::RANDOM_CURSE2)) {
107 this->curse_flags.set(get_curse(2, this));
112 * @brief オブジェクトが武器として装備できるかどうかを返す / Check if an object is weapon (including bows)
113 * @return 武器として使えるならばtrueを返す
115 bool ItemEntity::is_weapon() const
117 return (TV_WEAPON_BEGIN <= this->tval) && (this->tval <= TV_WEAPON_END);
121 * @brief オブジェクトが武器や矢弾として使用できるかを返す / Check if an object is weapon (including bows and ammo)
122 * Rare weapons/aromors including Blade of Chaos, Dragon armors, etc.
123 * @return 武器や矢弾として使えるならばtrueを返す
125 bool ItemEntity::is_weapon_ammo() const
127 return (TV_MISSILE_BEGIN <= this->tval) && (this->tval <= TV_WEAPON_END);
131 * @brief オブジェクトが武器、防具、矢弾として使用できるかを返す / Check if an object is weapon, armour or ammo
132 * @return 武器、防具、矢弾として使えるならばtrueを返す
134 bool ItemEntity::is_weapon_armour_ammo() const
136 return this->is_weapon_ammo() || this->is_armour();
140 * @brief オブジェクトが近接武器として装備できるかを返す / Melee weapons
141 * @return 近接武器として使えるならばtrueを返す
143 bool ItemEntity::is_melee_weapon() const
145 return (ItemKindType::DIGGING <= this->tval) && (this->tval <= ItemKindType::SWORD);
149 * @brief エッセンスの付加可能な武器や矢弾かを返す
150 * @return エッセンスの付加可能な武器か矢弾ならばtrueを返す。
152 bool ItemEntity::is_melee_ammo() const
154 switch (this->tval) {
155 case ItemKindType::HAFTED:
156 case ItemKindType::POLEARM:
157 case ItemKindType::DIGGING:
158 case ItemKindType::BOLT:
159 case ItemKindType::ARROW:
160 case ItemKindType::SHOT:
162 case ItemKindType::SWORD:
163 return this->sval != SV_POISON_NEEDLE;
170 * @brief オブジェクトが装備可能であるかを返す / Wearable including all weapon, all armour, bow, light source, amulet, and ring
171 * @return 装備可能ならばTRUEを返す
173 bool ItemEntity::is_wearable() const
175 return (TV_WEARABLE_BEGIN <= this->tval) && (this->tval <= TV_WEARABLE_END);
179 * @brief オブジェクトが装備品であるかを返す(ItemEntity::is_wearableに矢弾を含む) / Equipment including all wearable objects and ammo
180 * @return 装備品ならばtrueを返す
182 bool ItemEntity::is_equipment() const
184 return (TV_EQUIP_BEGIN <= this->tval) && (this->tval <= TV_EQUIP_END);
188 * @brief 武器匠の「武器」鑑定対象になるかを判定する。/ Hook to specify "weapon"
189 * @return 対象になるならtrueを返す。
191 bool ItemEntity::is_orthodox_melee_weapons() const
193 switch (this->tval) {
194 case ItemKindType::HAFTED:
195 case ItemKindType::POLEARM:
196 case ItemKindType::DIGGING:
198 case ItemKindType::SWORD:
199 return this->sval != SV_POISON_NEEDLE;
206 * @brief 修復対象となる壊れた武器かを判定する。 / Hook to specify "broken weapon"
207 * @return 修復対象になるならTRUEを返す。
209 bool ItemEntity::is_broken_weapon() const
211 if (this->tval != ItemKindType::SWORD) {
215 switch (this->sval) {
216 case SV_BROKEN_DAGGER:
217 case SV_BROKEN_SWORD:
225 * @brief オブジェクトが投射可能な武器かどうかを返す。
226 * @return 投射可能な武器ならばtrue
228 bool ItemEntity::is_throwable() const
230 switch (this->tval) {
231 case ItemKindType::DIGGING:
232 case ItemKindType::HAFTED:
233 case ItemKindType::POLEARM:
234 case ItemKindType::SWORD:
242 * @brief オブジェクトがどちらの手にも装備できる武器かどうかの判定
243 * @return 左右両方の手で装備できるならばtrueを返す。
245 bool ItemEntity::is_wieldable_in_etheir_hand() const
247 switch (this->tval) {
248 case ItemKindType::DIGGING:
249 case ItemKindType::HAFTED:
250 case ItemKindType::POLEARM:
251 case ItemKindType::SWORD:
252 case ItemKindType::SHIELD:
253 case ItemKindType::CAPTURE:
254 case ItemKindType::CARD:
262 * @brief オブジェクトが強化不能武器であるかを返す / Poison needle can not be enchanted
263 * @return 強化不能ならばtrueを返す
265 bool ItemEntity::refuse_enchant_weapon() const
267 return (this->tval == ItemKindType::SWORD) && (this->sval == SV_POISON_NEEDLE);
271 * @brief オブジェクトが強化可能武器であるかを返す /
272 * Check if an object is weapon (including bows and ammo) and allows enchantment
273 * @return 強化可能ならばtrueを返す
275 bool ItemEntity::allow_enchant_weapon() const
277 return this->is_weapon_ammo() && !this->refuse_enchant_weapon();
281 * @brief オブジェクトが強化可能な近接武器であるかを返す /
282 * Check if an object is melee weapon and allows enchantment
283 * @return 強化可能な近接武器ならばtrueを返す
285 bool ItemEntity::allow_enchant_melee_weapon() const
287 return this->is_melee_weapon() && !this->refuse_enchant_weapon();
291 * @brief オブジェクトが両手持ち可能な武器かを返す /
292 * Check if an object is melee weapon and allows wielding with two-hands
293 * @return 両手持ち可能ならばTRUEを返す
295 bool ItemEntity::allow_two_hands_wielding() const
297 return this->is_melee_weapon() && ((this->weight > 99) || (this->tval == ItemKindType::POLEARM));
301 * @brief オブジェクトが矢弾として使用できるかどうかを返す / Check if an object is ammo
302 * @return 矢弾として使えるならばtrueを返す
304 bool ItemEntity::is_ammo() const
306 return (TV_MISSILE_BEGIN <= this->tval) && (this->tval <= TV_MISSILE_END);
310 * @brief 対象のアイテムが矢やクロスボウの矢の材料になるかを返す。/
311 * Hook to determine if an object is contertible in an arrow/bolt
312 * @return 材料にできるならtrueを返す
314 bool ItemEntity::is_convertible() const
316 auto is_convertible = ((this->tval == ItemKindType::JUNK) || (this->tval == ItemKindType::SKELETON));
317 is_convertible |= ((this->tval == ItemKindType::CORPSE) && (this->sval == SV_SKELETON));
318 return is_convertible;
321 bool ItemEntity::is_lance() const
323 auto is_lance = this->tval == ItemKindType::POLEARM;
324 is_lance &= (this->sval == SV_LANCE) || (this->sval == SV_HEAVY_LANCE);
329 * @brief オブジェクトが防具として装備できるかどうかを返す / Check if an object is armour
330 * @return 防具として装備できるならばtrueを返す
332 bool ItemEntity::is_armour() const
334 return (TV_ARMOR_BEGIN <= this->tval) && (this->tval <= TV_ARMOR_END);
338 * @brief オブジェクトがレアアイテムかどうかを返す /
339 * Rare weapons/aromors including Blade of Chaos, Dragon armors, etc.
340 * @return レアアイテムならばTRUEを返す
342 bool ItemEntity::is_rare() const
344 static const std::unordered_map<ItemKindType, const std::set<OBJECT_SUBTYPE_VALUE>> rare_table = {
345 { ItemKindType::HAFTED, { SV_MACE_OF_DISRUPTION, SV_WIZSTAFF } },
346 { ItemKindType::POLEARM, { SV_SCYTHE_OF_SLICING, SV_DEATH_SCYTHE } },
347 { ItemKindType::SWORD, { SV_BLADE_OF_CHAOS, SV_DIAMOND_EDGE, SV_POISON_NEEDLE, SV_HAYABUSA } },
348 { ItemKindType::SHIELD, { SV_DRAGON_SHIELD, SV_MIRROR_SHIELD } },
349 { ItemKindType::HELM, { SV_DRAGON_HELM } },
350 { ItemKindType::BOOTS, { SV_PAIR_OF_DRAGON_GREAVE } },
351 { ItemKindType::CLOAK, { SV_ELVEN_CLOAK, SV_ETHEREAL_CLOAK, SV_SHADOW_CLOAK, SV_MAGIC_RESISTANCE_CLOAK } },
352 { ItemKindType::GLOVES, { SV_SET_OF_DRAGON_GLOVES } },
353 { ItemKindType::SOFT_ARMOR, { SV_KUROSHOUZOKU, SV_ABUNAI_MIZUGI } },
354 { ItemKindType::HARD_ARMOR, { SV_MITHRIL_CHAIN_MAIL, SV_MITHRIL_PLATE_MAIL, SV_ADAMANTITE_PLATE_MAIL } },
355 { ItemKindType::DRAG_ARMOR, { /* Any */ } },
358 if (auto it = rare_table.find(this->tval); it != rare_table.end()) {
359 const auto &svals = it->second;
360 return svals.empty() || (svals.find(this->sval) != svals.end());
367 * @brief オブジェクトがエゴアイテムかどうかを返す
368 * @return エゴアイテムならばtrueを返す
370 bool ItemEntity::is_ego() const
372 return this->ego_idx != EgoType::NONE;
376 * @brief オブジェクトが鍛冶師のエッセンス付加済みかを返す /
377 * Check if an object is made by a smith's special ability
378 * @return エッセンス付加済みならばTRUEを返す
380 bool ItemEntity::is_smith() const
382 return Smith::object_effect(this).has_value() || Smith::object_activation(this).has_value();
386 * @brief オブジェクトがアーティファクトかを返す /
387 * Check if an object is artifact
388 * @return アーティファクトならばtrueを返す
390 bool ItemEntity::is_artifact() const
392 return this->is_fixed_artifact() || (this->art_name != 0);
396 * @brief オブジェクトが固定アーティファクトかを返す /
397 * Check if an object is fixed artifact
398 * @return 固定アーティファクトならばtrueを返す
400 bool ItemEntity::is_fixed_artifact() const
402 return !this->is_specific_artifact(FixedArtifactId::NONE);
406 * @brief オブジェクトがランダムアーティファクトかを返す /
407 * Check if an object is random artifact
408 * @return ランダムアーティファクトならばtrueを返す
410 bool ItemEntity::is_random_artifact() const
412 return this->is_artifact() && !this->is_fixed_artifact();
416 * @brief オブジェクトが通常のアイテム(アーティファクト、エゴ、鍛冶師エッセンス付加いずれでもない)かを返す /
417 * Check if an object is neither artifact, ego, nor 'smith' object
418 * @return 通常のアイテムならばtrueを返す
420 bool ItemEntity::is_nameless() const
422 return !this->is_artifact() && !this->is_ego() && !this->is_smith();
425 bool ItemEntity::is_valid() const
427 return this->k_idx != 0;
430 bool ItemEntity::is_broken() const
432 return any_bits(this->ident, IDENT_BROKEN);
435 bool ItemEntity::is_cursed() const
437 return this->curse_flags.any();
440 bool ItemEntity::is_held_by_monster() const
442 return this->held_m_idx != 0;
446 * Determine if a given inventory item is "known"
447 * Test One -- Check for special "known" tag
448 * Test Two -- Check for "Easy Know" + "Aware"
450 bool ItemEntity::is_known() const
452 return any_bits(this->ident, IDENT_KNOWN) || (baseitems_info[this->k_idx].easy_know && baseitems_info[this->k_idx].aware);
455 bool ItemEntity::is_fully_known() const
457 return any_bits(this->ident, IDENT_FULL_KNOWN);
461 * @brief 与えられたオブジェクトのベースアイテムが鑑定済かを返す / Determine if a given inventory item is "aware"
464 bool ItemEntity::is_aware() const
466 return baseitems_info[this->k_idx].aware;
470 * Determine if a given inventory item is "tried"
472 bool ItemEntity::is_tried() const
474 return baseitems_info[this->k_idx].tried;
478 * @brief オブジェクトが薬であるかを返す
479 * @return オブジェクトが薬ならばtrueを返す
481 bool ItemEntity::is_potion() const
483 return baseitems_info[this->k_idx].bi_key.tval() == ItemKindType::POTION;
487 * @brief オブジェクトをプレイヤーが読むことができるかを判定する /
488 * Hook to determine if an object is readable
489 * @return 読むことが可能ならばtrueを返す
491 bool ItemEntity::is_readable() const
493 auto can_read = this->tval == ItemKindType::SCROLL;
494 can_read |= this->tval == ItemKindType::PARCHMENT;
495 can_read |= this->is_specific_artifact(FixedArtifactId::GHB);
496 can_read |= this->is_specific_artifact(FixedArtifactId::POWER);
501 * @brief オブジェクトがランタンの燃料になるかどうかを判定する
502 * An "item_tester_hook" for refilling lanterns
503 * @return オブジェクトがランタンの燃料になるならばTRUEを返す
505 bool ItemEntity::can_refill_lantern() const
507 return (this->tval == ItemKindType::FLASK) || ((this->tval == ItemKindType::LITE) && (this->sval == SV_LITE_LANTERN));
511 * @brief オブジェクトが松明に束ねられるかどうかを判定する
512 * An "item_tester_hook" for refilling torches
513 * @return オブジェクトが松明に束ねられるならばTRUEを返す
515 bool ItemEntity::can_refill_torch() const
517 return (this->tval == ItemKindType::LITE) && (this->sval == SV_LITE_TORCH);
521 * @brief 魔力充填が可能なアイテムかどうか判定する /
522 * Hook for "get_item()". Determine if something is rechargable.
523 * @return 魔力充填が可能ならばTRUEを返す
525 bool ItemEntity::is_rechargeable() const
527 switch (this->tval) {
528 case ItemKindType::STAFF:
529 case ItemKindType::WAND:
530 case ItemKindType::ROD:
538 * @brief 悪魔領域のグレーターデーモン召喚に利用可能な死体かどうかを返す。 / An "item_tester_hook" for offer
539 * @return 生贄に使用可能な死体ならばTRUEを返す。
541 bool ItemEntity::is_offerable() const
543 if ((this->tval != ItemKindType::CORPSE) || (this->sval != SV_CORPSE)) {
547 return angband_strchr("pht", monraces_info[i2enum<MonsterRaceId>(this->pval)].d_char) != nullptr;
551 * @brief オブジェクトをプレイヤーが魔道具として発動できるかを判定する /
552 * Hook to determine if an object is activatable
553 * @return 魔道具として発動可能ならばTRUEを返す
555 bool ItemEntity::is_activatable() const
557 if (!this->is_known()) {
561 auto flags = object_flags(this);
562 return flags.has(TR_ACTIVATE);
566 * @brief オブジェクトが燃料として使えるかを判定する
569 bool ItemEntity::is_fuel() const
571 auto is_fuel = (this->tval == ItemKindType::LITE) && ((this->sval == SV_LITE_TORCH) || (this->sval == SV_LITE_LANTERN));
572 is_fuel |= (this->tval == ItemKindType::FLASK) && (this->sval == SV_FLASK_OIL);
577 * @brief オブジェクトが魔法書かどうかを判定する
580 bool ItemEntity::is_book() const
582 switch (this->tval) {
583 case ItemKindType::LIFE_BOOK:
584 case ItemKindType::SORCERY_BOOK:
585 case ItemKindType::NATURE_BOOK:
586 case ItemKindType::CHAOS_BOOK:
587 case ItemKindType::DEATH_BOOK:
588 case ItemKindType::TRUMP_BOOK:
589 case ItemKindType::ARCANE_BOOK:
590 case ItemKindType::CRAFT_BOOK:
591 case ItemKindType::DEMON_BOOK:
592 case ItemKindType::CRUSADE_BOOK:
593 case ItemKindType::MUSIC_BOOK:
594 case ItemKindType::HISSATSU_BOOK:
595 case ItemKindType::HEX_BOOK:
603 * @brief オブジェクトが同一の命中値上昇及びダメージ上昇があるかを判定する
605 * @details 鍛冶師が篭手に殺戮エッセンスを付加した場合のみこの判定に意味がある
607 bool ItemEntity::is_glove_same_temper(const ItemEntity *j_ptr) const
609 if (!this->is_smith() || !j_ptr->is_smith()) {
613 if (this->smith_hit != j_ptr->smith_hit) {
617 if (this->smith_damage != j_ptr->smith_damage) {
625 * @brief オブジェクトが「2つの~~」と重ねられるかを一般的に判定する
627 * @details 個別のアイテムによっては別途条件があるが、それはこの関数では判定しない
629 bool ItemEntity::can_pile(const ItemEntity *j_ptr) const
631 if (this->is_known() != j_ptr->is_known()) {
635 if (this->feeling != j_ptr->feeling) {
639 if (this->to_h != j_ptr->to_h) {
643 if (this->to_d != j_ptr->to_d) {
647 if (this->to_a != j_ptr->to_a) {
651 if (this->pval != j_ptr->pval) {
655 if (this->is_artifact() || j_ptr->is_artifact()) {
659 if (this->ego_idx != j_ptr->ego_idx) {
663 if (this->timeout || j_ptr->timeout) {
667 if (this->ac != j_ptr->ac) {
671 if (this->dd != j_ptr->dd) {
675 if (this->ds != j_ptr->ds) {
679 if (Smith::object_effect(this) != Smith::object_effect(j_ptr)) {
683 if (Smith::object_activation(this) != Smith::object_activation(j_ptr)) {
692 * @details 未鑑定名のあるアイテム (薬等)は、未鑑定名の割り当てられた色を返す
693 * 未鑑定名のないアイテム (魔法書等)はベースアイテム定義そのままを返す
694 * その中でモンスターの死体以外は、ベースアイテムの色を返す
695 * モンスターの死体は、元モンスターの色を返す
696 * 異常アイテム (「何か」)の場合、ベースアイテム定義に基づき黒を返す
698 TERM_COLOR ItemEntity::get_color() const
700 const auto &base_item = baseitems_info[this->k_idx];
701 const auto flavor = base_item.flavor;
703 return baseitems_info[flavor].x_attr;
706 auto has_attr = this->k_idx == 0;
707 has_attr |= (this->tval != ItemKindType::CORPSE) || (this->sval != SV_CORPSE);
708 has_attr |= base_item.x_attr != TERM_DARK;
710 return base_item.x_attr;
713 return monraces_info[i2enum<MonsterRaceId>(this->pval)].x_attr;
717 * @brief アイテムシンボルを取得する
718 * @details 未鑑定名のないアイテム (魔法書等)はベースアイテム定義そのまま
719 * 未鑑定名のあるアイテム (薬等)は、未鑑定名の割り当てられたシンボル
720 * @todo 色と違って変える必要はない……はず?
722 char ItemEntity::get_symbol() const
724 const auto &base_item = baseitems_info[this->k_idx];
725 const auto flavor = base_item.flavor;
726 return flavor ? baseitems_info[flavor].x_char : base_item.x_char;
730 * @brief オブジェクト価格算出のメインルーチン
731 * @return オブジェクトの判明している現価格
733 int ItemEntity::get_price() const
736 const auto is_worthless = this->is_broken() || this->is_cursed();
737 if (this->is_known()) {
742 value = object_value_real(this);
744 if (any_bits(this->ident, IDENT_SENSE) && is_worthless) {
748 value = this->get_baseitem_price();
751 if (this->discount) {
752 value -= (value * this->discount / 100L);
759 * @brief 未鑑定なベースアイテムの基本価格を返す
760 * @return オブジェクトの未鑑定価格
762 int ItemEntity::get_baseitem_price() const
764 if (this->is_aware()) {
765 return baseitems_info[this->k_idx].cost;
768 switch (this->tval) {
769 case ItemKindType::FOOD:
771 case ItemKindType::POTION:
772 case ItemKindType::SCROLL:
774 case ItemKindType::STAFF:
776 case ItemKindType::WAND:
778 case ItemKindType::ROD:
780 case ItemKindType::RING:
781 case ItemKindType::AMULET:
783 case ItemKindType::FIGURINE:
784 return this->calc_figurine_value();
785 case ItemKindType::CAPTURE:
786 return this->calc_capture_value();
792 int ItemEntity::calc_figurine_value() const
794 auto figure_r_idx = i2enum<MonsterRaceId>(this->pval);
795 auto level = monraces_info[figure_r_idx].level;
801 return 1000 + (level - 20) * 150;
805 return 2500 + (level - 30) * 350;
809 return 6000 + (level - 40) * 800;
812 return 14000 + (level - 50) * 2000;
815 int ItemEntity::calc_capture_value() const
817 auto capture_r_idx = i2enum<MonsterRaceId>(this->pval);
818 if (!MonsterRace(capture_r_idx).is_valid()) {
822 return (monraces_info[capture_r_idx].level) * 50 + 1000;
825 bool ItemEntity::is_specific_artifact(FixedArtifactId id) const
827 return this->fixed_artifact_idx == id;