/* * Choose the best direction for "flowing". * * Note that ghosts and rock-eaters generally don't flow because they can move * through obstacles. * * Monsters first try to use up-to-date distance information ('sound') as * saved in cave->squares[y][x].cost. Failing that, they'll try using scent * ('when') which is just old cost information. * * Tracking by 'scent' means that monsters end up near enough the player to * switch to 'sound' (cost), or they end up somewhere the player left via * teleport. Teleporting away from a location will cause the monsters who * were chasing the player to converge on that location as long as the player * is still near enough to "annoy" them without being close enough to chase * directly. */ static bool get_moves_flow(struct chunk *c, struct monster *m_ptr) { int i; int best_when = 0; int best_cost = 999; int best_direction = 0; int py = player->py, px = player->px; int my = m_ptr->fy, mx = m_ptr->fx; /* Only use this algorithm for passwall monsters if near permanent walls, * to avoid getting snagged */ if (flags_test(m_ptr->race->flags, RF_SIZE, RF_PASS_WALL, RF_KILL_WALL, FLAG_END) && !near_permwall(m_ptr, c)) return (FALSE); /* If the player has never been near this grid, abort */ if (c->squares[my][mx].when == 0) return FALSE; /* Monster is too far away to notice the player */ if (c->squares[my][mx].cost > z_info->max_flow_depth) return FALSE; if (c->squares[my][mx].cost > m_ptr->race->aaf) return FALSE; /* If the player can see monster, run towards them */ if (player_has_los_bold(my, mx)) return FALSE; /* Check nearby grids, diagonals first */ /* This gives preference to the cardinal directions */ for (i = 7; i >= 0; i--) { /* Get the location */ int y = my + ddy_ddd[i]; int x = mx + ddx_ddd[i]; /* Ignore unvisited/unpassable locations */ if (c->squares[y][x].when == 0) continue; /* Ignore locations whose data is more stale */ if (c->squares[y][x].when < best_when) continue; /* Ignore locations which are farther away */ if (c->squares[y][x].cost > best_cost) continue; /* Save the cost and time */ best_when = c->squares[y][x].when; best_cost = c->squares[y][x].cost; best_direction = i; } /* Save the location to flow toward */ /* We multiply by 16 to angle slightly toward the player's actual location */ if (best_direction) { m_ptr->ty = py + 16 * ddy_ddd[best_direction]; m_ptr->tx = px + 16 * ddx_ddd[best_direction]; return TRUE; } return FALSE; }
/** * Choose the best direction for "flowing". * * Note that ghosts and rock-eaters generally don't flow because they can move * through obstacles. * * Monsters first try to use up-to-date distance information ('sound') as * saved in cave->squares[y][x].noise. Failing that, they'll try using scent * ('scent') which is just old noise information. * * Tracking by 'scent' means that monsters end up near enough the player to * switch to 'sound' (noise), or they end up somewhere the player left via * teleport. Teleporting away from a location will cause the monsters who * were chasing the player to converge on that location as long as the player * is still near enough to "annoy" them without being close enough to chase * directly. */ static bool get_moves_flow(struct chunk *c, struct monster *mon) { int i; int best_scent = 0; int best_noise = 999; int best_direction = 0; bool found_direction = false; int py = player->py, px = player->px; int my = mon->fy, mx = mon->fx; /* Only use this algorithm for passwall monsters if near permanent walls, * to avoid getting snagged */ if (flags_test(mon->race->flags, RF_SIZE, RF_PASS_WALL, RF_KILL_WALL, FLAG_END) && !near_permwall(mon, c)) return (false); /* The player is not currently near the monster grid */ if (c->squares[my][mx].scent < c->squares[py][px].scent) /* If the player has never been near this grid, abort */ if (c->squares[my][mx].scent == 0) return false; /* Monster is too far away to notice the player */ if (c->squares[my][mx].noise > z_info->max_flow_depth) return false; if (c->squares[my][mx].noise > mon->race->aaf) return false; /* If the player can see monster, set target and run towards them */ if (square_isview(c, my, mx)) { mon->ty = player->py; mon->tx = player->px; return false; } /* Check nearby grids, diagonals first */ /* This gives preference to the cardinal directions */ for (i = 7; i >= 0; i--) { /* Get the location */ int y = my + ddy_ddd[i]; int x = mx + ddx_ddd[i]; /* Bounds check */ if (!square_in_bounds(c, y, x)) continue; /* Ignore unvisited/unpassable locations */ if (c->squares[y][x].scent == 0) continue; /* Ignore locations whose data is more stale */ if (c->squares[y][x].scent < best_scent) continue; /* Ignore locations which are farther away */ if (c->squares[y][x].noise > best_noise) continue; /* Ignore lava if they can't handle the heat */ if (square_isfiery(c, y, x) && !rf_has(mon->race->flags, RF_IM_FIRE)) continue; /* Save the noise and time */ best_scent = c->squares[y][x].scent; best_noise = c->squares[y][x].noise; best_direction = i; found_direction = true; } /* Save the location to flow toward */ /* Multiply by 16 to angle slightly toward the player's actual location */ if (found_direction) { int dy = 0, dx = 0; /* Ridiculous - actually multiply by whatever doesn't underflow the * byte for ty and tx. Really should do a better solution - NRM */ for (i = 0; i < 16; i++) if ((py + dy > 0) && (px + dx > 0)) { dy += ddy_ddd[best_direction]; dx += ddx_ddd[best_direction]; } mon->ty = py + dy; mon->tx = px + dx; return true; } return false; }