use bevy::prelude::*;
#[cfg(feature = "bevy_auto_plugin")]
pub use bevy_auto_plugin::*;
pub use sbepistats_derive::StatType;
#[cfg(feature = "bevy_auto_plugin")]
mod bevy_auto_plugin;
pub trait StatType {
type DataType;
}
pub trait Add {
fn add(self, rhs: Self) -> Self;
fn zero() -> Self;
}
impl<T: std::ops::Add<T, Output = T> + num_traits::Zero> Add for T {
fn add(self, rhs: Self) -> Self {
self + rhs
}
fn zero() -> Self {
num_traits::Zero::zero()
}
}
pub trait Mul {
fn mul(self, rhs: Self) -> Self;
fn one() -> Self;
}
impl<T: std::ops::Mul<T, Output = T> + num_traits::One> Mul for T {
fn mul(self, rhs: Self) -> Self {
self * rhs
}
fn one() -> Self {
num_traits::One::one()
}
}
#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
pub enum DataTypeOp {
Add,
MulBefore,
MulAfter,
}
#[derive(Component)]
pub struct Stat<T: StatType> {
base: T::DataType,
running_total: T::DataType,
running_op_total: T::DataType,
}
impl<T: StatType<DataType: Clone + Add>> Stat<T> {
pub fn new(base: T::DataType) -> Self {
Stat {
base: base.clone(),
running_total: base,
running_op_total: Add::zero(),
}
}
}
impl<T: StatType<DataType: Clone>> Stat<T> {
fn clear(&mut self) {
self.running_total = self.base.clone();
}
pub fn base(&self) -> T::DataType {
self.base.clone()
}
pub fn total(&self) -> T::DataType {
self.running_total.clone()
}
}
pub trait StatModifierAdd<T: StatType<DataType: Add>> {
fn add(&self) -> T::DataType {
Add::zero()
}
}
pub trait StatModifierMul<T: StatType<DataType: Add + Mul>> {
fn mul_before(&self) -> T::DataType {
Add::zero()
}
fn mul_after(&self) -> T::DataType {
Add::zero()
}
}
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
pub enum StatsSystems {
Clear,
Op(DataTypeOp),
Apply(DataTypeOp),
}
fn clear_stat<T: StatType<DataType: Clone + Send + Sync + 'static> + Send + Sync + 'static>(
mut stats: Query<&mut Stat<T>>,
) {
for mut stat in stats.iter_mut() {
stat.clear();
}
}
fn apply_add<T: StatType<DataType: Add + Clone + Send + Sync + 'static> + Send + Sync + 'static>(
mut stats: Query<&mut Stat<T>>,
) {
for mut stat in stats.iter_mut() {
stat.running_total = stat
.running_total
.clone()
.add(stat.running_op_total.clone());
stat.running_op_total = Add::zero();
}
}
fn apply_mul_before<
T: StatType<DataType: Add + Mul + Clone + Send + Sync + 'static> + Send + Sync + 'static,
>(
mut stats: Query<&mut Stat<T>>,
) {
for mut stat in stats.iter_mut() {
stat.running_total = stat
.running_total
.clone()
.mul(T::DataType::one().add(stat.running_op_total.clone()));
stat.running_op_total = Add::zero();
}
}
fn apply_mul_after<
T: StatType<DataType: Add + Mul + Clone + Send + Sync + 'static> + Send + Sync + 'static,
>(
mut stats: Query<&mut Stat<T>>,
) {
for mut stat in stats.iter_mut() {
stat.running_total = stat
.running_total
.clone()
.mul(T::DataType::one().add(stat.running_op_total.clone()));
stat.running_op_total = Add::zero();
}
}
pub trait AppExt {
fn add_stat_type<T: StatType<DataType: Clone + Send + Sync + 'static> + Send + Sync + 'static>(
&mut self,
) -> &mut Self;
fn add_stat_modifier_add<
T: StatType<DataType: Add + Clone + Send + Sync + 'static> + Send + Sync + 'static,
Modifier: StatModifierAdd<T> + Component,
>(
&mut self,
) -> &mut Self;
fn add_stat_modifier_mul<
T: StatType<DataType: Add + Mul + Clone + Send + Sync + 'static> + Send + Sync + 'static,
Modifier: StatModifierMul<T> + Component,
>(
&mut self,
) -> &mut Self;
}
impl AppExt for App {
fn add_stat_type<
T: StatType<DataType: Clone + Send + Sync + 'static> + Send + Sync + 'static,
>(
&mut self,
) -> &mut Self {
self.add_systems(PreUpdate, clear_stat::<T>.in_set(StatsSystems::Clear));
self
}
fn add_stat_modifier_add<
T: StatType<DataType: Add + Clone + Send + Sync + 'static> + Send + Sync + 'static,
Modifier: StatModifierAdd<T> + Component,
>(
&mut self,
) -> &mut Self {
self.add_systems(
PreUpdate,
(
(move |mut stats: Query<(&mut Stat<T>, &Modifier)>| {
for (mut stat, modifier) in stats.iter_mut() {
stat.running_op_total = stat.running_op_total.clone().add(modifier.add());
}
})
.in_set(StatsSystems::Op(DataTypeOp::Add)),
apply_add::<T>.in_set(StatsSystems::Apply(DataTypeOp::Add)),
),
);
self
}
fn add_stat_modifier_mul<
T: StatType<DataType: Add + Mul + Clone + Send + Sync + 'static> + Send + Sync + 'static,
Modifier: StatModifierMul<T> + Component,
>(
&mut self,
) -> &mut Self {
self.add_systems(
PreUpdate,
(
(move |mut stats: Query<(&mut Stat<T>, &Modifier)>| {
for (mut stat, modifier) in stats.iter_mut() {
stat.running_op_total =
stat.running_op_total.clone().add(modifier.mul_before());
}
})
.in_set(StatsSystems::Op(DataTypeOp::MulBefore)),
apply_mul_before::<T>.in_set(StatsSystems::Apply(DataTypeOp::MulBefore)),
(move |mut stats: Query<(&mut Stat<T>, &Modifier)>| {
for (mut stat, modifier) in stats.iter_mut() {
stat.running_op_total =
stat.running_op_total.clone().add(modifier.mul_after());
}
})
.in_set(StatsSystems::Op(DataTypeOp::MulAfter)),
apply_mul_after::<T>.in_set(StatsSystems::Apply(DataTypeOp::MulAfter)),
),
);
self
}
}
pub struct StatsPlugin;
impl Plugin for StatsPlugin {
fn build(&self, app: &mut App) {
app.configure_sets(
PreUpdate,
(
StatsSystems::Clear,
StatsSystems::Op(DataTypeOp::MulBefore),
StatsSystems::Apply(DataTypeOp::MulBefore),
StatsSystems::Op(DataTypeOp::Add),
StatsSystems::Apply(DataTypeOp::Add),
StatsSystems::Op(DataTypeOp::MulAfter),
StatsSystems::Apply(DataTypeOp::MulAfter),
)
.chain(),
);
}
}