OSDN Git Service

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