2 * @file movement-execution.cpp
3 * @brief プレイヤーの歩行勝利実行定義
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"
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
63 static bool boundary_floor(grid_type *g_ptr, TerrainType *f_ptr, TerrainType *mimic_f_ptr)
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;
74 * @brief 該当地形のトラップがプレイヤーにとって無効かどうかを判定して返す /
75 * Move player in the given direction, with the given "pickup" flag.
76 * @param player_ptr プレイヤーへの参照ポインタ
78 * @param do_pickup 罠解除を試みながらの移動ならばTRUE
79 * @param break_trap トラップ粉砕処理を行うならばTRUE
80 * @return 実際に移動が行われたならばTRUEを返す。
82 * This routine should (probably) always induce energy expenditure.\n
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
88 void exe_movement(PlayerType *player_ptr, DIRECTION dir, bool do_pickup, bool break_trap)
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;
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;
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;
143 player_ptr->leaving = true;
144 PlayerEnergy(player_ptr).set_player_turn_energy(100);
151 auto *m_ptr = &floor_ptr->m_list[g_ptr->m_idx];
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;
160 if (player_ptr->inventory_list[INVEN_SUB_HAND].is_specific_artifact(stormbringer)) {
161 is_stormbringer = true;
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);
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);
186 if (!is_hallucinated) {
187 monster_race_track(player_ptr, m_ptr->ap_r_idx);
190 health_track(player_ptr, g_ptr->m_idx);
193 if ((is_stormbringer && (randint1(1000) > 666)) || PlayerClass(player_ptr).equals(PlayerClassType::BERSERKER)) {
194 do_cmd_attack(player_ptr, y, x, HISSATSU_NONE);
196 } else if (monster_can_cross_terrain(player_ptr, floor_ptr->grid_array[player_ptr->y][player_ptr->x].feat, r_ptr, 0)) {
199 msg_format(_("%s^が邪魔だ!", "%s^ is in your way!"), m_name.data());
200 PlayerEnergy(player_ptr).reset_player_turn();
204 do_cmd_attack(player_ptr, y, x, HISSATSU_NONE);
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();
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());
222 disturb(player_ptr, false, true);
223 } else if (player_ptr->riding_ryoute) {
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))) {
228 } else if (f_ptr->flags.has(TerrainCharacteristics::CAN_SWIM) && (riding_r_ptr->feature_flags.has(MonsterFeatureType::CAN_SWIM))) {
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();
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();
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();
244 disturb(player_ptr, false, true);
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());
251 disturb(player_ptr, false, true);
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;
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);
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);
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();
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."));
281 msg_format("%sが行く手をはばんでいるようだ。", name);
283 msg_format("You feel %s %s blocking your way.", is_a_vowel(name[0]) ? "an" : "a", name);
285 g_ptr->info |= (CAVE_MARK);
286 lite_spot(player_ptr, y, x);
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();
299 if (easy_open && is_closed_door(player_ptr, feat) && easy_open_door(player_ptr, y, x)) {
304 msg_format("%sが行く手をはばんでいる。", name);
306 msg_format("There is %s %s blocking your way.", is_a_vowel(name[0]) ? "an" : "a", name);
308 if (!(is_confused || is_stunned || is_hallucinated)) {
309 energy.reset_player_turn();
314 disturb(player_ptr, false, true);
315 if (!boundary_floor(g_ptr, f_ptr, mimic_f_ptr)) {
316 sound(SOUND_HITWALL);
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();
329 disturb(player_ptr, false, true);
337 if (player_ptr->warning && (!process_warning(player_ptr, x, y))) {
338 energy.set_player_turn_energy(25);
343 msg_format(_("%sを押し退けた。", "You push past %s."), m_name.data());
346 if (player_ptr->wild_mode) {
348 player_ptr->oldpy = 1;
352 player_ptr->oldpy = MAX_HGT - 2;
356 player_ptr->oldpy = MAX_HGT / 2;
360 player_ptr->oldpx = 1;
364 player_ptr->oldpx = MAX_WID - 2;
368 player_ptr->oldpx = MAX_WID / 2;
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);
377 uint32_t mpe_mode = MPE_ENERGY_USE;
378 if (do_pickup != always_pickup) {
379 mpe_mode |= MPE_DO_PICKUP;
383 mpe_mode |= MPE_BREAK_TRAP;
386 (void)move_player_effect(player_ptr, y, x, mpe_mode);