1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use crate::{
    component::{NavAgent, NavAgentTarget, SimpleNavDriverTag},
    resource::{NavMesh, NavMeshesRes, NavVec3},
    Scalar,
};
use core::{
    app::AppLifeCycle,
    ecs::{Entities, Entity, Join, Read, ReadExpect, ReadStorage, System, WriteStorage},
};
use std::collections::HashMap;

/// nav agents maintainment system. It's used to find path on nav mesh for agents.
#[derive(Default)]
pub struct NavAgentMaintainSystem(HashMap<Entity, Vec<NavVec3>>);

impl NavAgentMaintainSystem {
    pub fn with_cache_capacity(capacity: usize) -> Self {
        Self(HashMap::with_capacity(capacity))
    }
}

impl<'s> System<'s> for NavAgentMaintainSystem {
    type SystemData = (
        Entities<'s>,
        Read<'s, NavMeshesRes>,
        WriteStorage<'s, NavAgent>,
    );

    fn run(&mut self, (entities, meshes_res, mut agents): Self::SystemData) {
        self.0.clear();
        for (entity, agent) in (&entities, &agents).join() {
            if agent.dirty_path {
                if let Some(destination) = &agent.destination {
                    if let Some(mesh) = meshes_res.0.get(&destination.mesh) {
                        match destination.target {
                            NavAgentTarget::Point(point) => {
                                if let Some(path) = mesh.find_path(
                                    agent.position,
                                    point,
                                    destination.query,
                                    destination.mode,
                                ) {
                                    self.0.insert(entity, path);
                                }
                            }
                            NavAgentTarget::Entity(entity) => {
                                if let Some(other) = agents.get(entity) {
                                    if let Some(path) = mesh.find_path(
                                        agent.position,
                                        other.position,
                                        destination.query,
                                        destination.mode,
                                    ) {
                                        self.0.insert(entity, path);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        for (entity, path) in self.0.drain() {
            if let Some(agent) = agents.get_mut(entity) {
                agent.set_path(path);
            }
        }
    }
}

/// Simple nav driver system. It's used to apply simple movement of agents with `SimpleNavDriverTag`
/// component tag on their paths.
pub struct SimpleNavDriverSystem;

impl SimpleNavDriverSystem {
    /// Internal system run function.
    pub fn run_impl<'s>(
        delta_time: Scalar,
        (mut agents, drivers): (
            WriteStorage<'s, NavAgent>,
            ReadStorage<'s, SimpleNavDriverTag>,
        ),
    ) {
        if delta_time <= 0.0 {
            return;
        }
        for (agent, _) in (&mut agents, &drivers).join() {
            if let Some(path) = agent.path() {
                if let Some((target, _)) = NavMesh::path_target_point(
                    path,
                    agent.position,
                    agent.speed.max(agent.min_target_distance.max(0.0)) * delta_time,
                ) {
                    let diff = target - agent.position;
                    let dir = diff.normalize();
                    agent.position = agent.position
                        + dir * (agent.speed.max(0.0) * delta_time).min(diff.magnitude());
                    agent.direction = diff.normalize();
                }
            }
        }
    }
}

impl<'s> System<'s> for SimpleNavDriverSystem {
    type SystemData = (
        ReadExpect<'s, AppLifeCycle>,
        WriteStorage<'s, NavAgent>,
        ReadStorage<'s, SimpleNavDriverTag>,
    );

    fn run(&mut self, (lifecycle, agents, drivers): Self::SystemData) {
        let delta_time = lifecycle.delta_time_seconds();
        Self::run_impl(delta_time, (agents, drivers));
    }
}