use crate::{
components::{FlickerMarker, Flickered, NoFlicker, RepeatingFlicker},
config::FlickerPluginConfig,
events::FlickerStartEvent,
flicker::FlickerMaterial,
};
use bevy_ecs::{
entity::Entity,
hierarchy::{ChildOf, Children},
query::{With, Without},
system::{Commands, Query, Res, ResMut},
};
use bevy_sprite_render::MeshMaterial2d;
use bevy_ecs::message::{MessageWriter, MessageReader};
use bevy_asset::Assets;
use bevy_image::{Image, TextureAtlasLayout};
use bevy_log::{error, warn};
use bevy_math::{primitives::Rectangle, URect, Vec2, Vec3};
use bevy_sprite::Sprite;
use bevy_time::Time;
use bevy_transform::components::Transform;
use bevy_mesh::{Mesh2d, Mesh};
pub(crate) fn flicker_start(
sprites: Query<&Sprite, Without<NoFlicker>>,
mesh_components: Query<&Mesh2d, Without<NoFlicker>>,
mut flicker_materials: ResMut<Assets<FlickerMaterial>>,
mut flicker_start_events: MessageReader<FlickerStartEvent>,
mut meshes: ResMut<Assets<Mesh>>,
images: Res<Assets<Image>>,
atlas_layouts: Res<Assets<TextureAtlasLayout>>,
mut commands: Commands,
flickereds: Query<&FlickerMarker>,
config: Res<FlickerPluginConfig>,
with_children: Query<&Children>,
flicker_children: Query<Entity, With<Flickered>>,
) {
for e in flicker_start_events.read() {
if flickereds.get(e.entity).is_ok() && config.ignore_overlap() {
continue;
}
let (material, mesh) = if let Ok(sprite) = sprites.get(e.entity) {
let image_handle = &sprite.image;
let img = if let Some(img) = images.get(image_handle) {
img
} else {
error!("Could not get image from image handle to begin flicker");
continue;
};
if let Some(texture_atlas) = sprite.texture_atlas.as_ref() {
let index = texture_atlas.index;
if let Some(atlas) = atlas_layouts.get(&texture_atlas.layout) {
let curr_rect = atlas
.textures
.get(index)
.copied()
.unwrap_or(URect::new(0, 0, 0, 0));
let rect_size = Vec2::new(curr_rect.width() as f32, curr_rect.height() as f32);
let img_size = img.size().as_vec2();
let ratio = img_size / rect_size;
let offset = curr_rect.min.as_vec2() / img_size;
let size = rect_size / img_size;
let mesh_size = sprite.custom_size.unwrap_or(rect_size);
(
FlickerMaterial {
source_image: Some(image_handle.clone()),
offset,
size,
ratio,
color: e.color.into(),
..Default::default()
},
Mesh::from(Rectangle::new(mesh_size.x, mesh_size.y)),
)
} else {
error!(
"Could not get atlas to determine which part of sprite is currently active"
);
continue;
}
} else {
let mesh_size = sprite.custom_size.unwrap_or(img.size().as_vec2());
(
FlickerMaterial {
source_image: Some(image_handle.clone()),
color: e.color.into(),
..Default::default()
},
Mesh::from(Rectangle::new(mesh_size.x, mesh_size.y)),
)
}
} else if let Ok(mesh_handle) = mesh_components.get(e.entity) {
if let Some(mesh) = meshes.get(&mesh_handle.0).cloned() {
(
FlickerMaterial {
color: e.color.into(),
..Default::default()
},
mesh,
)
} else {
error!("Entity {:?} had an invalid mesh handle", e.entity);
continue;
}
} else {
warn!(
"Attempted to flicker on a despawned or sprite-less entity {:?}",
e.entity
);
continue;
};
if !config.ignore_overlap() {
if let Ok(children) = with_children.get(e.entity) {
for child in children {
if flicker_children.contains(*child) {
if let Ok(mut entity_commands) = commands.get_entity(*child) {
entity_commands.despawn();
}
}
}
}
}
if let Ok(mut entity_commands) = commands.get_entity(e.entity) {
entity_commands.with_children(|parent| {
parent.spawn((
MeshMaterial2d(flicker_materials.add(material)),
Mesh2d(meshes.add(mesh)),
Transform {
translation: Vec3::new(0.0, 0.0, 1.0),
..Default::default()
},
Flickered::with_secs(e.secs),
));
});
entity_commands.insert(FlickerMarker);
}
}
}
pub(crate) fn flicker_tick(
mut flickered: Query<(&ChildOf, Entity, &mut Flickered)>,
mut commands: Commands,
time: Res<Time>,
) {
for (child_of, entity, mut flickered) in flickered.iter_mut() {
flickered.0.tick(time.delta());
if flickered.0.is_finished() {
if let Ok(mut entity_commands) = commands.get_entity(entity) {
entity_commands.despawn();
}
if let Ok(mut entity_commands) = commands.get_entity(child_of.0) {
entity_commands.remove::<FlickerMarker>();
}
}
}
}
pub(crate) fn repeating_flicker_tick(
mut repeating_flickers: Query<(Entity, &mut RepeatingFlicker)>,
mut flicker_start_event_writer: MessageWriter<FlickerStartEvent>,
mut commands: Commands,
time: Res<Time>,
) {
for (entity, mut repeating_flicker) in repeating_flickers.iter_mut() {
if repeating_flicker.curr_pulse_count > 0 {
repeating_flicker.timer.tick(time.delta());
if repeating_flicker.timer.just_finished() {
flicker_start_event_writer.write(repeating_flicker.generate_start_event(entity));
repeating_flicker.curr_pulse_count -= 1;
if repeating_flicker.curr_pulse_count == 0 {
if let Some(count) = repeating_flicker.count.as_mut() {
*count -= 1;
if *count == 0 {
if let Ok(mut entity_commands) = commands.get_entity(entity) {
entity_commands.remove::<RepeatingFlicker>();
}
}
}
}
}
} else {
repeating_flicker.pulse_timer.tick(time.delta());
if repeating_flicker.pulse_timer.just_finished() {
repeating_flicker.curr_pulse_count = repeating_flicker.pulse_count;
}
}
}
}