OSDN Git Service

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