OSDN Git Service

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