mod extraction;
use bevy::prelude::*;
use std::marker::PhantomData;
pub struct StatbarDepth(pub f32);
pub trait StatbarObservable {
fn get_statbar_value(&self) -> f32;
}
#[derive(Component, Reflect)]
pub struct StatbarObserveEntity(pub Entity);
#[derive(Component, Reflect)]
pub struct StatbarObserveParent;
#[derive(Reflect)]
pub struct StatbarObserveResource<T>
where
T: 'static,
{
#[reflect(ignore)]
#[doc(hidden)]
pub _phantom: PhantomData<fn() -> T>,
}
impl<T> Default for StatbarObserveResource<T>
where
T: 'static,
{
fn default() -> Self {
Self {
_phantom: Default::default(),
}
}
}
#[derive(Component, Reflect)]
#[reflect(Component)]
pub struct Statbar<T = ()>
where
T: 'static,
{
pub color: Color,
pub empty_color: Color,
pub length: f32,
pub thickness: f32,
pub displacement: Vec2,
pub vertical: bool,
pub reverse: bool,
pub hide: bool,
pub value: f32,
#[reflect(ignore)]
#[doc(hidden)]
pub _phantom: PhantomData<fn() -> T>,
}
impl<T> Default for Statbar<T> {
fn default() -> Self {
Self {
color: Color::YELLOW,
empty_color: Color::rgb(0.2, 0.2, 0.0),
length: 100.,
thickness: 16.,
displacement: Vec2::ZERO,
vertical: false,
reverse: false,
hide: false,
value: 0.75,
_phantom: PhantomData,
}
}
}
#[derive(Clone, Copy, Debug, Component, Reflect)]
#[reflect(Component)]
pub struct StatbarBorder<T>
where
T: 'static,
{
pub color: Color,
left: f32,
right: f32,
bottom: f32,
top: f32,
#[reflect(ignore)]
phantom: PhantomData<fn() -> T>,
}
impl<T> StatbarBorder<T>
where
T: 'static,
{
pub fn all(color: Color, thickness: f32) -> Self {
Self {
color,
left: thickness,
right: thickness,
bottom: thickness,
top: thickness,
phantom: PhantomData,
}
}
}
impl<T> Default for StatbarBorder<T>
where
T: 'static,
{
fn default() -> Self {
Self::all(Color::WHITE, 2.0)
}
}
#[derive(Clone, Copy, Debug, Component, Reflect)]
#[reflect(Component)]
pub struct StatbarColorLerp<T>
where
T: 'static,
{
pub min: Color,
pub max: Color,
#[reflect(ignore)]
phantom: PhantomData<fn() -> T>,
}
impl<T> Default for StatbarColorLerp<T>
where
T: 'static,
{
fn default() -> Self {
Self {
min: Color::RED,
max: Color::GREEN,
phantom: Default::default(),
}
}
}
impl<T> StatbarColorLerp<T>
where
T: 'static,
{
pub fn new(min: Color, max: Color) -> Self {
Self {
min,
max,
phantom: Default::default(),
}
}
}
#[derive(Clone, Copy, Debug, Component, Reflect)]
#[reflect(Component)]
pub struct StatbarColorSwitch<T>
where
T: 'static,
{
pub pivot: f32,
pub low: Color,
pub high: Color,
#[reflect(ignore)]
phantom: PhantomData<fn() -> T>,
}
impl<T> Default for StatbarColorSwitch<T>
where
T: 'static,
{
fn default() -> Self {
Self {
pivot: 0.25,
low: Color::RED,
high: Color::GREEN,
phantom: Default::default(),
}
}
}
impl<T> StatbarColorSwitch<T>
where
T: 'static,
{
pub fn new(pivot: f32, low: Color, high: Color) -> Self {
Self {
pivot,
low,
high,
phantom: Default::default(),
}
}
}
fn switch_stat_bar_colors<T>(
mut color_switch_query: Query<
(&mut Statbar<T>, &mut StatbarColorSwitch<T>),
Changed<Statbar<T>>,
>,
) where
T: 'static,
{
color_switch_query.for_each_mut(|(mut bar, switcher)| {
bar.color = if bar.value <= switcher.pivot {
switcher.low
} else {
switcher.high
};
});
}
fn lerp_stat_bar_colors<T>(
mut color_lerp_query: Query<(&mut Statbar<T>, &mut StatbarColorLerp<T>), Changed<Statbar<T>>>,
) where
T: 'static,
{
color_lerp_query.for_each_mut(|(mut bar, lerper)| {
bar.color = Vec4::from(lerper.min)
.lerp(lerper.max.into(), bar.value)
.into();
});
}
fn update_statbar_values<T>(
mut statbar_query: Query<
(&mut Statbar<T>, &T),
(
Changed<T>,
Without<StatbarObserveParent>,
Without<StatbarObserveEntity>,
),
>,
) where
T: Component + StatbarObservable,
{
statbar_query.for_each_mut(|(mut statbar, value)| {
statbar.value = value.get_statbar_value();
});
}
fn update_statbar_values_from_parents<T>(
mut statbar_query: Query<
(&mut Statbar<T>, &Parent),
(With<StatbarObserveParent>, Without<StatbarObserveEntity>),
>,
parent_value_query: Query<&T, Changed<T>>,
) where
T: Component + StatbarObservable,
{
statbar_query.for_each_mut(|(mut statbar, parent)| {
if let Ok(value) = parent_value_query.get(parent.get()) {
statbar.value = value.get_statbar_value();
}
});
}
fn update_statbar_values_from_other<T>(
mut statbar_query: Query<
(&mut Statbar<T>, &StatbarObserveEntity),
Without<StatbarObserveParent>,
>,
other_value_query: Query<&T, Changed<T>>,
) where
T: Component + StatbarObservable,
{
statbar_query.for_each_mut(|(mut statbar, &StatbarObserveEntity(target))| {
if let Ok(value) = other_value_query.get(target) {
statbar.value = value.get_statbar_value();
}
});
}
fn update_statbar_from_resource<T>(resource: Res<T>, mut statbar_query: Query<&mut Statbar<T>>)
where
T: StatbarObservable + 'static + Send + Sync,
{
if resource.is_changed() {
statbar_query.for_each_mut(|mut statbar| {
statbar.value = resource.get_statbar_value();
});
}
}
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemLabel)]
pub enum StatbarSystem {
UpdateValues,
UpdateColors,
ExtractSprites,
}
pub trait RegisterStatbarSubject {
fn add_statbar_component_observer<T: StatbarObservable + Component>(&mut self) -> &mut Self;
fn add_statbar_resource_observer<T: StatbarObservable + 'static + Send + Sync>(
&mut self,
) -> &mut Self;
fn add_standalone_statbar<T: 'static>(&mut self) -> &mut Self;
}
impl RegisterStatbarSubject for App {
fn add_statbar_component_observer<T: StatbarObservable + Component>(&mut self) -> &mut Self {
if let Ok(render_app) = self.get_sub_app_mut(bevy::render::RenderApp) {
render_app.add_system_to_stage(
bevy::render::RenderStage::Extract,
extraction::extract_stat_bars::<T>
.after(bevy::sprite::SpriteSystem::ExtractSprites),
);
}
self.register_type::<Statbar<T>>()
.register_type::<StatbarBorder<T>>()
.register_type::<StatbarColorLerp<T>>()
.register_type::<StatbarColorSwitch<T>>()
.add_system_to_stage(
CoreStage::PostUpdate,
update_statbar_values::<T>.label(StatbarSystem::UpdateValues),
)
.add_system_to_stage(
CoreStage::PostUpdate,
update_statbar_values_from_other::<T>.label(StatbarSystem::UpdateValues),
)
.add_system_to_stage(
CoreStage::PostUpdate,
update_statbar_values_from_parents::<T>.label(StatbarSystem::UpdateValues),
)
.add_system_to_stage(
CoreStage::PostUpdate,
switch_stat_bar_colors::<T>
.after(StatbarSystem::UpdateValues)
.label(StatbarSystem::UpdateColors),
)
.add_system_to_stage(
CoreStage::PostUpdate,
lerp_stat_bar_colors::<T>
.after(StatbarSystem::UpdateValues)
.label(StatbarSystem::UpdateColors),
)
}
fn add_statbar_resource_observer<T: StatbarObservable + 'static + Send + Sync>(
&mut self,
) -> &mut Self {
if let Ok(render_app) = self.get_sub_app_mut(bevy::render::RenderApp) {
render_app.add_system_to_stage(
bevy::render::RenderStage::Extract,
extraction::extract_stat_bars::<T>
.after(bevy::sprite::SpriteSystem::ExtractSprites),
);
}
self.register_type::<Statbar<T>>()
.register_type::<StatbarBorder<T>>()
.register_type::<StatbarColorLerp<T>>()
.register_type::<StatbarColorSwitch<T>>()
.add_system_to_stage(
CoreStage::PostUpdate,
update_statbar_from_resource::<T>.label(StatbarSystem::UpdateValues),
)
.add_system_to_stage(
CoreStage::PostUpdate,
switch_stat_bar_colors::<T>
.after(StatbarSystem::UpdateValues)
.label(StatbarSystem::UpdateColors),
)
.add_system_to_stage(
CoreStage::PostUpdate,
lerp_stat_bar_colors::<T>
.after(StatbarSystem::UpdateValues)
.label(StatbarSystem::UpdateColors),
)
}
fn add_standalone_statbar<T: 'static>(&mut self) -> &mut Self {
if let Ok(render_app) = self.get_sub_app_mut(bevy::render::RenderApp) {
render_app.add_system_to_stage(
bevy::render::RenderStage::Extract,
extraction::extract_stat_bars::<T>
.after(bevy::sprite::SpriteSystem::ExtractSprites),
);
}
self.register_type::<Statbar<T>>()
.register_type::<StatbarBorder<T>>()
.register_type::<StatbarColorLerp<T>>()
.register_type::<StatbarColorSwitch<T>>()
.add_system_to_stage(
CoreStage::PostUpdate,
switch_stat_bar_colors::<T>.label(StatbarSystem::UpdateColors),
)
.add_system_to_stage(
CoreStage::PostUpdate,
lerp_stat_bar_colors::<T>.label(StatbarSystem::UpdateColors),
)
}
}