OSDN Git Service

[Refactor] #3733 optional 型の値取得処理 value() を撤廃した その3
[hengbandforosx/hengbandosx.git] / src / market / building-craft-fix.cpp
1 #include "market/building-craft-fix.h"
2 #include "artifact/artifact-info.h"
3 #include "artifact/fixed-art-types.h"
4 #include "artifact/random-art-effects.h"
5 #include "core/asking-player.h"
6 #include "core/stuff-handler.h"
7 #include "flavor/flavor-describer.h"
8 #include "flavor/object-flavor-types.h"
9 #include "floor/floor-object.h"
10 #include "inventory/inventory-object.h"
11 #include "market/building-util.h"
12 #include "object-enchant/object-boost.h"
13 #include "object-enchant/special-object-flags.h"
14 #include "object-enchant/tr-types.h"
15 #include "object-hook/hook-weapon.h"
16 #include "object/item-tester-hooker.h"
17 #include "object/item-use-flags.h"
18 #include "object/object-kind-hook.h"
19 #include "object/object-value.h"
20 #include "racial/racial-android.h"
21 #include "spell-realm/spells-hex.h"
22 #include "sv-definition/sv-other-types.h"
23 #include "sv-definition/sv-weapon-types.h"
24 #include "system/baseitem-info.h"
25 #include "system/item-entity.h"
26 #include "system/player-type-definition.h"
27 #include "system/redrawing-flags-updater.h"
28 #include "term/screen-processor.h"
29 #include "util/bit-flags-calculator.h"
30 #include "view/display-messages.h"
31 #include <utility>
32 #include <vector>
33
34 /*!
35  * @brief 修復材料のオブジェクトから修復対象に特性を移植する。
36  * @param to_ptr 修復対象オブジェクトの構造体の参照ポインタ。
37  * @param from_ptr 修復材料オブジェクトの構造体の参照ポインタ。
38  * @return 修復対象になるならTRUEを返す。
39  */
40 static void give_one_ability_of_object(ItemEntity *to_ptr, ItemEntity *from_ptr)
41 {
42     const auto to_flags = to_ptr->get_flags();
43     const auto from_flags = from_ptr->get_flags();
44
45     std::vector<tr_type> candidates;
46     for (int i = 0; i < TR_FLAG_MAX; i++) {
47         switch (i) {
48         case TR_IGNORE_ACID:
49         case TR_IGNORE_ELEC:
50         case TR_IGNORE_FIRE:
51         case TR_IGNORE_COLD:
52         case TR_ACTIVATE:
53         case TR_RIDING:
54         case TR_THROW:
55         case TR_SHOW_MODS:
56         case TR_HIDE_TYPE:
57         case TR_XXX_93:
58         case TR_XXX_94:
59         case TR_FULL_NAME:
60         case TR_FIXED_FLAVOR:
61             break;
62         default:
63             auto tr_flag = i2enum<tr_type>(i);
64             if (from_flags.has(tr_flag) && to_flags.has_not(tr_flag)) {
65                 if (!(TR_PVAL_FLAG_MASK.has(tr_flag) && (from_ptr->pval < 1))) {
66                     candidates.push_back(tr_flag);
67                 }
68             }
69         }
70     }
71
72     if (candidates.empty()) {
73         return;
74     }
75
76     const auto tr_idx = rand_choice(candidates);
77     to_ptr->art_flags.set(tr_idx);
78     if (TR_PVAL_FLAG_MASK.has(tr_idx)) {
79         to_ptr->pval = std::max<short>(to_ptr->pval, 1);
80     }
81     auto bmax = std::min<short>(3, std::max<short>(1, 40 / (to_ptr->dd * to_ptr->ds)));
82     if (tr_idx == TR_BLOWS) {
83         to_ptr->pval = std::min<short>(to_ptr->pval, bmax);
84     }
85     if (tr_idx == TR_SPEED) {
86         to_ptr->pval = std::min<short>(to_ptr->pval, 4);
87     }
88 }
89
90 static std::pair<short, ItemEntity *> select_repairing_broken_weapon(PlayerType *player_ptr, const int row)
91 {
92     prt(_("修復には材料となるもう1つの武器が必要です。", "Hand one material weapon to repair a broken weapon."), row, 2);
93     prt(_("材料に使用した武器はなくなります!", "The material weapon will disappear after repairing!!"), row + 1, 2);
94     constexpr auto q = _("どの折れた武器を修復しますか?", "Repair which broken weapon? ");
95     constexpr auto s = _("修復できる折れた武器がありません。", "You have no broken weapon to repair.");
96     short i_idx;
97     auto *o_ptr = choose_object(player_ptr, &i_idx, q, s, (USE_INVEN | USE_EQUIP), FuncItemTester(&ItemEntity::is_broken_weapon));
98     if (o_ptr == nullptr) {
99         return { i_idx, nullptr };
100     }
101
102     if (!o_ptr->is_ego() && !o_ptr->is_fixed_or_random_artifact()) {
103         msg_format(_("それは直してもしょうがないぜ。", "It is worthless to repair."));
104         return { i_idx, o_ptr };
105     }
106
107     if (o_ptr->number > 1) {
108         msg_format(_("一度に複数を修復することはできません!", "They are too many to repair at once!"));
109         return { i_idx, o_ptr };
110     }
111
112     return { i_idx, o_ptr };
113 }
114
115 static void display_reparing_weapon(PlayerType *player_ptr, ItemEntity *o_ptr, const int row)
116 {
117     const auto item_name = describe_flavor(player_ptr, o_ptr, OD_NAME_ONLY);
118     prt(format(_("修復する武器 : %s", "Repairing: %s"), item_name.data()), row + 3, 2);
119 }
120
121 static void display_repair_success_message(PlayerType *player_ptr, ItemEntity *o_ptr, const int cost)
122 {
123     const auto item_name = describe_flavor(player_ptr, o_ptr, OD_NAME_ONLY);
124 #ifdef JP
125     msg_format("$%dで%sに修復しました。", cost, item_name.data());
126 #else
127     msg_format("Repaired into %s for %d gold.", item_name.data(), cost);
128 #endif
129     msg_print(nullptr);
130 }
131
132 /*!
133  * @brief アイテム修復処理のメインルーチン / Repair broken weapon
134  * @param player_ptr プレイヤーへの参照ポインタ
135  * @param bcost 基本修復費用
136  * @return 実際にかかった費用
137  */
138 static PRICE repair_broken_weapon_aux(PlayerType *player_ptr, PRICE bcost)
139 {
140     clear_bldg(0, 22);
141     auto row = 7;
142     const auto &[i_idx, o_ptr] = select_repairing_broken_weapon(player_ptr, row);
143     if (o_ptr == nullptr) {
144         return 0;
145     }
146
147     display_reparing_weapon(player_ptr, o_ptr, row);
148     constexpr auto q = _("材料となる武器は?", "Which weapon for material? ");
149     constexpr auto s = _("材料となる武器がありません。", "You have no material for the repair.");
150     short mater;
151     auto *mo_ptr = choose_object(player_ptr, &mater, q, s, (USE_INVEN | USE_EQUIP), FuncItemTester(&ItemEntity::is_orthodox_melee_weapons));
152     if (!mo_ptr) {
153         return 0;
154     }
155
156     if (mater == i_idx) {
157         msg_print(_("クラインの壷じゃない!", "This is not a Klein bottle!"));
158         return 0;
159     }
160
161     const auto item_name = describe_flavor(player_ptr, mo_ptr, OD_NAME_ONLY);
162     prt(format(_("材料とする武器: %s", "Material : %s"), item_name.data()), row + 4, 2);
163     const auto cost = bcost + object_value_real(o_ptr) * 2;
164     if (!input_check(format(_("$%dかかりますがよろしいですか? ", "Costs %d gold, okay? "), cost))) {
165         return 0;
166     }
167
168     if (player_ptr->au < cost) {
169         msg_format(_("%sを修復するだけのゴールドがありません!", "You do not have the gold to repair %s!"), item_name.data());
170         msg_print(nullptr);
171         return 0;
172     }
173
174     short bi_id;
175     if (o_ptr->bi_key.sval() == SV_BROKEN_DAGGER) {
176         auto n = 1;
177         bi_id = 0;
178         for (const auto &baseitem : baseitems_info) {
179             if (baseitem.bi_key.tval() != ItemKindType::SWORD) {
180                 continue;
181             }
182
183             const auto sval = baseitem.bi_key.sval();
184             if ((sval == SV_BROKEN_DAGGER) || (sval == SV_BROKEN_SWORD) || (sval == SV_POISON_NEEDLE)) {
185                 continue;
186             }
187
188             if (baseitem.weight > 99) {
189                 continue;
190             }
191
192             if (one_in_(n)) {
193                 bi_id = baseitem.idx;
194                 n++;
195             }
196         }
197     } else {
198         auto tval = (one_in_(5) ? mo_ptr->bi_key.tval() : ItemKindType::SWORD);
199         while (true) {
200             bi_id = lookup_baseitem_id({ tval });
201             const auto &baseitem = baseitems_info[bi_id];
202             const auto sval = baseitem.bi_key.sval();
203             if (tval == ItemKindType::SWORD) {
204                 if ((sval == SV_BROKEN_DAGGER) || (sval == SV_BROKEN_SWORD) || (sval == SV_DIAMOND_EDGE) || (sval == SV_POISON_NEEDLE)) {
205                     continue;
206                 }
207             }
208
209             if (tval == ItemKindType::POLEARM) {
210                 if ((sval == SV_DEATH_SCYTHE) || (sval == SV_TSURIZAO)) {
211                     continue;
212                 }
213             }
214
215             if (tval == ItemKindType::HAFTED) {
216                 if ((sval == SV_GROND) || (sval == SV_WIZSTAFF) || (sval == SV_NAMAKE_HAMMER)) {
217                     continue;
218                 }
219             }
220
221             break;
222         }
223     }
224
225     const auto &baseitem_o = o_ptr->get_baseitem();
226     const auto &baseitem_mo = mo_ptr->get_baseitem();
227     auto dd_bonus = o_ptr->dd - baseitem_o.dd;
228     auto ds_bonus = o_ptr->ds - baseitem_o.ds;
229     dd_bonus += mo_ptr->dd - baseitem_mo.dd;
230     ds_bonus += mo_ptr->ds - baseitem_mo.ds;
231
232     const auto &baseitem = baseitems_info[bi_id];
233     o_ptr->bi_id = bi_id;
234     o_ptr->weight = baseitem.weight;
235     o_ptr->bi_key = baseitem.bi_key;
236     o_ptr->dd = baseitem.dd;
237     o_ptr->ds = baseitem.ds;
238     o_ptr->art_flags.set(baseitem.flags);
239     if (baseitem.pval) {
240         o_ptr->pval = std::max<short>(o_ptr->pval, randint1(baseitem.pval));
241     }
242
243     if (baseitem.flags.has(TR_ACTIVATE)) {
244         o_ptr->activation_id = baseitem.act_idx;
245     }
246
247     if (dd_bonus > 0) {
248         o_ptr->dd++;
249         for (int i = 1; i < dd_bonus; i++) {
250             if (one_in_(o_ptr->dd + i)) {
251                 o_ptr->dd++;
252             }
253         }
254     }
255
256     if (ds_bonus > 0) {
257         o_ptr->ds++;
258         for (int i = 1; i < ds_bonus; i++) {
259             if (one_in_(o_ptr->ds + i)) {
260                 o_ptr->ds++;
261             }
262         }
263     }
264
265     if (baseitem.flags.has(TR_BLOWS)) {
266         auto bmax = std::min<short>(3, std::max<short>(1, 40 / (o_ptr->dd * o_ptr->ds)));
267         o_ptr->pval = std::min<short>(o_ptr->pval, bmax);
268     }
269
270     give_one_ability_of_object(o_ptr, mo_ptr);
271     o_ptr->to_d += std::max(0, (mo_ptr->to_d / 3));
272     o_ptr->to_h += std::max<short>(0, (mo_ptr->to_h / 3));
273     o_ptr->to_a += std::max<short>(0, (mo_ptr->to_a));
274
275     const auto is_narsil = o_ptr->is_specific_artifact(FixedArtifactId::NARSIL);
276     if (is_narsil || (o_ptr->is_random_artifact() && one_in_(1)) || (o_ptr->is_ego() && one_in_(7))) {
277         if (o_ptr->is_ego()) {
278             o_ptr->art_flags.set(TR_IGNORE_FIRE);
279             o_ptr->art_flags.set(TR_IGNORE_ACID);
280         }
281
282         give_one_ability_of_object(o_ptr, mo_ptr);
283         if (activation_index(o_ptr) == RandomArtActType::NONE) {
284             one_activation(o_ptr);
285         }
286
287         if (is_narsil) {
288             one_high_resistance(o_ptr);
289             one_ability(o_ptr);
290         }
291
292         msg_print(_("これはかなりの業物だったようだ。", "This blade seems to be exceptional."));
293     }
294
295     display_repair_success_message(player_ptr, o_ptr, cost);
296     o_ptr->ident &= ~(IDENT_BROKEN);
297     o_ptr->discount = 99;
298
299     calc_android_exp(player_ptr);
300     inven_item_increase(player_ptr, mater, -1);
301     inven_item_optimize(player_ptr, mater);
302
303     RedrawingFlagsUpdater::get_instance().set_flag(StatusRecalculatingFlag::BONUS);
304     handle_stuff(player_ptr);
305     return cost;
306 }
307
308 /*!
309  * @brief アイテム修復処理の過渡ルーチン / Repair broken weapon
310  * @param player_ptr プレイヤーへの参照ポインタ
311  * @param bcost 基本鑑定費用
312  * @return 実際にかかった費用
313  */
314 int repair_broken_weapon(PlayerType *player_ptr, PRICE bcost)
315 {
316     PRICE cost;
317     screen_save();
318     cost = repair_broken_weapon_aux(player_ptr, bcost);
319     screen_load();
320     return cost;
321 }