use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_ecs::schedule::{InternedScheduleLabel, ScheduleLabel};
use bevy_state::prelude::*;
use bevy_state::state::FreelyMutableState;
use crate::prelude::*;
pub struct ProgressPlugin<S: FreelyMutableState> {
transitions: StateTransitionConfig<S>,
check_progress_schedule: InternedScheduleLabel,
autoclear_on_enter: bool,
autoclear_on_exit: bool,
#[cfg(feature = "assets")]
track_assets: bool,
#[cfg(feature = "assets")]
autoclear_assets_on_enter: bool,
#[cfg(feature = "assets")]
autoclear_assets_on_exit: bool,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, SystemSet)]
pub struct CheckProgressSet;
impl<S: FreelyMutableState> Default for ProgressPlugin<S> {
fn default() -> Self {
Self {
check_progress_schedule: Last.intern(),
transitions: Default::default(),
autoclear_on_enter: true,
autoclear_on_exit: false,
#[cfg(feature = "assets")]
track_assets: false,
#[cfg(feature = "assets")]
autoclear_assets_on_enter: false,
#[cfg(feature = "assets")]
autoclear_assets_on_exit: true,
}
}
}
impl<S: FreelyMutableState> ProgressPlugin<S> {
pub fn new() -> Self {
Self::default()
}
pub fn add_state_transition(&mut self, from: S, to: S) {
self.transitions.map_from_to.insert(from, to);
}
pub fn with_state_transition(mut self, from: S, to: S) -> Self {
self.add_state_transition(from, to);
self
}
pub fn check_progress_in<L: ScheduleLabel>(mut self, schedule: L) -> Self {
self.check_progress_schedule = schedule.intern();
self
}
pub fn auto_clear(mut self, on_enter: bool, on_exit: bool) -> Self {
self.autoclear_on_enter = on_enter;
self.autoclear_on_exit = on_exit;
self
}
pub fn set_auto_clear(&mut self, on_enter: bool, on_exit: bool) {
self.autoclear_on_enter = on_enter;
self.autoclear_on_exit = on_exit;
}
#[cfg(feature = "assets")]
pub fn auto_clear_assets(mut self, on_enter: bool, on_exit: bool) -> Self {
self.autoclear_assets_on_enter = on_enter;
self.autoclear_assets_on_exit = on_exit;
self
}
#[cfg(feature = "assets")]
pub fn set_auto_clear_assets(&mut self, on_enter: bool, on_exit: bool) {
self.autoclear_assets_on_enter = on_enter;
self.autoclear_assets_on_exit = on_exit;
}
#[cfg(feature = "assets")]
pub fn set_asset_tracking(&mut self, asset_tracking: bool) {
self.track_assets = asset_tracking;
}
#[cfg(feature = "assets")]
pub fn with_asset_tracking(mut self) -> Self {
self.track_assets = true;
self
}
}
impl<S: FreelyMutableState> Plugin for ProgressPlugin<S> {
fn build(&self, app: &mut App) {
app.init_resource::<ProgressTracker<S>>();
app.insert_resource(self.transitions.clone());
app.add_systems(
self.check_progress_schedule,
transition_if_ready::<S>
.run_if(rc_configured_state::<S>)
.in_set(CheckProgressSet),
);
app.add_systems(
PostUpdate,
apply_progress_from_entities::<S>
.run_if(rc_configured_state::<S>)
.run_if(any_with_component::<ProgressEntity<S>>),
);
for s in self.transitions.map_from_to.keys() {
if self.autoclear_on_enter {
app.add_systems(OnEnter(s.clone()), clear_global_progress::<S>);
}
if self.autoclear_on_exit {
app.add_systems(OnExit(s.clone()), clear_global_progress::<S>);
}
}
#[cfg(feature = "async")]
{
app.add_systems(
PreUpdate,
recv_progress_msgs::<S>
.run_if(rc_configured_state::<S>)
.run_if(rc_recv_progress_msgs::<S>),
);
}
#[cfg(feature = "debug")]
{
use crate::debug::*;
app.add_systems(
self.check_progress_schedule,
debug_progress::<S>
.run_if(rc_debug_progress::<S>)
.in_set(CheckProgressSet)
.before(transition_if_ready::<S>),
);
}
#[cfg(feature = "assets")]
if self.track_assets {
use crate::assets::*;
app.init_resource::<AssetsLoading<S>>();
app.add_systems(
PostUpdate,
assets_progress::<S>
.track_progress::<S>()
.in_set(AssetsTrackProgress)
.run_if(rc_configured_state::<S>),
);
for s in self.transitions.map_from_to.keys() {
if self.autoclear_assets_on_enter {
app.add_systems(
OnEnter(s.clone()),
assets_loading_reset::<S>
.after(clear_global_progress::<S>),
);
}
if self.autoclear_assets_on_exit {
app.add_systems(
OnExit(s.clone()),
assets_loading_reset::<S>
.after(clear_global_progress::<S>),
);
}
}
}
}
}