OSDN Git Service

[Implement] ウサウサストライカー召喚処理を追加
[hengbandforosx/hengbandosx.git] / src / monster-floor / monster-move.cpp
1 /*!
2  * @brief モンスターの移動に関する処理
3  * @date 2020/03/08
4  * @author Hourier
5  */
6
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/angband-system.h"
37 #include "system/floor-type-definition.h"
38 #include "system/grid-type-definition.h"
39 #include "system/monster-entity.h"
40 #include "system/monster-race-info.h"
41 #include "system/player-type-definition.h"
42 #include "system/redrawing-flags-updater.h"
43 #include "system/terrain-type-definition.h"
44 #include "target/projection-path-calculator.h"
45 #include "util/bit-flags-calculator.h"
46 #include "view/display-messages.h"
47
48 static bool check_hp_for_terrain_destruction(const TerrainType &terrain, const MonsterEntity &monster)
49 {
50     auto can_destroy = terrain.flags.has_not(TerrainCharacteristics::GLASS);
51     can_destroy |= monster.get_monrace().behavior_flags.has(MonsterBehaviorType::STUPID);
52     can_destroy |= monster.hp >= std::max(monster.maxhp / 3, 200);
53     return can_destroy;
54 }
55
56 /*!
57  * @brief モンスターによる壁の透過・破壊を行う
58  * @param player_ptr プレイヤーへの参照ポインタ
59  * @param monster モンスターへの参照
60  * @param pos モンスターの移動先座標
61  * @param can_cross モンスターが地形を踏破できるならばTRUE
62  * @return 透過も破壊もしなかった場合はFALSE、それ以外はTRUE
63  */
64 static bool process_wall(PlayerType *player_ptr, turn_flags *turn_flags_ptr, const MonsterEntity &monster, const Pos2D &pos, bool can_cross)
65 {
66     const auto &grid = player_ptr->current_floor_ptr->get_grid(pos);
67     const auto &terrain = grid.get_terrain();
68     if (player_ptr->is_located_at(pos)) {
69         turn_flags_ptr->do_move = true;
70         return true;
71     }
72
73     if (grid.m_idx > 0) {
74         turn_flags_ptr->do_move = true;
75         return true;
76     }
77
78     using Mft = MonsterFeatureType;
79     using Tc = TerrainCharacteristics;
80     const auto &monrace = monster.get_monrace();
81     auto can_kill_wall = monrace.feature_flags.has(Mft::KILL_WALL);
82     can_kill_wall &= can_cross ? terrain.flags.has_not(Tc::LOS) : !turn_flags_ptr->is_riding_mon;
83     can_kill_wall &= terrain.flags.has(Tc::HURT_DISI);
84     can_kill_wall &= terrain.flags.has_not(Tc::PERMANENT);
85     can_kill_wall &= check_hp_for_terrain_destruction(terrain, monster);
86     if (can_kill_wall) {
87         turn_flags_ptr->do_move = true;
88         if (!can_cross) {
89             turn_flags_ptr->must_alter_to_move = true;
90         }
91
92         turn_flags_ptr->did_kill_wall = true;
93         return true;
94     }
95
96     if (!can_cross) {
97         return false;
98     }
99
100     turn_flags_ptr->do_move = true;
101     if ((monrace.feature_flags.has(Mft::PASS_WALL)) && (!turn_flags_ptr->is_riding_mon || has_pass_wall(player_ptr)) && terrain.flags.has(Tc::CAN_PASS)) {
102         turn_flags_ptr->did_pass_wall = true;
103     }
104
105     return true;
106 }
107
108 /*!
109  * @brief モンスターが普通のドアを開ける処理
110  * @param player_ptr プレイヤーへの参照ポインタ
111  * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
112  * @param monster モンスターへの参照
113  * @param pos モンスターの移動先座標
114  * @return ドアを打ち破るならここでの処理は実行せずtrue、開けるだけなら開けてfalseを返す
115  * @todo 関数名と処理内容が不一致、後で直す
116  */
117 static bool bash_normal_door(PlayerType *player_ptr, turn_flags *turn_flags_ptr, const MonsterEntity &monster, const Pos2D &pos)
118 {
119     const auto &monrace = monster.get_monrace();
120     const auto &grid = player_ptr->current_floor_ptr->get_grid(pos);
121     const auto &terrain = grid.get_terrain();
122     turn_flags_ptr->do_move = false;
123     using Tc = TerrainCharacteristics;
124     auto can_bash = monrace.behavior_flags.has_not(MonsterBehaviorType::OPEN_DOOR);
125     can_bash |= terrain.flags.has_not(Tc::OPEN);
126     can_bash |= monster.is_pet() && ((player_ptr->pet_extra_flags & PF_OPEN_DOORS) == 0);
127     if (can_bash) {
128         return true;
129     }
130
131     if (terrain.power == 0) {
132         turn_flags_ptr->did_open_door = true;
133         turn_flags_ptr->do_turn = true;
134         return false;
135     }
136
137     if (randint0(monster.hp / 10) > terrain.power) {
138         cave_alter_feat(player_ptr, pos.y, pos.x, Tc::DISARM);
139         turn_flags_ptr->do_turn = true;
140         return false;
141     }
142
143     return true;
144 }
145
146 /*!
147  * @brief モンスターがガラスのドアを開ける処理
148  * @param player_ptr プレイヤーへの参照ポインタ
149  * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
150  * @param monster モンスターへの参照
151  * @param terrain 地形への参照
152  * @param may_bash ドアを打ち破るならtrue、開けるだけならfalse
153  * @todo 関数名と処理内容が不一致、後で直す
154  */
155 static void bash_glass_door(PlayerType *player_ptr, turn_flags *turn_flags_ptr, const MonsterEntity &monster, const TerrainType &terrain, bool may_bash)
156 {
157     const auto &monrace = monster.get_monrace();
158     auto can_bash = may_bash;
159     can_bash &= monrace.behavior_flags.has(MonsterBehaviorType::BASH_DOOR);
160     can_bash &= terrain.flags.has(TerrainCharacteristics::BASH);
161     can_bash &= !monster.is_pet() || any_bits(player_ptr->pet_extra_flags, PF_OPEN_DOORS);
162     if (!can_bash) {
163         return;
164     }
165
166     if (!check_hp_for_terrain_destruction(terrain, monster) || (randint0(monster.hp / 10) <= terrain.power)) {
167         return;
168     }
169
170     if (terrain.flags.has(TerrainCharacteristics::GLASS)) {
171         msg_print(_("ガラスが砕ける音がした!", "You hear glass breaking!"));
172     } else {
173         msg_print(_("ドアを叩き開ける音がした!", "You hear a door burst open!"));
174     }
175
176     if (disturb_minor) {
177         disturb(player_ptr, false, false);
178     }
179
180     turn_flags_ptr->did_bash_door = true;
181     turn_flags_ptr->do_move = true;
182     turn_flags_ptr->must_alter_to_move = true;
183 }
184
185 /*!
186  * @brief モンスターによるドアの開放・破壊を行う
187  * @param player_ptr プレイヤーへの参照ポインタ
188  * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
189  * @param monster モンスターへの参照
190  * @param pos モンスターの移動先座標
191  * @return モンスターが死亡した場合のみFALSE
192  */
193 static bool process_door(PlayerType *player_ptr, turn_flags *turn_flags_ptr, const MonsterEntity &monster, const Pos2D &pos)
194 {
195     auto &monrace = monster.get_monrace();
196     const auto &grid = player_ptr->current_floor_ptr->get_grid(pos);
197     if (!is_closed_door(player_ptr, grid.feat)) {
198         return true;
199     }
200
201     auto &terrain = grid.get_terrain();
202     auto may_bash = bash_normal_door(player_ptr, turn_flags_ptr, monster, pos);
203     bash_glass_door(player_ptr, turn_flags_ptr, monster, terrain, may_bash);
204     if (!turn_flags_ptr->did_open_door && !turn_flags_ptr->did_bash_door) {
205         return true;
206     }
207
208     const auto is_open = feat_state(player_ptr->current_floor_ptr, grid.feat, TerrainCharacteristics::OPEN) == grid.feat;
209     if (turn_flags_ptr->did_bash_door && ((randint0(100) < 50) || is_open || terrain.flags.has(TerrainCharacteristics::GLASS))) {
210         cave_alter_feat(player_ptr, pos.y, pos.x, TerrainCharacteristics::BASH);
211         if (!monster.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,
217             };
218             rfu.set_flags(flags);
219             if (is_original_ap_and_seen(player_ptr, &monster)) {
220                 monrace.r_behavior_flags.set(MonsterBehaviorType::BASH_DOOR);
221             }
222
223             return false;
224         }
225     } else {
226         cave_alter_feat(player_ptr, pos.y, pos.x, TerrainCharacteristics::OPEN);
227     }
228
229     turn_flags_ptr->do_view = true;
230     return true;
231 }
232
233 /*!
234  * @brief 守りのルーンによるモンスターの移動制限を処理する
235  * @param player_ptr プレイヤーへの参照ポインタ
236  * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
237  * @param m_ptr モンスターへの参照ポインタ
238  * @param pos モンスターの移動先座標
239  * @return ルーンに侵入できるか否か
240  */
241 static bool process_protection_rune(PlayerType *player_ptr, turn_flags *turn_flags_ptr, MonsterEntity *m_ptr, const Pos2D &pos)
242 {
243     auto &grid = player_ptr->current_floor_ptr->get_grid(pos);
244     const auto &monrace = m_ptr->get_monrace();
245     auto can_enter = turn_flags_ptr->do_move;
246     can_enter &= grid.is_rune_protection();
247     can_enter &= (monrace.behavior_flags.has_not(MonsterBehaviorType::NEVER_BLOW)) || !player_ptr->is_located_at(pos);
248     if (!can_enter) {
249         return false;
250     }
251
252     turn_flags_ptr->do_move = false;
253     if (m_ptr->is_pet() || (randint1(BREAK_RUNE_PROTECTION) >= monrace.level)) {
254         return true;
255     }
256
257     if (grid.is_mark()) {
258         msg_print(_("守りのルーンが壊れた!", "The rune of protection is broken!"));
259     }
260
261     grid.info &= ~(CAVE_MARK);
262     grid.info &= ~(CAVE_OBJECT);
263     grid.mimic = 0;
264     turn_flags_ptr->do_move = true;
265     note_spot(player_ptr, pos.y, pos.x);
266     return true;
267 }
268
269 /*!
270  * @brief 爆発のルーンを処理する
271  * @param player_ptr プレイヤーへの参照ポインタ
272  * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
273  * @param m_ptr モンスターへの参照ポインタ
274  * @param pos モンスターの移動先座標
275  * @return モンスターが死亡した場合のみFALSE
276  */
277 static bool process_explosive_rune(PlayerType *player_ptr, turn_flags *turn_flags_ptr, MonsterEntity *m_ptr, const Pos2D &pos)
278 {
279     auto &grid = player_ptr->current_floor_ptr->get_grid(pos);
280     const auto &monrace = m_ptr->get_monrace();
281     auto should_explode = turn_flags_ptr->do_move;
282     should_explode &= grid.is_rune_explosion();
283     should_explode &= (monrace.behavior_flags.has_not(MonsterBehaviorType::NEVER_BLOW)) || !player_ptr->is_located_at(pos);
284     if (!should_explode) {
285         return true;
286     }
287
288     turn_flags_ptr->do_move = false;
289     if (m_ptr->is_pet()) {
290         return true;
291     }
292
293     if (randint1(BREAK_RUNE_EXPLOSION) > monrace.level) {
294         if (grid.info & CAVE_MARK) {
295             msg_print(_("ルーンが爆発した!", "The rune explodes!"));
296             BIT_FLAGS project_flags = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_JUMP | PROJECT_NO_HANGEKI;
297             project(player_ptr, 0, 2, pos.y, pos.x, 2 * (player_ptr->lev + damroll(7, 7)), AttributeType::MANA, project_flags);
298         }
299     } else {
300         msg_print(_("爆発のルーンは解除された。", "An explosive rune was disarmed."));
301     }
302
303     grid.info &= ~(CAVE_MARK);
304     grid.info &= ~(CAVE_OBJECT);
305     grid.mimic = 0;
306
307     note_spot(player_ptr, pos.y, pos.x);
308     lite_spot(player_ptr, pos.y, pos.x);
309
310     if (!m_ptr->is_valid()) {
311         return false;
312     }
313
314     turn_flags_ptr->do_move = true;
315     return true;
316 }
317
318 /*!
319  * @brief モンスターが壁を掘った後続処理を実行する
320  * @param player_ptr プレイヤーへの参照ポインタ
321  * @turn_flags_ptr ターン経過処理フラグへの参照ポインタ
322  * @param monster モンスターへの参照
323  * @param pos モンスターの移動先座標
324  * @return モンスターが死亡した場合のみFALSE
325  */
326 static bool process_post_dig_wall(PlayerType *player_ptr, turn_flags *turn_flags_ptr, const MonsterEntity &monster, const Pos2D &pos)
327 {
328     auto &monrace = monster.get_monrace();
329     const auto &grid = player_ptr->current_floor_ptr->get_grid(pos);
330     const auto &terrain = grid.get_terrain();
331     if (!turn_flags_ptr->did_kill_wall || !turn_flags_ptr->do_move) {
332         return true;
333     }
334
335     constexpr auto chance_sound = 20;
336     if (one_in_(chance_sound)) {
337         if (terrain.flags.has(TerrainCharacteristics::GLASS)) {
338             msg_print(_("何かの砕ける音が聞こえる。", "There is a crashing sound."));
339         } else {
340             msg_print(_("ギシギシいう音が聞こえる。", "There is a grinding sound."));
341         }
342     }
343
344     cave_alter_feat(player_ptr, pos.y, pos.x, TerrainCharacteristics::HURT_DISI);
345
346     if (!monster.is_valid()) {
347         auto &rfu = RedrawingFlagsUpdater::get_instance();
348         rfu.set_flag(StatusRecalculatingFlag::FLOW);
349         static constexpr auto flags = {
350             SubWindowRedrawingFlag::OVERHEAD,
351             SubWindowRedrawingFlag::DUNGEON,
352         };
353         rfu.set_flags(flags);
354         if (is_original_ap_and_seen(player_ptr, &monster)) {
355             monrace.r_feature_flags.set(MonsterFeatureType::KILL_WALL);
356         }
357
358         return false;
359     }
360
361     turn_flags_ptr->do_view = true;
362     turn_flags_ptr->do_turn = true;
363     return true;
364 }
365
366 /*!
367  * @brief モンスターの移動に関するメインルーチン
368  * @param player_ptr プレイヤーへの参照ポインタ
369  * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
370  * @param m_idx モンスターID
371  * @param mm モンスターの移動方向
372  * @param pos モンスターの移動前座標
373  * @param count 移動回数 (のはず todo)
374  * @return 移動が阻害される何か (ドア等)があったらFALSE
375  * @todo 少し長いが、これといってブロックとしてまとまった部分もないので暫定でこのままとする
376  */
377 bool process_monster_movement(PlayerType *player_ptr, turn_flags *turn_flags_ptr, MONSTER_IDX m_idx, DIRECTION *mm, const Pos2D &pos, int *count)
378 {
379     for (int i = 0; mm[i]; i++) {
380         int d = mm[i];
381         if (d == 5) {
382             d = ddd[randint0(8)];
383         }
384
385         const Pos2D pos_neighbor(pos.y + ddy[d], pos.x + ddx[d]);
386         if (!in_bounds2(player_ptr->current_floor_ptr, pos_neighbor.y, pos_neighbor.x)) {
387             continue;
388         }
389
390         auto &grid = player_ptr->current_floor_ptr->get_grid(pos_neighbor);
391         auto &monster = player_ptr->current_floor_ptr->m_list[m_idx];
392         auto &monrace = monster.get_monrace();
393         bool can_cross = monster_can_cross_terrain(player_ptr, grid.feat, &monrace, turn_flags_ptr->is_riding_mon ? CEM_RIDING : 0);
394
395         if (!process_wall(player_ptr, turn_flags_ptr, monster, pos_neighbor, can_cross)) {
396             if (!process_door(player_ptr, turn_flags_ptr, monster, pos_neighbor)) {
397                 return false;
398             }
399         }
400
401         if (!process_protection_rune(player_ptr, turn_flags_ptr, &monster, pos_neighbor)) {
402             if (!process_explosive_rune(player_ptr, turn_flags_ptr, &monster, pos_neighbor)) {
403                 return false;
404             }
405         }
406
407         exe_monster_attack_to_player(player_ptr, turn_flags_ptr, m_idx, pos_neighbor);
408         if (process_monster_attack_to_monster(player_ptr, turn_flags_ptr, m_idx, &grid, can_cross)) {
409             return false;
410         }
411
412         if (turn_flags_ptr->is_riding_mon) {
413             const auto &monster_riding = player_ptr->current_floor_ptr->m_list[player_ptr->riding];
414             if (!player_ptr->riding_ryoute && !monster_riding.is_fearful()) {
415                 turn_flags_ptr->do_move = false;
416             }
417         }
418
419         if (!process_post_dig_wall(player_ptr, turn_flags_ptr, monster, pos_neighbor)) {
420             return false;
421         }
422
423         if (turn_flags_ptr->must_alter_to_move && monrace.feature_flags.has(MonsterFeatureType::AQUATIC)) {
424             if (!monster_can_cross_terrain(player_ptr, grid.feat, &monrace, turn_flags_ptr->is_riding_mon ? CEM_RIDING : 0)) {
425                 turn_flags_ptr->do_move = false;
426             }
427         }
428
429         if (turn_flags_ptr->do_move && !can_cross && !turn_flags_ptr->did_kill_wall && !turn_flags_ptr->did_bash_door) {
430             turn_flags_ptr->do_move = false;
431         }
432
433         if (turn_flags_ptr->do_move && monrace.behavior_flags.has(MonsterBehaviorType::NEVER_MOVE)) {
434             if (is_original_ap_and_seen(player_ptr, &monster)) {
435                 monrace.r_behavior_flags.set(MonsterBehaviorType::NEVER_MOVE);
436             }
437
438             turn_flags_ptr->do_move = false;
439         }
440
441         if (!turn_flags_ptr->do_move) {
442             if (turn_flags_ptr->do_turn) {
443                 break;
444             }
445
446             continue;
447         }
448
449         turn_flags_ptr->do_turn = true;
450         const auto &terrain = grid.get_terrain();
451         auto can_recover_energy = terrain.flags.has(TerrainCharacteristics::TREE);
452         can_recover_energy &= monrace.feature_flags.has_not(MonsterFeatureType::CAN_FLY);
453         can_recover_energy &= monrace.wilderness_flags.has_not(MonsterWildernessType::WILD_WOOD);
454         if (can_recover_energy) {
455             monster.energy_need += ENERGY_NEED();
456         }
457
458         if (!update_riding_monster(player_ptr, turn_flags_ptr, m_idx, pos.y, pos.x, pos_neighbor.y, pos_neighbor.x)) {
459             break;
460         }
461
462         const auto &ap_r_ref = monster.get_appearance_monrace();
463         const auto is_projectable = projectable(player_ptr, player_ptr->y, player_ptr->x, monster.fy, monster.fx);
464         const auto can_see = disturb_near && monster.mflag.has(MonsterTemporaryFlagType::VIEW) && is_projectable;
465         const auto is_high_level = disturb_high && (ap_r_ref.r_tkills > 0) && (ap_r_ref.level >= player_ptr->lev);
466         if (monster.ml && (disturb_move || can_see || is_high_level)) {
467             if (monster.is_hostile()) {
468                 disturb(player_ptr, false, true);
469             }
470         }
471
472         bool is_takable_or_killable = !grid.o_idx_list.empty();
473         is_takable_or_killable &= monrace.behavior_flags.has_any_of({ MonsterBehaviorType::TAKE_ITEM, MonsterBehaviorType::KILL_ITEM });
474
475         bool is_pickup_items = (player_ptr->pet_extra_flags & PF_PICKUP_ITEMS) != 0;
476         is_pickup_items &= monrace.behavior_flags.has(MonsterBehaviorType::TAKE_ITEM);
477
478         is_takable_or_killable &= !monster.is_pet() || is_pickup_items;
479         if (!is_takable_or_killable) {
480             if (turn_flags_ptr->do_turn) {
481                 break;
482             }
483
484             continue;
485         }
486
487         update_object_by_monster_movement(player_ptr, turn_flags_ptr, m_idx, pos_neighbor.y, pos_neighbor.x);
488         if (turn_flags_ptr->do_turn) {
489             break;
490         }
491
492         (*count)++;
493     }
494
495     return true;
496 }
497
498 static bool can_speak(const MonsterRaceInfo &ap_r_ref, MonsterSpeakType mon_speak_type)
499 {
500     const auto can_speak_all = ap_r_ref.speak_flags.has(MonsterSpeakType::SPEAK_ALL);
501     const auto can_speak_specific = ap_r_ref.speak_flags.has(mon_speak_type);
502     return can_speak_all || can_speak_specific;
503 }
504
505 static std::string_view get_speak_filename(const MonsterEntity &monster)
506 {
507     const auto &ap_monrace = monster.get_appearance_monrace();
508     if (monster.is_fearful() && can_speak(ap_monrace, MonsterSpeakType::SPEAK_FEAR)) {
509         return _("monfear_j.txt", "monfear.txt");
510     }
511
512     constexpr auto monspeak_txt(_("monspeak_j.txt", "monspeak.txt"));
513     if (monster.is_pet() && can_speak(ap_monrace, MonsterSpeakType::SPEAK_BATTLE)) {
514         return monspeak_txt;
515     }
516
517     if (monster.is_friendly() && can_speak(ap_monrace, MonsterSpeakType::SPEAK_FRIEND)) {
518         return _("monfrien_j.txt", "monfrien.txt");
519     }
520
521     if (can_speak(ap_monrace, MonsterSpeakType::SPEAK_BATTLE)) {
522         return monspeak_txt;
523     }
524
525     return "";
526 }
527
528 /*!
529  * @brief モンスターを喋らせたり足音を立てたりする
530  * @param player_ptr プレイヤーへの参照ポインタ
531  * @param m_idx モンスターID
532  * @param oy モンスターが元々いたY座標
533  * @param ox モンスターが元々いたX座標
534  * @param aware モンスターがプレイヤーに気付いているならばTRUE、超隠密状態ならばFALSE
535  */
536 void process_speak_sound(PlayerType *player_ptr, MONSTER_IDX m_idx, POSITION oy, POSITION ox, bool aware)
537 {
538     if (AngbandSystem::get_instance().is_phase_out()) {
539         return;
540     }
541
542     const auto &floor = *player_ptr->current_floor_ptr;
543     const auto &monster = floor.m_list[m_idx];
544     constexpr auto chance_noise = 20;
545     if (monster.ap_r_idx == MonsterRaceId::CYBER && one_in_(chance_noise) && !monster.ml && (monster.cdis <= MAX_PLAYER_SIGHT)) {
546         if (disturb_minor) {
547             disturb(player_ptr, false, false);
548         }
549         msg_print(_("重厚な足音が聞こえた。", "You hear heavy steps."));
550     }
551
552     const auto can_speak = monster.get_appearance_monrace().speak_flags.any();
553     constexpr auto chance_speak = 8;
554     if (!can_speak || !aware || !one_in_(chance_speak) || !floor.has_los({ oy, ox }) || !projectable(player_ptr, oy, ox, player_ptr->y, player_ptr->x)) {
555         return;
556     }
557
558     const auto m_name = monster.ml ? monster_desc(player_ptr, &monster, 0) : std::string(_("それ", "It"));
559     auto filename = get_speak_filename(monster);
560     if (filename.empty()) {
561         return;
562     }
563
564     const auto monmessage = get_random_line(filename.data(), enum2i(monster.ap_r_idx));
565     if (monmessage) {
566         msg_format(_("%s^%s", "%s^ %s"), m_name.data(), monmessage->data());
567     }
568 }
569
570 /*!
571  * @brief モンスターの目標地点をセットする / Set the target of counter attack
572  * @param m_ptr モンスターの参照ポインタ
573  * @param y 目標y座標
574  * @param x 目標x座標
575  */
576 void set_target(MonsterEntity *m_ptr, POSITION y, POSITION x)
577 {
578     m_ptr->target_y = y;
579     m_ptr->target_x = x;
580 }
581
582 /*!
583  * @brief モンスターの目標地点をリセットする / Reset the target of counter attack
584  * @param m_ptr モンスターの参照ポインタ
585  */
586 void reset_target(MonsterEntity *m_ptr)
587 {
588     set_target(m_ptr, 0, 0);
589 }