2 * @brief アイテムに影響のある魔法の処理
7 #include "spell/spells-object.h"
8 #include "artifact/fixed-art-types.h"
9 #include "avatar/avatar.h"
10 #include "core/window-redrawer.h"
11 #include "flavor/flavor-describer.h"
12 #include "flavor/object-flavor-types.h"
13 #include "floor/floor-object.h"
14 #include "game-option/disturbance-options.h"
15 #include "inventory/inventory-slot-types.h"
16 #include "monster-race/monster-race.h"
17 #include "monster-race/race-flags1.h"
18 #include "object-enchant/item-apply-magic.h"
19 #include "object-enchant/item-feeling.h"
20 #include "object-enchant/item-magic-applier.h"
21 #include "object-enchant/object-boost.h"
22 #include "object-enchant/object-ego.h"
23 #include "object-enchant/special-object-flags.h"
24 #include "object-enchant/trc-types.h"
25 #include "object-enchant/trg-types.h"
26 #include "object-hook/hook-armor.h"
27 #include "object-hook/hook-weapon.h"
28 #include "object/item-tester-hooker.h"
29 #include "object/item-use-flags.h"
30 #include "object/object-kind-hook.h"
31 #include "perception/object-perception.h"
32 #include "player-info/class-info.h"
33 #include "racial/racial-android.h"
34 #include "sv-definition/sv-other-types.h"
35 #include "sv-definition/sv-scroll-types.h"
36 #include "sv-definition/sv-weapon-types.h"
37 #include "system/artifact-type-definition.h"
38 #include "system/baseitem-info.h"
39 #include "system/floor-type-definition.h"
40 #include "system/item-entity.h"
41 #include "system/monster-race-info.h"
42 #include "system/player-type-definition.h"
43 #include "system/redrawing-flags-updater.h"
44 #include "term/screen-processor.h"
45 #include "util/bit-flags-calculator.h"
46 #include "util/probability-table.h"
47 #include "view/display-messages.h"
50 * @brief 装備強化処理の失敗率定数 (千分率)
51 * @details 強化値が負値から0までは必ず成功する
52 * 正値は+15までしか鍛えることができず、+16以上への強化を試みると確実に失敗する
54 static constexpr std::array<int, 16> enchant_table = { { 0, 10, 50, 100, 200, 300, 400, 500, 650, 800, 950, 987, 993, 995, 998, 1000 } };
57 * Scatter some "amusing" objects near the player
59 enum class AmusementFlagType : byte {
60 NOTHING, /* No restriction */
61 NO_UNIQUE, /* Don't make the amusing object of uniques */
62 FIXED_ART, /* Make a fixed artifact based on the amusing object */
63 MULTIPLE, /* Drop 1-3 objects for one type */
64 PILE, /* Drop 1-99 pile objects for one type */
67 class AmuseDefinition {
69 AmuseDefinition(const BaseitemKey &key, PERCENTAGE prob, AmusementFlagType flag)
78 AmusementFlagType flag;
81 static const std::array<AmuseDefinition, 13> amuse_info = { {
82 { { ItemKindType::BOTTLE }, 5, AmusementFlagType::NOTHING },
83 { { ItemKindType::JUNK }, 3, AmusementFlagType::MULTIPLE },
84 { { ItemKindType::SPIKE }, 10, AmusementFlagType::PILE },
85 { { ItemKindType::STATUE }, 15, AmusementFlagType::NOTHING },
86 { { ItemKindType::CORPSE }, 15, AmusementFlagType::NO_UNIQUE },
87 { { ItemKindType::SKELETON }, 10, AmusementFlagType::NO_UNIQUE },
88 { { ItemKindType::FIGURINE }, 10, AmusementFlagType::NO_UNIQUE },
89 { { ItemKindType::PARCHMENT }, 1, AmusementFlagType::NOTHING },
90 { { ItemKindType::POLEARM, SV_TSURIZAO }, 3, AmusementFlagType::NOTHING }, // Fishing Pole of Taikobo
91 { { ItemKindType::SWORD, SV_BROKEN_DAGGER }, 3, AmusementFlagType::FIXED_ART }, // Broken Dagger of Magician
92 { { ItemKindType::SWORD, SV_BROKEN_DAGGER }, 10, AmusementFlagType::NOTHING },
93 { { ItemKindType::SWORD, SV_BROKEN_SWORD }, 5, AmusementFlagType::NOTHING },
94 { { ItemKindType::SCROLL, SV_SCROLL_AMUSEMENT }, 10, AmusementFlagType::NOTHING },
97 static std::optional<FixedArtifactId> sweep_amusement_artifact(const bool insta_art, const short bi_id)
99 for (const auto &[a_idx, artifact] : artifacts_info) {
100 if (a_idx == FixedArtifactId::NONE) {
104 if (insta_art && !artifact.gen_flags.has(ItemGenerationTraitType::INSTA_ART)) {
108 if (artifact.bi_key != baseitems_info[bi_id].bi_key) {
112 if (artifact.is_generated) {
124 * @param player_ptr プレイヤーへの参照ポインタ
126 * @param known TRUEならばオブジェクトが必ず*鑑定*済になる
128 void generate_amusement(PlayerType *player_ptr, int num, bool known)
130 ProbabilityTable<const AmuseDefinition *> pt;
131 for (const auto &am_ref : amuse_info) {
132 pt.entry_item(&am_ref, am_ref.prob);
135 for (auto i = 0; i < num; i++) {
136 auto am_ptr = pt.pick_one_at_random();
137 const auto bi_id = lookup_baseitem_id(am_ptr->key);
142 const auto insta_art = baseitems_info[bi_id].gen_flags.has(ItemGenerationTraitType::INSTA_ART);
143 const auto flag = am_ptr->flag;
144 const auto fixed_art = flag == AmusementFlagType::FIXED_ART;
145 std::optional<FixedArtifactId> opt_a_idx(std::nullopt);
146 if (insta_art || fixed_art) {
147 opt_a_idx = sweep_amusement_artifact(insta_art, bi_id);
148 if (!opt_a_idx.has_value()) {
155 if (opt_a_idx.has_value()) {
156 item.fixed_artifact_idx = opt_a_idx.value();
159 ItemMagicApplier(player_ptr, &item, 1, AM_NO_FIXED_ART).execute();
160 if (flag == AmusementFlagType::NO_UNIQUE) {
161 if (monraces_info[i2enum<MonsterRaceId>(item.pval)].kind_flags.has(MonsterKindType::UNIQUE)) {
166 if (flag == AmusementFlagType::MULTIPLE) {
167 item.number = randint1(3);
170 if (flag == AmusementFlagType::PILE) {
171 item.number = randint1(99);
175 object_aware(player_ptr, &item);
179 (void)drop_near(player_ptr, &item, -1, player_ptr->y, player_ptr->x);
185 * Scatter some "great" objects near the player
186 * @param player_ptr プレイヤーへの参照ポインタ
187 * @param y1 配置したいフロアのY座標
188 * @param x1 配置したいフロアのX座標
190 * @param great TRUEならば必ず高級品以上を落とす
191 * @param special TRUEならば必ず特別品を落とす
192 * @param known TRUEならばオブジェクトが必ず*鑑定*済になる
194 void acquirement(PlayerType *player_ptr, POSITION y1, POSITION x1, int num, bool great, bool special, bool known)
196 auto mode = AM_GOOD | (great || special ? AM_GREAT : AM_NONE) | (special ? AM_SPECIAL : AM_NONE);
197 for (auto i = 0; i < num; i++) {
199 if (!make_object(player_ptr, &item, mode)) {
204 object_aware(player_ptr, &item);
208 (void)drop_near(player_ptr, &item, -1, y1, x1);
214 * Curse the players armor
215 * @return 何も持っていない場合を除き、常にTRUEを返す
216 * @todo 元のreturnは間違っているが、修正後の↓文がどれくらい正しいかは要チェック
218 bool curse_armor(PlayerType *player_ptr)
220 /* Curse the body armor */
221 auto *o_ptr = &player_ptr->inventory_list[INVEN_BODY];
222 if (!o_ptr->is_valid()) {
226 const auto item_name = describe_flavor(player_ptr, o_ptr, OD_OMIT_PREFIX);
228 if (o_ptr->is_fixed_or_random_artifact() && one_in_(2)) {
230 msg_format("%sが%sを包み込もうとしたが、%sはそれを跳ね返した!", "恐怖の暗黒オーラ", "防具", item_name.data());
232 msg_format("A %s tries to %s, but your %s resists the effects!", "terrible black aura", "surround your armor", item_name.data());
237 msg_format(_("恐怖の暗黒オーラがあなたの%sを包み込んだ!", "A terrible black aura blasts your %s!"), item_name.data());
238 chg_virtue(player_ptr, Virtue::ENCHANT, -5);
239 o_ptr->fixed_artifact_idx = FixedArtifactId::NONE;
240 o_ptr->ego_idx = EgoType::BLASTED;
241 o_ptr->to_a = 0 - randint1(5) - randint1(5);
247 o_ptr->art_flags.clear();
248 o_ptr->curse_flags.set(CurseTraitType::CURSED);
249 o_ptr->ident |= IDENT_BROKEN;
250 auto &rfu = RedrawingFlagsUpdater::get_instance();
251 const auto flags_srf = {
252 StatusRedrawingFlag::BONUS,
253 StatusRedrawingFlag::MP,
255 rfu.set_flags(flags_srf);
256 player_ptr->window_flags |= PW_INVENTORY | PW_EQUIPMENT | PW_PLAYER;
262 * Curse the players weapon
263 * @param player_ptr 所持者の参照ポインタ
264 * @param force 無条件に呪縛を行うならばTRUE
265 * @param o_ptr 呪縛する武器のアイテム情報参照ポインタ
266 * @return 何も持っていない場合を除き、常にTRUEを返す
267 * @todo 元のreturnは間違っているが、修正後の↓文がどれくらい正しいかは要チェック
269 bool curse_weapon_object(PlayerType *player_ptr, bool force, ItemEntity *o_ptr)
271 if (!o_ptr->is_valid()) {
275 const auto item_name = describe_flavor(player_ptr, o_ptr, OD_OMIT_PREFIX);
276 if (o_ptr->is_fixed_or_random_artifact() && one_in_(2) && !force) {
278 msg_format("%sが%sを包み込もうとしたが、%sはそれを跳ね返した!", "恐怖の暗黒オーラ", "武器", item_name.data());
280 msg_format("A %s tries to %s, but your %s resists the effects!", "terrible black aura", "surround your weapon", item_name.data());
286 msg_format(_("恐怖の暗黒オーラがあなたの%sを包み込んだ!", "A terrible black aura blasts your %s!"), item_name.data());
289 chg_virtue(player_ptr, Virtue::ENCHANT, -5);
290 o_ptr->fixed_artifact_idx = FixedArtifactId::NONE;
291 o_ptr->ego_idx = EgoType::SHATTERED;
292 o_ptr->to_h = 0 - randint1(5) - randint1(5);
293 o_ptr->to_d = 0 - randint1(5) - randint1(5);
298 o_ptr->art_flags.clear();
299 o_ptr->curse_flags.set(CurseTraitType::CURSED);
300 o_ptr->ident |= IDENT_BROKEN;
301 auto &rfu = RedrawingFlagsUpdater::get_instance();
302 const auto flags_srf = {
303 StatusRedrawingFlag::BONUS,
304 StatusRedrawingFlag::MP,
306 rfu.set_flags(flags_srf);
307 player_ptr->window_flags |= PW_INVENTORY | PW_EQUIPMENT | PW_PLAYER;
312 * @brief ボルトのエゴ化処理(火炎エゴのみ) /
314 * @param player_ptr プレイヤーへの参照ポインタ
316 void brand_bolts(PlayerType *player_ptr)
318 for (auto i = 0; i < INVEN_PACK; i++) {
319 auto *o_ptr = &player_ptr->inventory_list[i];
320 if (o_ptr->bi_key.tval() != ItemKindType::BOLT) {
324 if (o_ptr->is_fixed_or_random_artifact() || o_ptr->is_ego()) {
328 if (o_ptr->is_cursed() || o_ptr->is_broken()) {
332 if (randint0(100) < 75) {
336 msg_print(_("クロスボウの矢が炎のオーラに包まれた!", "Your bolts are covered in a fiery aura!"));
337 o_ptr->ego_idx = EgoType::FLAME;
338 enchant_equipment(player_ptr, o_ptr, randint0(3) + 4, ENCH_TOHIT | ENCH_TODAM);
346 msg_print(_("炎で強化するのに失敗した。", "The fiery enchantment failed."));
351 * Break the curse of an item
352 * @param o_ptr 呪い装備情報の参照ポインタ
354 static void break_curse(ItemEntity *o_ptr)
356 const auto has_heavier_curse = o_ptr->curse_flags.has_any_of({ CurseTraitType::PERMA_CURSE, CurseTraitType::HEAVY_CURSE });
357 const auto is_curse_broken = o_ptr->is_cursed() && !has_heavier_curse && one_in_(4);
358 if (!is_curse_broken) {
362 msg_print(_("かけられていた呪いが打ち破られた!", "The curse is broken!"));
363 o_ptr->curse_flags.clear();
364 o_ptr->ident |= IDENT_SENSE;
365 o_ptr->feeling = FEEL_NONE;
370 * @param player_ptr プレイヤーへの参照ポインタ
371 * @param o_ptr 強化するアイテムの参照ポインタ
373 * @param eflag 強化オプション(命中/ダメージ/AC)
374 * @return 強化に成功した場合TRUEを返す
376 bool enchant_equipment(PlayerType *player_ptr, ItemEntity *o_ptr, int n, int eflag)
378 auto prob = o_ptr->number * 100;
379 if (o_ptr->is_ammo()) {
385 auto a = o_ptr->is_fixed_or_random_artifact();
386 auto force = (eflag & ENCH_FORCE);
387 for (int i = 0; i < n; i++) {
388 if (!force && randint0(prob) >= 100) {
392 if (eflag & ENCH_TOHIT) {
393 if (o_ptr->to_h < 0) {
395 } else if (o_ptr->to_h > 15) {
398 chance = enchant_table[o_ptr->to_h];
401 if (force || ((randint1(1000) > chance) && (!a || (randint0(100) < 50)))) {
404 if (o_ptr->to_h >= 0) {
410 if (eflag & ENCH_TODAM) {
411 if (o_ptr->to_d < 0) {
413 } else if (o_ptr->to_d > 15) {
416 chance = enchant_table[o_ptr->to_d];
419 if (force || ((randint1(1000) > chance) && (!a || (randint0(100) < 50)))) {
422 if (o_ptr->to_d >= 0) {
428 if (!(eflag & ENCH_TOAC)) {
432 if (o_ptr->to_a < 0) {
434 } else if (o_ptr->to_a > 15) {
437 chance = enchant_table[o_ptr->to_a];
440 if (force || ((randint1(1000) > chance) && (!a || (randint0(100) < 50)))) {
443 if (o_ptr->to_a >= 0) {
453 auto &rfu = RedrawingFlagsUpdater::get_instance();
454 const auto flags_srf = {
455 StatusRedrawingFlag::BONUS,
456 StatusRedrawingFlag::COMBINATION,
457 StatusRedrawingFlag::REORDER,
459 rfu.set_flags(flags_srf);
460 set_bits(player_ptr->window_flags, PW_INVENTORY | PW_EQUIPMENT | PW_PLAYER | PW_FLOOR_ITEMS | PW_FOUND_ITEMS);
465 * @brief 装備修正強化処理のメインルーチン
466 * @param player_ptr プレイヤーへの参照ポインタ
467 * @param num_hit 命中修正量
468 * @param num_dam ダメージ修正量
469 * @param num_ac AC修正量
470 * @return 強化に成功した場合TRUEを返す
472 bool enchant_spell(PlayerType *player_ptr, HIT_PROB num_hit, int num_dam, ARMOUR_CLASS num_ac)
474 FuncItemTester item_tester(&ItemEntity::allow_enchant_weapon);
476 item_tester = FuncItemTester(&ItemEntity::is_protector);
479 const auto q = _("どのアイテムを強化しますか? ", "Enchant which item? ");
480 const auto s = _("強化できるアイテムがない。", "You have nothing to enchant.");
482 const auto options = USE_EQUIP | USE_INVEN | USE_FLOOR | IGNORE_BOTHHAND_SLOT;
483 auto *o_ptr = choose_object(player_ptr, &item, q, s, options, item_tester);
488 const auto item_name = describe_flavor(player_ptr, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
490 msg_format("%s は明るく輝いた!", item_name.data());
492 msg_format("%s %s glow%s brightly!", ((item >= 0) ? "Your" : "The"), item_name.data(), ((o_ptr->number > 1) ? "" : "s"));
495 auto is_enchant_successful = false;
496 if (enchant_equipment(player_ptr, o_ptr, num_hit, ENCH_TOHIT)) {
497 is_enchant_successful = true;
500 if (enchant_equipment(player_ptr, o_ptr, num_dam, ENCH_TODAM)) {
501 is_enchant_successful = true;
504 if (enchant_equipment(player_ptr, o_ptr, num_ac, ENCH_TOAC)) {
505 is_enchant_successful = true;
508 if (!is_enchant_successful) {
512 msg_print(_("強化に失敗した。", "The enchantment failed."));
514 chg_virtue(player_ptr, Virtue::ENCHANT, -1);
517 chg_virtue(player_ptr, Virtue::ENCHANT, 1);
520 calc_android_exp(player_ptr);
526 * @param player_ptr プレイヤーへの参照ポインタ
527 * @param brand_type エゴ化ID(EgoDefinitionsとは連動していない)
529 void brand_weapon(PlayerType *player_ptr, int brand_type)
531 const auto q = _("どの武器を強化しますか? ", "Enchant which weapon? ");
532 const auto s = _("強化できる武器がない。", "You have nothing to enchant.");
534 const auto options = USE_EQUIP | IGNORE_BOTHHAND_SLOT;
535 auto *o_ptr = choose_object(player_ptr, &item, q, s, options, FuncItemTester(&ItemEntity::allow_enchant_melee_weapon));
536 if (o_ptr == nullptr) {
540 const auto &bi_key = o_ptr->bi_key;
541 auto special_weapon = bi_key == BaseitemKey(ItemKindType::SWORD, SV_POISON_NEEDLE);
542 special_weapon |= bi_key == BaseitemKey(ItemKindType::POLEARM, SV_DEATH_SCYTHE);
543 special_weapon |= bi_key == BaseitemKey(ItemKindType::SWORD, SV_DIAMOND_EDGE);
544 const auto is_normal_item = o_ptr->is_valid() && !o_ptr->is_fixed_or_random_artifact() && !o_ptr->is_ego() && !o_ptr->is_cursed() && !special_weapon;
545 if (!is_normal_item) {
550 msg_print(_("属性付加に失敗した。", "The branding failed."));
551 chg_virtue(player_ptr, Virtue::ENCHANT, -2);
552 calc_android_exp(player_ptr);
556 const auto item_name = describe_flavor(player_ptr, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
557 concptr act = nullptr;
558 switch (brand_type) {
560 if (o_ptr->bi_key.tval() == ItemKindType::SWORD) {
561 act = _("は鋭さを増した!", "becomes very sharp!");
562 o_ptr->ego_idx = EgoType::SHARPNESS;
563 o_ptr->pval = (PARAMETER_VALUE)m_bonus(5, player_ptr->current_floor_ptr->dun_level) + 1;
564 if ((o_ptr->bi_key.sval() == SV_HAYABUSA) && (o_ptr->pval > 2)) {
568 act = _("は破壊力を増した!", "seems very powerful.");
569 o_ptr->ego_idx = EgoType::EARTHQUAKES;
570 o_ptr->pval = (PARAMETER_VALUE)m_bonus(3, player_ptr->current_floor_ptr->dun_level);
575 act = _("は人間の血を求めている!", "seems to be looking for humans!");
576 o_ptr->ego_idx = EgoType::KILL_HUMAN;
579 act = _("は電撃に覆われた!", "covered with lightning!");
580 o_ptr->ego_idx = EgoType::BRAND_ELEC;
583 act = _("は酸に覆われた!", "coated with acid!");
584 o_ptr->ego_idx = EgoType::BRAND_ACID;
587 act = _("は邪悪なる怪物を求めている!", "seems to be looking for evil monsters!");
588 o_ptr->ego_idx = EgoType::KILL_EVIL;
591 act = _("は異世界の住人の肉体を求めている!", "seems to be looking for demons!");
592 o_ptr->ego_idx = EgoType::KILL_DEMON;
595 act = _("は屍を求めている!", "seems to be looking for undead!");
596 o_ptr->ego_idx = EgoType::KILL_UNDEAD;
599 act = _("は動物の血を求めている!", "seems to be looking for animals!");
600 o_ptr->ego_idx = EgoType::KILL_ANIMAL;
603 act = _("はドラゴンの血を求めている!", "seems to be looking for dragons!");
604 o_ptr->ego_idx = EgoType::KILL_DRAGON;
607 act = _("はトロルの血を求めている!", "seems to be looking for troll!s");
608 o_ptr->ego_idx = EgoType::KILL_TROLL;
611 act = _("はオークの血を求めている!", "seems to be looking for orcs!");
612 o_ptr->ego_idx = EgoType::KILL_ORC;
615 act = _("は巨人の血を求めている!", "seems to be looking for giants!");
616 o_ptr->ego_idx = EgoType::KILL_GIANT;
619 act = _("は非常に不安定になったようだ。", "seems very unstable now.");
620 o_ptr->ego_idx = EgoType::TRUMP;
621 o_ptr->pval = randint1(2);
624 act = _("は血を求めている!", "thirsts for blood!");
625 o_ptr->ego_idx = EgoType::VAMPIRIC;
628 act = _("は毒に覆われた。", "is coated with poison.");
629 o_ptr->ego_idx = EgoType::BRAND_POIS;
632 act = _("は純ログルスに飲み込まれた。", "is engulfed in raw Logrus!");
633 o_ptr->ego_idx = EgoType::CHAOTIC;
636 act = _("は炎のシールドに覆われた!", "is covered in a fiery shield!");
637 o_ptr->ego_idx = EgoType::BRAND_FIRE;
640 act = _("は深く冷たいブルーに輝いた!", "glows deep, icy blue!");
641 o_ptr->ego_idx = EgoType::BRAND_COLD;
645 msg_format(_("あなたの%s%s", "Your %s %s"), item_name.data(), act);
646 enchant_equipment(player_ptr, o_ptr, randint0(3) + 4, ENCH_TOHIT | ENCH_TODAM);
647 o_ptr->discount = 99;
648 chg_virtue(player_ptr, Virtue::ENCHANT, 2);
649 calc_android_exp(player_ptr);