2 * @brief モンスターの移動に関する処理
7 #include "monster-floor/monster-move.h"
8 #include "core/disturbance.h"
9 #include "core/speed-table.h"
10 #include "core/window-redrawer.h"
11 #include "effect/attribute-types.h"
12 #include "effect/effect-characteristics.h"
13 #include "effect/effect-processor.h"
14 #include "floor/cave.h"
15 #include "floor/geometry.h"
16 #include "game-option/disturbance-options.h"
17 #include "grid/feature.h"
18 #include "grid/grid.h"
19 #include "io/files-util.h"
20 #include "monster-attack/monster-attack-processor.h"
21 #include "monster-floor/monster-object.h"
22 #include "monster-race/monster-race.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/monster-describer.h"
29 #include "monster/monster-flag-types.h"
30 #include "monster/monster-info.h"
31 #include "monster/monster-processor-util.h"
32 #include "monster/monster-status.h"
33 #include "monster/monster-update.h"
34 #include "pet/pet-util.h"
35 #include "player/player-status-flags.h"
36 #include "system/floor-type-definition.h"
37 #include "system/grid-type-definition.h"
38 #include "system/monster-entity.h"
39 #include "system/monster-race-info.h"
40 #include "system/player-type-definition.h"
41 #include "system/redrawing-flags-updater.h"
42 #include "system/terrain-type-definition.h"
43 #include "target/projection-path-calculator.h"
44 #include "util/bit-flags-calculator.h"
45 #include "view/display-messages.h"
47 static bool check_hp_for_terrain_destruction(TerrainType *f_ptr, MonsterEntity *m_ptr)
49 auto can_destroy = f_ptr->flags.has_not(TerrainCharacteristics::GLASS);
50 can_destroy |= m_ptr->get_monrace().behavior_flags.has(MonsterBehaviorType::STUPID);
51 can_destroy |= m_ptr->hp >= std::max(m_ptr->maxhp / 3, 200);
56 * @brief モンスターによる壁の透過・破壊を行う
57 * @param player_ptr プレイヤーへの参照ポインタ
58 * @param m_ptr モンスターへの参照ポインタ
61 * @param can_cross モンスターが地形を踏破できるならばTRUE
62 * @return 透過も破壊もしなかった場合はFALSE、それ以外はTRUE
64 static bool process_wall(PlayerType *player_ptr, turn_flags *turn_flags_ptr, MonsterEntity *m_ptr, POSITION ny, POSITION nx, bool can_cross)
66 auto *r_ptr = &m_ptr->get_monrace();
67 auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[ny][nx];
68 auto *f_ptr = &terrains_info[g_ptr->feat];
69 if (player_bold(player_ptr, ny, nx)) {
70 turn_flags_ptr->do_move = true;
74 if (g_ptr->m_idx > 0) {
75 turn_flags_ptr->do_move = true;
79 using Mft = MonsterFeatureType;
80 using Tc = TerrainCharacteristics;
81 auto can_kill_wall = r_ptr->feature_flags.has(Mft::KILL_WALL);
82 can_kill_wall &= can_cross ? f_ptr->flags.has_not(Tc::LOS) : !turn_flags_ptr->is_riding_mon;
83 can_kill_wall &= f_ptr->flags.has(Tc::HURT_DISI);
84 can_kill_wall &= f_ptr->flags.has_not(Tc::PERMANENT);
85 can_kill_wall &= check_hp_for_terrain_destruction(f_ptr, m_ptr);
87 turn_flags_ptr->do_move = true;
89 turn_flags_ptr->must_alter_to_move = true;
92 turn_flags_ptr->did_kill_wall = true;
100 turn_flags_ptr->do_move = true;
101 if ((r_ptr->feature_flags.has(Mft::PASS_WALL)) && (!turn_flags_ptr->is_riding_mon || has_pass_wall(player_ptr)) && f_ptr->flags.has(Tc::CAN_PASS)) {
102 turn_flags_ptr->did_pass_wall = true;
109 * @brief モンスターが普通のドアを開ける処理
110 * @param player_ptr プレイヤーへの参照ポインタ
111 * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
112 * @param m_ptr モンスターへの参照ポインタ
113 * @param ny モンスターのY座標
114 * @param nx モンスターのX座標
115 * @return ここではドアを開けず、ガラスのドアを開ける可能性があるならTRUE
117 static bool bash_normal_door(PlayerType *player_ptr, turn_flags *turn_flags_ptr, MonsterEntity *m_ptr, POSITION ny, POSITION nx)
119 auto *r_ptr = &m_ptr->get_monrace();
120 auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[ny][nx];
121 auto *f_ptr = &terrains_info[g_ptr->feat];
122 turn_flags_ptr->do_move = false;
123 using Tc = TerrainCharacteristics;
124 auto can_bash = r_ptr->behavior_flags.has_not(MonsterBehaviorType::OPEN_DOOR);
125 can_bash |= f_ptr->flags.has_not(Tc::OPEN);
126 can_bash |= m_ptr->is_pet() && ((player_ptr->pet_extra_flags & PF_OPEN_DOORS) == 0);
131 if (f_ptr->power == 0) {
132 turn_flags_ptr->did_open_door = true;
133 turn_flags_ptr->do_turn = true;
137 if (randint0(m_ptr->hp / 10) > f_ptr->power) {
138 cave_alter_feat(player_ptr, ny, nx, Tc::DISARM);
139 turn_flags_ptr->do_turn = true;
147 * @brief モンスターがガラスのドアを開ける処理
148 * @param player_ptr プレイヤーへの参照ポインタ
149 * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
150 * @param m_ptr モンスターへの参照ポインタ
151 * @param g_ptr グリッドへの参照ポインタ
152 * @param f_ptr 地形への参照ポインタ
154 static void bash_glass_door(PlayerType *player_ptr, turn_flags *turn_flags_ptr, MonsterEntity *m_ptr, TerrainType *f_ptr, bool may_bash)
156 auto *r_ptr = &m_ptr->get_monrace();
157 auto can_bash = may_bash;
158 can_bash &= r_ptr->behavior_flags.has(MonsterBehaviorType::BASH_DOOR);
159 can_bash &= f_ptr->flags.has(TerrainCharacteristics::BASH);
160 can_bash &= !m_ptr->is_pet() || any_bits(player_ptr->pet_extra_flags, PF_OPEN_DOORS);
165 if (!check_hp_for_terrain_destruction(f_ptr, m_ptr) || (randint0(m_ptr->hp / 10) <= f_ptr->power)) {
169 if (f_ptr->flags.has(TerrainCharacteristics::GLASS)) {
170 msg_print(_("ガラスが砕ける音がした!", "You hear glass breaking!"));
172 msg_print(_("ドアを叩き開ける音がした!", "You hear a door burst open!"));
176 disturb(player_ptr, false, false);
179 turn_flags_ptr->did_bash_door = true;
180 turn_flags_ptr->do_move = true;
181 turn_flags_ptr->must_alter_to_move = true;
185 * @brief モンスターによるドアの開放・破壊を行う
186 * @param player_ptr プレイヤーへの参照ポインタ
187 * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
188 * @param m_ptr モンスターへの参照ポインタ
189 * @param ny モンスターのY座標
190 * @param nx モンスターのX座標
191 * @return モンスターが死亡した場合のみFALSE
193 static bool process_door(PlayerType *player_ptr, turn_flags *turn_flags_ptr, MonsterEntity *m_ptr, POSITION ny, POSITION nx)
195 auto *r_ptr = &m_ptr->get_monrace();
196 const auto &g_ref = player_ptr->current_floor_ptr->grid_array[ny][nx];
197 if (!is_closed_door(player_ptr, g_ref.feat)) {
201 auto *terrain_ptr = &terrains_info[g_ref.feat];
202 auto may_bash = bash_normal_door(player_ptr, turn_flags_ptr, m_ptr, ny, nx);
203 bash_glass_door(player_ptr, turn_flags_ptr, m_ptr, terrain_ptr, may_bash);
204 if (!turn_flags_ptr->did_open_door && !turn_flags_ptr->did_bash_door) {
208 const auto is_open = feat_state(player_ptr->current_floor_ptr, g_ref.feat, TerrainCharacteristics::OPEN) == g_ref.feat;
209 if (turn_flags_ptr->did_bash_door && ((randint0(100) < 50) || is_open || terrain_ptr->flags.has(TerrainCharacteristics::GLASS))) {
210 cave_alter_feat(player_ptr, ny, nx, TerrainCharacteristics::BASH);
211 if (!m_ptr->is_valid()) {
212 auto &rfu = RedrawingFlagsUpdater::get_instance();
213 rfu.set_flag(StatusRecalculatingFlag::FLOW);
214 static constexpr auto flags = {
215 SubWindowRedrawingFlag::OVERHEAD,
216 SubWindowRedrawingFlag::DUNGEON,
218 rfu.set_flags(flags);
219 if (is_original_ap_and_seen(player_ptr, m_ptr)) {
220 r_ptr->r_behavior_flags.set(MonsterBehaviorType::BASH_DOOR);
226 cave_alter_feat(player_ptr, ny, nx, TerrainCharacteristics::OPEN);
229 turn_flags_ptr->do_view = true;
234 * @brief 守りのルーンによるモンスターの移動制限を処理する
235 * @param player_ptr プレイヤーへの参照ポインタ
236 * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
237 * @param m_ptr モンスターへの参照ポインタ
238 * @param ny モンスターのY座標
239 * @param nx モンスターのX座標
240 * @return ルーンに侵入できるか否か
242 static bool process_protection_rune(PlayerType *player_ptr, turn_flags *turn_flags_ptr, MonsterEntity *m_ptr, POSITION ny, POSITION nx)
244 auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[ny][nx];
245 auto *r_ptr = &m_ptr->get_monrace();
246 auto can_enter = turn_flags_ptr->do_move;
247 can_enter &= g_ptr->is_rune_protection();
248 can_enter &= (r_ptr->behavior_flags.has_not(MonsterBehaviorType::NEVER_BLOW)) || !player_bold(player_ptr, ny, nx);
253 turn_flags_ptr->do_move = false;
254 if (m_ptr->is_pet() || (randint1(BREAK_RUNE_PROTECTION) >= r_ptr->level)) {
258 if (g_ptr->is_mark()) {
259 msg_print(_("守りのルーンが壊れた!", "The rune of protection is broken!"));
262 g_ptr->info &= ~(CAVE_MARK);
263 g_ptr->info &= ~(CAVE_OBJECT);
265 turn_flags_ptr->do_move = true;
266 note_spot(player_ptr, ny, nx);
272 * @param player_ptr プレイヤーへの参照ポインタ
273 * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
274 * @param m_ptr モンスターへの参照ポインタ
275 * @param ny モンスターのY座標
276 * @param nx モンスターのX座標
277 * @return モンスターが死亡した場合のみFALSE
279 static bool process_explosive_rune(PlayerType *player_ptr, turn_flags *turn_flags_ptr, MonsterEntity *m_ptr, POSITION ny, POSITION nx)
281 auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[ny][nx];
282 auto *r_ptr = &m_ptr->get_monrace();
283 auto should_explode = turn_flags_ptr->do_move;
284 should_explode &= g_ptr->is_rune_explosion();
285 should_explode &= (r_ptr->behavior_flags.has_not(MonsterBehaviorType::NEVER_BLOW)) || !player_bold(player_ptr, ny, nx);
286 if (!should_explode) {
290 turn_flags_ptr->do_move = false;
291 if (m_ptr->is_pet()) {
295 if (randint1(BREAK_RUNE_EXPLOSION) > r_ptr->level) {
296 if (g_ptr->info & CAVE_MARK) {
297 msg_print(_("ルーンが爆発した!", "The rune explodes!"));
298 BIT_FLAGS project_flags = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_JUMP | PROJECT_NO_HANGEKI;
299 project(player_ptr, 0, 2, ny, nx, 2 * (player_ptr->lev + damroll(7, 7)), AttributeType::MANA, project_flags);
302 msg_print(_("爆発のルーンは解除された。", "An explosive rune was disarmed."));
305 g_ptr->info &= ~(CAVE_MARK);
306 g_ptr->info &= ~(CAVE_OBJECT);
309 note_spot(player_ptr, ny, nx);
310 lite_spot(player_ptr, ny, nx);
312 if (!m_ptr->is_valid()) {
316 turn_flags_ptr->do_move = true;
321 * @brief モンスターが壁を掘った後続処理を実行する
322 * @param player_ptr プレイヤーへの参照ポインタ
323 * @turn_flags_ptr ターン経過処理フラグへの参照ポインタ
324 * @param m_ptr モンスターへの参照ポインタ
325 * @param ny モンスターのY座標
326 * @param nx モンスターのX座標
327 * @return モンスターが死亡した場合のみFALSE
329 static bool process_post_dig_wall(PlayerType *player_ptr, turn_flags *turn_flags_ptr, MonsterEntity *m_ptr, POSITION ny, POSITION nx)
331 auto *r_ptr = &m_ptr->get_monrace();
333 g_ptr = &player_ptr->current_floor_ptr->grid_array[ny][nx];
335 f_ptr = &terrains_info[g_ptr->feat];
336 if (!turn_flags_ptr->did_kill_wall || !turn_flags_ptr->do_move) {
340 constexpr auto chance_sound = 20;
341 if (one_in_(chance_sound)) {
342 if (f_ptr->flags.has(TerrainCharacteristics::GLASS)) {
343 msg_print(_("何かの砕ける音が聞こえる。", "There is a crashing sound."));
345 msg_print(_("ギシギシいう音が聞こえる。", "There is a grinding sound."));
349 cave_alter_feat(player_ptr, ny, nx, TerrainCharacteristics::HURT_DISI);
351 if (!m_ptr->is_valid()) {
352 auto &rfu = RedrawingFlagsUpdater::get_instance();
353 rfu.set_flag(StatusRecalculatingFlag::FLOW);
354 static constexpr auto flags = {
355 SubWindowRedrawingFlag::OVERHEAD,
356 SubWindowRedrawingFlag::DUNGEON,
358 rfu.set_flags(flags);
359 if (is_original_ap_and_seen(player_ptr, m_ptr)) {
360 r_ptr->r_feature_flags.set(MonsterFeatureType::KILL_WALL);
366 f_ptr = &terrains_info[g_ptr->feat];
367 turn_flags_ptr->do_view = true;
368 turn_flags_ptr->do_turn = true;
373 * @brief モンスターの移動に関するメインルーチン
374 * @param player_ptr プレイヤーへの参照ポインタ
375 * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
376 * @param m_idx モンスターID
377 * @param mm モンスターの移動方向
378 * @param oy 移動前の、モンスターのY座標
379 * @param ox 移動前の、モンスターのX座標
380 * @param count 移動回数 (のはず todo)
381 * @return 移動が阻害される何か (ドア等)があったらFALSE
382 * @todo 少し長いが、これといってブロックとしてまとまった部分もないので暫定でこのままとする
384 bool process_monster_movement(PlayerType *player_ptr, turn_flags *turn_flags_ptr, MONSTER_IDX m_idx, DIRECTION *mm, POSITION oy, POSITION ox, int *count)
386 for (int i = 0; mm[i]; i++) {
389 d = ddd[randint0(8)];
392 POSITION ny = oy + ddy[d];
393 POSITION nx = ox + ddx[d];
394 if (!in_bounds2(player_ptr->current_floor_ptr, ny, nx)) {
399 g_ptr = &player_ptr->current_floor_ptr->grid_array[ny][nx];
400 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
401 auto *r_ptr = &m_ptr->get_monrace();
402 bool can_cross = monster_can_cross_terrain(player_ptr, g_ptr->feat, r_ptr, turn_flags_ptr->is_riding_mon ? CEM_RIDING : 0);
404 if (!process_wall(player_ptr, turn_flags_ptr, m_ptr, ny, nx, can_cross)) {
405 if (!process_door(player_ptr, turn_flags_ptr, m_ptr, ny, nx)) {
410 if (!process_protection_rune(player_ptr, turn_flags_ptr, m_ptr, ny, nx)) {
411 if (!process_explosive_rune(player_ptr, turn_flags_ptr, m_ptr, ny, nx)) {
416 exe_monster_attack_to_player(player_ptr, turn_flags_ptr, m_idx, ny, nx);
417 if (process_monster_attack_to_monster(player_ptr, turn_flags_ptr, m_idx, g_ptr, can_cross)) {
421 if (turn_flags_ptr->is_riding_mon) {
422 const auto &m_ref = player_ptr->current_floor_ptr->m_list[player_ptr->riding];
423 if (!player_ptr->riding_ryoute && !m_ref.is_fearful()) {
424 turn_flags_ptr->do_move = false;
428 if (!process_post_dig_wall(player_ptr, turn_flags_ptr, m_ptr, ny, nx)) {
432 if (turn_flags_ptr->must_alter_to_move && r_ptr->feature_flags.has(MonsterFeatureType::AQUATIC)) {
433 if (!monster_can_cross_terrain(player_ptr, g_ptr->feat, r_ptr, turn_flags_ptr->is_riding_mon ? CEM_RIDING : 0)) {
434 turn_flags_ptr->do_move = false;
438 if (turn_flags_ptr->do_move && !can_cross && !turn_flags_ptr->did_kill_wall && !turn_flags_ptr->did_bash_door) {
439 turn_flags_ptr->do_move = false;
442 if (turn_flags_ptr->do_move && r_ptr->behavior_flags.has(MonsterBehaviorType::NEVER_MOVE)) {
443 if (is_original_ap_and_seen(player_ptr, m_ptr)) {
444 r_ptr->r_behavior_flags.set(MonsterBehaviorType::NEVER_MOVE);
447 turn_flags_ptr->do_move = false;
450 if (!turn_flags_ptr->do_move) {
451 if (turn_flags_ptr->do_turn) {
458 turn_flags_ptr->do_turn = true;
459 const auto &terrain_ref = terrains_info[g_ptr->feat];
460 auto can_recover_energy = terrain_ref.flags.has(TerrainCharacteristics::TREE);
461 can_recover_energy &= r_ptr->feature_flags.has_not(MonsterFeatureType::CAN_FLY);
462 can_recover_energy &= r_ptr->wilderness_flags.has_not(MonsterWildernessType::WILD_WOOD);
463 if (can_recover_energy) {
464 m_ptr->energy_need += ENERGY_NEED();
467 if (!update_riding_monster(player_ptr, turn_flags_ptr, m_idx, oy, ox, ny, nx)) {
471 const auto &ap_r_ref = m_ptr->get_appearance_monrace();
472 const auto is_projectable = projectable(player_ptr, player_ptr->y, player_ptr->x, m_ptr->fy, m_ptr->fx);
473 const auto can_see = disturb_near && m_ptr->mflag.has(MonsterTemporaryFlagType::VIEW) && is_projectable;
474 const auto is_high_level = disturb_high && (ap_r_ref.r_tkills > 0) && (ap_r_ref.level >= player_ptr->lev);
475 if (m_ptr->ml && (disturb_move || can_see || is_high_level)) {
476 if (m_ptr->is_hostile()) {
477 disturb(player_ptr, false, true);
481 bool is_takable_or_killable = !g_ptr->o_idx_list.empty();
482 is_takable_or_killable &= r_ptr->behavior_flags.has_any_of({ MonsterBehaviorType::TAKE_ITEM, MonsterBehaviorType::KILL_ITEM });
484 bool is_pickup_items = (player_ptr->pet_extra_flags & PF_PICKUP_ITEMS) != 0;
485 is_pickup_items &= r_ptr->behavior_flags.has(MonsterBehaviorType::TAKE_ITEM);
487 is_takable_or_killable &= !m_ptr->is_pet() || is_pickup_items;
488 if (!is_takable_or_killable) {
489 if (turn_flags_ptr->do_turn) {
496 update_object_by_monster_movement(player_ptr, turn_flags_ptr, m_idx, ny, nx);
497 if (turn_flags_ptr->do_turn) {
507 static bool can_speak(const MonsterRaceInfo &ap_r_ref, MonsterSpeakType mon_speak_type)
509 const auto can_speak_all = ap_r_ref.speak_flags.has(MonsterSpeakType::SPEAK_ALL);
510 const auto can_speak_specific = ap_r_ref.speak_flags.has(mon_speak_type);
511 return can_speak_all || can_speak_specific;
514 static std::string_view get_speak_filename(MonsterEntity *m_ptr)
516 const auto &ap_r_ref = m_ptr->get_appearance_monrace();
517 if (m_ptr->is_fearful() && can_speak(ap_r_ref, MonsterSpeakType::SPEAK_FEAR)) {
518 return _("monfear_j.txt", "monfear.txt");
521 constexpr auto monspeak_txt(_("monspeak_j.txt", "monspeak.txt"));
522 if (m_ptr->is_pet() && can_speak(ap_r_ref, MonsterSpeakType::SPEAK_BATTLE)) {
526 if (m_ptr->is_friendly() && can_speak(ap_r_ref, MonsterSpeakType::SPEAK_FRIEND)) {
527 return _("monfrien_j.txt", "monfrien.txt");
530 if (can_speak(ap_r_ref, MonsterSpeakType::SPEAK_BATTLE)) {
538 * @brief モンスターを喋らせたり足音を立てたりする
539 * @param player_ptr プレイヤーへの参照ポインタ
540 * @param m_idx モンスターID
541 * @param oy モンスターが元々いたY座標
542 * @param ox モンスターが元々いたX座標
543 * @param aware モンスターがプレイヤーに気付いているならばTRUE、超隠密状態ならばFALSE
545 void process_speak_sound(PlayerType *player_ptr, MONSTER_IDX m_idx, POSITION oy, POSITION ox, bool aware)
547 if (player_ptr->phase_out) {
551 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
552 constexpr auto chance_noise = 20;
553 if (m_ptr->ap_r_idx == MonsterRaceId::CYBER && one_in_(chance_noise) && !m_ptr->ml && (m_ptr->cdis <= MAX_PLAYER_SIGHT)) {
555 disturb(player_ptr, false, false);
557 msg_print(_("重厚な足音が聞こえた。", "You hear heavy steps."));
560 auto can_speak = m_ptr->get_appearance_monrace().speak_flags.any();
561 constexpr auto chance_speak = 8;
562 if (!can_speak || !aware || !one_in_(chance_speak) || !player_has_los_bold(player_ptr, oy, ox) || !projectable(player_ptr, oy, ox, player_ptr->y, player_ptr->x)) {
566 const auto m_name = m_ptr->ml ? monster_desc(player_ptr, m_ptr, 0) : std::string(_("それ", "It"));
567 auto filename = get_speak_filename(m_ptr);
568 if (filename.empty()) {
572 const auto monmessage = get_random_line(filename.data(), enum2i(m_ptr->ap_r_idx));
573 if (monmessage.has_value()) {
574 msg_format(_("%s^%s", "%s^ %s"), m_name.data(), monmessage->data());
579 * @brief モンスターの目標地点をセットする / Set the target of counter attack
580 * @param m_ptr モンスターの参照ポインタ
584 void set_target(MonsterEntity *m_ptr, POSITION y, POSITION x)
591 * @brief モンスターの目標地点をリセットする / Reset the target of counter attack
592 * @param m_ptr モンスターの参照ポインタ
594 void reset_target(MonsterEntity *m_ptr)
596 set_target(m_ptr, 0, 0);