OSDN Git Service

[Refactor] #2645 display_object_list() を整形した
[hengbandforosx/hengbandosx.git] / src / object-enchant / object-ego.cpp
1 /*!
2  * @brief エゴアイテムに関する処理
3  * @date 2019/05/02
4  * @author deskull
5  * @details Ego-Item indexes (see "lib/edit/e_info.txt")
6  */
7 #include "object-enchant/object-ego.h"
8 #include "artifact/random-art-effects.h"
9 #include "object-enchant/object-boost.h"
10 #include "object-enchant/object-curse.h"
11 #include "object-enchant/special-object-flags.h"
12 #include "object-enchant/trc-types.h"
13 #include "object-hook/hook-weapon.h"
14 #include "object/tval-types.h"
15 #include "sv-definition/sv-protector-types.h"
16 #include "sv-definition/sv-weapon-types.h"
17 #include "system/object-type-definition.h"
18 #include "util/bit-flags-calculator.h"
19 #include "util/enum-converter.h"
20 #include "util/probability-table.h"
21 #include <vector>
22
23 /*
24  * The ego-item arrays
25  */
26 std::map<EgoType, ego_item_type> e_info;
27
28 /*!
29  * @brief アイテムのエゴをレア度の重みに合わせてランダムに選択する
30  * Choose random ego type
31  * @param slot 取得したいエゴの装備部位
32  * @param good TRUEならば通常のエゴ、FALSEならば呪いのエゴが選択対象となる。
33  * @return 選択されたエゴ情報のID、万一選択できなかった場合は0が返る。
34  */
35 EgoType get_random_ego(byte slot, bool good)
36 {
37     ProbabilityTable<EgoType> prob_table;
38     for (const auto &[e_idx, e_ref] : e_info) {
39         if (e_ref.idx == EgoType::NONE || e_ref.slot != slot || e_ref.rarity <= 0) {
40             continue;
41         }
42
43         bool worthless = e_ref.rating == 0 || e_ref.gen_flags.has_any_of({ ItemGenerationTraitType::CURSED, ItemGenerationTraitType::HEAVY_CURSE, ItemGenerationTraitType::PERMA_CURSE });
44
45         if (good != worthless) {
46             prob_table.entry_item(e_ref.idx, (255 / e_ref.rarity));
47         }
48     }
49
50     if (!prob_table.empty()) {
51         return prob_table.pick_one_at_random();
52     }
53
54     return EgoType::NONE;
55 }
56
57 /*!
58  * @brief エゴオブジェクトに呪いを付加する
59  * @param player_ptr プレイヤー情報への参照ポインタ
60  * @param o_ptr オブジェクト情報への参照ポインタ
61  * @param gen_flags 生成フラグ(参照渡し)
62  */
63 static void ego_invest_curse(ObjectType *o_ptr, EnumClassFlagGroup<ItemGenerationTraitType> &gen_flags)
64 {
65     if (gen_flags.has(ItemGenerationTraitType::CURSED)) {
66         o_ptr->curse_flags.set(CurseTraitType::CURSED);
67     }
68     if (gen_flags.has(ItemGenerationTraitType::HEAVY_CURSE)) {
69         o_ptr->curse_flags.set(CurseTraitType::HEAVY_CURSE);
70     }
71     if (gen_flags.has(ItemGenerationTraitType::PERMA_CURSE)) {
72         o_ptr->curse_flags.set(CurseTraitType::PERMA_CURSE);
73     }
74     if (gen_flags.has(ItemGenerationTraitType::RANDOM_CURSE0)) {
75         o_ptr->curse_flags.set(get_curse(0, o_ptr));
76     }
77     if (gen_flags.has(ItemGenerationTraitType::RANDOM_CURSE1)) {
78         o_ptr->curse_flags.set(get_curse(1, o_ptr));
79     }
80     if (gen_flags.has(ItemGenerationTraitType::RANDOM_CURSE2)) {
81         o_ptr->curse_flags.set(get_curse(2, o_ptr));
82     }
83 }
84
85 /*!
86  * @brief エゴオブジェクトに追加能力/耐性を付加する
87  * @param o_ptr オブジェクト情報への参照ポインタ
88  * @param gen_flags 生成フラグ(参照渡し)
89  */
90 static void ego_invest_extra_abilities(ObjectType *o_ptr, EnumClassFlagGroup<ItemGenerationTraitType> &gen_flags)
91 {
92     if (gen_flags.has(ItemGenerationTraitType::ONE_SUSTAIN)) {
93         one_sustain(o_ptr);
94     }
95     if (gen_flags.has(ItemGenerationTraitType::XTRA_POWER)) {
96         one_ability(o_ptr);
97     }
98     if (gen_flags.has(ItemGenerationTraitType::XTRA_H_RES)) {
99         one_high_resistance(o_ptr);
100     }
101     if (gen_flags.has(ItemGenerationTraitType::XTRA_E_RES)) {
102         one_ele_resistance(o_ptr);
103     }
104     if (gen_flags.has(ItemGenerationTraitType::XTRA_D_RES)) {
105         one_dragon_ele_resistance(o_ptr);
106     }
107     if (gen_flags.has(ItemGenerationTraitType::XTRA_L_RES)) {
108         one_lordly_high_resistance(o_ptr);
109     }
110     if (gen_flags.has(ItemGenerationTraitType::XTRA_RES)) {
111         one_resistance(o_ptr);
112     }
113     if (gen_flags.has(ItemGenerationTraitType::LIGHT_WEIGHT)) {
114         make_weight_ligten(o_ptr);
115     }
116     if (gen_flags.has(ItemGenerationTraitType::HEAVY_WEIGHT)) {
117         make_weight_heavy(o_ptr);
118     }
119     if (gen_flags.has(ItemGenerationTraitType::XTRA_AC)) {
120         add_xtra_ac(o_ptr);
121     }
122     if (gen_flags.has(ItemGenerationTraitType::HIGH_TELEPATHY)) {
123         add_high_telepathy(o_ptr);
124     }
125     if (gen_flags.has(ItemGenerationTraitType::LOW_TELEPATHY)) {
126         add_low_telepathy(o_ptr);
127     }
128     if (gen_flags.has(ItemGenerationTraitType::XTRA_L_ESP)) {
129         one_low_esp(o_ptr);
130     }
131     if (gen_flags.has(ItemGenerationTraitType::ADD_DICE)) {
132         o_ptr->dd++;
133     }
134     if (gen_flags.has(ItemGenerationTraitType::DOUBLED_DICE)) {
135         o_ptr->dd *= 2;
136     } else {
137         if (gen_flags.has(ItemGenerationTraitType::XTRA_DICE)) {
138             do {
139                 o_ptr->dd++;
140             } while (one_in_(o_ptr->dd));
141         }
142         if (gen_flags.has(ItemGenerationTraitType::XTRA_DICE_SIDE)) {
143             do {
144                 o_ptr->ds++;
145             } while (one_in_(o_ptr->ds));
146         }
147     }
148
149     if (o_ptr->dd > 9) {
150         o_ptr->dd = 9;
151     }
152 }
153
154 /*!
155  * @brief エゴアイテムの追加能力/耐性フラグを解釈する
156  * @param player_ptr プレイヤー情報への参照ポインタ
157  * @param o_ptr オブジェクト情報への参照ポインタ
158  * @param e_ptr エゴアイテム情報への参照ポインタ
159  * @param gen_flags 生成フラグ(参照渡し)
160  */
161 static void ego_interpret_extra_abilities(ObjectType *o_ptr, ego_item_type *e_ptr, EnumClassFlagGroup<ItemGenerationTraitType> &gen_flags)
162 {
163     for (auto &xtra : e_ptr->xtra_flags) {
164         if (xtra.mul == 0 || xtra.dev == 0) {
165             continue;
166         }
167
168         if (randint0(xtra.dev) >= xtra.mul) { //! @note mul/devで適用
169             continue;
170         }
171
172         auto n = xtra.tr_flags.size();
173         if (n > 0) {
174             auto f = xtra.tr_flags[randint0(n)];
175             auto except = (f == TR_VORPAL && o_ptr->tval != ItemKindType::SWORD);
176             if (!except) {
177                 o_ptr->art_flags.set(f);
178             }
179         }
180
181         for (auto f : xtra.trg_flags) {
182             gen_flags.set(f);
183         }
184     }
185 }
186
187 /*!
188  * @brief 0 および負数に対応した randint1()
189  * @param n
190  *
191  * n == 0 のとき、常に 0 を返す。
192  * n >  0 のとき、[1, n] の乱数を返す。
193  * n <  0 のとき、[n,-1] の乱数を返す。
194  */
195 static int randint1_signed(const int n)
196 {
197     if (n == 0) {
198         return 0;
199     }
200
201     return n > 0 ? randint1(n) : -randint1(-n);
202 }
203
204 /*!
205  * @brief 追加込みでエゴがフラグを保持しているか判定する
206  * @param o_ptr オブジェクト情報への参照ポインタ
207  * @param e_ptr エゴアイテム情報への参照ポインタ
208  * @param flag フラグ
209  * @return 持つならtrue、持たないならfalse
210  */
211 static bool ego_has_flag(ObjectType *o_ptr, ego_item_type *e_ptr, tr_type flag)
212 {
213     if (o_ptr->art_flags.has(flag)) {
214         return true;
215     }
216     if (e_ptr->flags.has(flag)) {
217         return true;
218     }
219     return false;
220 }
221
222 /*!
223  * @brief エゴに追加攻撃のpvalを付加する
224  * @param player_ptr プレイヤー情報への参照ポインタ
225  * @param o_ptr オブジェクト情報への参照ポインタ
226  * @param e_ptr エゴアイテム情報への参照ポインタ
227  * @param lev 生成階
228  */
229 void ego_invest_extra_attack(ObjectType *o_ptr, ego_item_type *e_ptr, DEPTH lev)
230 {
231     if (!o_ptr->is_weapon()) {
232         o_ptr->pval = e_ptr->max_pval >= 0 ? 1 : randint1_signed(e_ptr->max_pval);
233         return;
234     }
235
236     if (o_ptr->ego_idx == EgoType::ATTACKS) {
237         o_ptr->pval = randint1(e_ptr->max_pval * lev / 100 + 1);
238         if (o_ptr->pval > 3) {
239             o_ptr->pval = 3;
240         }
241         if ((o_ptr->tval == ItemKindType::SWORD) && (o_ptr->sval == SV_HAYABUSA)) {
242             o_ptr->pval += randint1(2);
243         }
244         return;
245     }
246
247     if (ego_has_flag(o_ptr, e_ptr, TR_EARTHQUAKE)) {
248         o_ptr->pval += randint1(e_ptr->max_pval);
249         return;
250     }
251
252     if (ego_has_flag(o_ptr, e_ptr, TR_SLAY_EVIL) || ego_has_flag(o_ptr, e_ptr, TR_KILL_EVIL)) {
253         o_ptr->pval++;
254         if ((lev > 60) && one_in_(3) && ((o_ptr->dd * (o_ptr->ds + 1)) < 15)) {
255             o_ptr->pval++;
256         }
257         return;
258     }
259
260     o_ptr->pval += randint1(2);
261 }
262
263 /*!
264  * @brief オブジェクトをエゴアイテムにする
265  * @param player_ptr プレイヤー情報への参照ポインタ
266  * @param o_ptr オブジェクト情報への参照ポインタ
267  * @param lev 生成階
268  */
269 void apply_ego(ObjectType *o_ptr, DEPTH lev)
270 {
271     auto e_ptr = &e_info[o_ptr->ego_idx];
272     auto gen_flags = e_ptr->gen_flags;
273
274     ego_interpret_extra_abilities(o_ptr, e_ptr, gen_flags);
275
276     if (!e_ptr->cost) {
277         o_ptr->ident |= (IDENT_BROKEN);
278     }
279
280     ego_invest_curse(o_ptr, gen_flags);
281     ego_invest_extra_abilities(o_ptr, gen_flags);
282
283     if (e_ptr->act_idx > RandomArtActType::NONE) {
284         o_ptr->activation_id = e_ptr->act_idx;
285     }
286
287     o_ptr->to_h += (HIT_PROB)e_ptr->base_to_h;
288     o_ptr->to_d += (int)e_ptr->base_to_d;
289     o_ptr->to_a += (ARMOUR_CLASS)e_ptr->base_to_a;
290
291     auto is_powerful = e_ptr->gen_flags.has(ItemGenerationTraitType::POWERFUL);
292     auto is_cursed = (o_ptr->is_cursed() || o_ptr->is_broken()) && !is_powerful;
293     if (is_cursed) {
294         if (e_ptr->max_to_h) {
295             o_ptr->to_h -= randint1(e_ptr->max_to_h);
296         }
297         if (e_ptr->max_to_d) {
298             o_ptr->to_d -= randint1(e_ptr->max_to_d);
299         }
300         if (e_ptr->max_to_a) {
301             o_ptr->to_a -= randint1(e_ptr->max_to_a);
302         }
303         if (e_ptr->max_pval) {
304             o_ptr->pval -= randint1(e_ptr->max_pval);
305         }
306     } else {
307         if (is_powerful) {
308             if (e_ptr->max_to_h > 0 && o_ptr->to_h < 0) {
309                 o_ptr->to_h = 0 - o_ptr->to_h;
310             }
311             if (e_ptr->max_to_d > 0 && o_ptr->to_d < 0) {
312                 o_ptr->to_d = 0 - o_ptr->to_d;
313             }
314             if (e_ptr->max_to_a > 0 && o_ptr->to_a < 0) {
315                 o_ptr->to_a = 0 - o_ptr->to_a;
316             }
317         }
318
319         o_ptr->to_h += (HIT_PROB)randint1_signed(e_ptr->max_to_h);
320         o_ptr->to_d += (int)randint1_signed(e_ptr->max_to_d);
321         o_ptr->to_a += (ARMOUR_CLASS)randint1_signed(e_ptr->max_to_a);
322
323         if (gen_flags.has(ItemGenerationTraitType::MOD_ACCURACY)) {
324             while (o_ptr->to_h < o_ptr->to_d + 10) {
325                 o_ptr->to_h += 5;
326                 o_ptr->to_d -= 5;
327             }
328             o_ptr->to_h = std::max<short>(o_ptr->to_h, 15);
329         }
330
331         if (gen_flags.has(ItemGenerationTraitType::MOD_VELOCITY)) {
332             while (o_ptr->to_d < o_ptr->to_h + 10) {
333                 o_ptr->to_d += 5;
334                 o_ptr->to_h -= 5;
335             }
336             o_ptr->to_d = std::max(o_ptr->to_d, 15);
337         }
338
339         if ((o_ptr->ego_idx == EgoType::PROTECTION) || (o_ptr->ego_idx == EgoType::S_PROTECTION) || (o_ptr->ego_idx == EgoType::H_PROTECTION)) {
340             o_ptr->to_a = std::max<short>(o_ptr->to_a, 15);
341         }
342
343         if (e_ptr->max_pval) {
344             if (o_ptr->ego_idx == EgoType::BAT) {
345                 o_ptr->pval = randint1(e_ptr->max_pval);
346                 if (o_ptr->sval == SV_ELVEN_CLOAK) {
347                     o_ptr->pval += randint1(2);
348                 }
349             } else {
350                 if (ego_has_flag(o_ptr, e_ptr, TR_BLOWS)) {
351                     ego_invest_extra_attack(o_ptr, e_ptr, lev);
352                 } else {
353                     if (e_ptr->max_pval > 0) {
354                         o_ptr->pval += randint1(e_ptr->max_pval);
355                     } else if (e_ptr->max_pval < 0) {
356                         o_ptr->pval -= randint1(0 - e_ptr->max_pval);
357                     }
358                 }
359             }
360         }
361
362         if ((o_ptr->ego_idx == EgoType::SPEED) && (lev < 50)) {
363             o_ptr->pval = randint1(o_ptr->pval);
364         }
365
366         if ((o_ptr->tval == ItemKindType::SWORD) && (o_ptr->sval == SV_HAYABUSA) && (o_ptr->pval > 2) && (o_ptr->ego_idx != EgoType::ATTACKS)) {
367             o_ptr->pval = 2;
368         }
369     }
370 }