use bevy::{
ecs::component::{ComponentHooks, StorageType},
prelude::*,
};
use crate::{ecs::CommandsExt, event::OnScore, scoring::Score};
#[derive(Reflect)]
#[derive(Clone, Copy, PartialEq, Debug, Default)]
#[reflect(Component)]
pub struct Product {
threshold: Score,
use_compensation: bool,
}
impl Product {
pub fn new(threshold: impl Into<Score>) -> Self {
Self {
threshold: threshold.into(),
use_compensation: false,
}
}
pub fn with_compensation(mut self, compensation: bool) -> Self {
self.use_compensation = compensation;
self
}
pub fn threshold(&self) -> Score {
self.threshold
}
pub fn set_threshold(&mut self, threshold: impl Into<Score>) {
self.threshold = threshold.into();
}
fn observer(trigger: Trigger<OnScore>, target: Query<(&Children, &Product)>, mut scores: Query<&mut Score>) {
let Ok((children, settings)) = target.get(trigger.entity()) else {
return;
};
let mut product: f32 = 1.;
let mut num_scores = 0;
for child_score in scores.iter_many(children) {
product *= child_score.get();
num_scores += 1;
}
if settings.use_compensation && num_scores > 0 {
let mod_factor = 1. - 1. / (num_scores as f32);
let makeup = (1. - product) * mod_factor;
product += makeup * product;
}
if product < settings.threshold().get() {
product = 0.;
}
let Ok(mut actor_score) = scores.get_mut(trigger.entity()) else {
return;
};
actor_score.set(product);
}
}
impl Component for Product {
const STORAGE_TYPE: StorageType = StorageType::Table;
fn register_component_hooks(hooks: &mut ComponentHooks) {
hooks.on_add(|mut world, _entity, _component| {
#[derive(Resource, Default)]
struct ProductObserverSpawned;
world
.commands()
.once::<ProductObserverSpawned>()
.observe(Self::observer);
});
}
}