use bevy::ecs::{
component::Component,
entity::Entity,
hierarchy::ChildOf,
system::{Query, Res, SystemParam},
};
use crate::{EntityPickingState, PickingStateMachine, PickingTransition};
#[derive(Debug, Clone, Copy, Component, Default)]
pub enum PickingPropagation {
#[default]
PropagateDown,
PropagateUp(usize),
AndPropagateUp(usize),
NoPropagation,
}
#[derive(Debug, SystemParam)]
pub struct PropagatedPickingStateMachine<'w, 's> {
pub state_machine: Res<'w, PickingStateMachine>,
pub parents: Query<'w, 's, &'static ChildOf>,
pub propagation: Query<'w, 's, &'static PickingPropagation>,
}
impl PropagatedPickingStateMachine<'_, '_> {
pub fn entity_equivalent(&self, active: Entity, to: Entity) -> bool {
if active == to {
return true;
}
match self.propagation.get(active) {
Ok(PickingPropagation::NoPropagation) => active == to,
Ok(PickingPropagation::PropagateDown) | Err(_) => {
let mut current = to;
while let Ok(parent) = self.parents.get(current) {
if parent.parent() == active {
return true;
}
current = parent.parent();
}
false
}
Ok(PickingPropagation::PropagateUp(count)) => {
let mut root = active;
for _ in 0..*count {
if let Ok(parent) = self.parents.get(root) {
root = parent.parent();
} else {
break;
}
}
let mut current = to;
while let Ok(parent) = self.parents.get(current) {
if parent.parent() == active || parent.parent() == root {
return true;
}
current = parent.parent();
}
false
}
Ok(PickingPropagation::AndPropagateUp(count)) => {
let mut current = to;
while let Ok(parent) = self.parents.get(current) {
if parent.parent() == active {
return true;
}
current = parent.parent();
}
let mut current = active;
for _ in 0..*count {
if let Ok(parent) = self.parents.get(current) {
current = parent.parent();
if current == to {
return true;
}
} else {
return false;
}
}
false
}
}
}
pub fn get_state(&self, entity: Entity) -> EntityPickingState {
let Some(active_entity) = self.state_machine.get_active_entity() else {
return EntityPickingState::None;
};
if self.entity_equivalent(active_entity, entity) {
self.state_machine.active_state()
} else {
EntityPickingState::None
}
}
pub fn get_transition(&self, entity: Entity) -> Option<PickingTransition> {
self.state_machine
.transitions
.iter()
.find(|x| self.entity_equivalent(x.entity(), entity))
}
}