OSDN Git Service

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