OSDN Git Service

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