use bevy::ecs::world::FilteredEntityMut;
use log::trace;
use crate::{condition::fns::ConditionFns, modifier::fns::ModifierFns, prelude::*};
pub(super) struct TriggerTracker {
value: ActionValue,
found_explicit: bool,
any_explicit_fired: bool,
found_active: bool,
found_implicit: bool,
all_implicits_fired: bool,
blocked: bool,
}
impl TriggerTracker {
#[must_use]
pub(super) fn new(value: ActionValue) -> Self {
Self {
value,
found_explicit: false,
any_explicit_fired: false,
found_active: false,
found_implicit: false,
all_implicits_fired: true,
blocked: false,
}
}
pub(super) fn apply_modifiers(
&mut self,
entity: &mut FilteredEntityMut,
actions: &ActionsQuery,
time: &ContextTime,
fns: &ModifierFns,
) {
for get_modifier in &**fns {
let modifier = get_modifier(entity);
let new_value = modifier.transform(actions, time, self.value);
trace!(
"`{modifier:?}` changes `{:?}` to `{new_value:?}`",
self.value
);
self.value = new_value;
}
}
pub(super) fn apply_conditions(
&mut self,
entity: &mut FilteredEntityMut,
actions: &ActionsQuery,
time: &ContextTime,
conditions: &ConditionFns,
) {
for get_condition in &**conditions {
let condition = get_condition(entity);
let state = condition.evaluate(actions, time, self.value);
trace!("`{condition:?}` returns state `{state:?}`");
match condition.kind() {
ConditionKind::Explicit => {
self.found_explicit = true;
self.any_explicit_fired |= state == ActionState::Fired;
self.found_active |= state != ActionState::None;
}
ConditionKind::Implicit => {
self.found_implicit = true;
self.all_implicits_fired &= state == ActionState::Fired;
self.found_active |= state != ActionState::None;
}
ConditionKind::Blocker => {
self.blocked |= state == ActionState::None;
}
}
}
}
pub(super) fn state(&self) -> ActionState {
if self.blocked {
return ActionState::None;
}
if !self.found_explicit && !self.found_implicit {
if self.value.as_bool() {
return ActionState::Fired;
} else {
return ActionState::None;
}
}
if (!self.found_explicit || self.any_explicit_fired) && self.all_implicits_fired {
ActionState::Fired
} else if self.found_active {
ActionState::Ongoing
} else {
ActionState::None
}
}
pub(super) fn value(&self) -> ActionValue {
self.value
}
pub(super) fn overwrite(&mut self, other: TriggerTracker) {
let dim = self.value.dim();
*self = other;
self.value = self.value.convert(dim);
}
pub(super) fn combine(&mut self, other: Self, accumulation: Accumulation) {
let accumulated = match accumulation {
Accumulation::MaxAbs => {
let mut value = self.value.as_axis3d().to_array();
let other_value = other.value.as_axis3d().to_array();
for (axis, other_axis) in value.iter_mut().zip(other_value) {
if axis.abs() < other_axis.abs() {
*axis = other_axis;
}
}
value.into()
}
Accumulation::Cumulative => self.value.as_axis3d() + other.value.as_axis3d(),
};
self.value = ActionValue::Axis3D(accumulated).convert(self.value.dim());
self.found_explicit |= other.found_explicit;
self.any_explicit_fired |= other.any_explicit_fired;
self.found_active |= other.found_active;
self.found_implicit |= other.found_implicit;
self.all_implicits_fired &= other.all_implicits_fired;
self.blocked |= other.blocked;
}
}