OSDN Git Service

Merge branch 'master' of git.osdn.net:/gitroot/hengband/hengband
[hengband/hengband.git] / src / monster-floor / monster-move.c
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/effect-characteristics.h"
13 #include "effect/effect-processor.h"
14 #include "floor/cave.h"
15 #include "game-option/disturbance-options.h"
16 #include "grid/feature.h"
17 #include "io/files-util.h"
18 #include "monster-attack/monster-attack-processor.h"
19 #include "monster-floor/monster-object.h"
20 #include "monster-race/monster-race.h"
21 #include "monster-race/race-flags1.h"
22 #include "monster-race/race-flags2.h"
23 #include "monster-race/race-flags7.h"
24 #include "monster-race/race-flags8.h"
25 #include "monster-race/race-indice-types.h"
26 #include "monster/monster-describer.h"
27 #include "monster/monster-flag-types.h"
28 #include "monster/monster-info.h"
29 #include "monster/monster-status.h"
30 #include "monster/monster-update.h"
31 #include "pet/pet-util.h"
32 #include "spell/spell-types.h"
33 #include "system/floor-type-definition.h"
34 #include "target/projection-path-calculator.h"
35 #include "util/bit-flags-calculator.h"
36 #include "view/display-messages.h"
37
38 static bool check_hp_for_feat_destruction(feature_type *f_ptr, monster_type *m_ptr)
39 {
40     return !have_flag(f_ptr->flags, FF_GLASS) || (r_info[m_ptr->r_idx].flags2 & RF2_STUPID) || (m_ptr->hp >= MAX(m_ptr->maxhp / 3, 200));
41 }
42
43 /*!
44  * @brief モンスターによる壁の透過・破壊を行う
45  * @param target_ptr プレーヤーへの参照ポインタ
46  * @param m_ptr モンスターへの参照ポインタ
47  * @param ny モンスターのY座標
48  * @param nx モンスターのX座標
49  * @param can_cross モンスターが地形を踏破できるならばTRUE
50  * @return 透過も破壊もしなかった場合はFALSE、それ以外はTRUE
51  */
52 static bool process_wall(player_type *target_ptr, turn_flags *turn_flags_ptr, monster_type *m_ptr, POSITION ny, POSITION nx, bool can_cross)
53 {
54     monster_race *r_ptr = &r_info[m_ptr->r_idx];
55     grid_type *g_ptr;
56     g_ptr = &target_ptr->current_floor_ptr->grid_array[ny][nx];
57     feature_type *f_ptr;
58     f_ptr = &f_info[g_ptr->feat];
59     if (player_bold(target_ptr, ny, nx)) {
60         turn_flags_ptr->do_move = TRUE;
61         return TRUE;
62     }
63
64     if (g_ptr->m_idx > 0) {
65         turn_flags_ptr->do_move = TRUE;
66         return TRUE;
67     }
68
69     if (((r_ptr->flags2 & RF2_KILL_WALL) != 0) && (can_cross ? !have_flag(f_ptr->flags, FF_LOS) : !turn_flags_ptr->is_riding_mon)
70         && have_flag(f_ptr->flags, FF_HURT_DISI) && !have_flag(f_ptr->flags, FF_PERMANENT) && check_hp_for_feat_destruction(f_ptr, m_ptr)) {
71         turn_flags_ptr->do_move = TRUE;
72         if (!can_cross)
73             turn_flags_ptr->must_alter_to_move = TRUE;
74
75         turn_flags_ptr->did_kill_wall = TRUE;
76         return TRUE;
77     }
78
79     if (!can_cross)
80         return FALSE;
81
82     turn_flags_ptr->do_move = TRUE;
83     if (((r_ptr->flags2 & RF2_PASS_WALL) != 0) && (!turn_flags_ptr->is_riding_mon || target_ptr->pass_wall) && have_flag(f_ptr->flags, FF_CAN_PASS)) {
84         turn_flags_ptr->did_pass_wall = TRUE;
85     }
86
87     return TRUE;
88 }
89
90 /*!
91  * @brief モンスターが普通のドアを開ける処理
92  * @param target_ptr プレーヤーへの参照ポインタ
93  * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
94  * @param m_ptr モンスターへの参照ポインタ
95  * @param ny モンスターのY座標
96  * @param nx モンスターのX座標
97  * @return ここではドアを開けず、ガラスのドアを開ける可能性があるならTRUE
98  */
99 static bool bash_normal_door(player_type *target_ptr, turn_flags *turn_flags_ptr, monster_type *m_ptr, POSITION ny, POSITION nx)
100 {
101     monster_race *r_ptr = &r_info[m_ptr->r_idx];
102     grid_type *g_ptr;
103     g_ptr = &target_ptr->current_floor_ptr->grid_array[ny][nx];
104     feature_type *f_ptr;
105     f_ptr = &f_info[g_ptr->feat];
106     turn_flags_ptr->do_move = FALSE;
107     if (((r_ptr->flags2 & RF2_OPEN_DOOR) == 0) || !have_flag(f_ptr->flags, FF_OPEN) || (is_pet(m_ptr) && ((target_ptr->pet_extra_flags & PF_OPEN_DOORS) == 0)))
108         return TRUE;
109
110     if (f_ptr->power == 0) {
111         turn_flags_ptr->did_open_door = TRUE;
112         turn_flags_ptr->do_turn = TRUE;
113         return FALSE;
114     }
115
116     if (randint0(m_ptr->hp / 10) > f_ptr->power) {
117         cave_alter_feat(target_ptr, ny, nx, FF_DISARM);
118         turn_flags_ptr->do_turn = TRUE;
119         return FALSE;
120     }
121
122     return TRUE;
123 }
124
125 /*!
126  * @brief モンスターがガラスのドアを開ける処理
127  * @param target_ptr プレーヤーへの参照ポインタ
128  * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
129  * @param m_ptr モンスターへの参照ポインタ
130  * @param g_ptr グリッドへの参照ポインタ
131  * @param f_ptr 地形への参照ポインタ
132  * @return なし
133  */
134 static void bash_glass_door(player_type *target_ptr, turn_flags *turn_flags_ptr, monster_type *m_ptr, feature_type *f_ptr, bool may_bash)
135 {
136     monster_race *r_ptr = &r_info[m_ptr->r_idx];
137     if (!may_bash || ((r_ptr->flags2 & RF2_BASH_DOOR) == 0) || !have_flag(f_ptr->flags, FF_BASH)
138         || (is_pet(m_ptr) && ((target_ptr->pet_extra_flags & PF_OPEN_DOORS) == 0)))
139         return;
140
141     if (!check_hp_for_feat_destruction(f_ptr, m_ptr) || (randint0(m_ptr->hp / 10) <= f_ptr->power))
142         return;
143
144     if (have_flag(f_ptr->flags, FF_GLASS))
145         msg_print(_("ガラスが砕ける音がした!", "You hear glass breaking!"));
146     else
147         msg_print(_("ドアを叩き開ける音がした!", "You hear a door burst open!"));
148
149     if (disturb_minor)
150         disturb(target_ptr, FALSE, FALSE);
151
152     turn_flags_ptr->did_bash_door = TRUE;
153     turn_flags_ptr->do_move = TRUE;
154     turn_flags_ptr->must_alter_to_move = TRUE;
155 }
156
157 /*!
158  * @brief モンスターによるドアの開放・破壊を行う
159  * @param target_ptr プレーヤーへの参照ポインタ
160  * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
161  * @param m_ptr モンスターへの参照ポインタ
162  * @param ny モンスターのY座標
163  * @param nx モンスターのX座標
164  * @return モンスターが死亡した場合のみFALSE
165  */
166 static bool process_door(player_type *target_ptr, turn_flags *turn_flags_ptr, monster_type *m_ptr, POSITION ny, POSITION nx)
167 {
168     monster_race *r_ptr = &r_info[m_ptr->r_idx];
169     grid_type *g_ptr;
170     g_ptr = &target_ptr->current_floor_ptr->grid_array[ny][nx];
171     if (!is_closed_door(target_ptr, g_ptr->feat))
172         return TRUE;
173
174     feature_type *f_ptr;
175     f_ptr = &f_info[g_ptr->feat];
176     bool may_bash = bash_normal_door(target_ptr, turn_flags_ptr, m_ptr, ny, nx);
177     bash_glass_door(target_ptr, turn_flags_ptr, m_ptr, f_ptr, may_bash);
178
179     if (!turn_flags_ptr->did_open_door && !turn_flags_ptr->did_bash_door)
180         return TRUE;
181
182     if (turn_flags_ptr->did_bash_door
183         && ((randint0(100) < 50) || (feat_state(target_ptr, g_ptr->feat, FF_OPEN) == g_ptr->feat) || have_flag(f_ptr->flags, FF_GLASS))) {
184         cave_alter_feat(target_ptr, ny, nx, FF_BASH);
185         if (!monster_is_valid(m_ptr)) {
186             target_ptr->update |= (PU_FLOW);
187             target_ptr->window |= (PW_OVERHEAD | PW_DUNGEON);
188             if (is_original_ap_and_seen(target_ptr, m_ptr))
189                 r_ptr->r_flags2 |= (RF2_BASH_DOOR);
190
191             return FALSE;
192         }
193     } else {
194         cave_alter_feat(target_ptr, ny, nx, FF_OPEN);
195     }
196
197     f_ptr = &f_info[g_ptr->feat];
198     turn_flags_ptr->do_view = TRUE;
199     return TRUE;
200 }
201
202 /*!
203  * @brief 守りのルーンによるモンスターの移動制限を処理する
204  * @param target_ptr プレーヤーへの参照ポインタ
205  * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
206  * @param m_ptr モンスターへの参照ポインタ
207  * @param ny モンスターのY座標
208  * @param nx モンスターのX座標
209  * @return ルーンのある/なし
210  */
211 static bool process_protection_rune(player_type *target_ptr, turn_flags *turn_flags_ptr, monster_type *m_ptr, POSITION ny, POSITION nx)
212 {
213     grid_type *g_ptr;
214     g_ptr = &target_ptr->current_floor_ptr->grid_array[ny][nx];
215     monster_race *r_ptr = &r_info[m_ptr->r_idx];
216     if (!turn_flags_ptr->do_move || !is_glyph_grid(g_ptr) || (((r_ptr->flags1 & RF1_NEVER_BLOW) != 0) && player_bold(target_ptr, ny, nx)))
217         return FALSE;
218
219     turn_flags_ptr->do_move = FALSE;
220     if (is_pet(m_ptr) || (randint1(BREAK_GLYPH) >= r_ptr->level))
221         return TRUE;
222
223     if (g_ptr->info & CAVE_MARK) {
224         msg_print(_("守りのルーンが壊れた!", "The rune of protection is broken!"));
225     }
226
227     g_ptr->info &= ~(CAVE_MARK);
228     g_ptr->info &= ~(CAVE_OBJECT);
229     g_ptr->mimic = 0;
230     turn_flags_ptr->do_move = TRUE;
231     note_spot(target_ptr, ny, nx);
232     return TRUE;
233 }
234
235 /*!
236  * @brief 爆発のルーンを処理する
237  * @param target_ptr プレーヤーへの参照ポインタ
238  * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
239  * @param m_ptr モンスターへの参照ポインタ
240  * @param ny モンスターのY座標
241  * @param nx モンスターのX座標
242  * @return モンスターが死亡した場合のみFALSE
243  */
244 static bool process_explosive_rune(player_type *target_ptr, turn_flags *turn_flags_ptr, monster_type *m_ptr, POSITION ny, POSITION nx)
245 {
246     grid_type *g_ptr;
247     g_ptr = &target_ptr->current_floor_ptr->grid_array[ny][nx];
248     monster_race *r_ptr = &r_info[m_ptr->r_idx];
249     if (!turn_flags_ptr->do_move || !is_explosive_rune_grid(g_ptr) || (((r_ptr->flags1 & RF1_NEVER_BLOW) != 0) && player_bold(target_ptr, ny, nx)))
250         return TRUE;
251
252     turn_flags_ptr->do_move = FALSE;
253     if (is_pet(m_ptr))
254         return TRUE;
255
256     if (randint1(BREAK_MINOR_GLYPH) > r_ptr->level) {
257         if (g_ptr->info & CAVE_MARK) {
258             msg_print(_("ルーンが爆発した!", "The rune explodes!"));
259             BIT_FLAGS project_flags = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_JUMP | PROJECT_NO_HANGEKI;
260             project(target_ptr, 0, 2, ny, nx, 2 * (target_ptr->lev + damroll(7, 7)), GF_MANA, project_flags, -1);
261         }
262     } else {
263         msg_print(_("爆発のルーンは解除された。", "An explosive rune was disarmed."));
264     }
265
266     g_ptr->info &= ~(CAVE_MARK);
267     g_ptr->info &= ~(CAVE_OBJECT);
268     g_ptr->mimic = 0;
269
270     note_spot(target_ptr, ny, nx);
271     lite_spot(target_ptr, ny, nx);
272
273     if (!monster_is_valid(m_ptr))
274         return FALSE;
275
276     turn_flags_ptr->do_move = TRUE;
277     return TRUE;
278 }
279
280 /*!
281  * @brief モンスターが壁を掘った後続処理を実行する
282  * @param target_ptr プレーヤーへの参照ポインタ
283  * @turn_flags_ptr ターン経過処理フラグへの参照ポインタ
284  * @param m_ptr モンスターへの参照ポインタ
285  * @param ny モンスターのY座標
286  * @param nx モンスターのX座標
287  * @return モンスターが死亡した場合のみFALSE
288  */
289 static bool process_post_dig_wall(player_type *target_ptr, turn_flags *turn_flags_ptr, monster_type *m_ptr, POSITION ny, POSITION nx)
290 {
291     monster_race *r_ptr = &r_info[m_ptr->r_idx];
292     grid_type *g_ptr;
293     g_ptr = &target_ptr->current_floor_ptr->grid_array[ny][nx];
294     feature_type *f_ptr;
295     f_ptr = &f_info[g_ptr->feat];
296     if (!turn_flags_ptr->did_kill_wall || !turn_flags_ptr->do_move)
297         return TRUE;
298
299     if (one_in_(GRINDNOISE)) {
300         if (have_flag(f_ptr->flags, FF_GLASS))
301             msg_print(_("何かの砕ける音が聞こえる。", "There is a crashing sound."));
302         else
303             msg_print(_("ギシギシいう音が聞こえる。", "There is a grinding sound."));
304     }
305
306     cave_alter_feat(target_ptr, ny, nx, FF_HURT_DISI);
307
308     if (!monster_is_valid(m_ptr)) {
309         target_ptr->update |= (PU_FLOW);
310         target_ptr->window |= (PW_OVERHEAD | PW_DUNGEON);
311         if (is_original_ap_and_seen(target_ptr, m_ptr))
312             r_ptr->r_flags2 |= (RF2_KILL_WALL);
313
314         return FALSE;
315     }
316
317     f_ptr = &f_info[g_ptr->feat];
318     turn_flags_ptr->do_view = TRUE;
319     turn_flags_ptr->do_turn = TRUE;
320     return TRUE;
321 }
322
323 /*!
324  * todo 少し長いが、これといってブロックとしてまとまった部分もないので暫定でこのままとする
325  * @brief モンスターの移動に関するメインルーチン
326  * @param target_ptr プレーヤーへの参照ポインタ
327  * @param turn_flags_ptr ターン経過処理フラグへの参照ポインタ
328  * @param m_idx モンスターID
329  * @param mm モンスターの移動方向
330  * @param oy 移動前の、モンスターのY座標
331  * @param ox 移動前の、モンスターのX座標
332  * @param count 移動回数 (のはず todo)
333  * @return 移動が阻害される何か (ドア等)があったらFALSE
334  */
335 bool process_monster_movement(player_type *target_ptr, turn_flags *turn_flags_ptr, MONSTER_IDX m_idx, DIRECTION *mm, POSITION oy, POSITION ox, int *count)
336 {
337     for (int i = 0; mm[i]; i++) {
338         int d = mm[i];
339         if (d == 5)
340             d = ddd[randint0(8)];
341
342         POSITION ny = oy + ddy[d];
343         POSITION nx = ox + ddx[d];
344         if (!in_bounds2(target_ptr->current_floor_ptr, ny, nx))
345             continue;
346
347         grid_type *g_ptr;
348         g_ptr = &target_ptr->current_floor_ptr->grid_array[ny][nx];
349         monster_type *m_ptr = &target_ptr->current_floor_ptr->m_list[m_idx];
350         monster_race *r_ptr = &r_info[m_ptr->r_idx];
351         bool can_cross = monster_can_cross_terrain(target_ptr, g_ptr->feat, r_ptr, turn_flags_ptr->is_riding_mon ? CEM_RIDING : 0);
352
353         if (!process_wall(target_ptr, turn_flags_ptr, m_ptr, ny, nx, can_cross)) {
354             if (!process_door(target_ptr, turn_flags_ptr, m_ptr, ny, nx))
355                 return FALSE;
356         }
357
358         if (!process_protection_rune(target_ptr, turn_flags_ptr, m_ptr, ny, nx)) {
359             if (!process_explosive_rune(target_ptr, turn_flags_ptr, m_ptr, ny, nx))
360                 return FALSE;
361         }
362
363         exe_monster_attack_to_player(target_ptr, turn_flags_ptr, m_idx, ny, nx);
364         if (process_monster_attack_to_monster(target_ptr, turn_flags_ptr, m_idx, g_ptr, can_cross))
365             return FALSE;
366
367         if (turn_flags_ptr->is_riding_mon) {
368             if (!target_ptr->riding_ryoute && !monster_fear_remaining(&target_ptr->current_floor_ptr->m_list[target_ptr->riding]))
369                 turn_flags_ptr->do_move = FALSE;
370         }
371
372         if (!process_post_dig_wall(target_ptr, turn_flags_ptr, m_ptr, ny, nx))
373             return FALSE;
374
375         if (turn_flags_ptr->must_alter_to_move && (r_ptr->flags7 & RF7_AQUATIC)) {
376             if (!monster_can_cross_terrain(target_ptr, g_ptr->feat, r_ptr, turn_flags_ptr->is_riding_mon ? CEM_RIDING : 0))
377                 turn_flags_ptr->do_move = FALSE;
378         }
379
380         if (turn_flags_ptr->do_move && !can_cross && !turn_flags_ptr->did_kill_wall && !turn_flags_ptr->did_bash_door)
381             turn_flags_ptr->do_move = FALSE;
382
383         if (turn_flags_ptr->do_move && (r_ptr->flags1 & RF1_NEVER_MOVE)) {
384             if (is_original_ap_and_seen(target_ptr, m_ptr))
385                 r_ptr->r_flags1 |= (RF1_NEVER_MOVE);
386
387             turn_flags_ptr->do_move = FALSE;
388         }
389
390         if (!turn_flags_ptr->do_move) {
391             if (turn_flags_ptr->do_turn)
392                 break;
393
394             continue;
395         }
396
397         turn_flags_ptr->do_turn = TRUE;
398         feature_type *f_ptr;
399         f_ptr = &f_info[g_ptr->feat];
400         if (have_flag(f_ptr->flags, FF_TREE)) {
401             if (!(r_ptr->flags7 & RF7_CAN_FLY) && !(r_ptr->flags8 & RF8_WILD_WOOD)) {
402                 m_ptr->energy_need += ENERGY_NEED();
403             }
404         }
405
406         if (!update_riding_monster(target_ptr, turn_flags_ptr, m_idx, oy, ox, ny, nx))
407             break;
408
409         monster_race *ap_r_ptr = &r_info[m_ptr->ap_r_idx];
410         if (m_ptr->ml
411             && (disturb_move || (disturb_near && (m_ptr->mflag & MFLAG_VIEW) && projectable(target_ptr, target_ptr->y, target_ptr->x, m_ptr->fy, m_ptr->fx))
412                 || (disturb_high && ap_r_ptr->r_tkills && ap_r_ptr->level >= target_ptr->lev))) {
413             if (is_hostile(m_ptr))
414                 disturb(target_ptr, FALSE, TRUE);
415         }
416
417         bool is_takable_or_killable = g_ptr->o_idx > 0;
418         is_takable_or_killable &= (r_ptr->flags2 & (RF2_TAKE_ITEM | RF2_KILL_ITEM)) != 0;
419
420         bool is_pickup_items = (target_ptr->pet_extra_flags & PF_PICKUP_ITEMS) != 0;
421         is_pickup_items &= (r_ptr->flags2 & RF2_TAKE_ITEM) != 0;
422
423         is_takable_or_killable &= !is_pet(m_ptr) || is_pickup_items;
424         if (!is_takable_or_killable) {
425             if (turn_flags_ptr->do_turn)
426                 break;
427
428             continue;
429         }
430
431         update_object_by_monster_movement(target_ptr, turn_flags_ptr, m_idx, ny, nx);
432         if (turn_flags_ptr->do_turn)
433             break;
434
435         (*count)++;
436     }
437
438     return TRUE;
439 }
440
441 /*!
442  * @brief モンスターを喋らせたり足音を立てたりする
443  * @param target_ptr プレーヤーへの参照ポインタ
444  * @param m_idx モンスターID
445  * @param oy モンスターが元々いたY座標
446  * @param ox モンスターが元々いたX座標
447  * @param aware モンスターがプレーヤーに気付いているならばTRUE、超隠密状態ならばFALSE
448  * @return なし
449  */
450 void process_speak_sound(player_type *target_ptr, MONSTER_IDX m_idx, POSITION oy, POSITION ox, bool aware)
451 {
452     if (target_ptr->phase_out)
453         return;
454
455     monster_type *m_ptr = &target_ptr->current_floor_ptr->m_list[m_idx];
456     monster_race *ap_r_ptr = &r_info[m_ptr->ap_r_idx];
457     if (m_ptr->ap_r_idx == MON_CYBER && one_in_(CYBERNOISE) && !m_ptr->ml && (m_ptr->cdis <= MAX_SIGHT)) {
458         if (disturb_minor)
459             disturb(target_ptr, FALSE, FALSE);
460         msg_print(_("重厚な足音が聞こえた。", "You hear heavy steps."));
461     }
462
463     if (((ap_r_ptr->flags2 & RF2_CAN_SPEAK) == 0) || !aware || !one_in_(SPEAK_CHANCE) || !player_has_los_bold(target_ptr, oy, ox)
464         || !projectable(target_ptr, oy, ox, target_ptr->y, target_ptr->x))
465         return;
466
467     GAME_TEXT m_name[MAX_NLEN];
468     char monmessage[1024];
469     concptr filename;
470
471     if (m_ptr->ml)
472         monster_desc(target_ptr, m_name, m_ptr, 0);
473     else
474         strcpy(m_name, _("それ", "It"));
475
476     if (monster_fear_remaining(m_ptr))
477         filename = _("monfear_j.txt", "monfear.txt");
478     else if (is_pet(m_ptr))
479         filename = _("monpet_j.txt", "monpet.txt");
480     else if (is_friendly(m_ptr))
481         filename = _("monfrien_j.txt", "monfrien.txt");
482     else
483         filename = _("monspeak_j.txt", "monspeak.txt");
484
485     if (get_rnd_line(filename, m_ptr->ap_r_idx, monmessage) == 0) {
486         msg_format(_("%^s%s", "%^s %s"), m_name, monmessage);
487     }
488 }
489
490 /*!
491  * @brief モンスターの目標地点をセットする / Set the target of counter attack
492  * @param m_ptr モンスターの参照ポインタ
493  * @param y 目標y座標
494  * @param x 目標x座標
495  * @return なし
496  */
497 void set_target(monster_type *m_ptr, POSITION y, POSITION x)
498 {
499     m_ptr->target_y = y;
500     m_ptr->target_x = x;
501 }
502
503 /*!
504  * @brief モンスターの目標地点をリセットする / Reset the target of counter attack
505  * @param m_ptr モンスターの参照ポインタ
506  * @return なし
507  */
508 void reset_target(monster_type *m_ptr) { set_target(m_ptr, 0, 0); }