use std::marker::PhantomData;
use bevy::prelude::*;
#[cfg(feature = "bevy_auto_plugin")]
pub use bevy_auto_plugin::*;
use derive_where::derive_where;
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(),
}
}
pub fn add_modifier(&mut self, rhs: T::DataType) {
self.running_op_total = self.running_op_total.clone().add(rhs);
}
}
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 struct StatsSystems;
#[derive(SystemSet)]
#[derive_where(Debug, Hash, PartialEq, Eq, Clone)]
pub enum StatSystems<T> {
Clear,
Op(DataTypeOp),
Apply(DataTypeOp),
Done,
_PhantomData(PhantomData<T>),
}
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 configure_stat_type_add<
T: StatType<DataType: Add + 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 configure_stat_type_mul<
T: StatType<DataType: Add + Mul + Clone + Send + Sync + 'static> + Send + Sync + 'static,
>(
&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;
fn order_stats<
TBefore: StatType + Send + Sync + 'static,
TAfter: StatType + Send + Sync + 'static,
>(
&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.configure_sets(
PreUpdate,
(
StatSystems::<T>::Clear,
StatSystems::<T>::Op(DataTypeOp::MulBefore),
StatSystems::<T>::Apply(DataTypeOp::MulBefore),
StatSystems::<T>::Op(DataTypeOp::Add),
StatSystems::<T>::Apply(DataTypeOp::Add),
StatSystems::<T>::Op(DataTypeOp::MulAfter),
StatSystems::<T>::Apply(DataTypeOp::MulAfter),
StatSystems::<T>::Done,
)
.chain()
.in_set(StatsSystems),
);
self.add_systems(PreUpdate, clear_stat::<T>.in_set(StatSystems::<T>::Clear));
self
}
fn configure_stat_type_add<
T: StatType<DataType: Add + Clone + Send + Sync + 'static> + Send + Sync + 'static,
>(
&mut self,
) -> &mut Self {
self.add_systems(
PreUpdate,
apply_add::<T>.in_set(StatSystems::<T>::Apply(DataTypeOp::Add)),
);
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.configure_stat_type_add::<T>().add_systems(
PreUpdate,
(move |mut stats: Query<(&mut Stat<T>, &Modifier)>| {
for (mut stat, modifier) in stats.iter_mut() {
stat.add_modifier(modifier.add());
}
})
.in_set(StatSystems::<T>::Op(DataTypeOp::Add)),
);
self
}
fn configure_stat_type_mul<
T: StatType<DataType: Add + Mul + Clone + Send + Sync + 'static> + Send + Sync + 'static,
>(
&mut self,
) -> &mut Self {
self.add_systems(
PreUpdate,
(
apply_mul_before::<T>.in_set(StatSystems::<T>::Apply(DataTypeOp::MulBefore)),
apply_mul_after::<T>.in_set(StatSystems::<T>::Apply(DataTypeOp::MulAfter)),
),
);
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.configure_stat_type_mul::<T>().add_systems(
PreUpdate,
(
(move |mut stats: Query<(&mut Stat<T>, &Modifier)>| {
for (mut stat, modifier) in stats.iter_mut() {
stat.add_modifier(modifier.mul_before());
}
})
.in_set(StatSystems::<T>::Op(DataTypeOp::MulBefore)),
(move |mut stats: Query<(&mut Stat<T>, &Modifier)>| {
for (mut stat, modifier) in stats.iter_mut() {
stat.add_modifier(modifier.mul_after());
}
})
.in_set(StatSystems::<T>::Op(DataTypeOp::MulAfter)),
),
);
self
}
fn order_stats<
TBefore: StatType + Send + Sync + 'static,
TAfter: StatType + Send + Sync + 'static,
>(
&mut self,
) -> &mut Self {
self.configure_sets(
PreUpdate,
StatSystems::<TBefore>::Done.before(StatSystems::<TAfter>::Clear),
);
self
}
}