OSDN Git Service

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