OSDN Git Service

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