OSDN Git Service

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