OSDN Git Service

[Refactor] #3286 Removed player-redraw-types.h
[hengbandforosx/hengbandosx.git] / src / cmd-item / cmd-equipment.cpp
1 #include "cmd-item/cmd-equipment.h"
2 #include "action/weapon-shield.h"
3 #include "artifact/fixed-art-types.h"
4 #include "autopick/autopick.h"
5 #include "avatar/avatar.h"
6 #include "core/asking-player.h"
7 #include "core/window-redrawer.h"
8 #include "dungeon/quest.h" //!< @todo 違和感、何故アイテムを装備するとクエストの成功判定が走るのか?.
9 #include "flavor/flavor-describer.h"
10 #include "flavor/object-flavor-types.h"
11 #include "floor/floor-object.h"
12 #include "game-option/birth-options.h"
13 #include "game-option/input-options.h"
14 #include "inventory/inventory-describer.h"
15 #include "inventory/inventory-object.h"
16 #include "inventory/inventory-slot-types.h"
17 #include "io/input-key-acceptor.h"
18 #include "io/input-key-requester.h"
19 #include "locale/japanese.h"
20 #include "main/sound-definitions-table.h"
21 #include "main/sound-of-music.h"
22 #include "object-enchant/item-feeling.h"
23 #include "object-enchant/special-object-flags.h"
24 #include "object-enchant/trc-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-flags.h"
30 #include "object/object-info.h"
31 #include "object/object-mark-types.h"
32 #include "perception/object-perception.h"
33 #include "player-base/player-class.h"
34 #include "player-base/player-race.h"
35 #include "player-info/equipment-info.h"
36 #include "player-info/samurai-data-type.h"
37 #include "player-status/player-energy.h"
38 #include "player-status/player-hand-types.h"
39 #include "player/attack-defense-types.h"
40 #include "player/player-status.h"
41 #include "player/special-defense-types.h"
42 #include "racial/racial-android.h"
43 #include "spell-kind/spells-perception.h"
44 #include "status/action-setter.h"
45 #include "status/shape-changer.h"
46 #include "system/item-entity.h"
47 #include "system/player-type-definition.h"
48 #include "system/redrawing-flags-updater.h"
49 #include "term/screen-processor.h"
50 #include "term/z-form.h"
51 #include "util/bit-flags-calculator.h"
52 #include "util/int-char-converter.h"
53 #include "view/display-inventory.h"
54 #include "view/display-messages.h"
55
56 /*!
57  * @brief 装備時にアイテムを呪う処理
58  */
59 static void do_curse_on_equip(OBJECT_IDX slot, ItemEntity *o_ptr, PlayerType *player_ptr)
60 {
61     auto &rfu = RedrawingFlagsUpdater::get_instance();
62     if (set_anubis_and_chariot(player_ptr) && ((slot == INVEN_MAIN_HAND) || (slot == INVEN_SUB_HAND))) {
63
64         ItemEntity *anubis = &(player_ptr->inventory_list[INVEN_MAIN_HAND]);
65         ItemEntity *chariot = &(player_ptr->inventory_list[INVEN_SUB_HAND]);
66
67         anubis->curse_flags.set(CurseTraitType::PERSISTENT_CURSE);
68         anubis->curse_flags.set(CurseTraitType::HEAVY_CURSE);
69         chariot->curse_flags.set(CurseTraitType::PERSISTENT_CURSE);
70         chariot->curse_flags.set(CurseTraitType::HEAVY_CURSE);
71         chariot->curse_flags.set(CurseTraitType::BERS_RAGE);
72         chariot->curse_flags.set(CurseTraitType::VUL_CURSE);
73
74         msg_format(_("『銀の戦車』プラス『アヌビス神』二刀流ッ!", "*Silver Chariot* plus *Anubis God* Two Swords!"));
75         rfu.set_flag(StatusRedrawingFlag::BONUS);
76         return;
77     }
78
79     auto should_curse = object_flags(o_ptr).has(TR_PERSISTENT_CURSE) || o_ptr->curse_flags.has(CurseTraitType::PERSISTENT_CURSE);
80     should_curse &= o_ptr->curse_flags.has_not(CurseTraitType::HEAVY_CURSE);
81     if (!should_curse) {
82         return;
83     }
84
85     const auto item_name = describe_flavor(player_ptr, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
86     o_ptr->curse_flags.set(CurseTraitType::HEAVY_CURSE);
87     msg_format(_("悪意に満ちた黒いオーラが%sをとりまいた...", "There is a malignant black aura surrounding your %s..."), item_name.data());
88     o_ptr->feeling = FEEL_NONE;
89     rfu.set_flag(StatusRedrawingFlag::BONUS);
90 }
91
92 /*!
93  * @brief 装備一覧を表示するコマンドのメインルーチン / Display equipment
94  */
95 void do_cmd_equip(PlayerType *player_ptr)
96 {
97     command_wrk = true;
98     if (easy_floor) {
99         command_wrk = USE_EQUIP;
100     }
101
102     screen_save();
103     (void)show_equipment(player_ptr, 0, USE_FULL, AllMatchItemTester());
104     auto weight = calc_inventory_weight(player_ptr);
105     auto weight_lim = calc_weight_limit(player_ptr);
106     const auto mes = _("装備: 合計 %3d.%1d kg (限界の%d%%) コマンド: ", "Equipment: carrying %d.%d pounds (%d%% of capacity). Command: ");
107 #ifdef JP
108     const auto out_val = format(mes, lb_to_kg_integer(weight), lb_to_kg_fraction(weight), weight * 100 / weight_lim);
109 #else
110     const auto out_val = format(mes, weight / 10, weight % 10, weight * 100 / weight_lim);
111 #endif
112
113     prt(out_val, 0, 0);
114     command_new = inkey();
115     screen_load();
116
117     if (command_new != ESCAPE) {
118         command_see = true;
119         return;
120     }
121
122     TERM_LEN wid, hgt;
123     term_get_size(&wid, &hgt);
124     command_new = 0;
125     command_gap = wid - 30;
126 }
127
128 /*!
129  * @brief 装備するコマンドのメインルーチン / Wield or wear a single item from the pack or floor
130  * @param player_ptr プレイヤーへの参照ポインタ
131  */
132 void do_cmd_wield(PlayerType *player_ptr)
133 {
134     OBJECT_IDX item, slot;
135     ItemEntity forge;
136     ItemEntity *q_ptr;
137     ItemEntity *o_ptr;
138     concptr act;
139     OBJECT_IDX need_switch_wielding = 0;
140     PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU });
141
142     concptr q = _("どれを装備しますか? ", "Wear/Wield which item? ");
143     concptr s = _("装備可能なアイテムがない。", "You have nothing you can wear or wield.");
144     o_ptr = choose_object(player_ptr, &item, q, s, (USE_INVEN | USE_FLOOR), FuncItemTester(item_tester_hook_wear, player_ptr));
145     if (!o_ptr) {
146         return;
147     }
148
149     slot = wield_slot(player_ptr, o_ptr);
150
151     const auto o_ptr_mh = &player_ptr->inventory_list[INVEN_MAIN_HAND];
152     const auto o_ptr_sh = &player_ptr->inventory_list[INVEN_SUB_HAND];
153     const auto tval = o_ptr->bi_key.tval();
154     switch (tval) {
155     case ItemKindType::CAPTURE:
156     case ItemKindType::SHIELD:
157     case ItemKindType::CARD:
158         if (has_melee_weapon(player_ptr, INVEN_MAIN_HAND) && has_melee_weapon(player_ptr, INVEN_SUB_HAND)) {
159             q = _("どちらの武器と取り替えますか?", "Replace which weapon? ");
160             s = _("おっと。", "Oops.");
161             if (!choose_object(player_ptr, &slot, q, s, (USE_EQUIP | IGNORE_BOTHHAND_SLOT), FuncItemTester(&ItemEntity::is_melee_weapon))) {
162                 return;
163             }
164
165             if (slot == INVEN_MAIN_HAND) {
166                 need_switch_wielding = INVEN_SUB_HAND;
167             }
168         } else if (has_melee_weapon(player_ptr, INVEN_SUB_HAND)) {
169             slot = INVEN_MAIN_HAND;
170         } else if (o_ptr_mh->is_valid() && o_ptr_sh->is_valid() &&
171                    ((tval == ItemKindType::CAPTURE) || (!o_ptr_mh->is_melee_weapon() && !o_ptr_sh->is_melee_weapon()))) {
172             q = _("どちらの手に装備しますか?", "Equip which hand? ");
173             s = _("おっと。", "Oops.");
174             if (!choose_object(player_ptr, &slot, q, s, (USE_EQUIP), FuncItemTester(&ItemEntity::is_wieldable_in_etheir_hand))) {
175                 return;
176             }
177         }
178
179         break;
180     case ItemKindType::DIGGING:
181     case ItemKindType::HAFTED:
182     case ItemKindType::POLEARM:
183     case ItemKindType::SWORD:
184         if (slot == INVEN_SUB_HAND) {
185             if (!get_check(_("二刀流で戦いますか?", "Dual wielding? "))) {
186                 slot = INVEN_MAIN_HAND;
187             }
188         } else if (!o_ptr_mh->is_valid() && has_melee_weapon(player_ptr, INVEN_SUB_HAND)) {
189             if (!get_check(_("二刀流で戦いますか?", "Dual wielding? "))) {
190                 slot = INVEN_SUB_HAND;
191             }
192         } else if (o_ptr_mh->is_valid() && o_ptr_sh->is_valid()) {
193             q = _("どちらの手に装備しますか?", "Equip which hand? ");
194             s = _("おっと。", "Oops.");
195             if (!choose_object(player_ptr, &slot, q, s, (USE_EQUIP), FuncItemTester(&ItemEntity::is_wieldable_in_etheir_hand))) {
196                 return;
197             }
198
199             if ((slot == INVEN_SUB_HAND) && !has_melee_weapon(player_ptr, INVEN_MAIN_HAND)) {
200                 need_switch_wielding = INVEN_MAIN_HAND;
201             }
202         }
203
204         break;
205     case ItemKindType::RING:
206         if (player_ptr->inventory_list[INVEN_SUB_RING].is_valid() && player_ptr->inventory_list[INVEN_MAIN_RING].is_valid()) {
207             q = _("どちらの指輪と取り替えますか?", "Replace which ring? ");
208         } else {
209             q = _("どちらの手に装備しますか?", "Equip which hand? ");
210         }
211
212         s = _("おっと。", "Oops.");
213         player_ptr->select_ring_slot = true;
214         if (!choose_object(player_ptr, &slot, q, s, (USE_EQUIP | IGNORE_BOTHHAND_SLOT))) {
215             player_ptr->select_ring_slot = false;
216             return;
217         }
218
219         player_ptr->select_ring_slot = false;
220         break;
221
222     default:
223         break;
224     }
225
226     if (player_ptr->inventory_list[slot].is_cursed()) {
227         const auto item_name = describe_flavor(player_ptr, &player_ptr->inventory_list[slot], OD_OMIT_PREFIX | OD_NAME_ONLY);
228 #ifdef JP
229         msg_format("%s%sは呪われているようだ。", describe_use(player_ptr, slot), item_name.data());
230 #else
231         msg_format("The %s you are %s appears to be cursed.", item_name.data(), describe_use(player_ptr, slot));
232 #endif
233         return;
234     }
235
236     auto should_equip_cursed = o_ptr->is_cursed() && o_ptr->is_known();
237     should_equip_cursed |= any_bits(o_ptr->ident, IDENT_SENSE) && (FEEL_BROKEN <= o_ptr->feeling) && (o_ptr->feeling <= FEEL_CURSED);
238     should_equip_cursed &= confirm_wear;
239     if (should_equip_cursed) {
240         const auto item_name = describe_flavor(player_ptr, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
241         if (!get_check(format(_("本当に%s{呪われている}を使いますか?", "Really use the %s {cursed}? "), item_name.data()))) {
242             return;
243         }
244     }
245
246     PlayerRace pr(player_ptr);
247     auto should_change_vampire = o_ptr->is_specific_artifact(FixedArtifactId::STONEMASK);
248     should_change_vampire &= o_ptr->is_known();
249     should_change_vampire &= !pr.equals(PlayerRaceType::VAMPIRE);
250     should_change_vampire &= !pr.equals(PlayerRaceType::ANDROID);
251     if (should_change_vampire) {
252         const auto item_name = describe_flavor(player_ptr, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
253         constexpr auto mes = _("%sを装備すると吸血鬼になります。よろしいですか?",
254             "%s will transform you into a vampire permanently when equipped. Do you become a vampire? ");
255         if (!get_check(format(mes, item_name.data()))) {
256             return;
257         }
258     }
259
260     sound(SOUND_WIELD);
261     if (need_switch_wielding && !player_ptr->inventory_list[need_switch_wielding].is_cursed()) {
262         ItemEntity *slot_o_ptr = &player_ptr->inventory_list[slot];
263         ItemEntity *switch_o_ptr = &player_ptr->inventory_list[need_switch_wielding];
264         ItemEntity object_tmp;
265         ItemEntity *otmp_ptr = &object_tmp;
266         const auto item_name = describe_flavor(player_ptr, switch_o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
267         otmp_ptr->copy_from(switch_o_ptr);
268         switch_o_ptr->copy_from(slot_o_ptr);
269         slot_o_ptr->copy_from(otmp_ptr);
270         msg_format(_("%sを%sに構えなおした。", "You wield %s at %s hand."), item_name.data(),
271             (slot == INVEN_MAIN_HAND) ? (left_hander ? _("左手", "left") : _("右手", "right")) : (left_hander ? _("右手", "right") : _("左手", "left")));
272         slot = need_switch_wielding;
273     }
274
275     check_find_art_quest_completion(player_ptr, o_ptr);
276     if (player_ptr->ppersonality == PERSONALITY_MUNCHKIN) {
277         identify_item(player_ptr, o_ptr);
278         autopick_alter_item(player_ptr, item, false);
279     }
280
281     PlayerEnergy(player_ptr).set_player_turn_energy(100);
282     q_ptr = &forge;
283     q_ptr->copy_from(o_ptr);
284     q_ptr->number = 1;
285     if (item >= 0) {
286         inven_item_increase(player_ptr, item, -1);
287         inven_item_optimize(player_ptr, item);
288     } else {
289         floor_item_increase(player_ptr, 0 - item, -1);
290         floor_item_optimize(player_ptr, 0 - item);
291     }
292
293     o_ptr = &player_ptr->inventory_list[slot];
294     if (o_ptr->is_valid()) {
295         (void)inven_takeoff(player_ptr, slot, 255);
296     }
297
298     o_ptr->copy_from(q_ptr);
299     o_ptr->marked.set(OmType::TOUCHED);
300     player_ptr->equip_cnt++;
301
302 #define STR_WIELD_HAND_RIGHT _("%s(%c)を右手に装備した。", "You are wielding %s (%c) in your right hand.")
303 #define STR_WIELD_HAND_LEFT _("%s(%c)を左手に装備した。", "You are wielding %s (%c) in your left hand.")
304 #define STR_WIELD_HANDS_TWO _("%s(%c)を両手で構えた。", "You are wielding %s (%c) with both hands.")
305
306     switch (slot) {
307     case INVEN_MAIN_HAND:
308         if (o_ptr->allow_two_hands_wielding() && (empty_hands(player_ptr, false) == EMPTY_HAND_SUB) && can_two_hands_wielding(player_ptr)) {
309             act = STR_WIELD_HANDS_TWO;
310         } else {
311             act = (left_hander ? STR_WIELD_HAND_LEFT : STR_WIELD_HAND_RIGHT);
312         }
313
314         break;
315     case INVEN_SUB_HAND:
316         if (o_ptr->allow_two_hands_wielding() && (empty_hands(player_ptr, false) == EMPTY_HAND_MAIN) && can_two_hands_wielding(player_ptr)) {
317             act = STR_WIELD_HANDS_TWO;
318         } else {
319             act = (left_hander ? STR_WIELD_HAND_RIGHT : STR_WIELD_HAND_LEFT);
320         }
321
322         break;
323     case INVEN_BOW:
324         act = _("%s(%c)を射撃用に装備した。", "You are shooting with %s (%c).");
325         break;
326     case INVEN_LITE:
327         act = _("%s(%c)を光源にした。", "Your light source is %s (%c).");
328         break;
329     default:
330         act = _("%s(%c)を装備した。", "You are wearing %s (%c).");
331         break;
332     }
333
334     const auto item_name = describe_flavor(player_ptr, o_ptr, 0);
335     msg_format(act, item_name.data(), index_to_label(slot));
336     if (o_ptr->is_cursed()) {
337         msg_print(_("うわ! すさまじく冷たい!", "Oops! It feels deathly cold!"));
338         chg_virtue(player_ptr, Virtue::HARMONY, -1);
339         o_ptr->ident |= (IDENT_SENSE);
340     }
341
342     do_curse_on_equip(slot, o_ptr, player_ptr);
343     if (o_ptr->is_specific_artifact(FixedArtifactId::STONEMASK)) {
344         auto is_specific_race = pr.equals(PlayerRaceType::VAMPIRE);
345         is_specific_race |= pr.equals(PlayerRaceType::ANDROID);
346         if (!is_specific_race) {
347             change_race(player_ptr, PlayerRaceType::VAMPIRE, "");
348         }
349     }
350
351     calc_android_exp(player_ptr);
352     const auto flags_srf = {
353         StatusRedrawingFlag::BONUS,
354         StatusRedrawingFlag::TORCH,
355         StatusRedrawingFlag::MP,
356     };
357     auto &rfu = RedrawingFlagsUpdater::get_instance();
358     rfu.set_flags(flags_srf);
359     rfu.set_flag(MainWindowRedrawingFlag::EQUIPPY);
360     player_ptr->window_flags |= PW_INVENTORY | PW_EQUIPMENT | PW_PLAYER;
361 }
362
363 /*!
364  * @brief 装備を外すコマンドのメインルーチン / Take off an item
365  */
366 void do_cmd_takeoff(PlayerType *player_ptr)
367 {
368     OBJECT_IDX item;
369     ItemEntity *o_ptr;
370     PlayerClass pc(player_ptr);
371     pc.break_samurai_stance({ SamuraiStanceType::MUSOU });
372
373     concptr q = _("どれを装備からはずしますか? ", "Take off which item? ");
374     concptr s = _("はずせる装備がない。", "You are not wearing anything to take off.");
375     o_ptr = choose_object(player_ptr, &item, q, s, (USE_EQUIP | IGNORE_BOTHHAND_SLOT));
376     if (!o_ptr) {
377         return;
378     }
379
380     PlayerEnergy energy(player_ptr);
381     auto &rfu = RedrawingFlagsUpdater::get_instance();
382     if (o_ptr->is_cursed()) {
383         if (o_ptr->curse_flags.has(CurseTraitType::PERMA_CURSE) || !pc.equals(PlayerClassType::BERSERKER)) {
384             msg_print(_("ふーむ、どうやら呪われているようだ。", "Hmmm, it seems to be cursed."));
385             return;
386         }
387
388         if ((o_ptr->curse_flags.has(CurseTraitType::HEAVY_CURSE) && one_in_(7)) || one_in_(4)) {
389             msg_print(_("呪われた装備を力づくで剥がした!", "You tore off a piece of cursed equipment by sheer strength!"));
390             o_ptr->ident |= (IDENT_SENSE);
391             o_ptr->curse_flags.clear();
392             o_ptr->feeling = FEEL_NONE;
393             rfu.set_flag(StatusRedrawingFlag::BONUS);
394             player_ptr->window_flags |= PW_EQUIPMENT;
395             msg_print(_("呪いを打ち破った。", "You break the curse."));
396         } else {
397             msg_print(_("装備を外せなかった。", "You couldn't remove the equipment."));
398             energy.set_player_turn_energy(50);
399             return;
400         }
401     }
402
403     sound(SOUND_TAKE_OFF);
404     energy.set_player_turn_energy(50);
405     (void)inven_takeoff(player_ptr, item, 255);
406     verify_equip_slot(player_ptr, item);
407     calc_android_exp(player_ptr);
408     const auto flags_srf = {
409         StatusRedrawingFlag::BONUS,
410         StatusRedrawingFlag::TORCH,
411         StatusRedrawingFlag::MP,
412     };
413     rfu.set_flags(flags_srf);
414     rfu.set_flag(MainWindowRedrawingFlag::EQUIPPY);
415     player_ptr->window_flags |= PW_INVENTORY | PW_EQUIPMENT | PW_PLAYER;
416 }