OSDN Git Service

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