OSDN Git Service

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