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 "object-enchant/item-apply-magic.h"
18 #include "object-enchant/item-feeling.h"
19 #include "object-enchant/item-magic-applier.h"
20 #include "object-enchant/object-boost.h"
21 #include "object-enchant/object-ego.h"
22 #include "object-enchant/special-object-flags.h"
23 #include "object-enchant/trc-types.h"
24 #include "object-enchant/trg-types.h"
25 #include "object-hook/hook-armor.h"
26 #include "object-hook/hook-weapon.h"
27 #include "object/item-tester-hooker.h"
28 #include "object/item-use-flags.h"
29 #include "object/object-kind-hook.h"
30 #include "perception/object-perception.h"
31 #include "player-info/class-info.h"
32 #include "racial/racial-android.h"
33 #include "sv-definition/sv-other-types.h"
34 #include "sv-definition/sv-scroll-types.h"
35 #include "sv-definition/sv-weapon-types.h"
36 #include "system/artifact-type-definition.h"
37 #include "system/baseitem-info.h"
38 #include "system/floor-type-definition.h"
39 #include "system/item-entity.h"
40 #include "system/monster-race-info.h"
41 #include "system/player-type-definition.h"
42 #include "system/redrawing-flags-updater.h"
43 #include "term/screen-processor.h"
44 #include "util/bit-flags-calculator.h"
45 #include "util/probability-table.h"
46 #include "view/display-messages.h"
49 * @brief 装備強化処理の失敗率定数 (千分率)
50 * @details 強化値が負値から0までは必ず成功する
51 * 正値は+15までしか鍛えることができず、+16以上への強化を試みると確実に失敗する
53 static constexpr std::array<int, 16> enchant_table = { { 0, 10, 50, 100, 200, 300, 400, 500, 650, 800, 950, 987, 993, 995, 998, 1000 } };
56 * Scatter some "amusing" objects near the player
58 enum class AmusementFlagType : byte {
59 NOTHING, /* No restriction */
60 NO_UNIQUE, /* Don't make the amusing object of uniques */
61 FIXED_ART, /* Make a fixed artifact based on the amusing object */
62 MULTIPLE, /* Drop 1-3 objects for one type */
63 PILE, /* Drop 1-99 pile objects for one type */
66 class AmuseDefinition {
68 AmuseDefinition(const BaseitemKey &key, PERCENTAGE prob, AmusementFlagType flag)
77 AmusementFlagType flag;
80 static const std::array<AmuseDefinition, 13> amuse_info = { {
81 { { ItemKindType::BOTTLE }, 5, AmusementFlagType::NOTHING },
82 { { ItemKindType::JUNK }, 3, AmusementFlagType::MULTIPLE },
83 { { ItemKindType::SPIKE }, 10, AmusementFlagType::PILE },
84 { { ItemKindType::STATUE }, 15, AmusementFlagType::NOTHING },
85 { { ItemKindType::CORPSE }, 15, AmusementFlagType::NO_UNIQUE },
86 { { ItemKindType::SKELETON }, 10, AmusementFlagType::NO_UNIQUE },
87 { { ItemKindType::FIGURINE }, 10, AmusementFlagType::NO_UNIQUE },
88 { { ItemKindType::PARCHMENT }, 1, AmusementFlagType::NOTHING },
89 { { ItemKindType::POLEARM, SV_TSURIZAO }, 3, AmusementFlagType::NOTHING }, // Fishing Pole of Taikobo
90 { { ItemKindType::SWORD, SV_BROKEN_DAGGER }, 3, AmusementFlagType::FIXED_ART }, // Broken Dagger of Magician
91 { { ItemKindType::SWORD, SV_BROKEN_DAGGER }, 10, AmusementFlagType::NOTHING },
92 { { ItemKindType::SWORD, SV_BROKEN_SWORD }, 5, AmusementFlagType::NOTHING },
93 { { ItemKindType::SCROLL, SV_SCROLL_AMUSEMENT }, 10, AmusementFlagType::NOTHING },
96 static std::optional<FixedArtifactId> sweep_amusement_artifact(const bool insta_art, const short bi_id)
98 for (const auto &[a_idx, artifact] : artifacts_info) {
99 if (a_idx == FixedArtifactId::NONE) {
103 if (insta_art && !artifact.gen_flags.has(ItemGenerationTraitType::INSTA_ART)) {
107 if (artifact.bi_key != baseitems_info[bi_id].bi_key) {
111 if (artifact.is_generated) {
123 * @param player_ptr プレイヤーへの参照ポインタ
125 * @param known TRUEならばオブジェクトが必ず*鑑定*済になる
127 void generate_amusement(PlayerType *player_ptr, int num, bool known)
129 ProbabilityTable<const AmuseDefinition *> pt;
130 for (const auto &am_ref : amuse_info) {
131 pt.entry_item(&am_ref, am_ref.prob);
134 for (auto i = 0; i < num; i++) {
135 auto am_ptr = pt.pick_one_at_random();
136 const auto bi_id = lookup_baseitem_id(am_ptr->key);
141 const auto insta_art = baseitems_info[bi_id].gen_flags.has(ItemGenerationTraitType::INSTA_ART);
142 const auto flag = am_ptr->flag;
143 const auto fixed_art = flag == AmusementFlagType::FIXED_ART;
144 std::optional<FixedArtifactId> opt_a_idx(std::nullopt);
145 if (insta_art || fixed_art) {
146 opt_a_idx = sweep_amusement_artifact(insta_art, bi_id);
155 item.fixed_artifact_idx = *opt_a_idx;
158 ItemMagicApplier(player_ptr, &item, 1, AM_NO_FIXED_ART).execute();
159 if (flag == AmusementFlagType::NO_UNIQUE) {
160 if (monraces_info[i2enum<MonsterRaceId>(item.pval)].kind_flags.has(MonsterKindType::UNIQUE)) {
165 if (flag == AmusementFlagType::MULTIPLE) {
166 item.number = randint1(3);
169 if (flag == AmusementFlagType::PILE) {
170 item.number = randint1(99);
174 object_aware(player_ptr, &item);
175 item.mark_as_known();
178 (void)drop_near(player_ptr, &item, -1, player_ptr->y, player_ptr->x);
184 * Scatter some "great" objects near the player
185 * @param player_ptr プレイヤーへの参照ポインタ
186 * @param y1 配置したいフロアのY座標
187 * @param x1 配置したいフロアのX座標
189 * @param great TRUEならば必ず高級品以上を落とす
191 void acquirement(PlayerType *player_ptr, POSITION y1, POSITION x1, int num, bool great)
193 auto mode = AM_GOOD | (great ? AM_GREAT : AM_NONE);
194 for (auto i = 0; i < num; i++) {
196 if (!make_object(player_ptr, &item, mode)) {
200 (void)drop_near(player_ptr, &item, -1, y1, x1);
206 * Curse the players armor
207 * @return 何も持っていない場合を除き、常にTRUEを返す
208 * @todo 元のreturnは間違っているが、修正後の↓文がどれくらい正しいかは要チェック
210 bool curse_armor(PlayerType *player_ptr)
212 /* Curse the body armor */
213 auto *o_ptr = &player_ptr->inventory_list[INVEN_BODY];
214 if (!o_ptr->is_valid()) {
218 const auto item_name = describe_flavor(player_ptr, o_ptr, OD_OMIT_PREFIX);
220 if (o_ptr->is_fixed_or_random_artifact() && one_in_(2)) {
222 msg_format("%sが%sを包み込もうとしたが、%sはそれを跳ね返した!", "恐怖の暗黒オーラ", "防具", item_name.data());
224 msg_format("A %s tries to %s, but your %s resists the effects!", "terrible black aura", "surround your armor", item_name.data());
229 msg_format(_("恐怖の暗黒オーラがあなたの%sを包み込んだ!", "A terrible black aura blasts your %s!"), item_name.data());
230 chg_virtue(player_ptr, Virtue::ENCHANT, -5);
231 o_ptr->fixed_artifact_idx = FixedArtifactId::NONE;
232 o_ptr->ego_idx = EgoType::BLASTED;
233 o_ptr->to_a = 0 - randint1(5) - randint1(5);
239 o_ptr->art_flags.clear();
240 o_ptr->curse_flags.set(CurseTraitType::CURSED);
241 o_ptr->ident |= IDENT_BROKEN;
242 auto &rfu = RedrawingFlagsUpdater::get_instance();
243 static constexpr auto flags_srf = {
244 StatusRecalculatingFlag::BONUS,
245 StatusRecalculatingFlag::MP,
247 rfu.set_flags(flags_srf);
248 static constexpr auto flags_swrf = {
249 SubWindowRedrawingFlag::INVENTORY,
250 SubWindowRedrawingFlag::EQUIPMENT,
251 SubWindowRedrawingFlag::PLAYER,
253 rfu.set_flags(flags_swrf);
259 * Curse the players weapon
260 * @param player_ptr 所持者の参照ポインタ
261 * @param force 無条件に呪縛を行うならばTRUE
262 * @param o_ptr 呪縛する武器のアイテム情報参照ポインタ
263 * @return 何も持っていない場合を除き、常にTRUEを返す
264 * @todo 元のreturnは間違っているが、修正後の↓文がどれくらい正しいかは要チェック
266 bool curse_weapon_object(PlayerType *player_ptr, bool force, ItemEntity *o_ptr)
268 if (!o_ptr->is_valid()) {
272 const auto item_name = describe_flavor(player_ptr, o_ptr, OD_OMIT_PREFIX);
273 if (o_ptr->is_fixed_or_random_artifact() && one_in_(2) && !force) {
275 msg_format("%sが%sを包み込もうとしたが、%sはそれを跳ね返した!", "恐怖の暗黒オーラ", "武器", item_name.data());
277 msg_format("A %s tries to %s, but your %s resists the effects!", "terrible black aura", "surround your weapon", item_name.data());
283 msg_format(_("恐怖の暗黒オーラがあなたの%sを包み込んだ!", "A terrible black aura blasts your %s!"), item_name.data());
286 chg_virtue(player_ptr, Virtue::ENCHANT, -5);
287 o_ptr->fixed_artifact_idx = FixedArtifactId::NONE;
288 o_ptr->ego_idx = EgoType::SHATTERED;
289 o_ptr->to_h = 0 - randint1(5) - randint1(5);
290 o_ptr->to_d = 0 - randint1(5) - randint1(5);
295 o_ptr->art_flags.clear();
296 o_ptr->curse_flags.set(CurseTraitType::CURSED);
297 o_ptr->ident |= IDENT_BROKEN;
298 auto &rfu = RedrawingFlagsUpdater::get_instance();
299 static constexpr auto flags_srf = {
300 StatusRecalculatingFlag::BONUS,
301 StatusRecalculatingFlag::MP,
303 rfu.set_flags(flags_srf);
304 static constexpr auto flags_swrf = {
305 SubWindowRedrawingFlag::INVENTORY,
306 SubWindowRedrawingFlag::EQUIPMENT,
307 SubWindowRedrawingFlag::PLAYER,
309 rfu.set_flags(flags_swrf);
314 * @brief ボルトのエゴ化処理(火炎エゴのみ) /
316 * @param player_ptr プレイヤーへの参照ポインタ
318 void brand_bolts(PlayerType *player_ptr)
320 for (auto i = 0; i < INVEN_PACK; i++) {
321 auto *o_ptr = &player_ptr->inventory_list[i];
322 if (o_ptr->bi_key.tval() != ItemKindType::BOLT) {
326 if (o_ptr->is_fixed_or_random_artifact() || o_ptr->is_ego()) {
330 if (o_ptr->is_cursed() || o_ptr->is_broken()) {
334 if (randint0(100) < 75) {
338 msg_print(_("クロスボウの矢が炎のオーラに包まれた!", "Your bolts are covered in a fiery aura!"));
339 o_ptr->ego_idx = EgoType::FLAME;
340 enchant_equipment(o_ptr, randint0(3) + 4, ENCH_TOHIT | ENCH_TODAM);
348 msg_print(_("炎で強化するのに失敗した。", "The fiery enchantment failed."));
353 * Break the curse of an item
354 * @param o_ptr 呪い装備情報の参照ポインタ
356 static void break_curse(ItemEntity *o_ptr)
358 const auto has_heavier_curse = o_ptr->curse_flags.has_any_of({ CurseTraitType::PERMA_CURSE, CurseTraitType::HEAVY_CURSE });
359 const auto is_curse_broken = o_ptr->is_cursed() && !has_heavier_curse && one_in_(4);
360 if (!is_curse_broken) {
364 msg_print(_("かけられていた呪いが打ち破られた!", "The curse is broken!"));
365 o_ptr->curse_flags.clear();
366 o_ptr->ident |= IDENT_SENSE;
367 o_ptr->feeling = FEEL_NONE;
372 * @param o_ptr 強化するアイテムの参照ポインタ
374 * @param eflag 強化オプション(命中/ダメージ/AC)
375 * @return 強化に成功した場合TRUEを返す
377 bool enchant_equipment(ItemEntity *o_ptr, int n, int eflag)
379 auto prob = o_ptr->number * 100;
380 if (o_ptr->is_ammo()) {
386 auto a = o_ptr->is_fixed_or_random_artifact();
387 auto force = (eflag & ENCH_FORCE);
388 for (int i = 0; i < n; i++) {
389 if (!force && randint0(prob) >= 100) {
393 if (eflag & ENCH_TOHIT) {
394 if (o_ptr->to_h < 0) {
396 } else if (o_ptr->to_h > 15) {
399 chance = enchant_table[o_ptr->to_h];
402 if (force || ((randint1(1000) > chance) && (!a || (randint0(100) < 50)))) {
405 if (o_ptr->to_h >= 0) {
411 if (eflag & ENCH_TODAM) {
412 if (o_ptr->to_d < 0) {
414 } else if (o_ptr->to_d > 15) {
417 chance = enchant_table[o_ptr->to_d];
420 if (force || ((randint1(1000) > chance) && (!a || (randint0(100) < 50)))) {
423 if (o_ptr->to_d >= 0) {
429 if (!(eflag & ENCH_TOAC)) {
433 if (o_ptr->to_a < 0) {
435 } else if (o_ptr->to_a > 15) {
438 chance = enchant_table[o_ptr->to_a];
441 if (force || ((randint1(1000) > chance) && (!a || (randint0(100) < 50)))) {
444 if (o_ptr->to_a >= 0) {
454 auto &rfu = RedrawingFlagsUpdater::get_instance();
455 static constexpr auto flags_srf = {
456 StatusRecalculatingFlag::BONUS,
457 StatusRecalculatingFlag::COMBINATION,
458 StatusRecalculatingFlag::REORDER,
460 rfu.set_flags(flags_srf);
461 static constexpr auto flags_swrf = {
462 SubWindowRedrawingFlag::INVENTORY,
463 SubWindowRedrawingFlag::EQUIPMENT,
464 SubWindowRedrawingFlag::PLAYER,
465 SubWindowRedrawingFlag::FLOOR_ITEMS,
466 SubWindowRedrawingFlag::FOUND_ITEMS,
468 rfu.set_flags(flags_swrf);
473 * @brief 装備修正強化処理のメインルーチン
474 * @param player_ptr プレイヤーへの参照ポインタ
475 * @param num_hit 命中修正量
476 * @param num_dam ダメージ修正量
477 * @param num_ac AC修正量
478 * @return 強化に成功した場合TRUEを返す
480 bool enchant_spell(PlayerType *player_ptr, HIT_PROB num_hit, int num_dam, ARMOUR_CLASS num_ac)
482 FuncItemTester item_tester(&ItemEntity::allow_enchant_weapon);
484 item_tester = FuncItemTester(&ItemEntity::is_protector);
487 constexpr auto q = _("どのアイテムを強化しますか? ", "Enchant which item? ");
488 constexpr auto s = _("強化できるアイテムがない。", "You have nothing to enchant.");
490 const auto options = USE_EQUIP | USE_INVEN | USE_FLOOR | IGNORE_BOTHHAND_SLOT;
491 auto *o_ptr = choose_object(player_ptr, &i_idx, q, s, options, item_tester);
496 const auto item_name = describe_flavor(player_ptr, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
498 msg_format("%s は明るく輝いた!", item_name.data());
500 msg_format("%s %s glow%s brightly!", ((i_idx >= 0) ? "Your" : "The"), item_name.data(), ((o_ptr->number > 1) ? "" : "s"));
503 auto is_enchant_successful = false;
504 if (enchant_equipment(o_ptr, num_hit, ENCH_TOHIT)) {
505 is_enchant_successful = true;
508 if (enchant_equipment(o_ptr, num_dam, ENCH_TODAM)) {
509 is_enchant_successful = true;
512 if (enchant_equipment(o_ptr, num_ac, ENCH_TOAC)) {
513 is_enchant_successful = true;
516 if (!is_enchant_successful) {
520 msg_print(_("強化に失敗した。", "The enchantment failed."));
522 chg_virtue(player_ptr, Virtue::ENCHANT, -1);
525 chg_virtue(player_ptr, Virtue::ENCHANT, 1);
528 calc_android_exp(player_ptr);
534 * @param player_ptr プレイヤーへの参照ポインタ
535 * @param brand_type エゴ化ID(EgoDefinitionsとは連動していない)
537 void brand_weapon(PlayerType *player_ptr, int brand_type)
539 constexpr auto q = _("どの武器を強化しますか? ", "Enchant which weapon? ");
540 constexpr auto s = _("強化できる武器がない。", "You have nothing to enchant.");
542 const auto options = USE_EQUIP | IGNORE_BOTHHAND_SLOT;
543 auto *o_ptr = choose_object(player_ptr, &i_idx, q, s, options, FuncItemTester(&ItemEntity::allow_enchant_melee_weapon));
544 if (o_ptr == nullptr) {
548 const auto &bi_key = o_ptr->bi_key;
549 auto special_weapon = bi_key == BaseitemKey(ItemKindType::SWORD, SV_POISON_NEEDLE);
550 special_weapon |= bi_key == BaseitemKey(ItemKindType::POLEARM, SV_DEATH_SCYTHE);
551 special_weapon |= bi_key == BaseitemKey(ItemKindType::SWORD, SV_DIAMOND_EDGE);
552 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;
553 if (!is_normal_item) {
558 msg_print(_("属性付加に失敗した。", "The branding failed."));
559 chg_virtue(player_ptr, Virtue::ENCHANT, -2);
560 calc_android_exp(player_ptr);
564 const auto item_name = describe_flavor(player_ptr, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
565 concptr act = nullptr;
566 switch (brand_type) {
568 if (o_ptr->bi_key.tval() == ItemKindType::SWORD) {
569 act = _("は鋭さを増した!", "becomes very sharp!");
570 o_ptr->ego_idx = EgoType::SHARPNESS;
571 o_ptr->pval = (PARAMETER_VALUE)m_bonus(5, player_ptr->current_floor_ptr->dun_level) + 1;
572 if ((o_ptr->bi_key.sval() == SV_HAYABUSA) && (o_ptr->pval > 2)) {
576 act = _("は破壊力を増した!", "seems very powerful.");
577 o_ptr->ego_idx = EgoType::EARTHQUAKES;
578 o_ptr->pval = (PARAMETER_VALUE)m_bonus(3, player_ptr->current_floor_ptr->dun_level);
583 act = _("は人間の血を求めている!", "seems to be looking for humans!");
584 o_ptr->ego_idx = EgoType::KILL_HUMAN;
587 act = _("は電撃に覆われた!", "covered with lightning!");
588 o_ptr->ego_idx = EgoType::BRAND_ELEC;
591 act = _("は酸に覆われた!", "coated with acid!");
592 o_ptr->ego_idx = EgoType::BRAND_ACID;
595 act = _("は邪悪なる怪物を求めている!", "seems to be looking for evil monsters!");
596 o_ptr->ego_idx = EgoType::KILL_EVIL;
599 act = _("は異世界の住人の肉体を求めている!", "seems to be looking for demons!");
600 o_ptr->ego_idx = EgoType::KILL_DEMON;
603 act = _("は屍を求めている!", "seems to be looking for undead!");
604 o_ptr->ego_idx = EgoType::KILL_UNDEAD;
607 act = _("は動物の血を求めている!", "seems to be looking for animals!");
608 o_ptr->ego_idx = EgoType::KILL_ANIMAL;
611 act = _("はドラゴンの血を求めている!", "seems to be looking for dragons!");
612 o_ptr->ego_idx = EgoType::KILL_DRAGON;
615 act = _("はトロルの血を求めている!", "seems to be looking for troll!s");
616 o_ptr->ego_idx = EgoType::KILL_TROLL;
619 act = _("はオークの血を求めている!", "seems to be looking for orcs!");
620 o_ptr->ego_idx = EgoType::KILL_ORC;
623 act = _("は巨人の血を求めている!", "seems to be looking for giants!");
624 o_ptr->ego_idx = EgoType::KILL_GIANT;
627 act = _("は非常に不安定になったようだ。", "seems very unstable now.");
628 o_ptr->ego_idx = EgoType::TRUMP;
629 o_ptr->pval = randint1(2);
632 act = _("は血を求めている!", "thirsts for blood!");
633 o_ptr->ego_idx = EgoType::VAMPIRIC;
636 act = _("は毒に覆われた。", "is coated with poison.");
637 o_ptr->ego_idx = EgoType::BRAND_POIS;
640 act = _("は純ログルスに飲み込まれた。", "is engulfed in raw Logrus!");
641 o_ptr->ego_idx = EgoType::CHAOTIC;
644 act = _("は炎のシールドに覆われた!", "is covered in a fiery shield!");
645 o_ptr->ego_idx = EgoType::BRAND_FIRE;
648 act = _("は深く冷たいブルーに輝いた!", "glows deep, icy blue!");
649 o_ptr->ego_idx = EgoType::BRAND_COLD;
653 msg_format(_("あなたの%s%s", "Your %s %s"), item_name.data(), act);
654 enchant_equipment(o_ptr, randint0(3) + 4, ENCH_TOHIT | ENCH_TODAM);
655 o_ptr->discount = 99;
656 chg_virtue(player_ptr, Virtue::ENCHANT, 2);
657 calc_android_exp(player_ptr);