OSDN Git Service

Merge pull request #3532 from sikabane-works/release/3.0.0.87-alpha
[hengbandforosx/hengbandosx.git] / src / specific-object / chest.cpp
1 #include "specific-object/chest.h"
2 #include "effect/attribute-types.h"
3 #include "floor/cave.h"
4 #include "floor/floor-object.h"
5 #include "grid/grid.h"
6 #include "grid/trap.h"
7 #include "main/sound-definitions-table.h"
8 #include "main/sound-of-music.h"
9 #include "monster-floor/monster-summon.h"
10 #include "monster-floor/place-monster-types.h"
11 #include "object-enchant/item-apply-magic.h"
12 #include "perception/object-perception.h"
13 #include "player-info/class-info.h"
14 #include "player/player-damage.h"
15 #include "player/player-status-flags.h"
16 #include "spell-kind/spells-equipment.h"
17 #include "spell-kind/spells-launcher.h"
18 #include "spell-kind/spells-sight.h"
19 #include "spell/spells-summon.h"
20 #include "spell/summon-types.h"
21 #include "status/bad-status-setter.h"
22 #include "status/base-status.h"
23 #include "status/element-resistance.h"
24 #include "sv-definition/sv-other-types.h"
25 #include "system/floor-type-definition.h"
26 #include "system/item-entity.h"
27 #include "system/player-type-definition.h"
28 #include "view/display-messages.h"
29
30 /*!< この値以降の小項目IDを持った箱は大型の箱としてドロップ数を増やす / Special "sval" limit -- first "large" chest */
31 #define SV_CHEST_MIN_LARGE 4
32
33 Chest::Chest(PlayerType *player_ptr)
34     : player_ptr(player_ptr)
35 {
36 }
37
38 /*!
39  * @brief 箱からアイテムを引き出す /
40  * Allocates objects upon opening a chest    -BEN-
41  * @param scatter TRUEならばトラップによるアイテムの拡散処理
42  * @param y 箱の存在するマスのY座標
43  * @param x 箱の存在するマスのX座標
44  * @param o_idx 箱のオブジェクトID
45  * @return なし
46  * @details
47  * <pre>
48  * Disperse treasures from the given chest, centered at (x,y).
49  *
50  * Small chests often contain "gold", while Large chests always contain
51  * items.  Wooden chests contain 2 items, Iron chests contain 4 items,
52  * and Steel chests contain 6 items.  The "value" of the items in a
53  * chest is based on the "power" of the chest, which is in turn based
54  * on the level on which the chest is generated.
55  * </pre>
56  */
57 void Chest::chest_death(bool scatter, POSITION y, POSITION x, OBJECT_IDX o_idx)
58 {
59     BIT_FLAGS mode = AM_GOOD | AM_FORBID_CHEST;
60     auto *floor_ptr = this->player_ptr->current_floor_ptr;
61     auto *o_ptr = &floor_ptr->o_list[o_idx];
62
63     /* Small chests often hold "gold" */
64     const auto sval = o_ptr->bi_key.sval().value();
65     auto small = sval < SV_CHEST_MIN_LARGE;
66
67     /* Determine how much to drop (see above) */
68     auto number = (sval % SV_CHEST_MIN_LARGE) * 2;
69
70     if (sval == SV_CHEST_KANDUME) {
71         number = 5;
72         small = false;
73         mode |= AM_GREAT;
74         floor_ptr->object_level = o_ptr->chest_level;
75     } else {
76         /* Determine the "value" of the items */
77         floor_ptr->object_level = std::abs(o_ptr->pval) + 10;
78     }
79
80     /* Zero pval means empty chest */
81     if (!o_ptr->pval) {
82         number = 0;
83     }
84
85     /* Drop some objects (non-chests) */
86     ItemEntity forge;
87     ItemEntity *q_ptr;
88     for (; number > 0; --number) {
89         q_ptr = &forge;
90         q_ptr->wipe();
91
92         /* Small chests often drop gold */
93         if (small && (randint0(100) < 25)) {
94             /* Make some gold */
95             if (!make_gold(this->player_ptr, q_ptr)) {
96                 continue;
97             }
98         }
99
100         /* Otherwise drop an item */
101         else {
102             /* Make a good object */
103             if (!make_object(this->player_ptr, q_ptr, mode)) {
104                 continue;
105             }
106         }
107
108         /* If chest scatters its contents, pick any floor square. */
109         if (scatter) {
110             int i;
111             for (i = 0; i < 200; i++) {
112                 /* Pick a totally random spot. */
113                 y = randint0(MAX_HGT);
114                 x = randint0(MAX_WID);
115
116                 /* Must be an empty floor. */
117                 if (!is_cave_empty_bold(this->player_ptr, y, x)) {
118                     continue;
119                 }
120
121                 /* Place the object there. */
122                 (void)drop_near(this->player_ptr, q_ptr, -1, y, x);
123
124                 /* Done. */
125                 break;
126             }
127         }
128         /* Normally, drop object near the chest. */
129         else {
130             (void)drop_near(this->player_ptr, q_ptr, -1, y, x);
131         }
132     }
133
134     /* Reset the object level */
135     floor_ptr->object_level = floor_ptr->base_level;
136
137     /* Empty */
138     o_ptr->pval = 0;
139
140     /* Known */
141     object_known(o_ptr);
142 }
143
144 /*!
145  * @brief 箱のトラップ処理 /
146  * Chests have traps too.
147  * @param y 箱の存在するマスのY座標
148  * @param x 箱の存在するマスのX座標
149  * @param o_idx 箱のオブジェクトID
150  * @return なし
151  * @details
152  * <pre>
153  * Exploding chest destroys contents (and traps).
154  * Note that the chest itself is never destroyed.
155  * </pre>
156  */
157 void Chest::chest_trap(POSITION y, POSITION x, OBJECT_IDX o_idx)
158 {
159     int i;
160
161     auto *o_ptr = &this->player_ptr->current_floor_ptr->o_list[o_idx];
162
163     int mon_level = o_ptr->chest_level;
164
165     /* Ignore disarmed chests */
166     if (o_ptr->pval <= 0) {
167         return;
168     }
169
170     /* Obtain the traps */
171     auto trap = chest_traps[o_ptr->pval];
172
173     /* Lose strength */
174     if (trap.has(ChestTrapType::LOSE_STR)) {
175         msg_print(_("仕掛けられていた小さな針に刺されてしまった!", "A small needle has pricked you!"));
176         take_hit(this->player_ptr, DAMAGE_NOESCAPE, damroll(1, 4), _("毒針", "a poison needle"));
177         (void)do_dec_stat(this->player_ptr, A_STR);
178     }
179
180     /* Lose constitution */
181     if (trap.has(ChestTrapType::LOSE_CON)) {
182         msg_print(_("仕掛けられていた小さな針に刺されてしまった!", "A small needle has pricked you!"));
183         take_hit(this->player_ptr, DAMAGE_NOESCAPE, damroll(1, 4), _("毒針", "a poison needle"));
184         (void)do_dec_stat(this->player_ptr, A_CON);
185     }
186
187     /* Poison */
188     if (trap.has(ChestTrapType::POISON)) {
189         msg_print(_("突如吹き出した緑色のガスに包み込まれた!", "A puff of green gas surrounds you!"));
190         if (!(has_resist_pois(this->player_ptr) || is_oppose_pois(this->player_ptr))) {
191             (void)BadStatusSetter(this->player_ptr).mod_poison(10 + randint1(20));
192         }
193     }
194
195     /* Paralyze */
196     if (trap.has(ChestTrapType::PARALYZE)) {
197         msg_print(_("突如吹き出した黄色いガスに包み込まれた!", "A puff of yellow gas surrounds you!"));
198         if (!this->player_ptr->free_act) {
199             (void)BadStatusSetter(this->player_ptr).mod_paralysis(10 + randint1(20));
200         }
201     }
202
203     /* Summon monsters */
204     if (trap.has(ChestTrapType::SUMMON)) {
205         int num = 2 + randint1(3);
206         msg_print(_("突如吹き出した煙に包み込まれた!", "You are enveloped in a cloud of smoke!"));
207         for (i = 0; i < num; i++) {
208             if (randint1(100) < this->player_ptr->current_floor_ptr->dun_level) {
209                 activate_hi_summon(this->player_ptr, this->player_ptr->y, this->player_ptr->x, false);
210             } else {
211                 (void)summon_specific(this->player_ptr, 0, y, x, mon_level, SUMMON_NONE, (PM_ALLOW_GROUP | PM_ALLOW_UNIQUE | PM_NO_PET));
212             }
213         }
214     }
215
216     /* Elemental summon. */
217     if (trap.has(ChestTrapType::E_SUMMON)) {
218         msg_print(_("宝を守るためにエレメンタルが現れた!", "Elemental beings appear to protect their treasures!"));
219         for (i = 0; i < randint1(3) + 5; i++) {
220             (void)summon_specific(this->player_ptr, 0, y, x, mon_level, SUMMON_ELEMENTAL, (PM_ALLOW_GROUP | PM_ALLOW_UNIQUE | PM_NO_PET));
221         }
222     }
223
224     /* Force clouds, then summon birds. */
225     if (trap.has(ChestTrapType::BIRD_STORM)) {
226         msg_print(_("鳥の群れがあなたを取り巻いた!", "A storm of birds swirls around you!"));
227
228         for (i = 0; i < randint1(3) + 3; i++) {
229             (void)fire_meteor(this->player_ptr, -1, AttributeType::FORCE, y, x, o_ptr->pval / 5, 7);
230         }
231
232         for (i = 0; i < randint1(5) + o_ptr->pval / 5; i++) {
233             (void)summon_specific(this->player_ptr, 0, y, x, mon_level, SUMMON_BIRD, (PM_ALLOW_GROUP | PM_ALLOW_UNIQUE | PM_NO_PET));
234         }
235     }
236
237     /* Various colorful summonings. */
238     if (trap.has(ChestTrapType::H_SUMMON)) {
239         /* Summon demons. */
240         if (one_in_(4)) {
241             msg_print(_("炎と硫黄の雲の中に悪魔が姿を現した!", "Demons materialize in clouds of fire and brimstone!"));
242             for (i = 0; i < randint1(3) + 2; i++) {
243                 (void)fire_meteor(this->player_ptr, -1, AttributeType::FIRE, y, x, 10, 5);
244                 (void)summon_specific(this->player_ptr, 0, y, x, mon_level, SUMMON_DEMON, (PM_ALLOW_GROUP | PM_ALLOW_UNIQUE | PM_NO_PET));
245             }
246         }
247
248         /* Summon dragons. */
249         else if (one_in_(3)) {
250             msg_print(_("暗闇にドラゴンの影がぼんやりと現れた!", "Draconic forms loom out of the darkness!"));
251             for (i = 0; i < randint1(3) + 2; i++) {
252                 (void)summon_specific(this->player_ptr, 0, y, x, mon_level, SUMMON_DRAGON, (PM_ALLOW_GROUP | PM_ALLOW_UNIQUE | PM_NO_PET));
253             }
254         }
255
256         /* Summon hybrids. */
257         else if (one_in_(2)) {
258             msg_print(_("奇妙な姿の怪物が襲って来た!", "Creatures strange and twisted assault you!"));
259             for (i = 0; i < randint1(5) + 3; i++) {
260                 (void)summon_specific(this->player_ptr, 0, y, x, mon_level, SUMMON_HYBRID, (PM_ALLOW_GROUP | PM_ALLOW_UNIQUE | PM_NO_PET));
261             }
262         }
263
264         /* Summon vortices (scattered) */
265         else {
266             msg_print(_("渦巻が合体し、破裂した!", "Vortices coalesce and wreak destruction!"));
267             for (i = 0; i < randint1(3) + 2; i++) {
268                 (void)summon_specific(this->player_ptr, 0, y, x, mon_level, SUMMON_VORTEX, (PM_ALLOW_GROUP | PM_ALLOW_UNIQUE | PM_NO_PET));
269             }
270         }
271     }
272
273     /* Dispel player. */
274     if ((trap.has(ChestTrapType::RUNES_OF_EVIL)) && o_ptr->is_valid()) {
275         msg_print(_("恐ろしい声が響いた:  「暗闇が汝をつつまん!」", "Hideous voices bid:  'Let the darkness have thee!'"));
276         for (auto count = 4 + randint0(3); count > 0; count--) {
277             if (randint1(100 + o_ptr->pval * 2) <= this->player_ptr->skill_sav) {
278                 continue;
279             }
280
281             if (one_in_(6)) {
282                 take_hit(this->player_ptr, DAMAGE_NOESCAPE, damroll(5, 20), _("破滅のトラップの宝箱", "a chest dispel-player trap"));
283                 continue;
284             }
285
286             BadStatusSetter bss(this->player_ptr);
287             if (one_in_(5)) {
288                 (void)bss.mod_cut(200);
289                 continue;
290             }
291
292             if (one_in_(4)) {
293                 if (!this->player_ptr->free_act) {
294                     (void)bss.mod_paralysis(2 + randint0(6));
295                 } else {
296                     (void)bss.mod_stun(10 + randint0(100));
297                 }
298
299                 continue;
300             }
301
302             if (one_in_(3)) {
303                 apply_disenchant(this->player_ptr, 0);
304                 continue;
305             }
306
307             if (one_in_(2)) {
308                 (void)do_dec_stat(this->player_ptr, A_STR);
309                 (void)do_dec_stat(this->player_ptr, A_DEX);
310                 (void)do_dec_stat(this->player_ptr, A_CON);
311                 (void)do_dec_stat(this->player_ptr, A_INT);
312                 (void)do_dec_stat(this->player_ptr, A_WIS);
313                 (void)do_dec_stat(this->player_ptr, A_CHR);
314                 continue;
315             }
316
317             (void)fire_meteor(this->player_ptr, -1, AttributeType::NETHER, y, x, 150, 1);
318         }
319     }
320
321     /* Aggravate monsters. */
322     if (trap.has(ChestTrapType::ALARM)) {
323         msg_print(_("けたたましい音が鳴り響いた!", "An alarm sounds!"));
324         aggravate_monsters(this->player_ptr, 0);
325     }
326
327     /* Explode */
328     if ((trap.has(ChestTrapType::EXPLODE)) && o_ptr->is_valid()) {
329         msg_print(_("突然、箱が爆発した!", "There is a sudden explosion!"));
330         msg_print(_("箱の中の物はすべて粉々に砕け散った!", "Everything inside the chest is destroyed!"));
331         o_ptr->pval = 0;
332         sound(SOUND_EXPLODE);
333         take_hit(this->player_ptr, DAMAGE_ATTACK, damroll(5, 8), _("爆発する箱", "an exploding chest"));
334     }
335     /* Scatter contents. */
336     if ((trap.has(ChestTrapType::SCATTER)) && o_ptr->is_valid()) {
337         msg_print(_("宝箱の中身はダンジョンじゅうに散乱した!", "The contents of the chest scatter all over the dungeon!"));
338         this->chest_death(true, y, x, o_idx);
339         o_ptr->pval = 0;
340     }
341 }