OSDN Git Service

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