use crate::ecs::components::*;
use crate::coordinates::{Distance, Neighbors};
use crate::pathfind::astar;
use std::collections::HashMap;
pub struct MovementSystem;
impl MovementSystem {
pub fn process_movement<C>(
world: &mut hecs::World,
movement_requests: &HashMap<hecs::Entity, C>,
) -> Vec<MovementResult<C>>
where
C: Distance + Neighbors + Clone + PartialEq + Eq + std::hash::Hash + Send + Sync + 'static,
{
let mut results = Vec::new();
for (entity, target) in movement_requests {
if let Ok((pos, movable)) = world.query_one_mut::<(&mut Position<C>, &Movable)>(*entity) {
let movement_result = Self::calculate_movement(&pos.coord, target, movable);
match movement_result
{
MovementResult::Success { path, new_position } =>
{
pos.coord = new_position.clone();
results.push(MovementResult::Success { path, new_position });
}
other => results.push(other),
}
}
}
results
}
fn calculate_movement<C>(
current: &C,
target: &C,
movable: &Movable,
) -> MovementResult<C>
where
C: Distance + Neighbors + Clone + PartialEq + Eq + std::hash::Hash,
{
let distance = current.distance(target);
if distance > movable.range {
return MovementResult::OutOfRange {
requested_distance: distance,
maximum_range: movable.range,
};
}
let path_result = astar(
current,
target,
|_coord| true, |_coord| 1, );
match path_result
{
Some((path, cost)) =>
{
if cost <= movable.range {
MovementResult::Success {
path: path.clone(),
new_position: target.clone(),
}
} else {
MovementResult::PathTooLong {
path_length: cost,
maximum_range: movable.range,
}
}
}
None => MovementResult::NoPathFound,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum MovementResult<C> {
Success {
path: Vec<C>,
new_position: C,
},
OutOfRange {
requested_distance: u32,
maximum_range: u32,
},
PathTooLong {
path_length: u32,
maximum_range: u32,
},
NoPathFound,
}
pub struct CombatSystem;
impl CombatSystem {
pub fn process_combat(world: &mut hecs::World) -> Vec<CombatEvent> {
let mut combat_events = Vec::new();
for (entity, health) in world.query::<&Health>().iter() {
if !health.is_alive() {
combat_events.push(CombatEvent::Defeated { entity });
}
}
combat_events
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CombatEvent {
Damage {
attacker: hecs::Entity,
target: hecs::Entity,
damage: u32,
},
Defeated {
entity: hecs::Entity,
},
}
pub struct AISystem;
impl AISystem {
pub fn update_ai(world: &mut hecs::World, dt: f32) {
for (_entity, ai) in world.query_mut::<&mut AI>() {
ai.update(dt);
if ai.should_make_decision() {
ai.reset_decision_timer();
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum AIAction<C> {
StartPursuit {
entity: hecs::Entity,
target: hecs::Entity,
target_position: C,
},
StartPatrol {
entity: hecs::Entity,
},
MoveToward {
entity: hecs::Entity,
target_position: C,
},
Attack {
entity: hecs::Entity,
target: hecs::Entity,
},
}
pub struct AnimationSystem;
impl AnimationSystem {
pub fn update_animations(world: &mut hecs::World, dt: f32) {
for (_entity, animation) in world.query_mut::<&mut Animation>() {
animation.update(dt);
}
}
}
pub struct CleanupSystem;
impl CleanupSystem {
pub fn cleanup_defeated_entities(world: &mut hecs::World) -> Vec<hecs::Entity> {
let mut entities_to_remove = Vec::new();
for (entity, health) in world.query::<&Health>().iter() {
if !health.is_alive() {
entities_to_remove.push(entity);
}
}
for entity in &entities_to_remove {
if world.despawn(*entity).is_ok() {
}
}
entities_to_remove
}
}
pub fn find_entities_in_range<C>(
world: &hecs::World,
center: &Position<C>,
range: u32,
) -> Vec<(hecs::Entity, Position<C>)>
where
C: Distance + Clone + Send + Sync + 'static,
{
let mut entities = Vec::new();
for (entity, pos) in world.query::<&Position<C>>().iter() {
if center.distance_to(pos) <= range {
entities.push((entity, pos.clone()));
}
}
entities
}
pub fn find_nearest_entity<C>(
world: &hecs::World,
center: &Position<C>,
) -> Option<(hecs::Entity, Position<C>, u32)>
where
C: Distance + Clone + Send + Sync + 'static,
{
let mut nearest = None;
let mut nearest_distance = u32::MAX;
for (entity, pos) in world.query::<&Position<C>>().iter() {
let distance = center.distance_to(pos);
if distance < nearest_distance {
nearest_distance = distance;
nearest = Some((entity, pos.clone(), distance));
}
}
nearest
}
pub struct CollisionSystem;
impl CollisionSystem {
pub fn detect_collisions<C>(
world: &hecs::World,
) -> Vec<CollisionEvent<C>>
where
C: Distance + Clone + PartialEq + Send + Sync + 'static,
{
let mut collisions = Vec::new();
let mut query = world.query::<(&Position<C>, &Collision)>();
let entities_with_collision: Vec<_> = query.iter().collect();
for i in 0..entities_with_collision.len() {
for j in (i + 1)..entities_with_collision.len() {
let (entity1, (pos1, collision1)) = entities_with_collision[i];
let (entity2, (pos2, collision2)) = entities_with_collision[j];
if Self::check_collision(pos1, collision1, pos2, collision2) {
collisions.push(CollisionEvent {
entity1,
entity2,
position1: pos1.clone(),
position2: pos2.clone(),
});
}
}
}
collisions
}
fn check_collision<C>(
pos1: &Position<C>,
collision1: &Collision,
pos2: &Position<C>,
collision2: &Collision,
) -> bool
where
C: Distance,
{
let distance = pos1.distance_to(pos2);
let collision_distance = collision1.radius + collision2.radius;
distance <= collision_distance
}
pub fn resolve_collisions<C>(
world: &mut hecs::World,
collisions: &[CollisionEvent<C>],
)
where
C: Distance + Neighbors + Clone + Send + Sync + 'static,
{
for collision in collisions {
if let Ok(pos1) = world.query_one_mut::<&mut Position<C>>(collision.entity1) {
let neighbors1 = pos1.coord.neighbors();
if let Some(best_pos1) = neighbors1.iter()
.max_by_key(|neighbor| collision.position2.coord.distance(neighbor))
{
pos1.coord = best_pos1.clone();
}
}
if let Ok(pos2) = world.query_one_mut::<&mut Position<C>>(collision.entity2) {
let neighbors2 = pos2.coord.neighbors();
if let Some(best_pos2) = neighbors2.iter()
.max_by_key(|neighbor| collision.position1.coord.distance(neighbor))
{
pos2.coord = best_pos2.clone();
}
}
}
}
}
#[derive(Debug, Clone)]
pub struct CollisionEvent<C> {
pub entity1: hecs::Entity,
pub entity2: hecs::Entity,
pub position1: Position<C>,
pub position2: Position<C>,
}
#[derive(Debug, Clone)]
pub struct Collision {
pub radius: u32,
pub solid: bool,
pub layer: u32,
}
impl Collision {
pub fn new(radius: u32) -> Self {
Self {
radius,
solid: true,
layer: 0,
}
}
pub fn non_solid(mut self) -> Self {
self.solid = false;
self
}
pub fn with_layer(mut self, layer: u32) -> Self {
self.layer = layer;
self
}
}
pub struct SpatialQuerySystem;
impl SpatialQuerySystem {
pub fn query_circle<C>(
world: &hecs::World,
center: &Position<C>,
radius: u32,
) -> Vec<(hecs::Entity, Position<C>)>
where
C: Distance + Clone + Send + Sync + 'static,
{
find_entities_in_range(world, center, radius)
}
pub fn query_line<C>(
world: &hecs::World,
start: &Position<C>,
end: &Position<C>,
) -> Vec<(hecs::Entity, Position<C>)>
where
C: Distance + Neighbors + Clone + PartialEq + std::hash::Hash + Send + Sync + 'static,
{
let mut entities = Vec::new();
let line_positions = Self::trace_line(&start.coord, &end.coord);
for line_pos in line_positions {
for (entity, pos) in world.query::<&Position<C>>().iter() {
if pos.coord == line_pos {
entities.push((entity, pos.clone()));
}
}
}
entities
}
pub fn query_rectangle<C>(
world: &hecs::World,
center: &Position<C>,
width: u32,
height: u32,
) -> Vec<(hecs::Entity, Position<C>)>
where
C: Distance + Clone + Send + Sync + 'static,
{
let mut entities = Vec::new();
let max_distance = ((width * width + height * height) as f32).sqrt() as u32;
for (entity, pos) in world.query::<&Position<C>>().iter() {
let distance = center.distance_to(pos);
if distance <= max_distance {
entities.push((entity, pos.clone()));
}
}
entities
}
pub fn query_by_team<C>(
world: &hecs::World,
center: &Position<C>,
radius: u32,
team_filter: impl Fn(&Team) -> bool,
) -> Vec<(hecs::Entity, Position<C>, Team)>
where
C: Distance + Clone + Send + Sync + 'static,
{
let mut entities = Vec::new();
for (entity, (pos, team)) in world.query::<(&Position<C>, &Team)>().iter() {
if center.distance_to(pos) <= radius && team_filter(team) {
entities.push((entity, pos.clone(), team.clone()));
}
}
entities
}
fn trace_line<C>(start: &C, end: &C) -> Vec<C>
where
C: Distance + Neighbors + Clone + PartialEq,
{
let mut line_positions = Vec::new();
let mut current = start.clone();
line_positions.push(current.clone());
while current != *end && line_positions.len() < 100 {
let neighbors = current.neighbors();
if let Some(next) = neighbors.iter()
.min_by_key(|neighbor| neighbor.distance(end))
{
if next == ¤t {
break; }
current = next.clone();
line_positions.push(current.clone());
} else {
break;
}
}
line_positions
}
}