OSDN Git Service

Merge pull request #2122 from sikabane-works/release/3.0.0Alpha52
[hengbandforosx/hengbandosx.git] / src / inventory / inventory-curse.cpp
1 #include "inventory/inventory-curse.h"
2 #include "artifact/fixed-art-types.h"
3 #include "core/asking-player.h"
4 #include "core/disturbance.h"
5 #include "core/player-redraw-types.h"
6 #include "core/player-update-types.h"
7 #include "flavor/flavor-describer.h"
8 #include "flavor/object-flavor-types.h"
9 #include "inventory/inventory-slot-types.h"
10 #include "io/files-util.h"
11 #include "monster-floor/monster-summon.h"
12 #include "monster-floor/place-monster-types.h"
13 #include "object-enchant/item-feeling.h"
14 #include "object-enchant/object-curse.h"
15 #include "object-enchant/special-object-flags.h"
16 #include "object-enchant/tr-types.h"
17 #include "object-enchant/trc-types.h"
18 #include "object/object-flags.h"
19 #include "perception/object-perception.h"
20 #include "player-info/race-types.h"
21 #include "player/player-damage.h"
22 #include "player/player-status-flags.h"
23 #include "player/player-status.h"
24 #include "spell-kind/spells-random.h"
25 #include "spell-kind/spells-teleport.h"
26 #include "spell/summon-types.h"
27 #include "status/bad-status-setter.h"
28 #include "status/buff-setter.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 #include "util/quarks.h"
34 #include "util/string-processor.h"
35 #include "view/display-messages.h"
36
37 namespace {
38 const EnumClassFlagGroup<CurseTraitType> TRC_P_FLAG_MASK({ CurseTraitType::TY_CURSE, CurseTraitType::DRAIN_EXP, CurseTraitType::ADD_L_CURSE, CurseTraitType::ADD_H_CURSE, CurseTraitType::CALL_ANIMAL, CurseTraitType::CALL_DEMON,
39     CurseTraitType::CALL_DRAGON, CurseTraitType::COWARDICE, CurseTraitType::TELEPORT, CurseTraitType::DRAIN_HP, CurseTraitType::DRAIN_MANA, CurseTraitType::CALL_UNDEAD, CurseTraitType::BERS_RAGE, CurseTraitType::PERSISTENT_CURSE });
40 const EnumClassFlagGroup<CurseSpecialTraitType> TRCS_P_FLAG_MASK({ CurseSpecialTraitType::TELEPORT_SELF, CurseSpecialTraitType::CHAINSWORD });
41 }
42
43 static bool is_specific_curse(CurseTraitType flag)
44 {
45     switch (flag) {
46     case CurseTraitType::ADD_L_CURSE:
47     case CurseTraitType::ADD_H_CURSE:
48     case CurseTraitType::DRAIN_HP:
49     case CurseTraitType::DRAIN_MANA:
50     case CurseTraitType::CALL_ANIMAL:
51     case CurseTraitType::CALL_DEMON:
52     case CurseTraitType::CALL_DRAGON:
53     case CurseTraitType::CALL_UNDEAD:
54     case CurseTraitType::COWARDICE:
55     case CurseTraitType::LOW_MELEE:
56     case CurseTraitType::LOW_AC:
57     case CurseTraitType::HARD_SPELL:
58     case CurseTraitType::FAST_DIGEST:
59     case CurseTraitType::SLOW_REGEN:
60     case CurseTraitType::BERS_RAGE:
61     case CurseTraitType::PERSISTENT_CURSE:
62         return true;
63     default:
64         return false;
65     }
66 }
67
68 static void choise_cursed_item(CurseTraitType flag, object_type *o_ptr, int *choices, int *number, int item_num)
69 {
70     if (!is_specific_curse(flag))
71         return;
72
73     tr_type cf = TR_STR;
74     auto flgs = object_flags(o_ptr);
75     switch (flag) {
76     case CurseTraitType::ADD_L_CURSE:
77         cf = TR_ADD_L_CURSE;
78         break;
79     case CurseTraitType::ADD_H_CURSE:
80         cf = TR_ADD_H_CURSE;
81         break;
82     case CurseTraitType::DRAIN_HP:
83         cf = TR_DRAIN_HP;
84         break;
85     case CurseTraitType::DRAIN_MANA:
86         cf = TR_DRAIN_MANA;
87         break;
88     case CurseTraitType::CALL_ANIMAL:
89         cf = TR_CALL_ANIMAL;
90         break;
91     case CurseTraitType::CALL_DEMON:
92         cf = TR_CALL_DEMON;
93         break;
94     case CurseTraitType::CALL_DRAGON:
95         cf = TR_CALL_DRAGON;
96         break;
97     case CurseTraitType::CALL_UNDEAD:
98         cf = TR_CALL_UNDEAD;
99         break;
100     case CurseTraitType::COWARDICE:
101         cf = TR_COWARDICE;
102         break;
103     case CurseTraitType::LOW_MELEE:
104         cf = TR_LOW_MELEE;
105         break;
106     case CurseTraitType::LOW_AC:
107         cf = TR_LOW_AC;
108         break;
109     case CurseTraitType::HARD_SPELL:
110         cf = TR_HARD_SPELL;
111         break;
112     case CurseTraitType::FAST_DIGEST:
113         cf = TR_FAST_DIGEST;
114         break;
115     case CurseTraitType::SLOW_REGEN:
116         cf = TR_SLOW_REGEN;
117         break;
118     case CurseTraitType::BERS_RAGE:
119         cf = TR_BERS_RAGE;
120         break;
121     case CurseTraitType::PERSISTENT_CURSE:
122         cf = TR_PERSISTENT_CURSE;
123         break;
124     case CurseTraitType::VUL_CURSE:
125         cf = TR_VUL_CURSE;
126         break;
127     default:
128         break;
129     }
130
131     if (flgs.has_not(cf))
132         return;
133
134     choices[*number] = item_num;
135     (*number)++;
136 }
137
138 /*!
139  * @brief 現在呪いを保持している装備品を一つランダムに探し出す / Choose one of items that have cursed flag
140  * @param flag 探し出したい呪いフラグ配列
141  * @return 該当の呪いが一つでもあった場合にランダムに選ばれた装備品のオブジェクト構造体参照ポインタを返す。\n
142  * 呪いがない場合nullptrを返す。
143  */
144 object_type *choose_cursed_obj_name(PlayerType *player_ptr, CurseTraitType flag)
145 {
146     int choices[INVEN_TOTAL - INVEN_MAIN_HAND];
147     int number = 0;
148     if (player_ptr->cursed.has_not(flag))
149         return nullptr;
150
151     for (int i = INVEN_MAIN_HAND; i < INVEN_TOTAL; i++) {
152         object_type *o_ptr = &player_ptr->inventory_list[i];
153         if (o_ptr->curse_flags.has(flag)) {
154             choices[number] = i;
155             number++;
156             continue;
157         }
158
159         choise_cursed_item(flag, o_ptr, choices, &number, i);
160     }
161
162     return &player_ptr->inventory_list[choices[randint0(number)]];
163 }
164
165 /*!
166  * @brief 呪われている、トランプエゴ等による装備品由来のテレポートを実行する
167  * @param player_ptr プレイヤーへの参照ポインタ
168  */
169 static void curse_teleport(PlayerType *player_ptr)
170 {
171     if ((player_ptr->cursed_special.has_not(CurseSpecialTraitType::TELEPORT_SELF)) || !one_in_(200))
172         return;
173
174     GAME_TEXT o_name[MAX_NLEN];
175     object_type *o_ptr;
176     int i_keep = 0, count = 0;
177     for (int i = INVEN_MAIN_HAND; i < INVEN_TOTAL; i++) {
178         o_ptr = &player_ptr->inventory_list[i];
179         if (!o_ptr->k_idx)
180             continue;
181
182         auto flgs = object_flags(o_ptr);
183
184         if (flgs.has_not(TR_TELEPORT))
185             continue;
186
187         if (o_ptr->inscription && angband_strchr(quark_str(o_ptr->inscription), '.'))
188             continue;
189
190         count++;
191         if (one_in_(count))
192             i_keep = i;
193     }
194
195     o_ptr = &player_ptr->inventory_list[i_keep];
196     describe_flavor(player_ptr, o_name, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
197     msg_format(_("%sがテレポートの能力を発動させようとしている。", "Your %s tries to teleport you."), o_name);
198     if (get_check_strict(player_ptr, _("テレポートしますか?", "Teleport? "), CHECK_OKAY_CANCEL)) {
199         disturb(player_ptr, false, true);
200         teleport_player(player_ptr, 50, TELEPORT_SPONTANEOUS);
201     } else {
202         msg_format(_("%sに{.}(ピリオド)と銘を刻むと発動を抑制できます。", "You can inscribe {.} on your %s to disable random teleportation. "), o_name);
203         disturb(player_ptr, true, true);
204     }
205 }
206
207 /*!
208  * @details 元々呪い効果の発揮ルーチン中にいたので、整合性保持のためここに置いておく
209  */
210 static void occur_chainsword_effect(PlayerType *player_ptr)
211 {
212     if ((player_ptr->cursed_special.has_not(CurseSpecialTraitType::CHAINSWORD)) || !one_in_(CHAINSWORD_NOISE))
213         return;
214
215     char noise[1024];
216     if (!get_rnd_line(_("chainswd_j.txt", "chainswd.txt"), 0, noise))
217         msg_print(noise);
218     disturb(player_ptr, false, false);
219 }
220
221 static void curse_drain_exp(PlayerType *player_ptr)
222 {
223     if ((player_ptr->prace == PlayerRaceType::ANDROID) || (player_ptr->cursed.has_not(CurseTraitType::DRAIN_EXP)) || !one_in_(4))
224         return;
225
226     player_ptr->exp -= (player_ptr->lev + 1) / 2;
227     if (player_ptr->exp < 0)
228         player_ptr->exp = 0;
229
230     player_ptr->max_exp -= (player_ptr->lev + 1) / 2;
231     if (player_ptr->max_exp < 0)
232         player_ptr->max_exp = 0;
233
234     check_experience(player_ptr);
235 }
236
237 static void multiply_low_curse(PlayerType *player_ptr)
238 {
239     if ((player_ptr->cursed.has_not(CurseTraitType::ADD_L_CURSE)) || !one_in_(2000))
240         return;
241
242     object_type *o_ptr;
243     o_ptr = choose_cursed_obj_name(player_ptr, CurseTraitType::ADD_L_CURSE);
244     auto new_curse = get_curse(0, o_ptr);
245     if (o_ptr->curse_flags.has(new_curse))
246         return;
247
248     GAME_TEXT o_name[MAX_NLEN];
249     describe_flavor(player_ptr, o_name, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
250     o_ptr->curse_flags.set(new_curse);
251     msg_format(_("悪意に満ちた黒いオーラが%sをとりまいた...", "There is a malignant black aura surrounding your %s..."), o_name);
252     o_ptr->feeling = FEEL_NONE;
253     player_ptr->update |= (PU_BONUS);
254 }
255
256 static void multiply_high_curse(PlayerType *player_ptr)
257 {
258     if ((player_ptr->cursed.has_not(CurseTraitType::ADD_H_CURSE)) || !one_in_(2000))
259         return;
260
261     object_type *o_ptr;
262     o_ptr = choose_cursed_obj_name(player_ptr, CurseTraitType::ADD_H_CURSE);
263     auto new_curse = get_curse(1, o_ptr);
264     if (o_ptr->curse_flags.has(new_curse))
265         return;
266
267     GAME_TEXT o_name[MAX_NLEN];
268     describe_flavor(player_ptr, o_name, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
269     o_ptr->curse_flags.set(new_curse);
270     msg_format(_("悪意に満ちた黒いオーラが%sをとりまいた...", "There is a malignant black aura surrounding your %s..."), o_name);
271     o_ptr->feeling = FEEL_NONE;
272     player_ptr->update |= (PU_BONUS);
273 }
274
275 static void persist_curse(PlayerType *player_ptr)
276 {
277     if ((player_ptr->cursed.has_not(CurseTraitType::PERSISTENT_CURSE)) || !one_in_(500))
278         return;
279
280     object_type *o_ptr;
281     o_ptr = choose_cursed_obj_name(player_ptr, CurseTraitType::PERSISTENT_CURSE);
282     if (o_ptr->curse_flags.has(CurseTraitType::HEAVY_CURSE))
283         return;
284
285     GAME_TEXT o_name[MAX_NLEN];
286     describe_flavor(player_ptr, o_name, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
287     o_ptr->curse_flags.set(CurseTraitType::HEAVY_CURSE);
288     msg_format(_("悪意に満ちた黒いオーラが%sをとりまいた...", "There is a malignant black aura surrounding your %s..."), o_name);
289     o_ptr->feeling = FEEL_NONE;
290     player_ptr->update |= (PU_BONUS);
291 }
292
293 static void curse_call_monster(PlayerType *player_ptr)
294 {
295     const int call_type = PM_ALLOW_GROUP | PM_ALLOW_UNIQUE | PM_NO_PET;
296     const int obj_desc_type = OD_OMIT_PREFIX | OD_NAME_ONLY;
297     floor_type *floor_ptr = player_ptr->current_floor_ptr;
298     if (player_ptr->cursed.has(CurseTraitType::CALL_ANIMAL) && one_in_(2500)) {
299         if (summon_specific(player_ptr, 0, player_ptr->y, player_ptr->x, floor_ptr->dun_level, SUMMON_ANIMAL, call_type)) {
300             GAME_TEXT o_name[MAX_NLEN];
301             describe_flavor(player_ptr, o_name, choose_cursed_obj_name(player_ptr, CurseTraitType::CALL_ANIMAL), obj_desc_type);
302             msg_format(_("%sが動物を引き寄せた!", "Your %s has attracted an animal!"), o_name);
303             disturb(player_ptr, false, true);
304         }
305     }
306
307     if (player_ptr->cursed.has(CurseTraitType::CALL_DEMON) && one_in_(1111)) {
308         if (summon_specific(player_ptr, 0, player_ptr->y, player_ptr->x, floor_ptr->dun_level, SUMMON_DEMON, call_type)) {
309             GAME_TEXT o_name[MAX_NLEN];
310             describe_flavor(player_ptr, o_name, choose_cursed_obj_name(player_ptr, CurseTraitType::CALL_DEMON), obj_desc_type);
311             msg_format(_("%sが悪魔を引き寄せた!", "Your %s has attracted a demon!"), o_name);
312             disturb(player_ptr, false, true);
313         }
314     }
315
316     if (player_ptr->cursed.has(CurseTraitType::CALL_DRAGON) && one_in_(800)) {
317         if (summon_specific(player_ptr, 0, player_ptr->y, player_ptr->x, floor_ptr->dun_level, SUMMON_DRAGON, call_type)) {
318             GAME_TEXT o_name[MAX_NLEN];
319             describe_flavor(player_ptr, o_name, choose_cursed_obj_name(player_ptr, CurseTraitType::CALL_DRAGON), obj_desc_type);
320             msg_format(_("%sがドラゴンを引き寄せた!", "Your %s has attracted a dragon!"), o_name);
321             disturb(player_ptr, false, true);
322         }
323     }
324
325     if (player_ptr->cursed.has(CurseTraitType::CALL_UNDEAD) && one_in_(1111)) {
326         if (summon_specific(player_ptr, 0, player_ptr->y, player_ptr->x, floor_ptr->dun_level, SUMMON_UNDEAD, call_type)) {
327             GAME_TEXT o_name[MAX_NLEN];
328             describe_flavor(player_ptr, o_name, choose_cursed_obj_name(player_ptr, CurseTraitType::CALL_UNDEAD), obj_desc_type);
329             msg_format(_("%sが死霊を引き寄せた!", "Your %s has attracted an undead!"), o_name);
330             disturb(player_ptr, false, true);
331         }
332     }
333 }
334
335 static void curse_cowardice(PlayerType *player_ptr)
336 {
337     if (player_ptr->cursed.has_not(CurseTraitType::COWARDICE)) {
338         return;
339     }
340
341     auto *o_ptr = choose_cursed_obj_name(player_ptr, CurseTraitType::COWARDICE);
342     auto chance = 1500;
343     short duration = 13 + randint1(26);
344     if (o_ptr->curse_flags.has(CurseTraitType::HEAVY_CURSE)) {
345         chance = 150;
346         duration *= 2;
347     }
348     
349     if (!one_in_(chance) || (has_resist_fear(player_ptr) != 0)) {
350         return;
351     }
352
353     disturb(player_ptr, false, true);
354     msg_print(_("とても暗い... とても恐い!", "It's so dark... so scary!"));
355     (void)BadStatusSetter(player_ptr).mod_afraidness(duration);
356 }
357
358 /*!
359  * @brief 装備による狂戦士化の発作を引き起こす
360  * @param player_ptr プレイヤー情報への参照ポインタ
361  */
362 static void curse_berserk_rage(PlayerType *player_ptr)
363 {
364     if (player_ptr->cursed.has_not(CurseTraitType::BERS_RAGE)) {
365         return;
366     }
367
368     auto *o_ptr = choose_cursed_obj_name(player_ptr, CurseTraitType::BERS_RAGE);
369     auto chance = 1500;
370     short duration = 10 + randint1(player_ptr->lev);
371
372     if (o_ptr->curse_flags.has(CurseTraitType::HEAVY_CURSE)) {
373         chance = 150;
374         duration *= 2;
375     }
376     
377     if (!one_in_(chance)) {
378         return;
379     }
380
381     disturb(player_ptr, false, true);
382     msg_print(_("ウガァァア!", "RAAAAGHH!"));
383     msg_print(_("激怒の発作に襲われた!", "You feel a fit of rage coming over you!"));
384     (void)set_shero(player_ptr, duration, false);
385     (void)BadStatusSetter(player_ptr).afraidness(0);
386 }
387
388 static void curse_drain_hp(PlayerType *player_ptr)
389 {
390     if ((player_ptr->cursed.has_not(CurseTraitType::DRAIN_HP)) || !one_in_(666))
391         return;
392
393     GAME_TEXT o_name[MAX_NLEN];
394     describe_flavor(player_ptr, o_name, choose_cursed_obj_name(player_ptr, CurseTraitType::DRAIN_HP), (OD_OMIT_PREFIX | OD_NAME_ONLY));
395     msg_format(_("%sはあなたの体力を吸収した!", "Your %s drains HP from you!"), o_name);
396     take_hit(player_ptr, DAMAGE_LOSELIFE, std::min(player_ptr->lev * 2, 100), o_name);
397 }
398
399 static void curse_drain_mp(PlayerType *player_ptr)
400 {
401     if ((player_ptr->cursed.has_not(CurseTraitType::DRAIN_MANA)) || (player_ptr->csp == 0) || !one_in_(666))
402         return;
403
404     GAME_TEXT o_name[MAX_NLEN];
405     describe_flavor(player_ptr, o_name, choose_cursed_obj_name(player_ptr, CurseTraitType::DRAIN_MANA), (OD_OMIT_PREFIX | OD_NAME_ONLY));
406     msg_format(_("%sはあなたの魔力を吸収した!", "Your %s drains mana from you!"), o_name);
407     player_ptr->csp -= std::min<short>(player_ptr->lev, 50);
408     if (player_ptr->csp < 0) {
409         player_ptr->csp = 0;
410         player_ptr->csp_frac = 0;
411     }
412
413     player_ptr->redraw |= PR_MANA;
414 }
415
416 static void occur_curse_effects(PlayerType *player_ptr)
417 {
418     if ((player_ptr->cursed.has_none_of(TRC_P_FLAG_MASK) && player_ptr->cursed_special.has_none_of(TRCS_P_FLAG_MASK)) || player_ptr->phase_out
419         || player_ptr->wild_mode)
420         return;
421
422     curse_teleport(player_ptr);
423     occur_chainsword_effect(player_ptr);
424     if (player_ptr->cursed.has(CurseTraitType::TY_CURSE) && one_in_(TY_CURSE_CHANCE)) {
425         int count = 0;
426         (void)activate_ty_curse(player_ptr, false, &count);
427     }
428
429     curse_drain_exp(player_ptr);
430     multiply_low_curse(player_ptr);
431     multiply_high_curse(player_ptr);
432     persist_curse(player_ptr);
433     curse_call_monster(player_ptr);
434     curse_cowardice(player_ptr);
435     curse_berserk_rage(player_ptr);
436     if (player_ptr->cursed.has(CurseTraitType::TELEPORT) && one_in_(200) && !player_ptr->anti_tele) {
437         disturb(player_ptr, false, true);
438         teleport_player(player_ptr, 40, TELEPORT_PASSIVE);
439     }
440
441     curse_drain_hp(player_ptr);
442     curse_drain_mp(player_ptr);
443 }
444
445 /*!
446  * @brief 10ゲームターンが進行するごとに装備効果の発動判定を行う処理
447  * / Handle curse effects once every 10 game turns
448  * @param player_ptr プレイヤーへの参照ポインタ
449  */
450 void execute_cursed_items_effect(PlayerType *player_ptr)
451 {
452     occur_curse_effects(player_ptr);
453     if (!one_in_(999) || player_ptr->anti_magic || (one_in_(2) && has_resist_curse(player_ptr)))
454         return;
455
456     object_type *o_ptr = &player_ptr->inventory_list[INVEN_LITE];
457     if (o_ptr->name1 != ART_JUDGE)
458         return;
459
460     if (o_ptr->is_known())
461         msg_print(_("『審判の宝石』はあなたの体力を吸収した!", "The Jewel of Judgement drains life from you!"));
462     else
463         msg_print(_("なにかがあなたの体力を吸収した!", "Something drains life from you!"));
464
465     take_hit(player_ptr, DAMAGE_LOSELIFE, std::min<short>(player_ptr->lev, 50), _("審判の宝石", "the Jewel of Judgement"));
466 }