1 #include "specific-object/chest.h"
2 #include "effect/attribute-types.h"
3 #include "floor/cave.h"
4 #include "floor/floor-object.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"
30 /*!< この値以降の小項目IDを持った箱は大型の箱としてドロップ数を増やす / Special "sval" limit -- first "large" chest */
31 #define SV_CHEST_MIN_LARGE 4
33 Chest::Chest(PlayerType *player_ptr)
34 : player_ptr(player_ptr)
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
48 * Disperse treasures from the given chest, centered at (x,y).
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.
57 void Chest::chest_death(bool scatter, POSITION y, POSITION x, OBJECT_IDX o_idx)
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];
63 /* Small chests often hold "gold" */
64 const auto sval = o_ptr->bi_key.sval().value();
65 auto small = sval < SV_CHEST_MIN_LARGE;
67 /* Determine how much to drop (see above) */
68 auto number = (sval % SV_CHEST_MIN_LARGE) * 2;
70 if (sval == SV_CHEST_KANDUME) {
74 floor_ptr->object_level = o_ptr->chest_level;
76 /* Determine the "value" of the items */
77 floor_ptr->object_level = std::abs(o_ptr->pval) + 10;
80 /* Zero pval means empty chest */
85 /* Drop some objects (non-chests) */
88 for (; number > 0; --number) {
92 /* Small chests often drop gold */
93 if (small && (randint0(100) < 25)) {
95 if (!make_gold(this->player_ptr, q_ptr)) {
100 /* Otherwise drop an item */
102 /* Make a good object */
103 if (!make_object(this->player_ptr, q_ptr, mode)) {
108 /* If chest scatters its contents, pick any floor square. */
111 for (i = 0; i < 200; i++) {
112 /* Pick a totally random spot. */
113 y = randint0(MAX_HGT);
114 x = randint0(MAX_WID);
116 /* Must be an empty floor. */
117 if (!is_cave_empty_bold(this->player_ptr, y, x)) {
121 /* Place the object there. */
122 (void)drop_near(this->player_ptr, q_ptr, -1, y, x);
128 /* Normally, drop object near the chest. */
130 (void)drop_near(this->player_ptr, q_ptr, -1, y, x);
134 /* Reset the object level */
135 floor_ptr->object_level = floor_ptr->base_level;
146 * Chests have traps too.
147 * @param y 箱の存在するマスのY座標
148 * @param x 箱の存在するマスのX座標
149 * @param o_idx 箱のオブジェクトID
153 * Exploding chest destroys contents (and traps).
154 * Note that the chest itself is never destroyed.
157 void Chest::chest_trap(POSITION y, POSITION x, OBJECT_IDX o_idx)
161 auto *o_ptr = &this->player_ptr->current_floor_ptr->o_list[o_idx];
163 int mon_level = o_ptr->chest_level;
165 /* Ignore disarmed chests */
166 if (o_ptr->pval <= 0) {
170 /* Obtain the traps */
171 auto trap = chest_traps[o_ptr->pval];
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);
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);
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));
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));
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);
211 (void)summon_specific(this->player_ptr, 0, y, x, mon_level, SUMMON_NONE, (PM_ALLOW_GROUP | PM_ALLOW_UNIQUE | PM_NO_PET));
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));
224 /* Force clouds, then summon birds. */
225 if (trap.has(ChestTrapType::BIRD_STORM)) {
226 msg_print(_("鳥の群れがあなたを取り巻いた!", "A storm of birds swirls around you!"));
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);
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));
237 /* Various colorful summonings. */
238 if (trap.has(ChestTrapType::H_SUMMON)) {
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));
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));
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));
264 /* Summon vortices (scattered) */
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));
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) {
282 take_hit(this->player_ptr, DAMAGE_NOESCAPE, damroll(5, 20), _("破滅のトラップの宝箱", "a chest dispel-player trap"));
286 BadStatusSetter bss(this->player_ptr);
288 (void)bss.mod_cut(200);
293 if (!this->player_ptr->free_act) {
294 (void)bss.mod_paralysis(2 + randint0(6));
296 (void)bss.mod_stun(10 + randint0(100));
303 apply_disenchant(this->player_ptr, 0);
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);
317 (void)fire_meteor(this->player_ptr, -1, AttributeType::NETHER, y, x, 150, 1);
321 /* Aggravate monsters. */
322 if (trap.has(ChestTrapType::ALARM)) {
323 msg_print(_("けたたましい音が鳴り響いた!", "An alarm sounds!"));
324 aggravate_monsters(this->player_ptr, 0);
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!"));
332 sound(SOUND_EXPLODE);
333 take_hit(this->player_ptr, DAMAGE_ATTACK, damroll(5, 8), _("爆発する箱", "an exploding chest"));
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);