OSDN Git Service

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