OSDN Git Service

Merge pull request #3532 from sikabane-works/release/3.0.0.87-alpha
[hengbandforosx/hengbandosx.git] / src / action / movement-execution.cpp
1 /*!
2  * @file movement-execution.cpp
3  * @brief プレイヤーの歩行勝利実行定義
4  */
5
6 #include "action/movement-execution.h"
7 #include "action/open-close-execution.h"
8 #include "artifact/fixed-art-types.h"
9 #include "cmd-action/cmd-attack.h"
10 #include "core/disturbance.h"
11 #include "core/stuff-handler.h"
12 #include "floor/geometry.h"
13 #include "floor/pattern-walk.h"
14 #include "game-option/input-options.h"
15 #include "grid/feature.h"
16 #include "grid/grid.h"
17 #include "inventory/inventory-slot-types.h"
18 #include "locale/english.h"
19 #include "main/sound-definitions-table.h"
20 #include "main/sound-of-music.h"
21 #include "monster-race/monster-race.h"
22 #include "monster-race/race-flags-resistance.h"
23 #include "monster-race/race-flags1.h"
24 #include "monster-race/race-flags2.h"
25 #include "monster-race/race-flags7.h"
26 #include "monster-race/race-flags8.h"
27 #include "monster-race/race-indice-types.h"
28 #include "monster-race/race-resistance-mask.h"
29 #include "monster/monster-describer.h"
30 #include "monster/monster-info.h"
31 #include "monster/monster-status-setter.h"
32 #include "monster/monster-status.h"
33 #include "mutation/mutation-flag-types.h"
34 #include "object/warning.h"
35 #include "player-base/player-class.h"
36 #include "player-status/player-energy.h"
37 #include "player/player-move.h"
38 #include "player/player-status-flags.h"
39 #include "player/player-status.h"
40 #include "system/floor-type-definition.h"
41 #include "system/grid-type-definition.h"
42 #include "system/item-entity.h"
43 #include "system/monster-entity.h"
44 #include "system/monster-race-info.h"
45 #include "system/player-type-definition.h"
46 #include "system/redrawing-flags-updater.h"
47 #include "system/terrain-type-definition.h"
48 #include "timed-effect/player-confusion.h"
49 #include "timed-effect/player-hallucination.h"
50 #include "timed-effect/player-stun.h"
51 #include "timed-effect/timed-effects.h"
52 #include "util/bit-flags-calculator.h"
53 #include "view/display-messages.h"
54
55 /*!
56  * Determine if a "boundary" grid is "floor mimic"
57  * @param grid_type *g_ptr
58  * @param TerrainType *f_ptr
59  * @param TerrainType  *mimic_f_ptr
60  * @return 移動不能であればTRUE
61  * @todo 負論理なので反転させたい
62  */
63 static bool boundary_floor(grid_type *g_ptr, TerrainType *f_ptr, TerrainType *mimic_f_ptr)
64 {
65     bool is_boundary_floor = g_ptr->mimic > 0;
66     is_boundary_floor &= permanent_wall(f_ptr);
67     is_boundary_floor &= mimic_f_ptr->flags.has_any_of({ TerrainCharacteristics::MOVE, TerrainCharacteristics::CAN_FLY });
68     is_boundary_floor &= mimic_f_ptr->flags.has(TerrainCharacteristics::PROJECT);
69     is_boundary_floor &= mimic_f_ptr->flags.has_not(TerrainCharacteristics::OPEN);
70     return is_boundary_floor;
71 }
72
73 /*!
74  * @brief 該当地形のトラップがプレイヤーにとって無効かどうかを判定して返す /
75  * Move player in the given direction, with the given "pickup" flag.
76  * @param player_ptr プレイヤーへの参照ポインタ
77  * @param dir 移動方向ID
78  * @param do_pickup 罠解除を試みながらの移動ならばTRUE
79  * @param break_trap トラップ粉砕処理を行うならばTRUE
80  * @return 実際に移動が行われたならばTRUEを返す。
81  * @note
82  * This routine should (probably) always induce energy expenditure.\n
83  * @details
84  * Note that moving will *always* take a turn, and will *always* hit\n
85  * any monster which might be in the destination grid.  Previously,\n
86  * moving into walls was "free" and did NOT hit invisible monsters.\n
87  */
88 void exe_movement(PlayerType *player_ptr, DIRECTION dir, bool do_pickup, bool break_trap)
89 {
90     POSITION y = player_ptr->y + ddy[dir];
91     POSITION x = player_ptr->x + ddx[dir];
92     auto *floor_ptr = player_ptr->current_floor_ptr;
93     auto *g_ptr = &floor_ptr->grid_array[y][x];
94     bool p_can_enter = player_can_enter(player_ptr, g_ptr->feat, CEM_P_CAN_ENTER_PATTERN);
95     if (!floor_ptr->dun_level && !player_ptr->wild_mode && ((x == 0) || (x == MAX_WID - 1) || (y == 0) || (y == MAX_HGT - 1))) {
96         if (g_ptr->mimic && player_can_enter(player_ptr, g_ptr->mimic, 0)) {
97             if ((y == 0) && (x == 0)) {
98                 player_ptr->wilderness_y--;
99                 player_ptr->wilderness_x--;
100                 player_ptr->oldpy = floor_ptr->height - 2;
101                 player_ptr->oldpx = floor_ptr->width - 2;
102                 player_ptr->ambush_flag = false;
103             } else if ((y == 0) && (x == MAX_WID - 1)) {
104                 player_ptr->wilderness_y--;
105                 player_ptr->wilderness_x++;
106                 player_ptr->oldpy = floor_ptr->height - 2;
107                 player_ptr->oldpx = 1;
108                 player_ptr->ambush_flag = false;
109             } else if ((y == MAX_HGT - 1) && (x == 0)) {
110                 player_ptr->wilderness_y++;
111                 player_ptr->wilderness_x--;
112                 player_ptr->oldpy = 1;
113                 player_ptr->oldpx = floor_ptr->width - 2;
114                 player_ptr->ambush_flag = false;
115             } else if ((y == MAX_HGT - 1) && (x == MAX_WID - 1)) {
116                 player_ptr->wilderness_y++;
117                 player_ptr->wilderness_x++;
118                 player_ptr->oldpy = 1;
119                 player_ptr->oldpx = 1;
120                 player_ptr->ambush_flag = false;
121             } else if (y == 0) {
122                 player_ptr->wilderness_y--;
123                 player_ptr->oldpy = floor_ptr->height - 2;
124                 player_ptr->oldpx = x;
125                 player_ptr->ambush_flag = false;
126             } else if (y == MAX_HGT - 1) {
127                 player_ptr->wilderness_y++;
128                 player_ptr->oldpy = 1;
129                 player_ptr->oldpx = x;
130                 player_ptr->ambush_flag = false;
131             } else if (x == 0) {
132                 player_ptr->wilderness_x--;
133                 player_ptr->oldpx = floor_ptr->width - 2;
134                 player_ptr->oldpy = y;
135                 player_ptr->ambush_flag = false;
136             } else if (x == MAX_WID - 1) {
137                 player_ptr->wilderness_x++;
138                 player_ptr->oldpx = 1;
139                 player_ptr->oldpy = y;
140                 player_ptr->ambush_flag = false;
141             }
142
143             player_ptr->leaving = true;
144             PlayerEnergy(player_ptr).set_player_turn_energy(100);
145             return;
146         }
147
148         p_can_enter = false;
149     }
150
151     auto *m_ptr = &floor_ptr->m_list[g_ptr->m_idx];
152
153     // @todo 「特定の武器を装備している」旨のメソッドを別途作る
154     constexpr auto stormbringer = FixedArtifactId::STORMBRINGER;
155     auto is_stormbringer = false;
156     if (player_ptr->inventory_list[INVEN_MAIN_HAND].is_specific_artifact(stormbringer)) {
157         is_stormbringer = true;
158     }
159
160     if (player_ptr->inventory_list[INVEN_SUB_HAND].is_specific_artifact(stormbringer)) {
161         is_stormbringer = true;
162     }
163
164     auto *f_ptr = &terrains_info[g_ptr->feat];
165     auto p_can_kill_walls = has_kill_wall(player_ptr);
166     p_can_kill_walls &= f_ptr->flags.has(TerrainCharacteristics::HURT_DISI);
167     p_can_kill_walls &= !p_can_enter || f_ptr->flags.has_not(TerrainCharacteristics::LOS);
168     p_can_kill_walls &= f_ptr->flags.has_not(TerrainCharacteristics::PERMANENT);
169     std::string m_name;
170     bool can_move = true;
171     bool do_past = false;
172     if (g_ptr->m_idx && (m_ptr->ml || p_can_enter || p_can_kill_walls)) {
173         auto *r_ptr = &monraces_info[m_ptr->r_idx];
174         auto effects = player_ptr->effects();
175         auto is_stunned = effects->stun()->is_stunned();
176         auto can_cast = !effects->confusion()->is_confused();
177         auto is_hallucinated = effects->hallucination()->is_hallucinated();
178         can_cast &= !is_hallucinated;
179         can_cast &= m_ptr->ml;
180         can_cast &= !is_stunned;
181         can_cast &= player_ptr->muta.has_not(PlayerMutationType::BERS_RAGE) || !is_shero(player_ptr);
182         if (!m_ptr->is_hostile() && can_cast && pattern_seq(player_ptr, player_ptr->y, player_ptr->x, y, x) && (p_can_enter || p_can_kill_walls)) {
183             (void)set_monster_csleep(player_ptr, g_ptr->m_idx, 0);
184             m_name = monster_desc(player_ptr, m_ptr, 0);
185             if (m_ptr->ml) {
186                 if (!is_hallucinated) {
187                     monster_race_track(player_ptr, m_ptr->ap_r_idx);
188                 }
189
190                 health_track(player_ptr, g_ptr->m_idx);
191             }
192
193             if ((is_stormbringer && (randint1(1000) > 666)) || PlayerClass(player_ptr).equals(PlayerClassType::BERSERKER)) {
194                 do_cmd_attack(player_ptr, y, x, HISSATSU_NONE);
195                 can_move = false;
196             } else if (monster_can_cross_terrain(player_ptr, floor_ptr->grid_array[player_ptr->y][player_ptr->x].feat, r_ptr, 0)) {
197                 do_past = true;
198             } else {
199                 msg_format(_("%s^が邪魔だ!", "%s^ is in your way!"), m_name.data());
200                 PlayerEnergy(player_ptr).reset_player_turn();
201                 can_move = false;
202             }
203         } else {
204             do_cmd_attack(player_ptr, y, x, HISSATSU_NONE);
205             can_move = false;
206         }
207     }
208
209     MonsterEntity *riding_m_ptr = &floor_ptr->m_list[player_ptr->riding];
210     PlayerEnergy energy(player_ptr);
211     if (can_move && player_ptr->riding) {
212         const auto *riding_r_ptr = &monraces_info[riding_m_ptr->r_idx];
213         if (riding_r_ptr->behavior_flags.has(MonsterBehaviorType::NEVER_MOVE)) {
214             msg_print(_("動けない!", "Can't move!"));
215             energy.reset_player_turn();
216             can_move = false;
217             disturb(player_ptr, false, true);
218         } else if (riding_m_ptr->is_fearful()) {
219             const auto steed_name = monster_desc(player_ptr, riding_m_ptr, 0);
220             msg_format(_("%sが恐怖していて制御できない。", "%s^ is too scared to control."), steed_name.data());
221             can_move = false;
222             disturb(player_ptr, false, true);
223         } else if (player_ptr->riding_ryoute) {
224             can_move = false;
225             disturb(player_ptr, false, true);
226         } else if (f_ptr->flags.has(TerrainCharacteristics::CAN_FLY) && (riding_r_ptr->feature_flags.has(MonsterFeatureType::CAN_FLY))) {
227             /* Allow moving */
228         } else if (f_ptr->flags.has(TerrainCharacteristics::CAN_SWIM) && (riding_r_ptr->feature_flags.has(MonsterFeatureType::CAN_SWIM))) {
229             /* Allow moving */
230         } else if (f_ptr->flags.has(TerrainCharacteristics::WATER) && riding_r_ptr->feature_flags.has_not(MonsterFeatureType::AQUATIC) && (f_ptr->flags.has(TerrainCharacteristics::DEEP) || riding_r_ptr->aura_flags.has(MonsterAuraType::FIRE))) {
231             msg_print(_(format("%sの上に行けない。", terrains_info[g_ptr->get_feat_mimic()].name.data()), "Can't swim."));
232             energy.reset_player_turn();
233             can_move = false;
234             disturb(player_ptr, false, true);
235         } else if (f_ptr->flags.has_not(TerrainCharacteristics::WATER) && riding_r_ptr->feature_flags.has(MonsterFeatureType::AQUATIC)) {
236             msg_print(_(format("%sから上がれない。", terrains_info[floor_ptr->grid_array[player_ptr->y][player_ptr->x].get_feat_mimic()].name.data()), "Can't land."));
237             energy.reset_player_turn();
238             can_move = false;
239             disturb(player_ptr, false, true);
240         } else if (f_ptr->flags.has(TerrainCharacteristics::LAVA) && riding_r_ptr->resistance_flags.has_none_of(RFR_EFF_IM_FIRE_MASK)) {
241             msg_print(_(format("%sの上に行けない。", terrains_info[g_ptr->get_feat_mimic()].name.data()), "Too hot to go through."));
242             energy.reset_player_turn();
243             can_move = false;
244             disturb(player_ptr, false, true);
245         }
246
247         if (can_move && riding_m_ptr->is_stunned() && one_in_(2)) {
248             const auto steed_name = monster_desc(player_ptr, riding_m_ptr, 0);
249             msg_format(_("%sが朦朧としていてうまく動けない!", "You cannot control stunned %s!"), steed_name.data());
250             can_move = false;
251             disturb(player_ptr, false, true);
252         }
253     }
254
255     if (!can_move) {
256     } else if (f_ptr->flags.has_not(TerrainCharacteristics::MOVE) && f_ptr->flags.has(TerrainCharacteristics::CAN_FLY) && !player_ptr->levitation) {
257         msg_format(_("空を飛ばないと%sの上には行けない。", "You need to fly to go through the %s."), terrains_info[g_ptr->get_feat_mimic()].name.data());
258         energy.reset_player_turn();
259         player_ptr->running = 0;
260         can_move = false;
261     } else if (f_ptr->flags.has(TerrainCharacteristics::TREE) && !p_can_kill_walls) {
262         auto riding_wild_wood = player_ptr->riding && monraces_info[riding_m_ptr->r_idx].wilderness_flags.has(MonsterWildernessType::WILD_WOOD);
263         if (!PlayerClass(player_ptr).equals(PlayerClassType::RANGER) && !player_ptr->levitation && !riding_wild_wood) {
264             energy.mul_player_turn_energy(2);
265         }
266     } else if ((do_pickup != easy_disarm) && f_ptr->flags.has(TerrainCharacteristics::DISARM) && !g_ptr->mimic) {
267         if (!trap_can_be_ignored(player_ptr, g_ptr->feat)) {
268             (void)exe_disarm(player_ptr, y, x, dir);
269             return;
270         }
271     } else if (!p_can_enter && !p_can_kill_walls) {
272         FEAT_IDX feat = g_ptr->get_feat_mimic();
273         TerrainType *mimic_f_ptr = &terrains_info[feat];
274         concptr name = mimic_f_ptr->name.data();
275         can_move = false;
276         if (!g_ptr->is_mark() && !player_can_see_bold(player_ptr, y, x)) {
277             if (boundary_floor(g_ptr, f_ptr, mimic_f_ptr)) {
278                 msg_print(_("それ以上先には進めないようだ。", "You feel you cannot go any more."));
279             } else {
280 #ifdef JP
281                 msg_format("%sが行く手をはばんでいるようだ。", name);
282 #else
283                 msg_format("You feel %s %s blocking your way.", is_a_vowel(name[0]) ? "an" : "a", name);
284 #endif
285                 g_ptr->info |= (CAVE_MARK);
286                 lite_spot(player_ptr, y, x);
287             }
288         } else {
289             auto effects = player_ptr->effects();
290             auto is_confused = effects->confusion()->is_confused();
291             auto is_stunned = effects->stun()->is_stunned();
292             auto is_hallucinated = effects->hallucination()->is_hallucinated();
293             if (boundary_floor(g_ptr, f_ptr, mimic_f_ptr)) {
294                 msg_print(_("それ以上先には進めない。", "You cannot go any more."));
295                 if (!(is_confused || is_stunned || is_hallucinated)) {
296                     energy.reset_player_turn();
297                 }
298             } else {
299                 if (easy_open && is_closed_door(player_ptr, feat) && easy_open_door(player_ptr, y, x)) {
300                     return;
301                 }
302
303 #ifdef JP
304                 msg_format("%sが行く手をはばんでいる。", name);
305 #else
306                 msg_format("There is %s %s blocking your way.", is_a_vowel(name[0]) ? "an" : "a", name);
307 #endif
308                 if (!(is_confused || is_stunned || is_hallucinated)) {
309                     energy.reset_player_turn();
310                 }
311             }
312         }
313
314         disturb(player_ptr, false, true);
315         if (!boundary_floor(g_ptr, f_ptr, mimic_f_ptr)) {
316             sound(SOUND_HITWALL);
317         }
318     }
319
320     if (can_move && !pattern_seq(player_ptr, player_ptr->y, player_ptr->x, y, x)) {
321         auto effects = player_ptr->effects();
322         auto is_confused = effects->confusion()->is_confused();
323         auto is_stunned = effects->stun()->is_stunned();
324         auto is_hallucinated = effects->hallucination()->is_hallucinated();
325         if (!(is_confused || is_stunned || is_hallucinated)) {
326             energy.reset_player_turn();
327         }
328
329         disturb(player_ptr, false, true);
330         can_move = false;
331     }
332
333     if (!can_move) {
334         return;
335     }
336
337     if (player_ptr->warning && (!process_warning(player_ptr, x, y))) {
338         energy.set_player_turn_energy(25);
339         return;
340     }
341
342     if (do_past) {
343         msg_format(_("%sを押し退けた。", "You push past %s."), m_name.data());
344     }
345
346     if (player_ptr->wild_mode) {
347         if (ddy[dir] > 0) {
348             player_ptr->oldpy = 1;
349         }
350
351         if (ddy[dir] < 0) {
352             player_ptr->oldpy = MAX_HGT - 2;
353         }
354
355         if (ddy[dir] == 0) {
356             player_ptr->oldpy = MAX_HGT / 2;
357         }
358
359         if (ddx[dir] > 0) {
360             player_ptr->oldpx = 1;
361         }
362
363         if (ddx[dir] < 0) {
364             player_ptr->oldpx = MAX_WID - 2;
365         }
366
367         if (ddx[dir] == 0) {
368             player_ptr->oldpx = MAX_WID / 2;
369         }
370     }
371
372     if (p_can_kill_walls) {
373         cave_alter_feat(player_ptr, y, x, TerrainCharacteristics::HURT_DISI);
374         RedrawingFlagsUpdater::get_instance().set_flag(StatusRecalculatingFlag::FLOW);
375     }
376
377     uint32_t mpe_mode = MPE_ENERGY_USE;
378     if (do_pickup != always_pickup) {
379         mpe_mode |= MPE_DO_PICKUP;
380     }
381
382     if (break_trap) {
383         mpe_mode |= MPE_BREAK_TRAP;
384     }
385
386     (void)move_player_effect(player_ptr, y, x, mpe_mode);
387 }