OSDN Git Service

Merge pull request #3569 from sikabane-works/release/3.0.0.88-alpha
[hengbandforosx/hengbandosx.git] / src / object-enchant / object-ego.cpp
1 /*!
2  * @brief エゴアイテムに関する処理
3  * @date 2019/05/02
4  * @author deskull
5  */
6 #include "object-enchant/object-ego.h"
7 #include "artifact/random-art-effects.h"
8 #include "object-enchant/object-boost.h"
9 #include "object-enchant/object-curse.h"
10 #include "object-enchant/special-object-flags.h"
11 #include "object-enchant/trc-types.h"
12 #include "object-hook/hook-weapon.h"
13 #include "object/tval-types.h"
14 #include "sv-definition/sv-protector-types.h"
15 #include "sv-definition/sv-weapon-types.h"
16 #include "system/item-entity.h"
17 #include "util/bit-flags-calculator.h"
18 #include "util/enum-converter.h"
19 #include "util/probability-table.h"
20 #include <vector>
21
22 std::map<EgoType, EgoItemDefinition> egos_info;
23
24 /*!
25  * @brief アイテムのエゴをレア度の重みに合わせてランダムに選択する
26  * Choose random ego type
27  * @param slot 取得したいエゴの装備部位
28  * @param good TRUEならば通常のエゴ、FALSEならば呪いのエゴが選択対象となる。
29  * @return 選択されたエゴ情報のID、万一選択できなかった場合は0が返る。
30  */
31 EgoType get_random_ego(byte slot, bool good)
32 {
33     ProbabilityTable<EgoType> prob_table;
34     for (const auto &[e_idx, ego] : egos_info) {
35         if (ego.idx == EgoType::NONE || ego.slot != slot || ego.rarity <= 0) {
36             continue;
37         }
38
39         const auto curses = {
40             ItemGenerationTraitType::CURSED,
41             ItemGenerationTraitType::HEAVY_CURSE,
42             ItemGenerationTraitType::PERMA_CURSE
43         };
44         const auto worthless = ego.rating == 0 || ego.gen_flags.has_any_of(curses);
45         if (good != worthless) {
46             prob_table.entry_item(ego.idx, (255 / ego.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(ItemEntity *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(ItemEntity *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 ego エゴアイテム情報への参照
159  * @param gen_flags 生成フラグ(参照渡し)
160  */
161 static void ego_interpret_extra_abilities(ItemEntity *o_ptr, const EgoItemDefinition &ego, EnumClassFlagGroup<ItemGenerationTraitType> &gen_flags)
162 {
163     for (const auto &xtra : ego.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         if (!xtra.tr_flags.empty()) {
173             const auto f = rand_choice(xtra.tr_flags);
174             const auto except = (f == TR_VORPAL) && (o_ptr->bi_key.tval() != ItemKindType::SWORD);
175             if (!except) {
176                 o_ptr->art_flags.set(f);
177             }
178         }
179
180         for (auto f : xtra.trg_flags) {
181             gen_flags.set(f);
182         }
183     }
184 }
185
186 /*!
187  * @brief 0 および負数に対応した randint1()
188  * @param n
189  *
190  * n == 0 のとき、常に 0 を返す。
191  * n >  0 のとき、[1, n] の乱数を返す。
192  * n <  0 のとき、[n,-1] の乱数を返す。
193  */
194 static int randint1_signed(const int n)
195 {
196     if (n == 0) {
197         return 0;
198     }
199
200     return n > 0 ? randint1(n) : -randint1(-n);
201 }
202
203 /*!
204  * @brief 追加込みでエゴがフラグを保持しているか判定する
205  * @param o_ptr オブジェクト情報への参照ポインタ
206  * @param ego エゴアイテム情報への参照
207  * @param flag フラグ
208  * @return 持つならtrue、持たないならfalse
209  */
210 static bool ego_has_flag(ItemEntity *o_ptr, const EgoItemDefinition &ego, tr_type flag)
211 {
212     if (o_ptr->art_flags.has(flag)) {
213         return true;
214     }
215     if (ego.flags.has(flag)) {
216         return true;
217     }
218     return false;
219 }
220
221 /*!
222  * @brief エゴに追加攻撃のpvalを付加する
223  * @param player_ptr プレイヤー情報への参照ポインタ
224  * @param o_ptr オブジェクト情報への参照ポインタ
225  * @param ego エゴアイテム情報への参照
226  * @param lev 生成階
227  */
228 void ego_invest_extra_attack(ItemEntity *o_ptr, const EgoItemDefinition &ego, DEPTH lev)
229 {
230     if (!o_ptr->is_weapon()) {
231         o_ptr->pval = ego.max_pval >= 0 ? 1 : randint1_signed(ego.max_pval);
232         return;
233     }
234
235     if (o_ptr->ego_idx == EgoType::ATTACKS) {
236         o_ptr->pval = randint1(ego.max_pval * lev / 100 + 1);
237         if (o_ptr->pval > 3) {
238             o_ptr->pval = 3;
239         }
240
241         if (o_ptr->bi_key == BaseitemKey(ItemKindType::SWORD, SV_HAYABUSA)) {
242             o_ptr->pval += randint1(2);
243         }
244
245         return;
246     }
247
248     if (ego_has_flag(o_ptr, ego, TR_EARTHQUAKE)) {
249         o_ptr->pval += randint1(ego.max_pval);
250         return;
251     }
252
253     if (ego_has_flag(o_ptr, ego, TR_SLAY_EVIL) || ego_has_flag(o_ptr, ego, TR_KILL_EVIL)) {
254         o_ptr->pval++;
255         if ((lev > 60) && one_in_(3) && ((o_ptr->dd * (o_ptr->ds + 1)) < 15)) {
256             o_ptr->pval++;
257         }
258         return;
259     }
260
261     o_ptr->pval += randint1(2);
262 }
263
264 /*!
265  * @brief オブジェクトをエゴアイテムにする
266  * @param player_ptr プレイヤー情報への参照ポインタ
267  * @param o_ptr オブジェクト情報への参照ポインタ
268  * @param lev 生成階
269  */
270 void apply_ego(ItemEntity *o_ptr, DEPTH lev)
271 {
272     const auto &ego = o_ptr->get_ego();
273     auto gen_flags = ego.gen_flags;
274
275     ego_interpret_extra_abilities(o_ptr, ego, gen_flags);
276
277     if (!ego.cost) {
278         o_ptr->ident |= (IDENT_BROKEN);
279     }
280
281     ego_invest_curse(o_ptr, gen_flags);
282     ego_invest_extra_abilities(o_ptr, gen_flags);
283
284     if (ego.act_idx > RandomArtActType::NONE) {
285         o_ptr->activation_id = ego.act_idx;
286     }
287
288     o_ptr->to_h += (HIT_PROB)ego.base_to_h;
289     o_ptr->to_d += (int)ego.base_to_d;
290     o_ptr->to_a += (ARMOUR_CLASS)ego.base_to_a;
291
292     auto is_powerful = ego.gen_flags.has(ItemGenerationTraitType::POWERFUL);
293     auto is_cursed = (o_ptr->is_cursed() || o_ptr->is_broken()) && !is_powerful;
294     if (is_cursed) {
295         if (ego.max_to_h) {
296             o_ptr->to_h -= randint1(ego.max_to_h);
297         }
298         if (ego.max_to_d) {
299             o_ptr->to_d -= randint1(ego.max_to_d);
300         }
301         if (ego.max_to_a) {
302             o_ptr->to_a -= randint1(ego.max_to_a);
303         }
304         if (ego.max_pval) {
305             o_ptr->pval -= randint1(ego.max_pval);
306         }
307     } else {
308         if (is_powerful) {
309             if (ego.max_to_h > 0 && o_ptr->to_h < 0) {
310                 o_ptr->to_h = 0 - o_ptr->to_h;
311             }
312             if (ego.max_to_d > 0 && o_ptr->to_d < 0) {
313                 o_ptr->to_d = 0 - o_ptr->to_d;
314             }
315             if (ego.max_to_a > 0 && o_ptr->to_a < 0) {
316                 o_ptr->to_a = 0 - o_ptr->to_a;
317             }
318         }
319
320         o_ptr->to_h += (HIT_PROB)randint1_signed(ego.max_to_h);
321         o_ptr->to_d += (int)randint1_signed(ego.max_to_d);
322         o_ptr->to_a += (ARMOUR_CLASS)randint1_signed(ego.max_to_a);
323
324         if (gen_flags.has(ItemGenerationTraitType::MOD_ACCURACY)) {
325             while (o_ptr->to_h < o_ptr->to_d + 10) {
326                 o_ptr->to_h += 5;
327                 o_ptr->to_d -= 5;
328             }
329             o_ptr->to_h = std::max<short>(o_ptr->to_h, 15);
330         }
331
332         if (gen_flags.has(ItemGenerationTraitType::MOD_VELOCITY)) {
333             while (o_ptr->to_d < o_ptr->to_h + 10) {
334                 o_ptr->to_d += 5;
335                 o_ptr->to_h -= 5;
336             }
337             o_ptr->to_d = std::max(o_ptr->to_d, 15);
338         }
339
340         if ((o_ptr->ego_idx == EgoType::PROTECTION) || (o_ptr->ego_idx == EgoType::S_PROTECTION) || (o_ptr->ego_idx == EgoType::H_PROTECTION)) {
341             o_ptr->to_a = std::max<short>(o_ptr->to_a, 15);
342         }
343
344         if (ego.max_pval) {
345             if (o_ptr->ego_idx == EgoType::BAT) {
346                 o_ptr->pval = randint1(ego.max_pval);
347                 if (o_ptr->bi_key.sval() == SV_ELVEN_CLOAK) {
348                     o_ptr->pval += randint1(2);
349                 }
350             } else {
351                 if (ego_has_flag(o_ptr, ego, TR_BLOWS)) {
352                     ego_invest_extra_attack(o_ptr, ego, lev);
353                 } else {
354                     if (ego.max_pval > 0) {
355                         o_ptr->pval += randint1(ego.max_pval);
356                     } else if (ego.max_pval < 0) {
357                         o_ptr->pval -= randint1(0 - ego.max_pval);
358                     }
359                 }
360             }
361         }
362
363         if ((o_ptr->ego_idx == EgoType::SPEED) && (lev < 50)) {
364             o_ptr->pval = randint1(o_ptr->pval);
365         }
366
367         if ((o_ptr->bi_key == BaseitemKey(ItemKindType::SWORD, SV_HAYABUSA)) && (o_ptr->pval > 2) && (o_ptr->ego_idx != EgoType::ATTACKS)) {
368             o_ptr->pval = 2;
369         }
370     }
371 }