use bevy::{
prelude::*,
tasks::{AsyncComputeTaskPool, futures::check_ready},
};
use crate::{
bundle::FlowFieldTiles,
flowfields::fields::{flow_field::FlowField, integration_field::IntegrationField},
};
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
pub enum OrderingSet {
Calculate,
}
pub struct FlowFieldTilesPlugin;
impl Plugin for FlowFieldTilesPlugin {
fn build(&self, app: &mut App) {
app.configure_sets(PostUpdate, OrderingSet::Calculate);
app.add_systems(
PostUpdate,
(process_costfield_update_queue, process_flow_queue).in_set(OrderingSet::Calculate),
);
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
fn process_costfield_update_queue(mut query: Query<&mut FlowFieldTiles>) {
for mut flowfield_tiles in &mut query {
if flowfield_tiles.flow_gen_task.is_some() {
return;
}
if let Some(portal_task) = &mut flowfield_tiles.portal_update_task {
if let Some(sector) = check_ready(portal_task) {
flowfield_tiles.portal_update_task = None;
let sectors = sector.get_surrounding_sectors();
flowfield_tiles
.flowfield_cache
.remove_steps_with_sectors(§ors);
let mut write_flow_queue = flowfield_tiles.flow_queue.write().unwrap();
write_flow_queue.retain(|r| {
for a in r {
if sectors.contains(a.get_sector()) {
return false;
}
}
true
});
} else {
return;
}
}
if flowfield_tiles.costfield_update_task.is_none() {
if let Some(item) = flowfield_tiles.costfield_update_queue.pop_front() {
let costfields = flowfield_tiles.get_sector_cost_fields_mut().clone();
let dimensions = *flowfield_tiles.get_dimensions();
let sector = *item.sector();
let field_cell = *item.cell();
let cost = item.cost();
let thread_pool = AsyncComputeTaskPool::get();
let task = thread_pool.spawn(async move {
let mut costfields = costfields.write().unwrap();
costfields.set_field_cost(§or, &field_cell, cost, &dimensions);
sector
});
flowfield_tiles.costfield_update_task = Some(task);
}
} else {
let mut cost_task = flowfield_tiles.costfield_update_task.as_mut().unwrap();
if let Some(sector) = check_ready(&mut cost_task) {
flowfield_tiles.costfield_update_task = None;
let costfields = flowfield_tiles.get_sector_cost_fields_mut().clone();
let portals = flowfield_tiles.portals.clone();
let thread_pool = AsyncComputeTaskPool::get();
let task = thread_pool.spawn(async move {
let costfields = costfields.read().unwrap();
let mut portals = portals.write().unwrap();
portals.update_portals(§or, &costfields);
sector
});
flowfield_tiles.portal_update_task = Some(task);
}
}
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
fn process_flow_queue(mut query: Query<&mut FlowFieldTiles>) {
for mut flowfield_tiles in &mut query {
if flowfield_tiles.costfield_update_task.is_some()
|| flowfield_tiles.portal_update_task.is_some()
{
return;
}
if let Some(mut poll) = flowfield_tiles.flow_gen_task.as_mut()
&& let Some(fields) = check_ready(&mut poll)
{
for (step, flowfield) in fields {
flowfield_tiles.flowfield_cache.insert(&step, flowfield);
}
flowfield_tiles.flow_gen_task = None;
return;
}
let costfields = flowfield_tiles.sector_cost_fields.clone();
let queue = flowfield_tiles.flow_queue.clone();
let thread_pool = AsyncComputeTaskPool::get();
let task = thread_pool.spawn(async move {
let read_costfields = costfields.read().unwrap();
let mut write_queue = queue.write().unwrap();
let mut generated = vec![];
if let Some(route) = write_queue.pop_front() {
let mut ints = vec![];
for step in route.iter().rev() {
let sector = step.get_sector();
let scaled_costfields = read_costfields.get_scaled_costs();
let scaled_costfield = scaled_costfields.get(sector).unwrap();
let mut integrationfield = IntegrationField::init(scaled_costfield, step);
integrationfield.build(scaled_costfield);
ints.push(integrationfield);
}
for (i, step) in route.iter().rev().enumerate() {
if i == 0 {
let mut flowfield = FlowField::new(step, &ints[i], None);
flowfield.build(&ints[i]);
generated.push((*step, flowfield));
} else {
let mut flowfield = FlowField::new(step, &ints[i], Some(&ints[i - 1]));
flowfield.build(&ints[i]);
generated.push((*step, flowfield));
}
}
}
generated
});
flowfield_tiles.flow_gen_task = Some(task);
}
}