2 * @brief モンスターの移動方向を走査する処理
7 #include "monster-floor/monster-sweep-grid.h"
8 #include "floor/cave.h"
9 #include "floor/geometry.h"
10 #include "floor/line-of-sight.h"
11 #include "grid/feature.h"
12 #include "grid/grid.h"
13 #include "monster-floor/monster-safety-hiding.h"
14 #include "monster-race/race-ability-mask.h"
15 #include "monster/monster-flag-types.h"
16 #include "monster/monster-info.h"
17 #include "monster/monster-processor-util.h"
18 #include "monster/monster-status.h"
19 #include "player/player-status-flags.h"
20 #include "system/floor-type-definition.h"
21 #include "system/grid-type-definition.h"
22 #include "system/monster-entity.h"
23 #include "system/monster-race-info.h"
24 #include "system/player-type-definition.h"
25 #include "target/projection-path-calculator.h"
26 #include "util/bit-flags-calculator.h"
30 * @param player_ptr プレイヤーへの参照ポインタ
31 * @param m_idx 逃走するモンスターの参照ID
32 * @param mm 移動方向を返す方向IDの参照ポインタ
34 MonsterSweepGrid::MonsterSweepGrid(PlayerType *player_ptr, MONSTER_IDX m_idx, DIRECTION *mm)
35 : player_ptr(player_ptr)
42 * @brief モンスターの移動方向を返す /
43 * Choose "logical" directions for monster movement
44 * @return 有効方向があった場合TRUEを返す
45 * @todo 分割したいが条件が多すぎて適切な関数名と詳細処理を追いきれない……
47 bool MonsterSweepGrid::get_movable_grid()
49 auto &floor = *this->player_ptr->current_floor_ptr;
50 const auto &monster_from = floor.m_list[this->m_idx];
51 auto &monrace = monster_from.get_monrace();
54 auto y2 = this->player_ptr->y;
55 auto x2 = this->player_ptr->x;
56 this->will_run = this->mon_will_run();
57 Pos2D pos_monster_from(monster_from.fy, monster_from.fx);
58 const auto no_flow = monster_from.mflag2.has(MonsterConstantFlagType::NOFLOW) && (floor.get_grid(pos_monster_from).get_cost(&monrace) > 2);
59 this->can_pass_wall = monrace.feature_flags.has(MonsterFeatureType::PASS_WALL) && ((this->m_idx != this->player_ptr->riding) || has_pass_wall(this->player_ptr));
60 if (!this->will_run && monster_from.target_y) {
61 Pos2D pos_target(monster_from.target_y, monster_from.target_x);
62 int t_m_idx = floor.get_grid(pos_target).m_idx;
64 const auto is_enemies = monster_from.is_hostile_to_melee(floor.m_list[t_m_idx]);
65 const auto is_los = los(this->player_ptr, monster_from.fy, monster_from.fx, monster_from.target_y, monster_from.target_x);
66 const auto is_projectable = projectable(this->player_ptr, monster_from.fy, monster_from.fx, monster_from.target_y, monster_from.target_x);
67 if (is_enemies && is_los && is_projectable) {
68 y = monster_from.fy - monster_from.target_y;
69 x = monster_from.fx - monster_from.target_x;
75 this->check_hiding_grid(&y, &x, &y2, &x2);
77 this->sweep_movable_grid(&y2, &x2, no_flow);
78 y = monster_from.fy - y2;
79 x = monster_from.fx - x2;
82 this->search_pet_runnable_grid(&y, &x, no_flow);
87 store_moves_val(this->mm, y, x);
92 * @brief モンスターがプレイヤーから逃走するかどうかを返す /
93 * Returns whether a given monster will try to run from the player.
94 * @return モンスターがプレイヤーから逃走するならばTRUEを返す。
96 bool MonsterSweepGrid::mon_will_run()
98 auto *m_ptr = &this->player_ptr->current_floor_ptr->m_list[this->m_idx];
99 auto *r_ptr = &m_ptr->get_monrace();
100 if (m_ptr->is_pet()) {
101 return (this->player_ptr->pet_follow_distance < 0) && (m_ptr->cdis <= (0 - this->player_ptr->pet_follow_distance));
104 if (m_ptr->cdis > MAX_PLAYER_SIGHT + 5) {
108 if (m_ptr->is_fearful()) {
112 if (m_ptr->cdis <= 5) {
116 auto p_lev = this->player_ptr->lev;
117 auto m_lev = r_ptr->level + (this->m_idx & 0x08) + 25;
118 if (m_lev > p_lev + 4) {
122 if (m_lev + 4 <= p_lev) {
126 auto p_chp = this->player_ptr->chp;
127 auto p_mhp = this->player_ptr->mhp;
128 auto m_chp = m_ptr->hp;
129 auto m_mhp = m_ptr->maxhp;
130 uint32_t p_val = (p_lev * p_mhp) + (p_chp << 2);
131 uint32_t m_val = (m_lev * m_mhp) + (m_chp << 2);
132 return p_val * m_mhp > m_val * p_mhp;
135 void MonsterSweepGrid::check_hiding_grid(POSITION *y, POSITION *x, POSITION *y2, POSITION *x2)
137 auto *floor_ptr = this->player_ptr->current_floor_ptr;
138 auto *m_ptr = &floor_ptr->m_list[this->m_idx];
139 auto *r_ptr = &m_ptr->get_monrace();
140 if (this->done || this->will_run || !m_ptr->is_hostile() || r_ptr->misc_flags.has_not(MonsterMiscType::HAS_FRIENDS)) {
144 if ((!los(this->player_ptr, m_ptr->fy, m_ptr->fx, this->player_ptr->y, this->player_ptr->x) || !projectable(this->player_ptr, m_ptr->fy, m_ptr->fx, this->player_ptr->y, this->player_ptr->x))) {
145 if (floor_ptr->grid_array[m_ptr->fy][m_ptr->fx].get_distance(r_ptr) >= MAX_PLAYER_SIGHT / 2) {
150 this->search_room_to_run(y, x);
151 if (this->done || (floor_ptr->grid_array[m_ptr->fy][m_ptr->fx].get_distance(r_ptr) >= 3)) {
155 for (auto i = 0; i < 8; i++) {
156 *y2 = this->player_ptr->y + ddy_ddd[(this->m_idx + i) & 7];
157 *x2 = this->player_ptr->x + ddx_ddd[(this->m_idx + i) & 7];
158 if ((m_ptr->fy == *y2) && (m_ptr->fx == *x2)) {
159 *y2 = this->player_ptr->y;
160 *x2 = this->player_ptr->x;
164 if (!in_bounds2(floor_ptr, *y2, *x2) || !monster_can_enter(this->player_ptr, *y2, *x2, r_ptr, 0)) {
171 *y = m_ptr->fy - *y2;
172 *x = m_ptr->fx - *x2;
176 void MonsterSweepGrid::search_room_to_run(POSITION *y, POSITION *x)
178 auto *floor_ptr = this->player_ptr->current_floor_ptr;
179 const auto &monrace = floor_ptr->m_list[this->m_idx].get_monrace();
180 if (monrace.kind_flags.has_not(MonsterKindType::ANIMAL) || this->can_pass_wall || monrace.feature_flags.has(MonsterFeatureType::KILL_WALL)) {
185 for (auto i = 0; i < 8; i++) {
186 auto xx = this->player_ptr->x + ddx_ddd[i];
187 auto yy = this->player_ptr->y + ddy_ddd[i];
188 if (!in_bounds2(floor_ptr, yy, xx)) {
192 auto *g_ptr = &floor_ptr->grid_array[yy][xx];
193 if (monster_can_cross_terrain(this->player_ptr, g_ptr->feat, &monrace, 0)) {
198 if (floor_ptr->grid_array[this->player_ptr->y][this->player_ptr->x].is_room()) {
202 if (monrace.ability_flags.none()) {
206 if (room >= (8 * (this->player_ptr->chp + this->player_ptr->csp)) / (this->player_ptr->mhp + this->player_ptr->msp)) {
210 if (find_hiding(this->player_ptr, this->m_idx, y, x)) {
215 void MonsterSweepGrid::search_pet_runnable_grid(POSITION *y, POSITION *x, bool no_flow)
217 auto *floor_ptr = this->player_ptr->current_floor_ptr;
218 auto *m_ptr = &floor_ptr->m_list[this->m_idx];
219 if (m_ptr->is_pet() && this->will_run) {
225 if (this->done || !this->will_run) {
231 if (find_safety(this->player_ptr, this->m_idx, y, x) && !no_flow && this->sweep_runnable_away_grid(y, x)) {
244 * @brief モンスターがプレイヤーに向けて接近することが可能なマスを走査する /
245 * Choose the "best" direction for "flowing"
246 * @param yp 移動先のマスのY座標を返す参照ポインタ
247 * @param xp 移動先のマスのX座標を返す参照ポインタ
248 * @param no_flow モンスターにFLOWフラグが経っていない状態でTRUE
250 void MonsterSweepGrid::sweep_movable_grid(POSITION *yp, POSITION *xp, bool no_flow)
252 auto &floor = *this->player_ptr->current_floor_ptr;
253 auto &monster = floor.m_list[this->m_idx];
254 auto &monrace = monster.get_monrace();
255 if (!this->check_movable_grid(yp, xp, no_flow)) {
259 auto y1 = monster.fy;
260 auto x1 = monster.fx;
261 const Pos2D pos(y1, x1);
262 const auto &grid = floor.get_grid(pos);
263 if (grid.has_los() && projectable(this->player_ptr, this->player_ptr->y, this->player_ptr->x, y1, x1)) {
264 if ((distance(y1, x1, this->player_ptr->y, this->player_ptr->x) == 1) || (monrace.freq_spell > 0) || (grid.get_cost(&monrace) > 5)) {
269 auto use_scent = false;
270 if (grid.get_cost(&monrace)) {
272 } else if (grid.when) {
273 const auto p_pos = this->player_ptr->get_position();
274 if (floor.get_grid(p_pos).when - grid.when > 127) {
284 this->determine_when_cost(yp, xp, y1, x1, use_scent);
287 bool MonsterSweepGrid::check_movable_grid(POSITION *yp, POSITION *xp, const bool no_flow)
289 const auto &monrace = this->player_ptr->current_floor_ptr->m_list[this->m_idx].get_monrace();
290 if ((monrace.ability_flags.has_any_of(RF_ABILITY_ATTACK_MASK)) && (sweep_ranged_attack_grid(yp, xp))) {
298 if (monrace.feature_flags.has(MonsterFeatureType::PASS_WALL) && ((this->m_idx != this->player_ptr->riding) || has_pass_wall(this->player_ptr))) {
302 if (monrace.feature_flags.has(MonsterFeatureType::KILL_WALL) && (this->m_idx != this->player_ptr->riding)) {
310 * @brief モンスターがプレイヤーに向けて遠距離攻撃を行うことが可能なマスを走査する /
311 * Search spell castable grid
312 * @param yp 適したマスのY座標を返す参照ポインタ
313 * @param xp 適したマスのX座標を返す参照ポインタ
314 * @return 有効なマスがあった場合TRUEを返す
316 bool MonsterSweepGrid::sweep_ranged_attack_grid(POSITION *yp, POSITION *xp)
318 auto *floor_ptr = this->player_ptr->current_floor_ptr;
319 auto *m_ptr = &floor_ptr->m_list[this->m_idx];
320 auto *r_ptr = &m_ptr->get_monrace();
323 if (projectable(this->player_ptr, y1, x1, this->player_ptr->y, this->player_ptr->x)) {
327 auto now_cost = (int)floor_ptr->grid_array[y1][x1].get_cost(r_ptr);
332 if (r_ptr->behavior_flags.has_any_of({ MonsterBehaviorType::BASH_DOOR, MonsterBehaviorType::OPEN_DOOR })) {
333 this->can_open_door = true;
336 for (auto i = 7; i >= 0; i--) {
337 const Pos2D pos(y1 + ddy_ddd[i], x1 + ddx_ddd[i]);
338 if (!in_bounds2(floor_ptr, pos.y, pos.x)) {
342 if (this->player_ptr->is_located_at(pos)) {
346 const auto &grid = floor_ptr->get_grid(pos);
347 this->cost = grid.get_cost(r_ptr);
348 if (!this->is_best_cost(pos.y, pos.x, now_cost)) {
352 this->best = this->cost;
353 *yp = y1 + ddy_ddd[i];
354 *xp = x1 + ddx_ddd[i];
357 return this->best != 999;
360 bool MonsterSweepGrid::is_best_cost(const POSITION y, const POSITION x, const int now_cost)
362 auto *floor_ptr = this->player_ptr->current_floor_ptr;
363 const auto &monrace = floor_ptr->m_list[this->m_idx].get_monrace();
364 auto is_riding = this->m_idx == this->player_ptr->riding;
365 if ((monrace.feature_flags.has_not(MonsterFeatureType::PASS_WALL) || (is_riding && !has_pass_wall(this->player_ptr))) && (monrace.feature_flags.has_not(MonsterFeatureType::KILL_WALL) || is_riding)) {
366 if (this->cost == 0) {
370 auto *g_ptr = &floor_ptr->grid_array[y][x];
371 if (!this->can_open_door && is_closed_door(this->player_ptr, g_ptr->feat)) {
376 if (this->cost == 0) {
380 if (now_cost < this->cost) {
384 if (!projectable(this->player_ptr, y, x, this->player_ptr->y, this->player_ptr->x)) {
388 if (this->best < this->cost) {
396 * @brief モンスターがプレイヤーから逃走することが可能なマスを走査する /
397 * Provide a location to flee to, but give the player a wide berth.
398 * @param yp 移動先のマスのY座標を返す参照ポインタ
399 * @param xp 移動先のマスのX座標を返す参照ポインタ
400 * @return 有効なマスがあった場合TRUEを返す
402 bool MonsterSweepGrid::sweep_runnable_away_grid(POSITION *yp, POSITION *xp)
406 auto *floor_ptr = this->player_ptr->current_floor_ptr;
407 auto *m_ptr = &floor_ptr->m_list[this->m_idx];
408 auto *r_ptr = &m_ptr->get_monrace();
414 for (auto i = 7; i >= 0; i--) {
415 auto y = fy + ddy_ddd[i];
416 auto x = fx + ddx_ddd[i];
417 if (!in_bounds2(floor_ptr, y, x)) {
421 auto dis = distance(y, x, y1, x1);
422 auto s = 5000 / (dis + 3) - 500 / (floor_ptr->grid_array[y][x].get_distance(r_ptr) + 1);
445 void MonsterSweepGrid::determine_when_cost(POSITION *yp, POSITION *xp, POSITION y1, POSITION x1, const bool use_scent)
447 auto *floor_ptr = this->player_ptr->current_floor_ptr;
448 for (auto i = 7; i >= 0; i--) {
449 auto y = y1 + ddy_ddd[i];
450 auto x = x1 + ddx_ddd[i];
451 if (!in_bounds2(floor_ptr, y, x)) {
455 auto *g_ptr = &floor_ptr->grid_array[y][x];
457 int when = g_ptr->when;
458 if (this->best > when) {
464 const auto &monrace = floor_ptr->m_list[this->m_idx].get_monrace();
465 this->cost = monrace.behavior_flags.has_any_of({ MonsterBehaviorType::BASH_DOOR, MonsterBehaviorType::OPEN_DOOR }) ? g_ptr->get_distance(&monrace) : g_ptr->get_cost(&monrace);
466 if ((this->cost == 0) || (this->best < this->cost)) {
470 this->best = this->cost;
473 *yp = this->player_ptr->y + 16 * ddy_ddd[i];
474 *xp = this->player_ptr->x + 16 * ddx_ddd[i];