OSDN Git Service

Merge pull request #2690 from Hourier/Unify-History-Japanese-English-1
[hengbandforosx/hengbandosx.git] / src / artifact / fixed-art-generator.cpp
1 /*!
2  * @file fixed-art-generator.cpp
3  * @brief 固定アーティファクトの生成 / Artifact code
4  * @date 2020/07/14
5  * @author
6  * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
7  * This software may be copied and distributed for educational, research, and
8  * not for profit purposes provided that this copyright and statement are
9  * included in all such copies.
10  * 2013 Deskull rearranged comment for Doxygen.
11  * 2020 Hourier rearranged
12  */
13
14 #include "artifact/fixed-art-generator.h"
15 #include "artifact/fixed-art-types.h"
16 #include "floor/floor-object.h"
17 #include "object-enchant/object-boost.h"
18 #include "object-enchant/object-curse.h"
19 #include "object-enchant/special-object-flags.h"
20 #include "object-enchant/tr-types.h"
21 #include "object-enchant/trc-types.h"
22 #include "object-enchant/trg-types.h"
23 #include "object/object-kind-hook.h"
24 #include "player-base/player-class.h"
25 #include "player/player-sex.h"
26 #include "specific-object/bloody-moon.h"
27 #include "system/artifact-type-definition.h"
28 #include "system/baseitem-info-definition.h"
29 #include "system/floor-type-definition.h"
30 #include "system/object-type-definition.h"
31 #include "system/player-type-definition.h"
32 #include "util/bit-flags-calculator.h"
33
34 /*!
35  * @brief 恐怖の仮面への特殊処理
36  * @param player_ptr プレイヤーへの参照ポインタ
37  * @param o_ptr 対象のオブジェクト構造体への参照ポインタ
38  * @return 追加能力/耐性がもらえるならtrue、もらえないならfalse
39  * @details
40  * 純戦士系職業は追加能力/耐性がもらえる。
41  * それ以外では、反感、太古の怨念、呪いが付き追加能力/耐性はもらえない。
42  */
43 static bool invest_terror_mask(PlayerType *player_ptr, ObjectType *o_ptr)
44 {
45     if (o_ptr->fixed_artifact_idx != FixedArtifactId::TERROR) {
46         return false;
47     }
48
49     switch (player_ptr->pclass) {
50     case PlayerClassType::WARRIOR:
51     case PlayerClassType::ARCHER:
52     case PlayerClassType::CAVALRY:
53     case PlayerClassType::BERSERKER:
54         return true;
55     default:
56         o_ptr->art_flags.set(TR_AGGRAVATE);
57         o_ptr->art_flags.set(TR_TY_CURSE);
58         o_ptr->curse_flags.set({ CurseTraitType::CURSED, CurseTraitType::HEAVY_CURSE });
59         o_ptr->curse_flags.set(get_curse(2, o_ptr));
60         return false;
61     }
62 }
63
64 /*!
65  * @brief 戦乙女ミリムの危ない水着への特殊処理 (セクシーギャルのみpval追加)
66  * @param player_ptr プレイヤーへの参照ポインタ
67  * @param o_ptr 対象のオブジェクト構造体への参照ポインタ
68  */
69 static void milim_swimsuit(PlayerType *player_ptr, ObjectType *o_ptr)
70 {
71     if ((o_ptr->fixed_artifact_idx != FixedArtifactId::MILIM) || (player_ptr->ppersonality != PERSONALITY_SEXY)) {
72         return;
73     }
74
75     o_ptr->pval = 3;
76     o_ptr->art_flags.set(TR_STR);
77     o_ptr->art_flags.set(TR_INT);
78     o_ptr->art_flags.set(TR_WIS);
79     o_ptr->art_flags.set(TR_DEX);
80     o_ptr->art_flags.set(TR_CON);
81     o_ptr->art_flags.set(TR_CHR);
82 }
83
84 /*!
85  * @brief 特定の固定アーティファクトの条件付き追加能力/耐性を付加する
86  * @attention プレイヤーの各種ステータスに依存した処理がある。
87  * @todo 折を見て関数名を変更すること。
88  * @param player_ptr プレイヤーへの参照ポインタ
89  * @param o_ptr 対象のオブジェクト構造体ポインタ
90  * @param a_ptr 生成する固定アーティファクト構造体ポインタ
91  * @details
92  * 対象は村正、ロビントンのハープ、龍争虎鬪、ブラッディムーン、羽衣、天女の羽衣、ミリム
93  */
94 static void invest_special_artifact_abilities(PlayerType *player_ptr, ObjectType *o_ptr)
95 {
96     const auto pc = PlayerClass(player_ptr);
97     switch (o_ptr->fixed_artifact_idx) {
98     case FixedArtifactId::MURAMASA:
99         if (!pc.equals(PlayerClassType::SAMURAI)) {
100             o_ptr->art_flags.set(TR_NO_MAGIC);
101             o_ptr->curse_flags.set(CurseTraitType::HEAVY_CURSE);
102         }
103         return;
104     case FixedArtifactId::ROBINTON:
105         if (pc.equals(PlayerClassType::BARD)) {
106             o_ptr->art_flags.set(TR_DEC_MANA);
107         }
108         return;
109     case FixedArtifactId::XIAOLONG:
110         if (pc.equals(PlayerClassType::MONK)) {
111             o_ptr->art_flags.set(TR_BLOWS);
112         }
113         return;
114     case FixedArtifactId::BLOOD:
115         get_bloody_moon_flags(o_ptr);
116         return;
117     case FixedArtifactId::HEAVENLY_MAIDEN:
118         if (player_ptr->psex != SEX_FEMALE) {
119             o_ptr->art_flags.set(TR_AGGRAVATE);
120         }
121         return;
122     case FixedArtifactId::MILIM:
123         milim_swimsuit(player_ptr, o_ptr);
124         return;
125     default:
126         break;
127     }
128 }
129
130 /*!
131  * @brief 固定アーティファクトオブジェクトに追加能力/耐性を付加する
132  * @param player_ptr プレイヤー情報への参照ポインタ
133  * @param a_ptr 固定アーティファクト情報への参照ポインタ
134  * @param q_ptr オブジェクト情報への参照ポインタ
135  */
136 static void fixed_artifact_random_abilities(PlayerType *player_ptr, const ArtifactType &a_ref, ObjectType *o_ptr)
137 {
138     auto give_power = false;
139     auto give_resistance = false;
140
141     if (invest_terror_mask(player_ptr, o_ptr)) {
142         give_power = true;
143         give_resistance = true;
144     }
145
146     invest_special_artifact_abilities(player_ptr, o_ptr);
147
148     if (a_ref.gen_flags.has(ItemGenerationTraitType::XTRA_POWER)) {
149         give_power = true;
150     }
151
152     if (a_ref.gen_flags.has(ItemGenerationTraitType::XTRA_H_RES)) {
153         give_resistance = true;
154     }
155
156     if (a_ref.gen_flags.has(ItemGenerationTraitType::XTRA_RES_OR_POWER)) {
157         if (one_in_(2)) {
158             give_resistance = true;
159         } else {
160             give_power = true;
161         }
162     }
163
164     if (give_power) {
165         one_ability(o_ptr);
166     }
167
168     if (give_resistance) {
169         one_high_resistance(o_ptr);
170     }
171
172     if (a_ref.gen_flags.has(ItemGenerationTraitType::XTRA_DICE)) {
173         do {
174             o_ptr->dd++;
175         } while (one_in_(o_ptr->dd));
176
177         if (o_ptr->dd > 9) {
178             o_ptr->dd = 9;
179         }
180     }
181 }
182
183 /*!
184  * @brief 固定アーティファクトオブジェクトに呪いフラグを付加する
185  * @param player_ptr プレイヤー情報への参照ポインタ
186  * @param a_ptr 固定アーティファクト情報への参照ポインタ
187  * @param q_ptr オブジェクト情報への参照ポインタ
188  */
189 static void invest_curse_to_fixed_artifact(const ArtifactType &a_ref, ObjectType *o_ptr)
190 {
191     if (!a_ref.cost) {
192         set_bits(o_ptr->ident, IDENT_BROKEN);
193     }
194
195     if (a_ref.gen_flags.has(ItemGenerationTraitType::CURSED)) {
196         o_ptr->curse_flags.set(CurseTraitType::CURSED);
197     }
198
199     if (a_ref.gen_flags.has(ItemGenerationTraitType::HEAVY_CURSE)) {
200         o_ptr->curse_flags.set(CurseTraitType::HEAVY_CURSE);
201     }
202
203     if (a_ref.gen_flags.has(ItemGenerationTraitType::PERMA_CURSE)) {
204         o_ptr->curse_flags.set(CurseTraitType::PERMA_CURSE);
205     }
206
207     if (a_ref.gen_flags.has(ItemGenerationTraitType::RANDOM_CURSE0)) {
208         o_ptr->curse_flags.set(get_curse(0, o_ptr));
209     }
210
211     if (a_ref.gen_flags.has(ItemGenerationTraitType::RANDOM_CURSE1)) {
212         o_ptr->curse_flags.set(get_curse(1, o_ptr));
213     }
214
215     if (a_ref.gen_flags.has(ItemGenerationTraitType::RANDOM_CURSE2)) {
216         o_ptr->curse_flags.set(get_curse(2, o_ptr));
217     }
218 }
219
220 /*!
221  * @brief オブジェクトに指定した固定アーティファクトをオブジェクトに割り当てる。
222  * @param player_ptr プレイヤーへの参照ポインタ
223  * @param o_ptr 生成に割り当てたいオブジェクトの構造体参照ポインタ
224  * @return 適用したアーティファクト情報への参照ポインタ
225  */
226 void apply_artifact(PlayerType *player_ptr, ObjectType *o_ptr)
227 {
228     auto &a_ref = a_info.at(o_ptr->fixed_artifact_idx);
229     o_ptr->pval = a_ref.pval;
230     o_ptr->ac = a_ref.ac;
231     o_ptr->dd = a_ref.dd;
232     o_ptr->ds = a_ref.ds;
233     o_ptr->to_a = a_ref.to_a;
234     o_ptr->to_h = a_ref.to_h;
235     o_ptr->to_d = a_ref.to_d;
236     o_ptr->weight = a_ref.weight;
237     o_ptr->activation_id = a_ref.act_idx;
238
239     invest_curse_to_fixed_artifact(a_ref, o_ptr);
240     fixed_artifact_random_abilities(player_ptr, a_ref, o_ptr);
241 }
242
243 /*!
244  * @brief フロアの指定された位置に固定アーティファクトを生成する。 / Create the artifact of the specified number
245  * @details 固定アーティファクト構造体から基本ステータスをコピーした後、所定の座標でdrop_item()で落とす。
246  * @param player_ptr プレイヤーへの参照ポインタ
247  * @param a_idx 生成する固定アーティファクト構造体のID
248  * @param y アイテムを落とす地点のy座標
249  * @param x アイテムを落とす地点のx座標
250  * @return 生成が成功したか否か、失敗はIDの不全、ベースアイテムの不全、drop_item()の失敗時に起こる。
251  * @attention この処理はdrop_near()内で普通の固定アーティファクトが重ならない性質に依存する.
252  * 仮に2個以上存在可能かつ装備品以外の固定アーティファクトが作成されれば
253  * drop_near()関数の返り値は信用できなくなる.
254  */
255 bool create_named_art(PlayerType *player_ptr, FixedArtifactId a_idx, POSITION y, POSITION x)
256 {
257     auto &a_ref = a_info.at(a_idx);
258     if (a_ref.name.empty()) {
259         return false;
260     }
261
262     auto i = lookup_kind(a_ref.tval, a_ref.sval);
263     if (i == 0) {
264         return true;
265     }
266
267     ObjectType forge;
268     auto q_ptr = &forge;
269     q_ptr->prep(i);
270     q_ptr->fixed_artifact_idx = a_idx;
271     apply_artifact(player_ptr, q_ptr);
272     if (drop_near(player_ptr, q_ptr, -1, y, x) == 0) {
273         return false;
274     }
275
276     a_ref.is_generated = true;
277     return true;
278 }
279
280 /*!
281  * @brief 非INSTA_ART型の固定アーティファクトの生成を確率に応じて試行する。
282  * Mega-Hack -- Attempt to create one of the "Special Objects"
283  * @param player_ptr プレイヤーへの参照ポインタ
284  * @param o_ptr 生成に割り当てたいオブジェクトの構造体参照ポインタ
285  * @return 生成に成功したらTRUEを返す。
286  * @details
287  * Attempt to change an object into an artifact\n
288  * This routine should only be called by "apply_magic()"\n
289  * Note -- see "make_artifact_special()" and "apply_magic()"\n
290  */
291 bool make_artifact(PlayerType *player_ptr, ObjectType *o_ptr)
292 {
293     auto floor_ptr = player_ptr->current_floor_ptr;
294     if (floor_ptr->dun_level == 0) {
295         return false;
296     }
297
298     if (o_ptr->number != 1) {
299         return false;
300     }
301
302     for (const auto &[a_idx, a_ref] : a_info) {
303         if (a_ref.name.empty()) {
304             continue;
305         }
306
307         if (a_ref.is_generated) {
308             continue;
309         }
310
311         if (a_ref.gen_flags.has(ItemGenerationTraitType::QUESTITEM)) {
312             continue;
313         }
314
315         if (a_ref.gen_flags.has(ItemGenerationTraitType::INSTA_ART)) {
316             continue;
317         }
318
319         if (a_ref.tval != o_ptr->tval) {
320             continue;
321         }
322
323         if (a_ref.sval != o_ptr->sval) {
324             continue;
325         }
326
327         if (a_ref.level > floor_ptr->dun_level) {
328             int d = (a_ref.level - floor_ptr->dun_level) * 2;
329             if (!one_in_(d)) {
330                 continue;
331             }
332         }
333
334         if (!one_in_(a_ref.rarity)) {
335             continue;
336         }
337
338         o_ptr->fixed_artifact_idx = a_idx;
339         return true;
340     }
341
342     return false;
343 }
344
345 /*!
346  * @brief INSTA_ART型の固定アーティファクトの生成を確率に応じて試行する。
347  * Mega-Hack -- Attempt to create one of the "Special Objects"
348  * @param player_ptr プレイヤーへの参照ポインタ
349  * @param o_ptr 生成に割り当てたいオブジェクトの構造体参照ポインタ
350  * @return 生成に成功したらTRUEを返す。
351  * @details
352  * We are only called from "make_object()", and we assume that\n
353  * "apply_magic()" is called immediately after we return.\n
354  *\n
355  * Note -- see "make_artifact()" and "apply_magic()"\n
356  */
357 bool make_artifact_special(PlayerType *player_ptr, ObjectType *o_ptr)
358 {
359     KIND_OBJECT_IDX k_idx = 0;
360
361     /*! @note 地上ではキャンセルする / No artifacts in the town */
362     auto floor_ptr = player_ptr->current_floor_ptr;
363     if (floor_ptr->dun_level == 0) {
364         return false;
365     }
366
367     /*! @note get_obj_num_hookによる指定がある場合は生成をキャンセルする / Themed object */
368     if (get_obj_num_hook) {
369         return false;
370     }
371
372     /*! @note 全固定アーティファクト中からIDの若い順に生成対象とその確率を走査する / Check the artifact list (just the "specials") */
373     for (const auto &[a_idx, a_ref] : a_info) {
374         /*! @note アーティファクト名が空の不正なデータは除外する / Skip "empty" artifacts */
375         if (a_ref.name.empty()) {
376             continue;
377         }
378
379         /*! @note 既に生成回数がカウントされたアーティファクト、QUESTITEMと非INSTA_ARTは除外 / Cannot make an artifact twice */
380         if (a_ref.is_generated) {
381             continue;
382         }
383         if (a_ref.gen_flags.has(ItemGenerationTraitType::QUESTITEM)) {
384             continue;
385         }
386         if (!(a_ref.gen_flags.has(ItemGenerationTraitType::INSTA_ART))) {
387             continue;
388         }
389
390         /*! @note アーティファクト生成階が現在に対して足りない場合は高確率で1/(不足階層*2)を満たさないと生成リストに加えられない */
391         if (a_ref.level > floor_ptr->object_level) {
392             /* @note  / Acquire the "out-of-depth factor". Roll for out-of-depth creation. */
393             int d = (a_ref.level - floor_ptr->object_level) * 2;
394             if (!one_in_(d)) {
395                 continue;
396             }
397         }
398
399         /*! @note 1/(レア度)の確率を満たさないと除外される / Artifact "rarity roll" */
400         if (!one_in_(a_ref.rarity)) {
401             continue;
402         }
403
404         /*!
405          * @note INSTA_ART型固定アーティファクトのベースアイテムもチェック対象とする。
406          * ベースアイテムの生成階層が足りない場合1/(不足階層*5)を満たさないと除外される。
407          */
408         k_idx = lookup_kind(a_ref.tval, a_ref.sval);
409         if (k_info[k_idx].level > floor_ptr->object_level) {
410             int d = (k_info[k_idx].level - floor_ptr->object_level) * 5;
411             if (!one_in_(d)) {
412                 continue;
413             }
414         }
415
416         /*! @note 前述の条件を満たしたら、後のIDのアーティファクトはチェックせずすぐ確定し生成処理に移す /
417          * Assign the template. Mega-Hack -- mark the item as an artifact. Hack: Some artifacts get random extra powers. Success. */
418         o_ptr->prep(k_idx);
419
420         o_ptr->fixed_artifact_idx = a_idx;
421         return true;
422     }
423
424     /*! @note 全INSTA_ART固定アーティファクトを試行しても決まらなかった場合 FALSEを返す / Failure */
425     return false;
426 }