OSDN Git Service

912e158582d683fd15b9ef999fe2d59a8eda5cbb
[hengbandforosx/hengbandosx.git] / src / cmd-action / cmd-move.cpp
1 #include "cmd-action/cmd-move.h"
2 #include "action/action-limited.h"
3 #include "action/movement-execution.h"
4 #include "action/run-execution.h"
5 #include "avatar/avatar.h"
6 #include "cmd-io/cmd-save.h"
7 #include "core/asking-player.h"
8 #include "core/disturbance.h"
9 #include "core/player-redraw-types.h"
10 #include "core/stuff-handler.h"
11 #include "dungeon/quest.h"
12 #include "floor/cave.h"
13 #include "floor/floor-mode-changer.h"
14 #include "floor/wild.h"
15 #include "game-option/birth-options.h"
16 #include "game-option/input-options.h"
17 #include "game-option/map-screen-options.h"
18 #include "game-option/play-record-options.h"
19 #include "game-option/special-options.h"
20 #include "info-reader/fixed-map-parser.h"
21 #include "io/input-key-requester.h"
22 #include "io/write-diary.h"
23 #include "main/sound-definitions-table.h"
24 #include "main/sound-of-music.h"
25 #include "mind/mind-ninja.h"
26 #include "player-base/player-class.h"
27 #include "player-info/samurai-data-type.h"
28 #include "player-status/player-energy.h"
29 #include "player/attack-defense-types.h"
30 #include "player/player-move.h"
31 #include "player/player-status.h"
32 #include "player/special-defense-types.h"
33 #include "spell-realm/spells-hex.h"
34 #include "spell-realm/spells-song.h"
35 #include "status/action-setter.h"
36 #include "system/dungeon-info.h"
37 #include "system/floor-type-definition.h"
38 #include "system/grid-type-definition.h"
39 #include "system/player-type-definition.h"
40 #include "system/redrawing-flags-updater.h"
41 #include "system/terrain-type-definition.h"
42 #include "target/target-getter.h"
43 #include "timed-effect/player-cut.h"
44 #include "timed-effect/player-stun.h"
45 #include "timed-effect/timed-effects.h"
46 #include "util/bit-flags-calculator.h"
47 #include "view/display-messages.h"
48
49 /*!
50  * @brief フロア脱出時に出戻りが不可能だった場合に警告を加える処理
51  * @param down_stair TRUEならば階段を降りる処理、FALSEなら階段を昇る処理による内容
52  * @return フロア移動を実際に行うならTRUE、キャンセルする場合はFALSE
53  */
54 static bool confirm_leave_level(PlayerType *player_ptr, bool down_stair)
55 {
56     const auto &quest_list = QuestList::get_instance();
57     const auto *q_ptr = &quest_list[player_ptr->current_floor_ptr->quest_number];
58
59     auto caution_in_tower = any_bits(q_ptr->flags, QUEST_FLAG_TOWER);
60     caution_in_tower &= q_ptr->status != QuestStatusType::STAGE_COMPLETED || (down_stair && (quest_list[QuestId::TOWER1].status != QuestStatusType::COMPLETED));
61
62     auto caution_in_quest = q_ptr->type == QuestKindType::RANDOM;
63     caution_in_quest |= q_ptr->flags & QUEST_FLAG_ONCE && q_ptr->status != QuestStatusType::COMPLETED;
64     caution_in_quest |= caution_in_tower;
65
66     if (confirm_quest && inside_quest(player_ptr->current_floor_ptr->quest_number) && caution_in_quest) {
67         msg_print(_("この階を一度去ると二度と戻って来られません。", "You can't come back here once you leave this floor."));
68         return get_check(_("本当にこの階を去りますか?", "Really leave this floor? "));
69     }
70
71     return true;
72 }
73
74 /*!
75  * @brief 階段を使って階層を昇る処理 / Go up one level
76  */
77 void do_cmd_go_up(PlayerType *player_ptr)
78 {
79     auto &quest_list = QuestList::get_instance();
80     bool go_up = false;
81     auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[player_ptr->y][player_ptr->x];
82     auto *f_ptr = &terrains_info[g_ptr->feat];
83     int up_num = 0;
84     PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU });
85
86     if (f_ptr->flags.has_not(TerrainCharacteristics::LESS)) {
87         msg_print(_("ここには上り階段が見当たらない。", "I see no up staircase here."));
88         return;
89     }
90
91     const auto &floor_ptr = player_ptr->current_floor_ptr;
92     if (f_ptr->flags.has(TerrainCharacteristics::QUEST)) {
93         if (!confirm_leave_level(player_ptr, false)) {
94             return;
95         }
96
97         if (is_echizen(player_ptr)) {
98             msg_print(_("なんだこの階段は!", "What's this STAIRWAY!"));
99         } else {
100             msg_print(_("上の階に登った。", "You enter the up staircase."));
101         }
102
103         sound(SOUND_STAIRWAY);
104
105         leave_quest_check(player_ptr);
106         floor_ptr->quest_number = i2enum<QuestId>(g_ptr->special);
107         const auto quest_number = floor_ptr->quest_number;
108         auto &quest = quest_list[quest_number];
109         if (quest.status == QuestStatusType::UNTAKEN) {
110             if (quest.type != QuestKindType::RANDOM) {
111                 init_flags = INIT_ASSIGN;
112                 parse_fixed_map(player_ptr, QUEST_DEFINITION_LIST, 0, 0, 0, 0);
113             }
114
115             quest.status = QuestStatusType::TAKEN;
116         }
117
118         if (!inside_quest(quest_number)) {
119             floor_ptr->dun_level = 0;
120             player_ptr->word_recall = 0;
121         }
122
123         player_ptr->leaving = true;
124         player_ptr->oldpx = 0;
125         player_ptr->oldpy = 0;
126         PlayerEnergy(player_ptr).set_player_turn_energy(100);
127         return;
128     }
129
130     if (!floor_ptr->is_in_dungeon()) {
131         go_up = true;
132     } else {
133         go_up = confirm_leave_level(player_ptr, false);
134     }
135
136     if (!go_up) {
137         return;
138     }
139
140     PlayerEnergy(player_ptr).set_player_turn_energy(100);
141
142     if (autosave_l) {
143         do_cmd_save_game(player_ptr, true);
144     }
145
146     const auto quest_number = player_ptr->current_floor_ptr->quest_number;
147     auto &quest = quest_list[quest_number];
148
149     if (inside_quest(quest_number) && quest.type == QuestKindType::RANDOM) {
150         leave_quest_check(player_ptr);
151         player_ptr->current_floor_ptr->quest_number = QuestId::NONE;
152     }
153
154     if (inside_quest(quest_number) && quest.type != QuestKindType::RANDOM) {
155         leave_quest_check(player_ptr);
156         player_ptr->current_floor_ptr->quest_number = i2enum<QuestId>(g_ptr->special);
157         player_ptr->current_floor_ptr->dun_level = 0;
158         up_num = 0;
159     } else {
160         if (f_ptr->flags.has(TerrainCharacteristics::SHAFT)) {
161             prepare_change_floor_mode(player_ptr, CFM_SAVE_FLOORS | CFM_UP | CFM_SHAFT);
162             up_num = 2;
163         } else {
164             prepare_change_floor_mode(player_ptr, CFM_SAVE_FLOORS | CFM_UP);
165             up_num = 1;
166         }
167
168         if (player_ptr->current_floor_ptr->dun_level - up_num < dungeons_info[player_ptr->dungeon_idx].mindepth) {
169             up_num = player_ptr->current_floor_ptr->dun_level;
170         }
171     }
172
173     if (record_stair) {
174         exe_write_diary(player_ptr, DIARY_STAIR, 0 - up_num, _("階段を上った", "climbed up the stairs to"));
175     }
176
177     if (up_num == player_ptr->current_floor_ptr->dun_level) {
178         if (is_echizen(player_ptr)) {
179             msg_print(_("なんだこの階段は!", "What's this STAIRWAY!"));
180         } else {
181             msg_print(_("地上に戻った。", "You go back to the surface."));
182         }
183         player_ptr->word_recall = 0;
184     } else {
185         if (is_echizen(player_ptr)) {
186             msg_print(_("なんだこの階段は!", "What's this STAIRWAY!"));
187         } else {
188             msg_print(_("階段を上って新たなる迷宮へと足を踏み入れた。", "You enter a maze of up staircases."));
189         }
190     }
191
192     sound(SOUND_STAIRWAY);
193
194     player_ptr->leaving = true;
195 }
196
197 /*!
198  * @brief 階段を使って階層を降りる処理 / Go down one level
199  * @param player_ptr プレイヤーへの参照ポインタ
200  */
201 void do_cmd_go_down(PlayerType *player_ptr)
202 {
203     bool fall_trap = false;
204     int down_num = 0;
205     PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU });
206
207     auto *floor_ptr = player_ptr->current_floor_ptr;
208     auto *g_ptr = &floor_ptr->grid_array[player_ptr->y][player_ptr->x];
209     auto *f_ptr = &terrains_info[g_ptr->feat];
210     if (f_ptr->flags.has_not(TerrainCharacteristics::MORE)) {
211         msg_print(_("ここには下り階段が見当たらない。", "I see no down staircase here."));
212         return;
213     }
214
215     if (f_ptr->flags.has(TerrainCharacteristics::TRAP)) {
216         fall_trap = true;
217     }
218
219     if (f_ptr->flags.has(TerrainCharacteristics::QUEST_ENTER)) {
220         do_cmd_quest(player_ptr);
221         return;
222     }
223
224     if (f_ptr->flags.has(TerrainCharacteristics::QUEST)) {
225         auto &quest_list = QuestList::get_instance();
226         if (!confirm_leave_level(player_ptr, true)) {
227             return;
228         }
229
230         if (is_echizen(player_ptr)) {
231             msg_print(_("なんだこの階段は!", "What's this STAIRWAY!"));
232         } else {
233             msg_print(_("下の階に降りた。", "You enter the down staircase."));
234         }
235
236         sound(SOUND_STAIRWAY);
237
238         leave_quest_check(player_ptr);
239         leave_tower_check(player_ptr);
240         floor_ptr->quest_number = i2enum<QuestId>(g_ptr->special);
241
242         auto &current_quest = quest_list[floor_ptr->quest_number];
243         if (current_quest.status == QuestStatusType::UNTAKEN) {
244             if (current_quest.type != QuestKindType::RANDOM) {
245                 init_flags = INIT_ASSIGN;
246                 parse_fixed_map(player_ptr, QUEST_DEFINITION_LIST, 0, 0, 0, 0);
247             }
248
249             current_quest.status = QuestStatusType::TAKEN;
250         }
251
252         if (!inside_quest(floor_ptr->quest_number)) {
253             floor_ptr->dun_level = 0;
254             player_ptr->word_recall = 0;
255         }
256
257         player_ptr->leaving = true;
258         player_ptr->oldpx = 0;
259         player_ptr->oldpy = 0;
260         PlayerEnergy(player_ptr).set_player_turn_energy(100);
261         return;
262     }
263
264     DUNGEON_IDX target_dungeon = 0;
265     if (!floor_ptr->is_in_dungeon()) {
266         target_dungeon = f_ptr->flags.has(TerrainCharacteristics::ENTRANCE) ? g_ptr->special : DUNGEON_ANGBAND;
267         if (ironman_downward && (target_dungeon != DUNGEON_ANGBAND)) {
268             msg_print(_("ダンジョンの入口は塞がれている!", "The entrance of this dungeon is closed!"));
269             return;
270         }
271
272         if (!max_dlv[target_dungeon]) {
273             const auto mes = _("ここには%sの入り口(%d階相当)があります", "There is the entrance of %s (Danger level: %d)");
274             const auto &dungeon = dungeons_info[target_dungeon];
275             msg_format(mes, dungeon.name.data(), dungeon.mindepth);
276             if (!get_check(_("本当にこのダンジョンに入りますか?", "Do you really get in this dungeon? "))) {
277                 return;
278             }
279         }
280
281         player_ptr->oldpx = player_ptr->x;
282         player_ptr->oldpy = player_ptr->y;
283         player_ptr->dungeon_idx = target_dungeon;
284         prepare_change_floor_mode(player_ptr, CFM_FIRST_FLOOR);
285     }
286
287     PlayerEnergy(player_ptr).set_player_turn_energy(100);
288     if (autosave_l) {
289         do_cmd_save_game(player_ptr, true);
290     }
291
292     if (f_ptr->flags.has(TerrainCharacteristics::SHAFT)) {
293         down_num += 2;
294     } else {
295         down_num += 1;
296     }
297
298     if (!floor_ptr->is_in_dungeon()) {
299         player_ptr->enter_dungeon = true;
300         down_num = dungeons_info[player_ptr->dungeon_idx].mindepth;
301     }
302
303     if (record_stair) {
304         if (fall_trap) {
305             exe_write_diary(player_ptr, DIARY_STAIR, down_num, _("落とし戸に落ちた", "fell through a trap door"));
306         } else {
307             exe_write_diary(player_ptr, DIARY_STAIR, down_num, _("階段を下りた", "climbed down the stairs to"));
308         }
309     }
310
311     if (fall_trap) {
312         msg_print(_("わざと落とし戸に落ちた。", "You deliberately jump through the trap door."));
313     } else {
314         if (target_dungeon) {
315             msg_format(_("%sへ入った。", "You entered %s."), dungeons_info[player_ptr->dungeon_idx].text.data());
316         } else {
317             if (is_echizen(player_ptr)) {
318                 msg_print(_("なんだこの階段は!", "What's this STAIRWAY!"));
319             } else {
320                 msg_print(_("階段を下りて新たなる迷宮へと足を踏み入れた。", "You enter a maze of down staircases."));
321             }
322         }
323
324         sound(SOUND_STAIRWAY);
325     }
326
327     player_ptr->leaving = true;
328
329     if (fall_trap) {
330         prepare_change_floor_mode(player_ptr, CFM_SAVE_FLOORS | CFM_DOWN | CFM_RAND_PLACE | CFM_RAND_CONNECT);
331         return;
332     }
333
334     if (f_ptr->flags.has(TerrainCharacteristics::SHAFT)) {
335         prepare_change_floor_mode(player_ptr, CFM_SAVE_FLOORS | CFM_DOWN | CFM_SHAFT);
336     } else {
337         prepare_change_floor_mode(player_ptr, CFM_SAVE_FLOORS | CFM_DOWN);
338     }
339 }
340
341 /*!
342  * @brief 「歩く」動作コマンドのメインルーチン /
343  * Support code for the "Walk" and "Jump" commands
344  * @param player_ptr プレイヤーへの参照ポインタ
345  * @param pickup アイテムの自動拾いを行うならTRUE
346  */
347 void do_cmd_walk(PlayerType *player_ptr, bool pickup)
348 {
349     if (command_arg) {
350         command_rep = command_arg - 1;
351         RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::ACTION);
352         command_arg = 0;
353     }
354
355     bool more = false;
356     DIRECTION dir;
357     if (get_rep_dir(player_ptr, &dir, false)) {
358         PlayerEnergy energy(player_ptr);
359         energy.set_player_turn_energy(100);
360         if (dir != 5) {
361             PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU });
362         }
363
364         if (player_ptr->wild_mode) {
365             energy.mul_player_turn_energy((MAX_HGT + MAX_WID) / 2);
366         }
367
368         if (player_ptr->action == ACTION_HAYAGAKE) {
369             auto energy_use = (ENERGY)(player_ptr->energy_use * (45 - (player_ptr->lev / 2)) / 100);
370             energy.set_player_turn_energy(energy_use);
371         }
372
373         exe_movement(player_ptr, dir, pickup, false);
374         more = true;
375     }
376
377     if (player_ptr->wild_mode && !cave_has_flag_bold(player_ptr->current_floor_ptr, player_ptr->y, player_ptr->x, TerrainCharacteristics::TOWN)) {
378         int tmp = 120 + player_ptr->lev * 10 - wilderness[player_ptr->y][player_ptr->x].level + 5;
379         if (tmp < 1) {
380             tmp = 1;
381         }
382
383         if (((wilderness[player_ptr->y][player_ptr->x].level + 5) > (player_ptr->lev / 2)) && randint0(tmp) < (21 - player_ptr->skill_stl)) {
384             msg_print(_("襲撃だ!", "You are ambushed !"));
385             player_ptr->oldpy = randint1(MAX_HGT - 2);
386             player_ptr->oldpx = randint1(MAX_WID - 2);
387             change_wild_mode(player_ptr, true);
388             PlayerEnergy(player_ptr).set_player_turn_energy(100);
389         }
390     }
391
392     if (!more) {
393         disturb(player_ptr, false, false);
394     }
395 }
396
397 /*!
398  * @brief 「走る」動作コマンドのメインルーチン /
399  * Start running.
400  * @param player_ptr プレイヤーへの参照ポインタ
401  */
402 void do_cmd_run(PlayerType *player_ptr)
403 {
404     DIRECTION dir;
405     if (cmd_limit_confused(player_ptr)) {
406         return;
407     }
408
409     PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU });
410
411     if (get_rep_dir(player_ptr, &dir, false)) {
412         player_ptr->running = (command_arg ? command_arg : 1000);
413         run_step(player_ptr, dir);
414     }
415 }
416
417 /*!
418  * @brief 「留まる」動作コマンドのメインルーチン /
419  * Stay still.  Search.  Enter stores.
420  * Pick up treasure if "pickup" is true.
421  * @param player_ptr プレイヤーへの参照ポインタ
422  * @param pickup アイテムの自動拾いを行うならTRUE
423  */
424 void do_cmd_stay(PlayerType *player_ptr, bool pickup)
425 {
426     uint32_t mpe_mode = MPE_STAYING | MPE_ENERGY_USE;
427     if (command_arg) {
428         command_rep = command_arg - 1;
429         RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::ACTION);
430         command_arg = 0;
431     }
432
433     PlayerEnergy(player_ptr).set_player_turn_energy(100);
434     if (pickup) {
435         mpe_mode |= MPE_DO_PICKUP;
436     }
437
438     (void)move_player_effect(player_ptr, player_ptr->y, player_ptr->x, mpe_mode);
439 }
440
441 /*!
442  * @brief 「休む」動作コマンドのメインルーチン /
443  * Resting allows a player to safely restore his hp     -RAK-
444  * @param player_ptr プレイヤーへの参照ポインタ
445  */
446 void do_cmd_rest(PlayerType *player_ptr)
447 {
448     set_action(player_ptr, ACTION_NONE);
449     if (PlayerClass(player_ptr).equals(PlayerClassType::BARD)) {
450         auto is_singing = get_singing_song_effect(player_ptr) != 0;
451         is_singing |= get_interrupting_song_effect(player_ptr) != 0;
452         if (is_singing) {
453             stop_singing(player_ptr);
454         }
455     }
456
457     SpellHex spell_hex(player_ptr);
458     if (spell_hex.is_spelling_any()) {
459         (void)spell_hex.stop_all_spells();
460     }
461
462     if (command_arg <= 0) {
463         concptr p = _("休憩 (0-9999, '*' で HP/MP全快, '&' で必要なだけ): ", "Rest (0-9999, '*' for HP/SP, '&' as needed): ");
464         char out_val[80];
465         strcpy(out_val, "&");
466         if (!get_string(p, out_val, 4)) {
467             return;
468         }
469
470         if (out_val[0] == '&') {
471             command_arg = COMMAND_ARG_REST_UNTIL_DONE;
472         } else if (out_val[0] == '*') {
473             command_arg = COMMAND_ARG_REST_FULL_HEALING;
474         } else {
475             command_arg = (COMMAND_ARG)atoi(out_val);
476             if (command_arg <= 0) {
477                 return;
478             }
479         }
480     }
481
482     if (command_arg > 9999) {
483         command_arg = 9999;
484     }
485
486     set_superstealth(player_ptr, false);
487
488     PlayerEnergy(player_ptr).set_player_turn_energy(100);
489     if (command_arg > 100) {
490         chg_virtue(player_ptr, Virtue::DILIGENCE, -1);
491     }
492
493     if (player_ptr->is_fully_healthy()) {
494         chg_virtue(player_ptr, Virtue::DILIGENCE, -1);
495     }
496
497     player_ptr->resting = command_arg;
498     player_ptr->action = ACTION_REST;
499     auto &rfu = RedrawingFlagsUpdater::get_instance();
500     rfu.set_flag(StatusRedrawingFlag::BONUS);
501     rfu.set_flag(MainWindowRedrawingFlag::ACTION);
502     handle_stuff(player_ptr);
503     term_fresh();
504 }