2 * @brief モンスターの移動に関する処理
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"
46 static bool check_hp_for_feat_destruction(feature_type *f_ptr, monster_type *m_ptr)
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));
52 * @brief モンスターによる壁の透過・破壊を行う
53 * @param player_ptr プレイヤーへの参照ポインタ
54 * @param m_ptr モンスターへの参照ポインタ
57 * @param can_cross モンスターが地形を踏破できるならばTRUE
58 * @return 透過も破壊もしなかった場合はFALSE、それ以外はTRUE
60 static bool process_wall(PlayerType *player_ptr, turn_flags *turn_flags_ptr, monster_type *m_ptr, POSITION ny, POSITION nx, bool can_cross)
62 auto *r_ptr = &r_info[m_ptr->r_idx];
64 g_ptr = &player_ptr->current_floor_ptr->grid_array[ny][nx];
66 f_ptr = &f_info[g_ptr->feat];
67 if (player_bold(player_ptr, ny, nx)) {
68 turn_flags_ptr->do_move = true;
72 if (g_ptr->m_idx > 0) {
73 turn_flags_ptr->do_move = true;
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;
80 turn_flags_ptr->must_alter_to_move = true;
83 turn_flags_ptr->did_kill_wall = true;
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;
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
108 static bool bash_normal_door(PlayerType *player_ptr, turn_flags *turn_flags_ptr, monster_type *m_ptr, POSITION ny, POSITION nx)
110 auto *r_ptr = &r_info[m_ptr->r_idx];
112 g_ptr = &player_ptr->current_floor_ptr->grid_array[ny][nx];
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) || (is_pet(m_ptr) && ((player_ptr->pet_extra_flags & PF_OPEN_DOORS) == 0))) {
120 if (f_ptr->power == 0) {
121 turn_flags_ptr->did_open_door = true;
122 turn_flags_ptr->do_turn = true;
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;
136 * @brief モンスターがガラスのドアを開ける処理
137 * @param player_ptr プレイヤーへの参照ポインタ
138 * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
139 * @param m_ptr モンスターへの参照ポインタ
140 * @param g_ptr グリッドへの参照ポインタ
141 * @param f_ptr 地形への参照ポインタ
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)
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) || (is_pet(m_ptr) && ((player_ptr->pet_extra_flags & PF_OPEN_DOORS) == 0))) {
150 if (!check_hp_for_feat_destruction(f_ptr, m_ptr) || (randint0(m_ptr->hp / 10) <= f_ptr->power)) {
154 if (f_ptr->flags.has(FloorFeatureType::GLASS)) {
155 msg_print(_("ガラスが砕ける音がした!", "You hear glass breaking!"));
157 msg_print(_("ドアを叩き開ける音がした!", "You hear a door burst open!"));
161 disturb(player_ptr, false, false);
164 turn_flags_ptr->did_bash_door = true;
165 turn_flags_ptr->do_move = true;
166 turn_flags_ptr->must_alter_to_move = true;
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
178 static bool process_door(PlayerType *player_ptr, turn_flags *turn_flags_ptr, monster_type *m_ptr, POSITION ny, POSITION nx)
180 auto *r_ptr = &r_info[m_ptr->r_idx];
182 g_ptr = &player_ptr->current_floor_ptr->grid_array[ny][nx];
183 if (!is_closed_door(player_ptr, g_ptr->feat)) {
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);
192 if (!turn_flags_ptr->did_open_door && !turn_flags_ptr->did_bash_door) {
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);
208 cave_alter_feat(player_ptr, ny, nx, FloorFeatureType::OPEN);
211 f_ptr = &f_info[g_ptr->feat];
212 turn_flags_ptr->do_view = true;
217 * @brief 守りのルーンによるモンスターの移動制限を処理する
218 * @param player_ptr プレイヤーへの参照ポインタ
219 * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
220 * @param m_ptr モンスターへの参照ポインタ
221 * @param ny モンスターのY座標
222 * @param nx モンスターのX座標
225 static bool process_protection_rune(PlayerType *player_ptr, turn_flags *turn_flags_ptr, monster_type *m_ptr, POSITION ny, POSITION nx)
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))) {
234 turn_flags_ptr->do_move = false;
235 if (is_pet(m_ptr) || (randint1(BREAK_RUNE_PROTECTION) >= r_ptr->level)) {
239 if (g_ptr->is_mark()) {
240 msg_print(_("守りのルーンが壊れた!", "The rune of protection is broken!"));
243 g_ptr->info &= ~(CAVE_MARK);
244 g_ptr->info &= ~(CAVE_OBJECT);
246 turn_flags_ptr->do_move = true;
247 note_spot(player_ptr, ny, nx);
253 * @param player_ptr プレイヤーへの参照ポインタ
254 * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
255 * @param m_ptr モンスターへの参照ポインタ
256 * @param ny モンスターのY座標
257 * @param nx モンスターのX座標
258 * @return モンスターが死亡した場合のみFALSE
260 static bool process_explosive_rune(PlayerType *player_ptr, turn_flags *turn_flags_ptr, monster_type *m_ptr, POSITION ny, POSITION nx)
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))) {
269 turn_flags_ptr->do_move = false;
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);
281 msg_print(_("爆発のルーンは解除された。", "An explosive rune was disarmed."));
284 g_ptr->info &= ~(CAVE_MARK);
285 g_ptr->info &= ~(CAVE_OBJECT);
288 note_spot(player_ptr, ny, nx);
289 lite_spot(player_ptr, ny, nx);
291 if (!monster_is_valid(m_ptr)) {
295 turn_flags_ptr->do_move = true;
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
308 static bool process_post_dig_wall(PlayerType *player_ptr, turn_flags *turn_flags_ptr, monster_type *m_ptr, POSITION ny, POSITION nx)
310 auto *r_ptr = &r_info[m_ptr->r_idx];
312 g_ptr = &player_ptr->current_floor_ptr->grid_array[ny][nx];
314 f_ptr = &f_info[g_ptr->feat];
315 if (!turn_flags_ptr->did_kill_wall || !turn_flags_ptr->do_move) {
319 if (one_in_(GRINDNOISE)) {
320 if (f_ptr->flags.has(FloorFeatureType::GLASS)) {
321 msg_print(_("何かの砕ける音が聞こえる。", "There is a crashing sound."));
323 msg_print(_("ギシギシいう音が聞こえる。", "There is a grinding sound."));
327 cave_alter_feat(player_ptr, ny, nx, FloorFeatureType::HURT_DISI);
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);
339 f_ptr = &f_info[g_ptr->feat];
340 turn_flags_ptr->do_view = true;
341 turn_flags_ptr->do_turn = true;
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 少し長いが、これといってブロックとしてまとまった部分もないので暫定でこのままとする
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)
359 for (int i = 0; mm[i]; i++) {
362 d = ddd[randint0(8)];
365 POSITION ny = oy + ddy[d];
366 POSITION nx = ox + ddx[d];
367 if (!in_bounds2(player_ptr->current_floor_ptr, ny, nx)) {
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);
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)) {
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)) {
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)) {
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;
400 if (!process_post_dig_wall(player_ptr, turn_flags_ptr, m_ptr, ny, nx)) {
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;
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;
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);
419 turn_flags_ptr->do_move = false;
422 if (!turn_flags_ptr->do_move) {
423 if (turn_flags_ptr->do_turn) {
430 turn_flags_ptr->do_turn = true;
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();
437 if (!update_riding_monster(player_ptr, turn_flags_ptr, m_idx, oy, ox, ny, nx)) {
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 (is_hostile(m_ptr)) {
444 disturb(player_ptr, false, true);
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 });
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);
454 is_takable_or_killable &= !is_pet(m_ptr) || is_pickup_items;
455 if (!is_takable_or_killable) {
456 if (turn_flags_ptr->do_turn) {
463 update_object_by_monster_movement(player_ptr, turn_flags_ptr, m_idx, ny, nx);
464 if (turn_flags_ptr->do_turn) {
475 * @brief モンスターを喋らせたり足音を立てたりする
476 * @param player_ptr プレイヤーへの参照ポインタ
477 * @param m_idx モンスターID
478 * @param oy モンスターが元々いたY座標
479 * @param ox モンスターが元々いたX座標
480 * @param aware モンスターがプレイヤーに気付いているならばTRUE、超隠密状態ならばFALSE
482 void process_speak_sound(PlayerType *player_ptr, MONSTER_IDX m_idx, POSITION oy, POSITION ox, bool aware)
484 if (player_ptr->phase_out) {
488 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
489 monster_race *ap_r_ptr = &r_info[m_ptr->ap_r_idx];
490 if (m_ptr->ap_r_idx == MonsterRaceId::CYBER && one_in_(CYBERNOISE) && !m_ptr->ml && (m_ptr->cdis <= MAX_SIGHT)) {
492 disturb(player_ptr, false, false);
494 msg_print(_("重厚な足音が聞こえた。", "You hear heavy steps."));
497 if ((ap_r_ptr->speak_flags.has_not(MonsterSpeakType::SPEAK_ALL)) || !aware || !one_in_(SPEAK_CHANCE) || !player_has_los_bold(player_ptr, oy, ox) || !projectable(player_ptr, oy, ox, player_ptr->y, player_ptr->x)) {
501 GAME_TEXT m_name[MAX_NLEN];
502 char monmessage[1024];
506 monster_desc(player_ptr, m_name, m_ptr, 0);
508 strcpy(m_name, _("それ", "It"));
511 if (monster_fear_remaining(m_ptr)) {
512 filename = _("monfear_j.txt", "monfear.txt");
513 } else if (is_pet(m_ptr)) {
514 filename = _("monpet_j.txt", "monpet.txt");
515 } else if (is_friendly(m_ptr)) {
516 filename = _("monfrien_j.txt", "monfrien.txt");
518 filename = _("monspeak_j.txt", "monspeak.txt");
521 if (get_rnd_line(filename, enum2i(m_ptr->ap_r_idx), monmessage) == 0) {
522 msg_format(_("%^s%s", "%^s %s"), m_name, monmessage);
527 * @brief モンスターの目標地点をセットする / Set the target of counter attack
528 * @param m_ptr モンスターの参照ポインタ
532 void set_target(monster_type *m_ptr, POSITION y, POSITION x)
539 * @brief モンスターの目標地点をリセットする / Reset the target of counter attack
540 * @param m_ptr モンスターの参照ポインタ
542 void reset_target(monster_type *m_ptr)
544 set_target(m_ptr, 0, 0);