use crate::components::{FinalTag, MultiEdge, OnCompletion, SingleEdge, TaskProgress};
use legion::{prelude::*, systems::SystemId};
pub fn task_is_complete(world: &SubWorld, entity: Entity) -> bool {
if let Some(progress) = world.get_component::<TaskProgress>(entity) {
progress.is_complete()
} else {
false
}
}
pub fn fork_is_complete(world: &SubWorld, multi_children: &[Entity]) -> bool {
for child in multi_children.iter() {
if !entity_is_complete(world, *child) {
return false;
}
}
true
}
pub fn entity_is_complete(world: &SubWorld, entity: Entity) -> bool {
if let Some(edge) = world.get_component::<MultiEdge>(entity) {
fork_is_complete(world, &edge.children)
} else {
task_is_complete(world, entity)
}
}
pub fn delete_descendents(cmd: &CommandBuffer, world: &SubWorld, entity: Entity) {
if let Some(edge) = world.get_component::<MultiEdge>(entity) {
for child in edge.children.iter() {
delete_entity_and_descendents(cmd, world, *child);
}
}
if let Some(edge) = world.get_component::<SingleEdge>(entity) {
delete_entity_and_descendents(cmd, world, edge.child);
}
}
pub fn delete_entity_and_descendents(cmd: &CommandBuffer, world: &SubWorld, entity: Entity) {
if !world.is_alive(entity) {
return;
}
delete_descendents(cmd, world, entity);
log::debug!("Deleting {:?}", entity);
cmd.delete(entity);
}
fn maintain_task_and_descendents(
cmd: &CommandBuffer,
world: &mut SubWorld,
entity: Entity,
) -> bool {
let (is_unblocked, is_complete) =
if let Some(progress) = world.get_component::<TaskProgress>(entity) {
(progress.is_unblocked, progress.is_complete())
} else {
return true;
};
if is_complete {
log::debug!(
"Noticed task {:?} is complete, removing TaskProgress",
entity
);
return true;
}
if is_unblocked {
return false;
}
let mut child_complete = true;
if let Some(edge) = world
.get_component::<SingleEdge>(entity)
.map(|e| (*e).clone())
{
child_complete = maintain_entity_and_descendents(cmd, world, edge.child);
}
if child_complete {
log::debug!("Unblocking task {:?}", entity);
let mut progress = world
.get_component_mut::<TaskProgress>(entity)
.expect("Blocked task must have progress");
progress.unblock();
}
false
}
fn maintain_fork_and_descendents(
cmd: &CommandBuffer,
world: &mut SubWorld,
entity: Entity,
multi_edge_children: &[Entity],
) -> bool {
let mut single_child_complete = true;
if let Some(edge) = world
.get_component::<SingleEdge>(entity)
.map(|e| (*e).clone())
{
single_child_complete = maintain_entity_and_descendents(cmd, world, edge.child);
}
let mut multi_children_complete = true;
if single_child_complete {
for child in multi_edge_children.iter() {
multi_children_complete &= maintain_entity_and_descendents(cmd, world, *child);
}
}
single_child_complete && multi_children_complete
}
fn maintain_entity_and_descendents(
cmd: &CommandBuffer,
world: &mut SubWorld,
entity: Entity,
) -> bool {
if let Some(edge) = world
.get_component::<MultiEdge>(entity)
.map(|e| (*e).clone())
{
maintain_fork_and_descendents(cmd, world, entity, &edge.children)
} else {
maintain_task_and_descendents(cmd, world, entity)
}
}
pub fn build_task_manager_system<I: Into<SystemId>>(id: I) -> Box<dyn Schedulable> {
SystemBuilder::new(id)
.read_component::<MultiEdge>()
.write_component::<MultiEdge>()
.read_component::<SingleEdge>()
.write_component::<SingleEdge>()
.read_component::<TaskProgress>()
.write_component::<TaskProgress>()
.with_query(<Read<FinalTag>>::query())
.build(|cmd, world, _, final_tasks_query| {
let final_entities: Vec<(Entity, FinalTag)> = final_tasks_query
.iter_entities(world)
.map(|(e, f)| (e, *f))
.collect();
for (entity, FinalTag { on_completion }) in final_entities.into_iter() {
let final_complete = maintain_entity_and_descendents(cmd, world, entity);
if final_complete {
match on_completion {
OnCompletion::Delete => {
delete_entity_and_descendents(cmd, world, entity);
}
OnCompletion::None => {
log::debug!("Removing FinalTag from {:?}", entity);
cmd.remove_component::<FinalTag>(entity);
}
}
}
}
})
}