OSDN Git Service

Merge pull request #3569 from sikabane-works/release/3.0.0.88-alpha
[hengbandforosx/hengbandosx.git] / src / player / player-move.cpp
1 /*!
2  *  @brief プレイヤーの移動処理 / Movement commands
3  *  @date 2014/01/02
4  *  @author
5  *  Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
6  */
7
8 #include "player/player-move.h"
9 #include "core/disturbance.h"
10 #include "core/special-internal-keys.h"
11 #include "core/stuff-handler.h"
12 #include "core/window-redrawer.h"
13 #include "dungeon/dungeon-flag-types.h"
14 #include "dungeon/quest.h"
15 #include "effect/attribute-types.h"
16 #include "effect/effect-characteristics.h"
17 #include "effect/effect-processor.h"
18 #include "floor/cave.h"
19 #include "floor/floor-util.h"
20 #include "floor/geometry.h"
21 #include "game-option/disturbance-options.h"
22 #include "grid/feature.h"
23 #include "grid/grid.h"
24 #include "grid/trap.h"
25 #include "inventory/player-inventory.h"
26 #include "io/input-key-requester.h"
27 #include "mind/mind-ninja.h"
28 #include "monster/monster-update.h"
29 #include "perception/object-perception.h"
30 #include "player-base/player-class.h"
31 #include "player-base/player-race.h"
32 #include "player-status/player-energy.h"
33 #include "player/attack-defense-types.h"
34 #include "player/player-status-flags.h"
35 #include "player/player-status.h"
36 #include "realm/realm-song-numbers.h"
37 #include "spell-kind/spells-floor.h"
38 #include "spell-realm/spells-song.h"
39 #include "status/action-setter.h"
40 #include "system/dungeon-info.h"
41 #include "system/floor-type-definition.h"
42 #include "system/grid-type-definition.h"
43 #include "system/item-entity.h"
44 #include "system/monster-entity.h"
45 #include "system/player-type-definition.h"
46 #include "system/redrawing-flags-updater.h"
47 #include "system/terrain-type-definition.h"
48 #include "target/target-checker.h"
49 #include "timed-effect/player-blindness.h"
50 #include "timed-effect/player-confusion.h"
51 #include "timed-effect/player-hallucination.h"
52 #include "timed-effect/timed-effects.h"
53 #include "util/bit-flags-calculator.h"
54 #include "util/enum-converter.h"
55 #include "view/display-messages.h"
56
57 int flow_head = 0;
58 int flow_tail = 0;
59 POSITION temp2_x[MAX_SHORT];
60 POSITION temp2_y[MAX_SHORT];
61
62 /*!
63  * @brief 地形やその上のアイテムの隠された要素を全て明かす /
64  * Search for hidden things
65  * @param player_ptr プレイヤーへの参照ポインタ
66  * @param y 対象となるマスのY座標
67  * @param x 対象となるマスのX座標
68  */
69 static void discover_hidden_things(PlayerType *player_ptr, POSITION y, POSITION x)
70 {
71     grid_type *g_ptr;
72     auto *floor_ptr = player_ptr->current_floor_ptr;
73     g_ptr = &floor_ptr->grid_array[y][x];
74     if (g_ptr->mimic && is_trap(player_ptr, g_ptr->feat)) {
75         disclose_grid(player_ptr, y, x);
76         msg_print(_("トラップを発見した。", "You have found a trap."));
77         disturb(player_ptr, false, true);
78     }
79
80     if (is_hidden_door(player_ptr, g_ptr)) {
81         msg_print(_("隠しドアを発見した。", "You have found a secret door."));
82         disclose_grid(player_ptr, y, x);
83         disturb(player_ptr, false, false);
84     }
85
86     for (const auto this_o_idx : g_ptr->o_idx_list) {
87         ItemEntity *o_ptr;
88         o_ptr = &floor_ptr->o_list[this_o_idx];
89         if (o_ptr->bi_key.tval() != ItemKindType::CHEST) {
90             continue;
91         }
92
93         if (o_ptr->pval <= 0 || chest_traps[o_ptr->pval].none()) {
94             continue;
95         }
96
97         if (!o_ptr->is_known()) {
98             msg_print(_("箱に仕掛けられたトラップを発見した!", "You have discovered a trap on the chest!"));
99             object_known(o_ptr);
100             disturb(player_ptr, false, false);
101         }
102     }
103 }
104
105 /*!
106  * @brief プレイヤーの探索処理判定
107  * @param player_ptr プレイヤーへの参照ポインタ
108  */
109 void search(PlayerType *player_ptr)
110 {
111     PERCENTAGE chance = player_ptr->skill_srh;
112     const auto effects = player_ptr->effects();
113     if (effects->blindness()->is_blind() || no_lite(player_ptr)) {
114         chance = chance / 10;
115     }
116
117     if (effects->confusion()->is_confused() || effects->hallucination()->is_hallucinated()) {
118         chance = chance / 10;
119     }
120
121     for (DIRECTION i = 0; i < 9; ++i) {
122         if (randint0(100) < chance) {
123             discover_hidden_things(player_ptr, player_ptr->y + ddy_ddd[i], player_ptr->x + ddx_ddd[i]);
124         }
125     }
126 }
127
128 /*!
129  * @brief 移動に伴うプレイヤーのステータス変化処理
130  * @param player_ptr プレイヤーへの参照ポインタ
131  * @param ny 移動先Y座標
132  * @param nx 移動先X座標
133  * @param mpe_mode 移動オプションフラグ
134  * @return プレイヤーが死亡やフロア離脱を行わず、実際に移動が可能ならばTRUEを返す。
135  */
136 bool move_player_effect(PlayerType *player_ptr, POSITION ny, POSITION nx, BIT_FLAGS mpe_mode)
137 {
138     POSITION oy = player_ptr->y;
139     POSITION ox = player_ptr->x;
140     auto *floor_ptr = player_ptr->current_floor_ptr;
141     auto *g_ptr = &floor_ptr->grid_array[ny][nx];
142     grid_type *oc_ptr = &floor_ptr->grid_array[oy][ox];
143     auto *f_ptr = &terrains_info[g_ptr->feat];
144     TerrainType *of_ptr = &terrains_info[oc_ptr->feat];
145
146     auto &rfu = RedrawingFlagsUpdater::get_instance();
147     if (!(mpe_mode & MPE_STAYING)) {
148         MONSTER_IDX om_idx = oc_ptr->m_idx;
149         MONSTER_IDX nm_idx = g_ptr->m_idx;
150         player_ptr->y = ny;
151         player_ptr->x = nx;
152         if (!(mpe_mode & MPE_DONT_SWAP_MON)) {
153             g_ptr->m_idx = om_idx;
154             oc_ptr->m_idx = nm_idx;
155             if (om_idx > 0) {
156                 MonsterEntity *om_ptr = &floor_ptr->m_list[om_idx];
157                 om_ptr->fy = ny;
158                 om_ptr->fx = nx;
159                 update_monster(player_ptr, om_idx, true);
160             }
161
162             if (nm_idx > 0) {
163                 MonsterEntity *nm_ptr = &floor_ptr->m_list[nm_idx];
164                 nm_ptr->fy = oy;
165                 nm_ptr->fx = ox;
166                 update_monster(player_ptr, nm_idx, true);
167             }
168         }
169
170         lite_spot(player_ptr, oy, ox);
171         lite_spot(player_ptr, ny, nx);
172         verify_panel(player_ptr);
173         if (mpe_mode & MPE_FORGET_FLOW) {
174             forget_flow(floor_ptr);
175             rfu.set_flag(StatusRecalculatingFlag::UN_VIEW);
176             rfu.set_flag(MainWindowRedrawingFlag::MAP);
177         }
178
179         static constexpr auto flags_srf = {
180             StatusRecalculatingFlag::VIEW,
181             StatusRecalculatingFlag::LITE,
182             StatusRecalculatingFlag::FLOW,
183             StatusRecalculatingFlag::MONSTER_LITE,
184             StatusRecalculatingFlag::DISTANCE,
185         };
186         rfu.set_flags(flags_srf);
187         static constexpr auto flags_swrf = {
188             SubWindowRedrawingFlag::OVERHEAD,
189             SubWindowRedrawingFlag::DUNGEON,
190         };
191         rfu.set_flags(flags_swrf);
192         if ((!player_ptr->effects()->blindness()->is_blind() && !no_lite(player_ptr)) || !is_trap(player_ptr, g_ptr->feat)) {
193             g_ptr->info &= ~(CAVE_UNSAFE);
194         }
195
196         if (floor_ptr->dun_level && floor_ptr->get_dungeon_definition().flags.has(DungeonFeatureType::FORGET)) {
197             wiz_dark(player_ptr);
198         }
199
200         if (mpe_mode & MPE_HANDLE_STUFF) {
201             handle_stuff(player_ptr);
202         }
203
204         if (PlayerClass(player_ptr).equals(PlayerClassType::NINJA)) {
205             if (g_ptr->info & (CAVE_GLOW)) {
206                 set_superstealth(player_ptr, false);
207             } else if (player_ptr->cur_lite <= 0) {
208                 set_superstealth(player_ptr, true);
209             }
210         }
211
212         using Tc = TerrainCharacteristics;
213         if ((player_ptr->action == ACTION_HAYAGAKE) && (f_ptr->flags.has_not(Tc::PROJECT) || (!player_ptr->levitation && f_ptr->flags.has(Tc::DEEP)))) {
214             msg_print(_("ここでは素早く動けない。", "You cannot run in here."));
215             set_action(player_ptr, ACTION_NONE);
216         }
217
218         if (PlayerRace(player_ptr).equals(PlayerRaceType::MERFOLK)) {
219             if (f_ptr->flags.has(Tc::WATER) ^ of_ptr->flags.has(Tc::WATER)) {
220                 rfu.set_flag(StatusRecalculatingFlag::BONUS);
221                 update_creature(player_ptr);
222             }
223         }
224     }
225
226     if (mpe_mode & MPE_ENERGY_USE) {
227         if (music_singing(player_ptr, MUSIC_WALL)) {
228             (void)project(player_ptr, 0, 0, player_ptr->y, player_ptr->x, (60 + player_ptr->lev), AttributeType::DISINTEGRATE, PROJECT_KILL | PROJECT_ITEM);
229             if (!player_bold(player_ptr, ny, nx) || player_ptr->is_dead || player_ptr->leaving) {
230                 return false;
231             }
232         }
233
234         if ((player_ptr->skill_fos >= 50) || (0 == randint0(50 - player_ptr->skill_fos))) {
235             search(player_ptr);
236         }
237
238         if (player_ptr->action == ACTION_SEARCH) {
239             search(player_ptr);
240         }
241     }
242
243     if (!(mpe_mode & MPE_DONT_PICKUP)) {
244         carry(player_ptr, any_bits(mpe_mode, MPE_DO_PICKUP));
245     }
246
247     // 自動拾い/自動破壊により床上のアイテムリストが変化した可能性があるので表示を更新
248     if (!player_ptr->running) {
249         static constexpr auto flags_swrf = {
250             SubWindowRedrawingFlag::FLOOR_ITEMS,
251             SubWindowRedrawingFlag::FOUND_ITEMS,
252         };
253         rfu.set_flags(flags_swrf);
254         window_stuff(player_ptr);
255     }
256
257     PlayerEnergy energy(player_ptr);
258     if (f_ptr->flags.has(TerrainCharacteristics::STORE)) {
259         disturb(player_ptr, false, true);
260         energy.reset_player_turn();
261         command_new = SPECIAL_KEY_STORE;
262     } else if (f_ptr->flags.has(TerrainCharacteristics::BLDG)) {
263         disturb(player_ptr, false, true);
264         energy.reset_player_turn();
265         command_new = SPECIAL_KEY_BUILDING;
266     } else if (f_ptr->flags.has(TerrainCharacteristics::QUEST_ENTER)) {
267         disturb(player_ptr, false, true);
268         energy.reset_player_turn();
269         command_new = SPECIAL_KEY_QUEST;
270     } else if (f_ptr->flags.has(TerrainCharacteristics::QUEST_EXIT)) {
271         const auto &quest_list = QuestList::get_instance();
272         if (quest_list[floor_ptr->quest_number].type == QuestKindType::FIND_EXIT) {
273             complete_quest(player_ptr, floor_ptr->quest_number);
274         }
275         leave_quest_check(player_ptr);
276         floor_ptr->quest_number = i2enum<QuestId>(g_ptr->special);
277         floor_ptr->dun_level = 0;
278         if (!inside_quest(floor_ptr->quest_number)) {
279             player_ptr->word_recall = 0;
280         }
281         player_ptr->oldpx = 0;
282         player_ptr->oldpy = 0;
283         player_ptr->leaving = true;
284     } else if (f_ptr->flags.has(TerrainCharacteristics::HIT_TRAP) && !(mpe_mode & MPE_STAYING)) {
285         disturb(player_ptr, false, true);
286         if (g_ptr->mimic || f_ptr->flags.has(TerrainCharacteristics::SECRET)) {
287             msg_print(_("トラップだ!", "You found a trap!"));
288             disclose_grid(player_ptr, player_ptr->y, player_ptr->x);
289         }
290
291         hit_trap(player_ptr, any_bits(mpe_mode, MPE_BREAK_TRAP));
292         if (!player_bold(player_ptr, ny, nx) || player_ptr->is_dead || player_ptr->leaving) {
293             return false;
294         }
295     }
296
297     if (!(mpe_mode & MPE_STAYING) && (disturb_trap_detect || alert_trap_detect) && player_ptr->dtrap && !(g_ptr->info & CAVE_IN_DETECT)) {
298         player_ptr->dtrap = false;
299         if (!(g_ptr->info & CAVE_UNSAFE)) {
300             if (alert_trap_detect) {
301                 msg_print(_("* 注意:この先はトラップの感知範囲外です! *", "*Leaving trap detect region!*"));
302             }
303
304             if (disturb_trap_detect) {
305                 disturb(player_ptr, false, true);
306             }
307         }
308     }
309
310     return player_bold(player_ptr, ny, nx) && !player_ptr->is_dead && !player_ptr->leaving;
311 }
312
313 /*!
314  * @brief 該当地形のトラップがプレイヤーにとって無効かどうかを判定して返す
315  * @param player_ptr プレイヤーへの参照ポインタ
316  * @param feat 地形ID
317  * @return トラップが自動的に無効ならばTRUEを返す
318  */
319 bool trap_can_be_ignored(PlayerType *player_ptr, FEAT_IDX feat)
320 {
321     auto *f_ptr = &terrains_info[feat];
322     if (f_ptr->flags.has_not(TerrainCharacteristics::TRAP)) {
323         return true;
324     }
325
326     switch (i2enum<TrapType>(f_ptr->subtype)) {
327     case TrapType::TRAPDOOR:
328     case TrapType::PIT:
329     case TrapType::SPIKED_PIT:
330     case TrapType::POISON_PIT:
331         if (player_ptr->levitation) {
332             return true;
333         }
334         break;
335     case TrapType::TELEPORT:
336         if (player_ptr->anti_tele) {
337             return true;
338         }
339         break;
340     case TrapType::FIRE:
341         if (has_immune_fire(player_ptr)) {
342             return true;
343         }
344         break;
345     case TrapType::ACID:
346         if (has_immune_acid(player_ptr)) {
347             return true;
348         }
349         break;
350     case TrapType::BLIND:
351         if (has_resist_blind(player_ptr)) {
352             return true;
353         }
354         break;
355     case TrapType::CONFUSE:
356         if (has_resist_conf(player_ptr)) {
357             return true;
358         }
359         break;
360     case TrapType::POISON:
361         if (has_resist_pois(player_ptr)) {
362             return true;
363         }
364         break;
365     case TrapType::SLEEP:
366         if (player_ptr->free_act) {
367             return true;
368         }
369         break;
370     default:
371         break;
372     }
373
374     return false;
375 }