OSDN Git Service

Merge pull request #2335 from sikabane-works/release/3.0.0Alpha54
[hengbandforosx/hengbandosx.git] / src / cmd-action / cmd-open-close.cpp
1 #include "cmd-action/cmd-open-close.h"
2 #include "action/open-close-execution.h"
3 #include "action/open-util.h"
4 #include "cmd-action/cmd-attack.h"
5 #include "core/disturbance.h"
6 #include "core/player-redraw-types.h"
7 #include "floor/geometry.h"
8 #include "game-option/disturbance-options.h"
9 #include "game-option/input-options.h"
10 #include "grid/feature.h"
11 #include "grid/grid.h"
12 #include "inventory/inventory-object.h"
13 #include "inventory/inventory-slot-types.h"
14 #include "io/input-key-requester.h"
15 #include "object/tval-types.h"
16 #include "player-base/player-class.h"
17 #include "player-info/samurai-data-type.h"
18 #include "player-status/player-energy.h"
19 #include "player/attack-defense-types.h"
20 #include "player/special-defense-types.h"
21 #include "specific-object/chest.h"
22 #include "status/action-setter.h"
23 #include "status/experience.h"
24 #include "system/floor-type-definition.h"
25 #include "system/grid-type-definition.h"
26 #include "system/object-type-definition.h"
27 #include "system/player-type-definition.h"
28 #include "target/target-getter.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  * Attempt to open the given chest at the given location
39  * @param y 箱の存在するマスのY座標
40  * @param x 箱の存在するマスのX座標
41  * @param o_idx 箱のオブジェクトID
42  * @return 箱が開かなかった場合TRUE / Returns TRUE if repeated commands may continue
43  * @details
44  * Assume there is no monster blocking the destination
45  */
46 static bool exe_open_chest(PlayerType *player_ptr, POSITION y, POSITION x, OBJECT_IDX o_idx)
47 {
48     bool flag = true;
49     bool more = false;
50     auto *o_ptr = &player_ptr->current_floor_ptr->o_list[o_idx];
51     PlayerEnergy(player_ptr).set_player_turn_energy(100);
52     if (o_ptr->pval > 0) {
53         flag = false;
54         int i = player_ptr->skill_dis;
55         if (player_ptr->blind || no_lite(player_ptr)) {
56             i = i / 10;
57         }
58
59         auto effects = player_ptr->effects();
60         if (effects->confusion()->is_confused() || effects->hallucination()->is_hallucinated()) {
61             i = i / 10;
62         }
63
64         int j = i - o_ptr->pval;
65         if (j < 2) {
66             j = 2;
67         }
68
69         if (randint0(100) < j) {
70             msg_print(_("鍵をはずした。", "You have picked the lock."));
71             gain_exp(player_ptr, 1);
72             flag = true;
73         } else {
74             more = true;
75             if (flush_failure) {
76                 flush();
77             }
78
79             msg_print(_("鍵をはずせなかった。", "You failed to pick the lock."));
80         }
81     }
82
83     if (flag) {
84         Chest chest(player_ptr);
85         chest.chest_trap(y, x, o_idx);
86         chest.chest_death(false, y, x, o_idx);
87     }
88
89     return more;
90 }
91
92 /*!
93  * @brief 「開ける」コマンドのメインルーチン /
94  * Open a closed/locked/jammed door or a closed/locked chest.
95  * @details
96  * Unlocking a locked door/chest is worth one experience point.
97  */
98 void do_cmd_open(PlayerType *player_ptr)
99 {
100     POSITION y, x;
101     DIRECTION dir;
102     OBJECT_IDX o_idx;
103     bool more = false;
104     if (player_ptr->wild_mode) {
105         return;
106     }
107
108     PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU });
109
110     if (easy_open) {
111         int num_doors = count_dt(player_ptr, &y, &x, is_closed_door, false);
112         int num_chests = count_chests(player_ptr, &y, &x, false);
113         if (num_doors || num_chests) {
114             bool too_many = (num_doors && num_chests) || (num_doors > 1) || (num_chests > 1);
115             if (!too_many) {
116                 command_dir = coords_to_dir(player_ptr, y, x);
117             }
118         }
119     }
120
121     if (command_arg) {
122         command_rep = command_arg - 1;
123         player_ptr->redraw |= PR_STATE;
124         command_arg = 0;
125     }
126
127     if (get_rep_dir(player_ptr, &dir, true)) {
128         FEAT_IDX feat;
129         grid_type *g_ptr;
130         y = player_ptr->y + ddy[dir];
131         x = player_ptr->x + ddx[dir];
132         g_ptr = &player_ptr->current_floor_ptr->grid_array[y][x];
133         feat = g_ptr->get_feat_mimic();
134         o_idx = chest_check(player_ptr->current_floor_ptr, y, x, false);
135         if (f_info[feat].flags.has_not(FloorFeatureType::OPEN) && !o_idx) {
136             msg_print(_("そこには開けるものが見当たらない。", "You see nothing there to open."));
137         } else if (g_ptr->m_idx && player_ptr->riding != g_ptr->m_idx) {
138             PlayerEnergy(player_ptr).set_player_turn_energy(100);
139             msg_print(_("モンスターが立ちふさがっている!", "There is a monster in the way!"));
140             do_cmd_attack(player_ptr, y, x, HISSATSU_NONE);
141         } else if (o_idx) {
142             more = exe_open_chest(player_ptr, y, x, o_idx);
143         } else {
144             more = exe_open(player_ptr, y, x);
145         }
146     }
147
148     if (!more) {
149         disturb(player_ptr, false, false);
150     }
151 }
152
153 /*!
154  * @brief 「閉じる」コマンドのメインルーチン /
155  * Close an open door.
156  * @details
157  * Unlocking a locked door/chest is worth one experience point.
158  */
159 void do_cmd_close(PlayerType *player_ptr)
160 {
161     POSITION y, x;
162     DIRECTION dir;
163     bool more = false;
164     if (player_ptr->wild_mode) {
165         return;
166     }
167
168     PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU });
169
170     if (easy_open && (count_dt(player_ptr, &y, &x, is_open, false) == 1)) {
171         command_dir = coords_to_dir(player_ptr, y, x);
172     }
173
174     if (command_arg) {
175         command_rep = command_arg - 1;
176         player_ptr->redraw |= (PR_STATE);
177         command_arg = 0;
178     }
179
180     if (get_rep_dir(player_ptr, &dir, false)) {
181         grid_type *g_ptr;
182         FEAT_IDX feat;
183         y = player_ptr->y + ddy[dir];
184         x = player_ptr->x + ddx[dir];
185         g_ptr = &player_ptr->current_floor_ptr->grid_array[y][x];
186         feat = g_ptr->get_feat_mimic();
187         if (f_info[feat].flags.has_not(FloorFeatureType::CLOSE)) {
188             msg_print(_("そこには閉じるものが見当たらない。", "You see nothing there to close."));
189         } else if (g_ptr->m_idx) {
190             PlayerEnergy(player_ptr).set_player_turn_energy(100);
191             msg_print(_("モンスターが立ちふさがっている!", "There is a monster in the way!"));
192             do_cmd_attack(player_ptr, y, x, HISSATSU_NONE);
193         } else {
194             more = exe_close(player_ptr, y, x);
195         }
196     }
197
198     if (!more) {
199         disturb(player_ptr, false, false);
200     }
201 }
202
203 /*!
204  * @brief 箱、床のトラップ解除処理双方の統合メインルーチン /
205  * Disarms a trap, or chest
206  */
207 void do_cmd_disarm(PlayerType *player_ptr)
208 {
209     POSITION y, x;
210     DIRECTION dir;
211     OBJECT_IDX o_idx;
212     bool more = false;
213     if (player_ptr->wild_mode) {
214         return;
215     }
216
217     PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU });
218
219     if (easy_disarm) {
220         int num_traps = count_dt(player_ptr, &y, &x, is_trap, true);
221         int num_chests = count_chests(player_ptr, &y, &x, true);
222         if (num_traps || num_chests) {
223             bool too_many = (num_traps && num_chests) || (num_traps > 1) || (num_chests > 1);
224             if (!too_many) {
225                 command_dir = coords_to_dir(player_ptr, y, x);
226             }
227         }
228     }
229
230     if (command_arg) {
231         command_rep = command_arg - 1;
232         player_ptr->redraw |= (PR_STATE);
233         command_arg = 0;
234     }
235
236     if (get_rep_dir(player_ptr, &dir, true)) {
237         grid_type *g_ptr;
238         FEAT_IDX feat;
239         y = player_ptr->y + ddy[dir];
240         x = player_ptr->x + ddx[dir];
241         g_ptr = &player_ptr->current_floor_ptr->grid_array[y][x];
242         feat = g_ptr->get_feat_mimic();
243         o_idx = chest_check(player_ptr->current_floor_ptr, y, x, true);
244         if (!is_trap(player_ptr, feat) && !o_idx) {
245             msg_print(_("そこには解除するものが見当たらない。", "You see nothing there to disarm."));
246         } else if (g_ptr->m_idx && player_ptr->riding != g_ptr->m_idx) {
247             msg_print(_("モンスターが立ちふさがっている!", "There is a monster in the way!"));
248             do_cmd_attack(player_ptr, y, x, HISSATSU_NONE);
249         } else if (o_idx) {
250             more = exe_disarm_chest(player_ptr, y, x, o_idx);
251         } else {
252             more = exe_disarm(player_ptr, y, x, dir);
253         }
254     }
255
256     if (!more) {
257         disturb(player_ptr, false, false);
258     }
259 }
260
261 /*!
262  * @brief 「打ち破る」動作コマンドのメインルーチン /
263  * Bash open a door, success based on character strength
264  * @details
265  * <pre>
266  * For a closed door, pval is positive if locked; negative if stuck.
267  *
268  * For an open door, pval is positive for a broken door.
269  *
270  * A closed door can be opened - harder if locked. Any door might be
271  * bashed open (and thereby broken). Bashing a door is (potentially)
272  * faster! You move into the door way. To open a stuck door, it must
273  * be bashed. A closed door can be jammed (see do_cmd_spike()).
274  *
275  * Creatures can also open or bash doors, see elsewhere.
276  * </pre>
277  */
278 void do_cmd_bash(PlayerType *player_ptr)
279 {
280     POSITION y, x;
281     DIRECTION dir;
282     grid_type *g_ptr;
283     bool more = false;
284     if (player_ptr->wild_mode) {
285         return;
286     }
287
288     PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU });
289
290     if (command_arg) {
291         command_rep = command_arg - 1;
292         player_ptr->redraw |= (PR_STATE);
293         command_arg = 0;
294     }
295
296     if (get_rep_dir(player_ptr, &dir, false)) {
297         FEAT_IDX feat;
298         y = player_ptr->y + ddy[dir];
299         x = player_ptr->x + ddx[dir];
300         g_ptr = &player_ptr->current_floor_ptr->grid_array[y][x];
301         feat = g_ptr->get_feat_mimic();
302         if (f_info[feat].flags.has_not(FloorFeatureType::BASH)) {
303             msg_print(_("そこには体当たりするものが見当たらない。", "You see nothing there to bash."));
304         } else if (g_ptr->m_idx) {
305             PlayerEnergy(player_ptr).set_player_turn_energy(100);
306             msg_print(_("モンスターが立ちふさがっている!", "There is a monster in the way!"));
307             do_cmd_attack(player_ptr, y, x, HISSATSU_NONE);
308         } else {
309             more = exe_bash(player_ptr, y, x, dir);
310         }
311     }
312
313     if (!more) {
314         disturb(player_ptr, false, false);
315     }
316 }
317
318 /*!
319  * @brief 「くさびを打つ」ために必要なオブジェクトを所持しているかどうかの判定を返す /
320  * Find the index of some "spikes", if possible.
321  * @param ip くさびとして打てるオブジェクトのID
322  * @return オブジェクトがある場合TRUEを返す
323  * @details
324  * <pre>
325  * Let user choose a pile of spikes, perhaps?
326  * </pre>
327  */
328 static bool get_spike(PlayerType *player_ptr, INVENTORY_IDX *ip)
329 {
330     for (INVENTORY_IDX i = 0; i < INVEN_PACK; i++) {
331         auto *o_ptr = &player_ptr->inventory_list[i];
332         if (!o_ptr->k_idx) {
333             continue;
334         }
335
336         if (o_ptr->tval == ItemKindType::SPIKE) {
337             *ip = i;
338             return true;
339         }
340     }
341
342     return false;
343 }
344
345 /*!
346  * @brief 「くさびを打つ」動作コマンドのメインルーチン /
347  * Jam a closed door with a spike
348  * @param player_ptr プレイヤーへの参照ポインタ
349  * @details
350  * <pre>
351  * This command may NOT be repeated
352  * </pre>
353  */
354 void do_cmd_spike(PlayerType *player_ptr)
355 {
356     DIRECTION dir;
357     if (player_ptr->wild_mode) {
358         return;
359     }
360
361     PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU });
362
363     if (!get_rep_dir(player_ptr, &dir, false)) {
364         return;
365     }
366
367     POSITION y = player_ptr->y + ddy[dir];
368     POSITION x = player_ptr->x + ddx[dir];
369     grid_type *g_ptr;
370     g_ptr = &player_ptr->current_floor_ptr->grid_array[y][x];
371     FEAT_IDX feat = g_ptr->get_feat_mimic();
372     INVENTORY_IDX item;
373     if (f_info[feat].flags.has_not(FloorFeatureType::SPIKE)) {
374         msg_print(_("そこにはくさびを打てるものが見当たらない。", "You see nothing there to spike."));
375     } else if (!get_spike(player_ptr, &item)) {
376         msg_print(_("くさびを持っていない!", "You have no spikes!"));
377     } else if (g_ptr->m_idx) {
378         PlayerEnergy(player_ptr).set_player_turn_energy(100);
379         msg_print(_("モンスターが立ちふさがっている!", "There is a monster in the way!"));
380         do_cmd_attack(player_ptr, y, x, HISSATSU_NONE);
381     } else {
382         PlayerEnergy(player_ptr).set_player_turn_energy(100);
383         msg_format(_("%sにくさびを打ち込んだ。", "You jam the %s with a spike."), f_info[feat].name.c_str());
384         cave_alter_feat(player_ptr, y, x, FloorFeatureType::SPIKE);
385         vary_item(player_ptr, item, -1);
386     }
387 }