1#![doc=include_str!("../README.md")]
2#![forbid(unsafe_code)]
3#![warn(missing_docs)]
4
5use std::marker::PhantomData;
6use bevy_app::prelude::*;
7use bevy_ecs::{prelude::*, schedule::{ScheduleLabel, InternedScheduleLabel}};
8
9pub struct ResourceProgressTrackingPlugin<T: ?Sized> {
11 pub check_schedule: InternedScheduleLabel,
13
14 pub reset_schedule: InternedScheduleLabel,
17
18 _p1: PhantomData<T>,
19}
20
21impl<T: ?Sized> Default for ResourceProgressTrackingPlugin<T> {
22 fn default() -> Self {
23 Self {
24 check_schedule: PostUpdate.intern(),
25 reset_schedule: Last.intern(),
26 _p1: PhantomData,
27 }
28 }
29}
30
31impl<T: Send + Sync + 'static> Plugin for ResourceProgressTrackingPlugin<T> {
32 fn build(&self, app: &mut App) {
33 app.add_systems(self.check_schedule, resource_progress_check_system::<T>
34 .in_set(ProgressSystems::Check));
35
36 app.add_systems(self.reset_schedule, resource_progress_reset_system::<T>
37 .in_set(ProgressSystems::Reset)
38 .after(ProgressSystems::Check));
39 }
40}
41
42fn resource_progress_check_system<T: ?Sized + Send + Sync + 'static>(
43 mut commands: Commands,
44 resource: Option<Res<Progress<T>>>,
45) {
46 let resource = match resource {
47 Some(v) => v,
48 None => return,
49 };
50
51 if !resource.done() { return }
52 commands.trigger(Done::<T> {
53 work: resource.total,
54 _p1: PhantomData,
55 });
56}
57
58fn resource_progress_reset_system<T: ?Sized + Send + Sync + 'static>(
59 resource: Option<ResMut<Progress<T>>>,
60) {
61 if let Some(mut resource) = resource {
62 resource.done = 0;
63 resource.total = 0;
64 }
65}
66
67pub struct EntityProgressTrackingPlugin<T: ?Sized> {
69 pub check_schedule: InternedScheduleLabel,
71
72 pub reset_schedule: InternedScheduleLabel,
75
76 _p1: PhantomData<T>,
77}
78
79impl<T: ?Sized> Default for EntityProgressTrackingPlugin<T> {
80 fn default() -> Self {
81 Self {
82 check_schedule: PostUpdate.intern(),
83 reset_schedule: Last.intern(),
84 _p1: PhantomData,
85 }
86 }
87}
88
89impl<T: Send + Sync + 'static> Plugin for EntityProgressTrackingPlugin<T> {
90 fn build(&self, app: &mut App) {
91 app.add_systems(self.check_schedule, entity_progress_check_system::<T>
92 .in_set(ProgressSystems::Check));
93
94 app.add_systems(self.reset_schedule, entity_progress_reset_system::<T>
95 .in_set(ProgressSystems::Reset)
96 .after(ProgressSystems::Check));
97 }
98}
99
100fn entity_progress_check_system<T: ?Sized + Send + Sync + 'static>(
101 mut commands: Commands,
102 query: Query<(Entity, &Progress<T>)>,
103) {
104 for (entity, tracker) in &query {
105 if !tracker.done() { continue }
106 commands.trigger_targets(Done::<T> {
107 work: tracker.total,
108 _p1: PhantomData,
109 }, [entity]);
110 }
111}
112
113fn entity_progress_reset_system<T: ?Sized + Send + Sync + 'static>(
114 mut query: Query<&mut Progress<T>>,
115) {
116 for mut tracker in &mut query {
117 tracker.done = 0;
118 tracker.total = 0;
119 }
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SystemSet)]
124pub enum ProgressSystems {
125 Check,
128
129 Reset,
132}
133
134#[derive(Component, Resource)]
139pub struct Progress<T: ?Sized> {
140 done: u64,
141 total: u64,
142 _p1: PhantomData<T>,
143}
144
145impl<T: ?Sized> Progress<T> {
146 pub fn new() -> Self {
148 Self {
149 done: 0,
150 total: 0,
151 _p1: PhantomData,
152 }
153 }
154}
155
156impl<T: ?Sized> Default for Progress<T> {
157 #[inline]
158 fn default() -> Self {
159 Self::new()
160 }
161}
162
163impl<T: ?Sized> Progress<T> {
164 pub fn track(&mut self, done: u32, total: u32) {
166 self.done += done as u64;
167 self.total += total as u64;
168 }
169
170 pub fn work(&self) -> (u64, u64) {
172 (self.done, self.total)
173 }
174
175 pub fn fract(&self) -> f32 {
177 let (done, total) = self.work();
178 return done as f32 / total as f32;
179 }
180
181 fn done(&self) -> bool {
182 let (done, total) = self.work();
183 return done >= total;
184 }
185}
186
187#[derive(Event)]
189pub struct Done<T: ?Sized> {
190 work: u64,
191 _p1: PhantomData<T>,
192}
193
194impl<T: ?Sized> Done<T> {
195 #[inline]
197 pub fn work(&self) -> u64 {
198 self.work
199 }
200}