use std::marker::PhantomData;
use bevy::{prelude::*, reflect::GetTypeRegistration};
pub trait Mirror<T>: for<'a> From<&'a T> {
fn apply(&self, val: &mut T);
}
fn reflect_mirror_add<T: Component, U: Mirror<T> + Component>(
query: Query<(Entity, &T), Added<T>>,
mut cmds: Commands,
) {
for (entity, added) in &query {
cmds.entity(entity).insert(U::from(added));
}
}
#[allow(clippy::type_complexity)]
fn reflect_mirror_component<T: Component, U: Mirror<T> + Component>(
mut query: ParamSet<(
(Query<(Entity, &T), Changed<T>>, Query<&mut U>),
(Query<&mut T>, Query<(Entity, &U), Changed<U>>),
)>,
) {
let (changed, mut to_update) = query.p0();
for (entity, changed) in &changed {
if let Ok(mut to_update) = to_update.get_mut(entity) {
*to_update = changed.into()
}
}
let (mut to_update, changed) = query.p1();
for (entity, changed) in &changed {
if let Ok(mut to_update) = to_update.get_mut(entity) {
changed.apply(&mut to_update)
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SystemLabel)]
pub enum MirrorSystems {
Update,
Add,
}
pub struct MirrorPlugin<T: Component, U: Mirror<T> + Component + GetTypeRegistration>(
PhantomData<(T, U)>,
);
impl<T: Component, U: Mirror<T> + Component + GetTypeRegistration> MirrorPlugin<T, U> {
pub fn new() -> Self {
Self(PhantomData)
}
}
impl<T: Component, U: Mirror<T> + Component + GetTypeRegistration> Plugin for MirrorPlugin<T, U> {
fn build(&self, app: &mut App) {
app.register_type::<U>()
.add_system_to_stage(
CoreStage::Last,
reflect_mirror_add::<T, U>.label(MirrorSystems::Add),
)
.add_system_to_stage(
CoreStage::First,
reflect_mirror_component::<T, U>.label(MirrorSystems::Update),
);
}
}