OSDN Git Service

c53be94c7676b2f7865a52806eee08daebba3be0
[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/player-update-types.h"
12 #include "core/stuff-handler.h"
13 #include "floor/geometry.h"
14 #include "floor/pattern-walk.h"
15 #include "game-option/input-options.h"
16 #include "grid/feature.h"
17 #include "grid/grid.h"
18 #include "inventory/inventory-slot-types.h"
19 #include "locale/english.h"
20 #include "main/sound-definitions-table.h"
21 #include "main/sound-of-music.h"
22 #include "monster-race/monster-race.h"
23 #include "monster-race/race-flags-resistance.h"
24 #include "monster-race/race-flags1.h"
25 #include "monster-race/race-flags2.h"
26 #include "monster-race/race-flags7.h"
27 #include "monster-race/race-flags8.h"
28 #include "monster-race/race-indice-types.h"
29 #include "monster-race/race-resistance-mask.h"
30 #include "monster/monster-describer.h"
31 #include "monster/monster-info.h"
32 #include "monster/monster-status-setter.h"
33 #include "monster/monster-status.h"
34 #include "mutation/mutation-flag-types.h"
35 #include "object/warning.h"
36 #include "player-base/player-class.h"
37 #include "player-status/player-energy.h"
38 #include "player/player-move.h"
39 #include "player/player-status-flags.h"
40 #include "player/player-status.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/monster-race-info.h"
46 #include "system/player-type-definition.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     GAME_TEXT m_name[MAX_NLEN];
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             monster_desc(player_ptr, m_name, 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);
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             GAME_TEXT steed_name[MAX_NLEN];
220             monster_desc(player_ptr, steed_name, riding_m_ptr, 0);
221             msg_format(_("%sが恐怖していて制御できない。", "%^s is too scared to control."), steed_name);
222             can_move = false;
223             disturb(player_ptr, false, true);
224         } else if (player_ptr->riding_ryoute) {
225             can_move = false;
226             disturb(player_ptr, false, true);
227         } else if (f_ptr->flags.has(TerrainCharacteristics::CAN_FLY) && (riding_r_ptr->feature_flags.has(MonsterFeatureType::CAN_FLY))) {
228             /* Allow moving */
229         } else if (f_ptr->flags.has(TerrainCharacteristics::CAN_SWIM) && (riding_r_ptr->feature_flags.has(MonsterFeatureType::CAN_SWIM))) {
230             /* Allow moving */
231         } 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))) {
232             msg_format(_("%sの上に行けない。", "Can't swim."), terrains_info[g_ptr->get_feat_mimic()].name.data());
233             energy.reset_player_turn();
234             can_move = false;
235             disturb(player_ptr, false, true);
236         } else if (f_ptr->flags.has_not(TerrainCharacteristics::WATER) && riding_r_ptr->feature_flags.has(MonsterFeatureType::AQUATIC)) {
237             msg_format(_("%sから上がれない。", "Can't land."), terrains_info[floor_ptr->grid_array[player_ptr->y][player_ptr->x].get_feat_mimic()].name.data());
238             energy.reset_player_turn();
239             can_move = false;
240             disturb(player_ptr, false, true);
241         } else if (f_ptr->flags.has(TerrainCharacteristics::LAVA) && riding_r_ptr->resistance_flags.has_none_of(RFR_EFF_IM_FIRE_MASK)) {
242             msg_format(_("%sの上に行けない。", "Too hot to go through."), terrains_info[g_ptr->get_feat_mimic()].name.data());
243             energy.reset_player_turn();
244             can_move = false;
245             disturb(player_ptr, false, true);
246         }
247
248         if (can_move && riding_m_ptr->is_stunned() && one_in_(2)) {
249             GAME_TEXT steed_name[MAX_NLEN];
250             monster_desc(player_ptr, steed_name, riding_m_ptr, 0);
251             msg_format(_("%sが朦朧としていてうまく動けない!", "You cannot control stunned %s!"), steed_name);
252             can_move = false;
253             disturb(player_ptr, false, true);
254         }
255     }
256
257     if (!can_move) {
258     } else if (f_ptr->flags.has_not(TerrainCharacteristics::MOVE) && f_ptr->flags.has(TerrainCharacteristics::CAN_FLY) && !player_ptr->levitation) {
259         msg_format(_("空を飛ばないと%sの上には行けない。", "You need to fly to go through the %s."), terrains_info[g_ptr->get_feat_mimic()].name.data());
260         energy.reset_player_turn();
261         player_ptr->running = 0;
262         can_move = false;
263     } else if (f_ptr->flags.has(TerrainCharacteristics::TREE) && !p_can_kill_walls) {
264         auto riding_wild_wood = player_ptr->riding && monraces_info[riding_m_ptr->r_idx].wilderness_flags.has(MonsterWildernessType::WILD_WOOD);
265         if (!PlayerClass(player_ptr).equals(PlayerClassType::RANGER) && !player_ptr->levitation && !riding_wild_wood) {
266             energy.mul_player_turn_energy(2);
267         }
268     } else if ((do_pickup != easy_disarm) && f_ptr->flags.has(TerrainCharacteristics::DISARM) && !g_ptr->mimic) {
269         if (!trap_can_be_ignored(player_ptr, g_ptr->feat)) {
270             (void)exe_disarm(player_ptr, y, x, dir);
271             return;
272         }
273     } else if (!p_can_enter && !p_can_kill_walls) {
274         FEAT_IDX feat = g_ptr->get_feat_mimic();
275         TerrainType *mimic_f_ptr = &terrains_info[feat];
276         concptr name = mimic_f_ptr->name.data();
277         can_move = false;
278         if (!g_ptr->is_mark() && !player_can_see_bold(player_ptr, y, x)) {
279             if (boundary_floor(g_ptr, f_ptr, mimic_f_ptr)) {
280                 msg_print(_("それ以上先には進めないようだ。", "You feel you cannot go any more."));
281             } else {
282 #ifdef JP
283                 msg_format("%sが行く手をはばんでいるようだ。", name);
284 #else
285                 msg_format("You feel %s %s blocking your way.", is_a_vowel(name[0]) ? "an" : "a", name);
286 #endif
287                 g_ptr->info |= (CAVE_MARK);
288                 lite_spot(player_ptr, y, x);
289             }
290         } else {
291             auto effects = player_ptr->effects();
292             auto is_confused = effects->confusion()->is_confused();
293             auto is_stunned = effects->stun()->is_stunned();
294             auto is_hallucinated = effects->hallucination()->is_hallucinated();
295             if (boundary_floor(g_ptr, f_ptr, mimic_f_ptr)) {
296                 msg_print(_("それ以上先には進めない。", "You cannot go any more."));
297                 if (!(is_confused || is_stunned || is_hallucinated)) {
298                     energy.reset_player_turn();
299                 }
300             } else {
301                 if (easy_open && is_closed_door(player_ptr, feat) && easy_open_door(player_ptr, y, x)) {
302                     return;
303                 }
304
305 #ifdef JP
306                 msg_format("%sが行く手をはばんでいる。", name);
307 #else
308                 msg_format("There is %s %s blocking your way.", is_a_vowel(name[0]) ? "an" : "a", name);
309 #endif
310                 if (!(is_confused || is_stunned || is_hallucinated)) {
311                     energy.reset_player_turn();
312                 }
313             }
314         }
315
316         disturb(player_ptr, false, true);
317         if (!boundary_floor(g_ptr, f_ptr, mimic_f_ptr)) {
318             sound(SOUND_HITWALL);
319         }
320     }
321
322     if (can_move && !pattern_seq(player_ptr, player_ptr->y, player_ptr->x, y, x)) {
323         auto effects = player_ptr->effects();
324         auto is_confused = effects->confusion()->is_confused();
325         auto is_stunned = effects->stun()->is_stunned();
326         auto is_hallucinated = effects->hallucination()->is_hallucinated();
327         if (!(is_confused || is_stunned || is_hallucinated)) {
328             energy.reset_player_turn();
329         }
330
331         disturb(player_ptr, false, true);
332         can_move = false;
333     }
334
335     if (!can_move) {
336         return;
337     }
338
339     if (player_ptr->warning && (!process_warning(player_ptr, x, y))) {
340         energy.set_player_turn_energy(25);
341         return;
342     }
343
344     if (do_past) {
345         msg_format(_("%sを押し退けた。", "You push past %s."), m_name);
346     }
347
348     if (player_ptr->wild_mode) {
349         if (ddy[dir] > 0) {
350             player_ptr->oldpy = 1;
351         }
352
353         if (ddy[dir] < 0) {
354             player_ptr->oldpy = MAX_HGT - 2;
355         }
356
357         if (ddy[dir] == 0) {
358             player_ptr->oldpy = MAX_HGT / 2;
359         }
360
361         if (ddx[dir] > 0) {
362             player_ptr->oldpx = 1;
363         }
364
365         if (ddx[dir] < 0) {
366             player_ptr->oldpx = MAX_WID - 2;
367         }
368
369         if (ddx[dir] == 0) {
370             player_ptr->oldpx = MAX_WID / 2;
371         }
372     }
373
374     if (p_can_kill_walls) {
375         cave_alter_feat(player_ptr, y, x, TerrainCharacteristics::HURT_DISI);
376         player_ptr->update |= PU_FLOW;
377     }
378
379     uint32_t mpe_mode = MPE_ENERGY_USE;
380     if (do_pickup != always_pickup) {
381         mpe_mode |= MPE_DO_PICKUP;
382     }
383
384     if (break_trap) {
385         mpe_mode |= MPE_BREAK_TRAP;
386     }
387
388     (void)move_player_effect(player_ptr, y, x, mpe_mode);
389 }