OSDN Git Service

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