OSDN Git Service

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