OSDN Git Service

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