OSDN Git Service

[Implement] ウサウサストライカー召喚処理を追加
[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, const Pos2D &pos, OBJECT_IDX o_idx)
49 {
50     auto flag = true;
51     auto 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.fire_trap(pos, o_idx);
88         chest.open(false, pos, 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     auto more = false;
103     if (player_ptr->wild_mode) {
104         return;
105     }
106
107     PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU });
108     if (easy_open) {
109         const auto &[num_doors, pos_door] = count_dt(player_ptr, is_closed_door, false);
110         const auto &[num_chests, pos_chest] = count_chests(player_ptr, false);
111         if ((num_doors > 0) || (num_chests > 0)) {
112             const auto pos = pos_chest == Pos2D(0, 0) ? pos_door : pos_chest;
113             const auto too_many = (num_doors && num_chests) || (num_doors > 1) || (num_chests > 1);
114             if (!too_many) {
115                 command_dir = coords_to_dir(player_ptr, pos.y, pos.x);
116             }
117         }
118     }
119
120     if (command_arg) {
121         command_rep = command_arg - 1;
122         RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::ACTION);
123         command_arg = 0;
124     }
125
126     int dir;
127     if (get_rep_dir(player_ptr, &dir, true)) {
128         const Pos2D pos(player_ptr->y + ddy[dir], player_ptr->x + ddx[dir]);
129         const auto &grid = player_ptr->current_floor_ptr->get_grid(pos);
130         const auto o_idx = chest_check(player_ptr->current_floor_ptr, pos, false);
131         if (grid.get_terrain_mimic().flags.has_not(TerrainCharacteristics::OPEN) && !o_idx) {
132             msg_print(_("そこには開けるものが見当たらない。", "You see nothing there to open."));
133         } else if (grid.m_idx && player_ptr->riding != grid.m_idx) {
134             PlayerEnergy(player_ptr).set_player_turn_energy(100);
135             msg_print(_("モンスターが立ちふさがっている!", "There is a monster in the way!"));
136             do_cmd_attack(player_ptr, pos.y, pos.x, HISSATSU_NONE);
137         } else if (o_idx) {
138             more = exe_open_chest(player_ptr, pos, o_idx);
139         } else {
140             more = exe_open(player_ptr, pos.y, pos.x);
141         }
142     }
143
144     if (!more) {
145         disturb(player_ptr, false, false);
146     }
147 }
148
149 /*!
150  * @brief 「閉じる」コマンドのメインルーチン /
151  * Close an open door.
152  * @details
153  * Unlocking a locked door/chest is worth one experience point.
154  */
155 void do_cmd_close(PlayerType *player_ptr)
156 {
157     if (player_ptr->wild_mode) {
158         return;
159     }
160
161     PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU });
162     if (easy_open) {
163         const auto &[num_doors, pos] = count_dt(player_ptr, is_open, false);
164         if (num_doors == 1) {
165             command_dir = coords_to_dir(player_ptr, pos.y, pos.x);
166         }
167     }
168
169     if (command_arg) {
170         command_rep = command_arg - 1;
171         RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::ACTION);
172         command_arg = 0;
173     }
174
175     auto more = false;
176     int dir;
177     if (get_rep_dir(player_ptr, &dir)) {
178         const Pos2D pos(player_ptr->y + ddy[dir], player_ptr->x + ddx[dir]);
179         const auto &grid = player_ptr->current_floor_ptr->get_grid(pos);
180         if (grid.get_terrain_mimic().flags.has_not(TerrainCharacteristics::CLOSE)) {
181             msg_print(_("そこには閉じるものが見当たらない。", "You see nothing there to close."));
182         } else if (grid.m_idx) {
183             PlayerEnergy(player_ptr).set_player_turn_energy(100);
184             msg_print(_("モンスターが立ちふさがっている!", "There is a monster in the way!"));
185             do_cmd_attack(player_ptr, pos.y, pos.x, HISSATSU_NONE);
186         } else {
187             more = exe_close(player_ptr, pos.y, pos.x);
188         }
189     }
190
191     if (!more) {
192         disturb(player_ptr, false, false);
193     }
194 }
195
196 /*!
197  * @brief 箱、床のトラップ解除処理双方の統合メインルーチン /
198  * Disarms a trap, or chest
199  */
200 void do_cmd_disarm(PlayerType *player_ptr)
201 {
202     if (player_ptr->wild_mode) {
203         return;
204     }
205
206     PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU });
207     if (easy_disarm) {
208         const auto &[num_traps, pos_trap] = count_dt(player_ptr, is_trap, true);
209         const auto &[num_chests, pos_chest] = count_chests(player_ptr, true);
210         if ((num_traps > 0) || (num_chests > 0)) {
211             const auto pos = pos_chest == Pos2D(0, 0) ? pos_trap : pos_chest;
212             const auto too_many = (num_traps && num_chests) || (num_traps > 1) || (num_chests > 1);
213             if (!too_many) {
214                 command_dir = coords_to_dir(player_ptr, pos.y, pos.x);
215             }
216         }
217     }
218
219     if (command_arg) {
220         command_rep = command_arg - 1;
221         RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::ACTION);
222         command_arg = 0;
223     }
224
225     int dir;
226     auto more = false;
227     if (get_rep_dir(player_ptr, &dir, true)) {
228         const Pos2D pos(player_ptr->y + ddy[dir], player_ptr->x + ddx[dir]);
229         const auto &grid = player_ptr->current_floor_ptr->get_grid(pos);
230         const auto feat = grid.get_feat_mimic();
231         const auto o_idx = chest_check(player_ptr->current_floor_ptr, pos, true);
232         if (!is_trap(player_ptr, feat) && !o_idx) {
233             msg_print(_("そこには解除するものが見当たらない。", "You see nothing there to disarm."));
234         } else if (grid.m_idx && player_ptr->riding != grid.m_idx) {
235             msg_print(_("モンスターが立ちふさがっている!", "There is a monster in the way!"));
236             do_cmd_attack(player_ptr, pos.y, pos.x, HISSATSU_NONE);
237         } else if (o_idx) {
238             more = exe_disarm_chest(player_ptr, pos.y, pos.x, o_idx);
239         } else {
240             more = exe_disarm(player_ptr, pos.y, pos.x, dir);
241         }
242     }
243
244     if (!more) {
245         disturb(player_ptr, false, false);
246     }
247 }
248
249 /*!
250  * @brief 「打ち破る」動作コマンドのメインルーチン /
251  * Bash open a door, success based on character strength
252  * @details
253  * <pre>
254  * For a closed door, pval is positive if locked; negative if stuck.
255  *
256  * For an open door, pval is positive for a broken door.
257  *
258  * A closed door can be opened - harder if locked. Any door might be
259  * bashed open (and thereby broken). Bashing a door is (potentially)
260  * faster! You move into the door way. To open a stuck door, it must
261  * be bashed. A closed door can be jammed (see do_cmd_spike()).
262  *
263  * Creatures can also open or bash doors, see elsewhere.
264  * </pre>
265  */
266 void do_cmd_bash(PlayerType *player_ptr)
267 {
268     auto more = false;
269     if (player_ptr->wild_mode) {
270         return;
271     }
272
273     PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU });
274
275     if (command_arg) {
276         command_rep = command_arg - 1;
277         RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::ACTION);
278         command_arg = 0;
279     }
280
281     int dir;
282     if (get_rep_dir(player_ptr, &dir)) {
283         const Pos2D pos(player_ptr->y + ddy[dir], player_ptr->x + ddx[dir]);
284         const Grid &grid = player_ptr->current_floor_ptr->get_grid(pos);
285         if (grid.get_terrain_mimic().flags.has_not(TerrainCharacteristics::BASH)) {
286             msg_print(_("そこには体当たりするものが見当たらない。", "You see nothing there to bash."));
287         } else if (grid.m_idx) {
288             PlayerEnergy(player_ptr).set_player_turn_energy(100);
289             msg_print(_("モンスターが立ちふさがっている!", "There is a monster in the way!"));
290             do_cmd_attack(player_ptr, pos.y, pos.x, HISSATSU_NONE);
291         } else {
292             more = exe_bash(player_ptr, pos.y, pos.x, dir);
293         }
294     }
295
296     if (!more) {
297         disturb(player_ptr, false, false);
298     }
299 }
300
301 /*!
302  * @brief 「くさびを打つ」ために必要なオブジェクトを所持しているかどうかの判定を返す /
303  * Find the index of some "spikes", if possible.
304  * @param ip くさびとして打てるオブジェクトのID
305  * @return オブジェクトがある場合TRUEを返す
306  * @details
307  * <pre>
308  * Let user choose a pile of spikes, perhaps?
309  * </pre>
310  */
311 static bool get_spike(PlayerType *player_ptr, INVENTORY_IDX *ip)
312 {
313     for (INVENTORY_IDX i = 0; i < INVEN_PACK; i++) {
314         auto *o_ptr = &player_ptr->inventory_list[i];
315         if (!o_ptr->is_valid()) {
316             continue;
317         }
318
319         if (o_ptr->bi_key.tval() == ItemKindType::SPIKE) {
320             *ip = i;
321             return true;
322         }
323     }
324
325     return false;
326 }
327
328 /*!
329  * @brief 「くさびを打つ」動作コマンドのメインルーチン /
330  * Jam a closed door with a spike
331  * @param player_ptr プレイヤーへの参照ポインタ
332  * @details
333  * <pre>
334  * This command may NOT be repeated
335  * </pre>
336  */
337 void do_cmd_spike(PlayerType *player_ptr)
338 {
339     DIRECTION dir;
340     if (player_ptr->wild_mode) {
341         return;
342     }
343
344     PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU });
345
346     if (!get_rep_dir(player_ptr, &dir)) {
347         return;
348     }
349
350     const Pos2D pos(player_ptr->y + ddy[dir], player_ptr->x + ddx[dir]);
351     const auto &grid = player_ptr->current_floor_ptr->get_grid(pos);
352     const auto &terrain_mimic = grid.get_terrain_mimic();
353     INVENTORY_IDX i_idx;
354     if (terrain_mimic.flags.has_not(TerrainCharacteristics::SPIKE)) {
355         msg_print(_("そこにはくさびを打てるものが見当たらない。", "You see nothing there to spike."));
356     } else if (!get_spike(player_ptr, &i_idx)) {
357         msg_print(_("くさびを持っていない!", "You have no spikes!"));
358     } else if (grid.m_idx) {
359         PlayerEnergy(player_ptr).set_player_turn_energy(100);
360         msg_print(_("モンスターが立ちふさがっている!", "There is a monster in the way!"));
361         do_cmd_attack(player_ptr, pos.y, pos.x, HISSATSU_NONE);
362     } else {
363         PlayerEnergy(player_ptr).set_player_turn_energy(100);
364         msg_format(_("%sにくさびを打ち込んだ。", "You jam the %s with a spike."), terrain_mimic.name.data());
365         cave_alter_feat(player_ptr, pos.y, pos.x, TerrainCharacteristics::SPIKE);
366         vary_item(player_ptr, i_idx, -1);
367     }
368 }