OSDN Git Service

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