use std::{
collections::VecDeque,
sync::{Arc, RwLock},
};
use bevy::{
prelude::*,
tasks::{AsyncComputeTaskPool, Task},
};
use crate::flowfields::{
dimensions::Dimensions,
fields::{FieldCell, flow_field::FlowField},
flowfield_cache::FlowFieldCache,
portal::Portals,
route::RouteStep,
sectors::{
SectorID,
sector_cost::{CostFieldUpdateItem, SectorCostFields},
},
};
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Component)]
pub struct FlowFieldTiles {
pub dimensions: Dimensions,
pub sector_cost_fields: Arc<RwLock<SectorCostFields>>,
pub portals: Arc<RwLock<Portals>>,
pub costfield_update_queue: VecDeque<CostFieldUpdateItem>,
#[cfg_attr(feature = "serde", serde(skip))]
pub costfield_update_task: Option<Task<SectorID>>,
#[cfg_attr(feature = "serde", serde(skip))]
pub portal_update_task: Option<Task<SectorID>>,
pub flow_queue: Arc<RwLock<VecDeque<Vec<RouteStep>>>>,
#[cfg_attr(feature = "serde", serde(skip))]
pub flow_gen_task: Option<Task<Vec<(RouteStep, FlowField)>>>,
pub flowfield_cache: FlowFieldCache,
}
impl FlowFieldTiles {
pub fn get_dimensions(&self) -> &Dimensions {
&self.dimensions
}
pub fn get_sector_cost_fields(&self) -> &Arc<RwLock<SectorCostFields>> {
&self.sector_cost_fields
}
pub fn get_sector_cost_fields_mut(&mut self) -> &mut Arc<RwLock<SectorCostFields>> {
&mut self.sector_cost_fields
}
pub fn get_portals(&self) -> &Arc<RwLock<Portals>> {
&self.portals
}
pub fn new(
origin: (f32, f32),
size: (f32, f32),
world_unit_size: f32,
actor_radius: f32,
) -> Self {
let dimensions = Dimensions::new(origin, size, world_unit_size, actor_radius);
let costfields = Arc::new(RwLock::new(SectorCostFields::new(&dimensions)));
let c = costfields.read().unwrap();
let portals = Arc::new(RwLock::new(Portals::new(&c)));
drop(c);
FlowFieldTiles {
dimensions,
sector_cost_fields: costfields,
portals,
costfield_update_queue: VecDeque::new(),
costfield_update_task: None,
portal_update_task: None,
flow_queue: Arc::new(RwLock::new(VecDeque::new())),
flow_gen_task: None,
flowfield_cache: FlowFieldCache::default(),
}
}
pub fn new_with_cost(
origin: (f32, f32),
size: (f32, f32),
world_unit_size: f32,
actor_radius: f32,
cost: u8,
) -> Self {
let dimensions = Dimensions::new(origin, size, world_unit_size, actor_radius);
let costfields = Arc::new(RwLock::new(SectorCostFields::new_with_cost(
&dimensions,
cost,
)));
let c = costfields.read().unwrap();
let portals = Arc::new(RwLock::new(Portals::new(&c)));
drop(c);
FlowFieldTiles {
dimensions,
sector_cost_fields: costfields,
portals,
costfield_update_queue: VecDeque::new(),
costfield_update_task: None,
portal_update_task: None,
flow_queue: Arc::new(RwLock::new(VecDeque::new())),
flow_gen_task: None,
flowfield_cache: FlowFieldCache::default(),
}
}
#[cfg(feature = "ron")]
pub fn from_ron(
origin: (f32, f32),
size: (f32, f32),
world_unit_size: f32,
actor_radius: f32,
file_path: &str,
) -> Self {
let dimensions = Dimensions::new(origin, size, world_unit_size, actor_radius);
let costfields = Arc::new(RwLock::new(SectorCostFields::from_ron(
file_path.into(),
&dimensions,
)));
let c = costfields.read().unwrap();
let portals = Arc::new(RwLock::new(Portals::new(&c)));
drop(c);
FlowFieldTiles {
dimensions,
sector_cost_fields: costfields,
portals,
costfield_update_queue: VecDeque::new(),
costfield_update_task: None,
portal_update_task: None,
flow_queue: Arc::new(RwLock::new(VecDeque::new())),
flow_gen_task: None,
flowfield_cache: FlowFieldCache::default(),
}
}
#[cfg(feature = "heightmap")]
pub fn from_heightmap(
origin: (f32, f32),
size: (f32, f32),
world_unit_size: f32,
actor_radius: f32,
file_path: &str,
) -> Self {
let dimensions = Dimensions::new(origin, size, world_unit_size, actor_radius);
let costfields = Arc::new(RwLock::new(SectorCostFields::from_heightmap(
&dimensions,
file_path.into(),
)));
let c = costfields.read().unwrap();
let portals = Arc::new(RwLock::new(Portals::new(&c)));
drop(c);
FlowFieldTiles {
dimensions,
sector_cost_fields: costfields,
portals,
costfield_update_queue: VecDeque::new(),
costfield_update_task: None,
portal_update_task: None,
flow_queue: Arc::new(RwLock::new(VecDeque::new())),
flow_gen_task: None,
flowfield_cache: FlowFieldCache::default(),
}
}
#[cfg(feature = "2d")]
pub fn add_costfield_update_2d(&mut self, position: Vec2, cost: u8) {
if let Some((sector, cell)) = self.dimensions.get_sector_and_field_cell_from_xy(position) {
let item = CostFieldUpdateItem::new(§or, &cell, cost);
self.costfield_update_queue.push_back(item);
}
}
#[cfg(feature = "3d")]
pub fn add_costfield_update_3d(&mut self, position: Vec3, cost: u8) {
if let Some((sector, cell)) = self.dimensions.get_sector_and_field_cell_from_xyz(position) {
let item = CostFieldUpdateItem::new(§or, &cell, cost);
self.costfield_update_queue.push_back(item);
}
}
#[cfg(feature = "2d")]
pub fn get_route_2d(&self, from: Vec2, to: Vec2) -> Option<Task<Option<Vec<RouteStep>>>> {
let (source_sector, source_cell) =
self.dimensions.get_sector_and_field_cell_from_xy(from)?;
let (goal_sector, goal_cell) = self.dimensions.get_sector_and_field_cell_from_xy(to)?;
self.get_route(source_sector, source_cell, goal_sector, goal_cell)
}
#[cfg(feature = "3d")]
pub fn get_route_3d(&self, from: Vec3, to: Vec3) -> Option<Task<Option<Vec<RouteStep>>>> {
let (source_sector, source_cell) =
self.dimensions.get_sector_and_field_cell_from_xyz(from)?;
let (goal_sector, goal_cell) = self.dimensions.get_sector_and_field_cell_from_xyz(to)?;
self.get_route(source_sector, source_cell, goal_sector, goal_cell)
}
fn get_route(
&self,
source_sector: SectorID,
source_cell: FieldCell,
goal_sector: SectorID,
goal_cell: FieldCell,
) -> Option<Task<Option<Vec<RouteStep>>>> {
let costfields = self.sector_cost_fields.clone();
let portals = self.portals.clone();
let queue = self.flow_queue.clone();
let thread_pool = AsyncComputeTaskPool::get();
let task = thread_pool.spawn(async move {
let read_costfields = costfields.read().unwrap();
let read_portals = portals.read().unwrap();
let path = read_portals.find_path(
&source_sector,
&source_cell,
&goal_sector,
&goal_cell,
&read_costfields,
);
if let Some(p) = path {
let mut write_queue = queue.write().unwrap();
write_queue.push_back(p.clone());
Some(p)
} else {
None
}
});
Some(task)
}
pub fn read_flowfield(&self, route_step: &RouteStep) -> Option<&FlowField> {
self.flowfield_cache.get(route_step)
}
pub fn flowfield_cache(&self) -> &FlowFieldCache {
&self.flowfield_cache
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new() {
let origin = (0.0, 0.0);
let size = (1920.0, 1920.0);
let world_unit_size = 64.0;
let actor_radius = 16.0;
let f = FlowFieldTiles::new(origin, size, world_unit_size, actor_radius);
assert_eq!(3, f.get_dimensions().get_sector_column_count());
}
#[test]
fn new_cost() {
let origin = (0.0, 0.0);
let size = (1920.0, 1920.0);
let world_unit_size = 64.0;
let actor_radius = 16.0;
let cost = 3;
let f = FlowFieldTiles::new_with_cost(origin, size, world_unit_size, actor_radius, cost);
assert_eq!(3, f.get_dimensions().get_sector_column_count());
}
#[test]
fn new_ron() {
let path = env!("CARGO_MANIFEST_DIR").to_string()
+ "/assets/sector_costfields_continuous_layout.ron";
let origin = (0.0, 0.0);
let size = (1920.0, 1920.0);
let world_unit_size = 64.0;
let actor_radius = 16.0;
let f = FlowFieldTiles::from_ron(origin, size, world_unit_size, actor_radius, &path);
assert_eq!(3, f.get_dimensions().get_sector_column_count());
}
#[test]
fn new_heightmap() {
let path = env!("CARGO_MANIFEST_DIR").to_string() + "/assets/heightmap.png";
let origin = (0.0, 0.0);
let size = (1920.0, 1920.0);
let world_unit_size = 64.0;
let actor_radius = 16.0;
let f = FlowFieldTiles::from_heightmap(origin, size, world_unit_size, actor_radius, &path);
assert_eq!(3, f.get_dimensions().get_sector_column_count());
}
}