OSDN Git Service

17a6fdf250036ec214e5cee6b2df09f5981533ba
[hengbandforosx/hengbandosx.git] / src / action / open-close-execution.cpp
1 /*!
2  * @file open-close-execution.cpp
3  * @brief 扉や箱を開ける処理
4  * @date 2020/07/11
5  * @author Hourier
6  */
7
8 #include "action/open-close-execution.h"
9 #include "action/movement-execution.h"
10 #include "combat/attack-power-table.h"
11 #include "game-option/disturbance-options.h"
12 #include "game-option/input-options.h"
13 #include "grid/feature.h"
14 #include "grid/grid.h"
15 #include "grid/trap.h"
16 #include "main/sound-definitions-table.h"
17 #include "main/sound-of-music.h"
18 #include "perception/object-perception.h"
19 #include "player-base/player-class.h"
20 #include "player-status/player-energy.h"
21 #include "player/player-status-table.h"
22 #include "specific-object/chest.h"
23 #include "status/bad-status-setter.h"
24 #include "status/experience.h"
25 #include "system/floor-type-definition.h"
26 #include "system/grid-type-definition.h"
27 #include "system/object-type-definition.h"
28 #include "system/player-type-definition.h"
29 #include "term/screen-processor.h"
30 #include "util/bit-flags-calculator.h"
31 #include "view/display-messages.h"
32
33 /*!
34  * @brief 「開ける」動作コマンドのサブルーチン /
35  * Perform the basic "open" command on doors
36  * @param y 対象を行うマスのY座標
37  * @param x 対象を行うマスのX座標
38  * @return 連続でコマンドを実行する時のみTRUE、1回きりの時はFALSE
39  */
40 bool exe_open(PlayerType *player_ptr, POSITION y, POSITION x)
41 {
42     auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[y][x];
43     feature_type *f_ptr = &f_info[g_ptr->feat];
44     PlayerEnergy(player_ptr).set_player_turn_energy(100);
45     if (f_ptr->flags.has_not(FloorFeatureType::OPEN)) {
46         msg_format(_("%sはがっちりと閉じられているようだ。", "The %s appears to be stuck."), f_info[g_ptr->get_feat_mimic()].name.c_str());
47         return false;
48     }
49
50     if (!f_ptr->power) {
51         cave_alter_feat(player_ptr, y, x, FloorFeatureType::OPEN);
52         sound(SOUND_OPENDOOR);
53         return false;
54     }
55
56     int i = player_ptr->skill_dis;
57     if (player_ptr->blind || no_lite(player_ptr))
58         i = i / 10;
59
60     if (player_ptr->confused || player_ptr->hallucinated)
61         i = i / 10;
62
63     int j = f_ptr->power;
64     j = i - (j * 4);
65     if (j < 2)
66         j = 2;
67
68     if (randint0(100) >= j) {
69         if (flush_failure)
70             flush();
71
72         msg_print(_("鍵をはずせなかった。", "You failed to pick the lock."));
73         return true;
74     }
75
76     msg_print(_("鍵をはずした。", "You have picked the lock."));
77     cave_alter_feat(player_ptr, y, x, FloorFeatureType::OPEN);
78     sound(SOUND_OPENDOOR);
79     gain_exp(player_ptr, 1);
80     return false;
81 }
82
83 /*!
84  * @brief 「閉じる」動作コマンドのサブルーチン /
85  * Perform the basic "close" command
86  * @param y 対象を行うマスのY座標
87  * @param x 対象を行うマスのX座標
88  * @return 実際に処理が行われた場合TRUEを返す。
89  * @details
90  * Assume destination is an open/broken door
91  * Assume there is no monster blocking the destination
92  * Returns TRUE if repeated commands may continue
93  * @todo 常にFALSEを返している
94  */
95 bool exe_close(PlayerType *player_ptr, POSITION y, POSITION x)
96 {
97     auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[y][x];
98     FEAT_IDX old_feat = g_ptr->feat;
99     bool more = false;
100     PlayerEnergy(player_ptr).set_player_turn_energy(100);
101     if (f_info[old_feat].flags.has_not(FloorFeatureType::CLOSE))
102         return more;
103
104     int16_t closed_feat = feat_state(player_ptr->current_floor_ptr, old_feat, FloorFeatureType::CLOSE);
105     if ((!g_ptr->o_idx_list.empty() || g_ptr->is_object()) && (closed_feat != old_feat) && f_info[closed_feat].flags.has_not(FloorFeatureType::DROP)) {
106         msg_print(_("何かがつっかえて閉まらない。", "Something prevents it from closing."));
107         return more;
108     }
109
110     cave_alter_feat(player_ptr, y, x, FloorFeatureType::CLOSE);
111     if (old_feat == g_ptr->feat) {
112         msg_print(_("ドアは壊れてしまっている。", "The door appears to be broken."));
113     } else {
114         sound(SOUND_SHUTDOOR);
115     }
116
117     return more;
118 }
119
120 /*!
121  * @brief 移動処理による簡易な「開く」処理 /
122  * easy_open_door --
123  * @return 開く処理が実際に試みられた場合TRUEを返す
124  * @details
125  * <pre>
126  *      If there is a jammed/closed/locked door at the given location,
127  *      then attempt to unlock/open it. Return TRUE if an attempt was
128  *      made (successful or not), otherwise return FALSE.
129  *
130  *      The code here should be nearly identical to that in
131  *      do_cmd_open_test() and exe_open().
132  * </pre>
133  */
134 bool easy_open_door(PlayerType *player_ptr, POSITION y, POSITION x)
135 {
136     int i, j;
137     auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[y][x];
138     feature_type *f_ptr = &f_info[g_ptr->feat];
139     if (!is_closed_door(player_ptr, g_ptr->feat))
140         return false;
141
142     if (f_ptr->flags.has_not(FloorFeatureType::OPEN)) {
143         msg_format(_("%sはがっちりと閉じられているようだ。", "The %s appears to be stuck."), f_info[g_ptr->get_feat_mimic()].name.c_str());
144     } else if (f_ptr->power) {
145         i = player_ptr->skill_dis;
146         if (player_ptr->blind || no_lite(player_ptr))
147             i = i / 10;
148
149         if (player_ptr->confused || player_ptr->hallucinated)
150             i = i / 10;
151
152         j = f_ptr->power;
153         j = i - (j * 4);
154         if (j < 2)
155             j = 2;
156
157         if (randint0(100) < j) {
158             msg_print(_("鍵をはずした。", "You have picked the lock."));
159             cave_alter_feat(player_ptr, y, x, FloorFeatureType::OPEN);
160             sound(SOUND_OPENDOOR);
161             gain_exp(player_ptr, 1);
162         } else {
163             if (flush_failure)
164                 flush();
165
166             msg_print(_("鍵をはずせなかった。", "You failed to pick the lock."));
167         }
168     } else {
169         cave_alter_feat(player_ptr, y, x, FloorFeatureType::OPEN);
170         sound(SOUND_OPENDOOR);
171     }
172
173     return true;
174 }
175
176 /*!
177  * @brief 箱のトラップを解除する実行処理 /
178  * Perform the basic "disarm" command
179  * @param y 解除を行うマスのY座標
180  * @param x 解除を行うマスのX座標
181  * @param o_idx 箱のオブジェクトID
182  * @return ターンを消費する処理が行われた場合TRUEを返す
183  * @details
184  * <pre>
185  * Assume destination is a visible trap
186  * Assume there is no monster blocking the destination
187  * Returns TRUE if repeated commands may continue
188  * </pre>
189  */
190 bool exe_disarm_chest(PlayerType *player_ptr, POSITION y, POSITION x, OBJECT_IDX o_idx)
191 {
192     bool more = false;
193     auto *o_ptr = &player_ptr->current_floor_ptr->o_list[o_idx];
194     PlayerEnergy(player_ptr).set_player_turn_energy(100);
195     int i = player_ptr->skill_dis;
196     if (player_ptr->blind || no_lite(player_ptr))
197         i = i / 10;
198
199     if (player_ptr->confused || player_ptr->hallucinated)
200         i = i / 10;
201
202     int j = i - o_ptr->pval;
203     if (j < 2)
204         j = 2;
205
206     if (!o_ptr->is_known()) {
207         msg_print(_("トラップが見あたらない。", "I don't see any traps."));
208     } else if (o_ptr->pval <= 0) {
209         msg_print(_("箱にはトラップが仕掛けられていない。", "The chest is not trapped."));
210     } else if (chest_traps[o_ptr->pval].none()) {
211         msg_print(_("箱にはトラップが仕掛けられていない。", "The chest is not trapped."));
212     } else if (randint0(100) < j) {
213         msg_print(_("箱に仕掛けられていたトラップを解除した。", "You have disarmed the chest."));
214         gain_exp(player_ptr, o_ptr->pval);
215         o_ptr->pval = (0 - o_ptr->pval);
216     } else if ((i > 5) && (randint1(i) > 5)) {
217         more = true;
218         if (flush_failure)
219             flush();
220
221         msg_print(_("箱のトラップ解除に失敗した。", "You failed to disarm the chest."));
222     } else {
223         msg_print(_("トラップを作動させてしまった!", "You set off a trap!"));
224         sound(SOUND_FAIL);
225         Chest(player_ptr).chest_trap(y, x, o_idx);
226     }
227
228     return more;
229 }
230
231 /*!
232  * @brief 箱のトラップを解除するコマンドのサブルーチン /
233  * Perform the basic "disarm" command
234  * @param y 解除を行うマスのY座標
235  * @param x 解除を行うマスのX座標
236  * @param dir プレイヤーからみた方向ID
237  * @return ターンを消費する処理が行われた場合TRUEを返す
238  * @details
239  * <pre>
240  * Assume destination is a visible trap
241  * Assume there is no monster blocking the destination
242  * Returns TRUE if repeated commands may continue
243  * </pre>
244  */
245
246 bool exe_disarm(PlayerType *player_ptr, POSITION y, POSITION x, DIRECTION dir)
247 {
248     auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[y][x];
249     feature_type *f_ptr = &f_info[g_ptr->feat];
250     concptr name = f_ptr->name.c_str();
251     int power = f_ptr->power;
252     bool more = false;
253     int i = player_ptr->skill_dis;
254     PlayerEnergy(player_ptr).set_player_turn_energy(100);
255     if (player_ptr->blind || no_lite(player_ptr))
256         i = i / 10;
257
258     if (player_ptr->confused || player_ptr->hallucinated)
259         i = i / 10;
260
261     int j = i - power;
262     if (j < 2)
263         j = 2;
264
265     if (randint0(100) < j) {
266         msg_format(_("%sを解除した。", "You have disarmed the %s."), name);
267         gain_exp(player_ptr, power);
268         cave_alter_feat(player_ptr, y, x, FloorFeatureType::DISARM);
269         exe_movement(player_ptr, dir, easy_disarm, false);
270     } else if ((i > 5) && (randint1(i) > 5)) {
271         if (flush_failure)
272             flush();
273
274         msg_format(_("%sの解除に失敗した。", "You failed to disarm the %s."), name);
275         more = true;
276     } else {
277         msg_format(_("%sを作動させてしまった!", "You set off the %s!"), name);
278         exe_movement(player_ptr, dir, easy_disarm, false);
279     }
280
281     return more;
282 }
283
284 /*!
285  * @brief 「打ち破る」動作コマンドのサブルーチン /
286  * Perform the basic "bash" command
287  * @param y 対象を行うマスのY座標
288  * @param x 対象を行うマスのX座標
289  * @param dir プレイヤーから見たターゲットの方角ID
290  * @return 実際に処理が行われた場合TRUEを返す。
291  * @details
292  * <pre>
293  * Assume destination is a closed/locked/jammed door
294  * Assume there is no monster blocking the destination
295  * Returns TRUE if repeated commands may continue
296  * </pre>
297  */
298 bool exe_bash(PlayerType *player_ptr, POSITION y, POSITION x, DIRECTION dir)
299 {
300     auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[y][x];
301     feature_type *f_ptr = &f_info[g_ptr->feat];
302     int bash = adj_str_blow[player_ptr->stat_index[A_STR]];
303     int temp = f_ptr->power;
304     bool more = false;
305     concptr name = f_info[g_ptr->get_feat_mimic()].name.c_str();
306     PlayerEnergy(player_ptr).set_player_turn_energy(100);
307     msg_format(_("%sに体当たりをした!", "You smash into the %s!"), name);
308     temp = (bash - (temp * 10));
309     if (PlayerClass(player_ptr).equals(PlayerClassType::BERSERKER))
310         temp *= 2;
311
312     if (temp < 1)
313         temp = 1;
314
315     if (randint0(100) < temp) {
316         msg_format(_("%sを壊した!", "The %s crashes open!"), name);
317         sound(f_ptr->flags.has(FloorFeatureType::GLASS) ? SOUND_GLASS : SOUND_OPENDOOR);
318         if ((randint0(100) < 50) || (feat_state(player_ptr->current_floor_ptr, g_ptr->feat, FloorFeatureType::OPEN) == g_ptr->feat) || f_ptr->flags.has(FloorFeatureType::GLASS)) {
319             cave_alter_feat(player_ptr, y, x, FloorFeatureType::BASH);
320         } else {
321             cave_alter_feat(player_ptr, y, x, FloorFeatureType::OPEN);
322         }
323
324         exe_movement(player_ptr, dir, false, false);
325     } else if (randint0(100) < adj_dex_safe[player_ptr->stat_index[A_DEX]] + player_ptr->lev) {
326         msg_format(_("この%sは頑丈だ。", "The %s holds firm."), name);
327         more = true;
328     } else {
329         msg_print(_("体のバランスをくずしてしまった。", "You are off-balance."));
330         (void)BadStatusSetter(player_ptr).mod_paralysis(2 + randint0(2));
331     }
332
333     return more;
334 }