This commit is contained in:
Joshua Barretto 2020-01-25 19:03:25 +00:00
parent feeccc2ff3
commit b22ee24362
4 changed files with 4 additions and 236 deletions

View File

@ -34,90 +34,6 @@ impl<S: Eq> PartialOrd for PathEntry<S> {
}
}
fn reconstruct_path<S>(came_from: &HashMap<S, S>, target: &S) -> Vec<S>
where
S: Clone + Eq + Hash,
{
let mut path = Vec::new();
path.push(target.to_owned());
let mut cur_node = target;
while let Some(node) = came_from.get(cur_node) {
path.push(node.to_owned());
cur_node = node;
}
path
}
pub fn astar<S, I>(
initial: S,
target: S,
mut heuristic: impl FnMut(&S, &S) -> f32,
mut neighbors: impl FnMut(&S) -> I,
mut transition_cost: impl FnMut(&S, &S) -> f32,
) -> Option<Vec<S>>
where
S: Clone + Eq + Hash,
I: IntoIterator<Item = S>,
{
// Set of discovered nodes so far
let mut potential_nodes = BinaryHeap::new();
potential_nodes.push(PathEntry {
cost: 0.0f32,
node: initial.clone(),
});
// For entry e, contains the cheapest node preceding it on the known path from start to e
let mut came_from = HashMap::new();
// Contains cheapest cost from 'initial' to the current entry
let mut cheapest_scores = HashMap::new();
cheapest_scores.insert(initial.clone(), 0.0f32);
// Contains cheapest score to get to node + heuristic to the end, for an entry
let mut final_scores = HashMap::new();
final_scores.insert(initial.clone(), heuristic(&initial, &target));
// Set of nodes we have already visited
let mut visited = HashSet::new();
visited.insert(initial.clone());
let mut iters = 0;
while let Some(PathEntry { node: current, .. }) = potential_nodes.pop() {
if current == target {
return Some(reconstruct_path(&came_from, &current));
}
let current_neighbors = neighbors(&current);
for neighbor in current_neighbors {
let current_cheapest_score = cheapest_scores.get(&current).unwrap_or(&f32::MAX);
let neighbor_cheapest_score = cheapest_scores.get(&neighbor).unwrap_or(&f32::MAX);
let score = current_cheapest_score + transition_cost(&current, &neighbor);
if score < *neighbor_cheapest_score {
// Path to the neighbor is better than anything yet recorded
came_from.insert(neighbor.to_owned(), current.to_owned());
cheapest_scores.insert(neighbor.clone(), score);
let neighbor_score = score + heuristic(&neighbor, &target);
final_scores.insert(neighbor.clone(), neighbor_score);
if visited.insert(neighbor.clone()) {
potential_nodes.push(PathEntry {
node: neighbor.clone(),
cost: neighbor_score,
});
}
}
}
iters += 1;
if iters >= 10000 {
println!("Ran out of turns!");
break;
}
}
None
}
pub enum PathResult<T> {
None(Path<T>),
Exhausted(Path<T>),

View File

@ -94,9 +94,8 @@ impl<'a> System<'a> for Sys {
};
}
if let Some(wander_pos) = wander_pos {
if let Some(bearing) = chaser.chase(&*terrain, pos.0, *wander_pos, 2.0)
{
if let Some(wp) = wander_pos {
if let Some(bearing) = chaser.chase(&*terrain, pos.0, *wp, 2.0) {
inputs.move_dir =
Vec2::from(bearing).try_normalized().unwrap_or(Vec2::zero());
inputs.jump.set_state(bearing.z > 1.0);
@ -227,153 +226,6 @@ impl<'a> System<'a> for Sys {
}
}
/*
const PET_DIST: f32 = 6.0;
const MAX_PET_DIST: f32 = 16.0;
const PATROL_DIST: f32 = 32.0;
const SIGHT_DIST: f32 = 24.0;
const MIN_ATTACK_DIST: f32 = 3.25;
const CHASE_TIME_MIN: f64 = 4.0;
let mut chase_tgt = None;
let mut choose_target = false;
let mut new_target = None;
if let Some((target, aggro_time)) = agent.target {
// Chase / attack target
if let (Some(tgt_pos), stats) = (positions.get(target), stats.get(target)) {
if stats.map(|s| s.is_dead).unwrap_or(false) {
// Don't target dead entities
choose_target = true;
} else if pos.0.distance(tgt_pos.0) < SIGHT_DIST
|| (time.0 - aggro_time) < CHASE_TIME_MIN
{
chase_tgt = Some((tgt_pos.0, 1.5, true))
} else {
// Lose sight of enemies
choose_target = true;
}
} else {
choose_target = true;
}
} else {
choose_target = thread_rng().gen::<f32>() < 0.05;
}
// Return to owner
if let Some(owner) = agent.owner {
if let Some(tgt_pos) = positions.get(owner) {
let dist = pos.0.distance(tgt_pos.0);
if dist > MAX_PET_DIST || (dist > PET_DIST && agent.target.is_none()) {
// Follow owner
chase_tgt = Some((tgt_pos.0, 6.0, false));
} else if agent.target.is_none() {
choose_target = thread_rng().gen::<f32>() < 0.02;
}
} else {
agent.owner = None;
}
} else if let Some(patrol_origin) = agent.patrol_origin {
if pos.0.distance(patrol_origin) > PATROL_DIST {
// Return to patrol origin
chase_tgt = Some((patrol_origin, 64.0, false));
}
}
// Attack a target that's attacking us
if let Some(stats) = stats.get(entity) {
match stats.health.last_change.1.cause {
comp::HealthSource::Attack { by } => {
if agent.target.is_none() {
new_target = uid_allocator.retrieve_entity_internal(by.id());
} else if thread_rng().gen::<f32>() < 0.005 {
new_target = uid_allocator.retrieve_entity_internal(by.id());
}
}
_ => {}
}
}
// Choose a new target
if choose_target {
// Search for new targets
let entities = (&entities, &positions, &stats, alignments.maybe())
.join()
.filter(|(e, e_pos, e_stats, e_alignment)| {
(e_pos.0 - pos.0).magnitude() < SIGHT_DIST
&& *e != entity
&& !e_stats.is_dead
&& alignment
.and_then(|a| e_alignment.map(|b| a.hostile_towards(*b)))
.unwrap_or(false)
})
.map(|(e, _, _, _)| e)
.collect::<Vec<_>>();
new_target = (&entities).choose(&mut thread_rng()).cloned();
}
// Update target when attack begins
match agent.target {
Some((tgt, time)) if Some(tgt) == new_target => {},
_ => agent.target = new_target.map(|tgt| (tgt, time.0))
}
// Chase target
if let Some((tgt_pos, min_dist, aggressive)) = chase_tgt {
if let Some(bearing) = agent.chaser.chase(&*terrain, pos.0, tgt_pos, min_dist) {
inputs.move_dir = Vec2::from(bearing).try_normalized().unwrap_or(Vec2::zero());
inputs.jump.set_state(bearing.z > 1.0);
}
if aggressive && pos.0.distance(tgt_pos) < MIN_ATTACK_DIST {
inputs.look_dir = tgt_pos - pos.0;
inputs.move_dir = Vec2::from(tgt_pos - pos.0)
.try_normalized()
.unwrap_or(Vec2::zero())
* 0.01;
inputs.primary.set_state(true);
}
// We're not wandering
agent.wander_pos = None;
} else {
if let Some(wander_pos) = agent.wander_pos {
if pos.0.distance(wander_pos) < 4.0 {
agent.wander_pos = None;
} else {
if let Some(bearing) = agent.chaser.chase(&*terrain, pos.0, wander_pos, 3.0)
{
inputs.move_dir =
Vec2::from(bearing).try_normalized().unwrap_or(Vec2::zero()) * 0.5;
inputs.jump.set_state(bearing.z > 1.0);
}
}
}
// Choose new wander position
/*
if agent.wander_pos.is_none() || thread_rng().gen::<f32>() < 0.005 {
agent.wander_pos = if thread_rng().gen::<f32>() < 0.5 {
let max_dist = if agent.owner.is_some() {
PET_DIST
} else {
PATROL_DIST
};
Some(
agent
.patrol_origin
.unwrap_or(pos.0)
.map(|e| e + (thread_rng().gen::<f32>() - 0.5) * max_dist),
)
} else {
None
};
}
*/
}
*/
debug_assert!(inputs.move_dir.map(|e| !e.is_nan()).reduce_and());
debug_assert!(inputs.look_dir.map(|e| !e.is_nan()).reduce_and());
}

View File

@ -11,7 +11,7 @@ use common::{
npc::{get_npc_name, NpcKind},
state::TimeOfDay,
sync::{Uid, WorldSyncExt},
terrain::{Block, BlockKind, TerrainChunkSize},
terrain::TerrainChunkSize,
vol::RectVolSize,
};
use rand::Rng;

View File

@ -1,5 +1,5 @@
use common::comp::{Player, Pos, Waypoint, WaypointArea};
use specs::{Entities, Join, Read, ReadStorage, System, Write, WriteStorage};
use specs::{Entities, Join, ReadStorage, System, WriteStorage};
/// This system will handle loading generated chunks and unloading uneeded chunks.
/// 1. Inserts newly generated chunks into the TerrainGrid