OSDN Git Service

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