OSDN Git Service

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