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/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"
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);
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);
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);
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 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);
223 disturb(player_ptr, false, true);
224 } else if (player_ptr->riding_ryoute) {
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))) {
229 } else if (f_ptr->flags.has(TerrainCharacteristics::CAN_SWIM) && (riding_r_ptr->feature_flags.has(MonsterFeatureType::CAN_SWIM))) {
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();
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();
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();
245 disturb(player_ptr, false, true);
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);
253 disturb(player_ptr, false, true);
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;
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);
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);
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();
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."));
283 msg_format("%sが行く手をはばんでいるようだ。", name);
285 msg_format("You feel %s %s blocking your way.", is_a_vowel(name[0]) ? "an" : "a", name);
287 g_ptr->info |= (CAVE_MARK);
288 lite_spot(player_ptr, y, x);
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();
301 if (easy_open && is_closed_door(player_ptr, feat) && easy_open_door(player_ptr, y, x)) {
306 msg_format("%sが行く手をはばんでいる。", name);
308 msg_format("There is %s %s blocking your way.", is_a_vowel(name[0]) ? "an" : "a", name);
310 if (!(is_confused || is_stunned || is_hallucinated)) {
311 energy.reset_player_turn();
316 disturb(player_ptr, false, true);
317 if (!boundary_floor(g_ptr, f_ptr, mimic_f_ptr)) {
318 sound(SOUND_HITWALL);
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();
331 disturb(player_ptr, false, true);
339 if (player_ptr->warning && (!process_warning(player_ptr, x, y))) {
340 energy.set_player_turn_energy(25);
345 msg_format(_("%sを押し退けた。", "You push past %s."), m_name);
348 if (player_ptr->wild_mode) {
350 player_ptr->oldpy = 1;
354 player_ptr->oldpy = MAX_HGT - 2;
358 player_ptr->oldpy = MAX_HGT / 2;
362 player_ptr->oldpx = 1;
366 player_ptr->oldpx = MAX_WID - 2;
370 player_ptr->oldpx = MAX_WID / 2;
374 if (p_can_kill_walls) {
375 cave_alter_feat(player_ptr, y, x, TerrainCharacteristics::HURT_DISI);
376 player_ptr->update |= PU_FLOW;
379 uint32_t mpe_mode = MPE_ENERGY_USE;
380 if (do_pickup != always_pickup) {
381 mpe_mode |= MPE_DO_PICKUP;
385 mpe_mode |= MPE_BREAK_TRAP;
388 (void)move_player_effect(player_ptr, y, x, mpe_mode);