OSDN Git Service

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