use super::*;
use beet_core::prelude::*;
use beet_flow::prelude::*;
use bevy::animation::RepeatAnimation;
use std::time::Duration;
#[derive(Debug, Clone, Component)]
#[require(ContinueRun)]
pub struct TriggerOnAnimationEnd<P> {
pub payload: P,
pub handle: Handle<AnimationClip>,
pub animation_index: AnimationNodeIndex,
pub transition_duration: Duration,
}
impl<P> TriggerOnAnimationEnd<P> {
pub fn new(
handle: Handle<AnimationClip>,
index: AnimationNodeIndex,
payload: P,
) -> Self {
Self {
payload,
handle,
animation_index: index,
transition_duration: DEFAULT_ANIMATION_TRANSITION,
}
}
pub fn with_transition_duration(mut self, duration: Duration) -> Self {
self.transition_duration = duration;
self
}
}
pub(crate) fn trigger_on_animation_end<P: ActionEvent + Clone>(
mut commands: Commands,
clips: When<Res<Assets<AnimationClip>>>,
mut query: Populated<(Entity, &TriggerOnAnimationEnd<P>), With<Running>>,
agents: AgentQuery<&AnimationPlayer>,
) -> Result {
for (action, on_end) in query.iter_mut() {
let player = agents.get_descendent(action)?;
let clip = clips
.get(&on_end.handle)
.ok_or_else(|| bevyhow!("clip not found"))?;
let Some(active_animation) = player.animation(on_end.animation_index)
else {
warn!(
"animation is not playing, TriggerOnAnimationEnd will not be called"
);
continue;
};
let remaining_time = match active_animation.repeat_mode() {
RepeatAnimation::Never => {
clip.duration() - active_animation.seek_time()
}
RepeatAnimation::Count(count) => {
let total = clip.duration() * count as f32;
let current = clip.duration()
* active_animation.completions() as f32
+ active_animation.seek_time();
total - current
}
RepeatAnimation::Forever => f32::INFINITY,
};
let duration = on_end.transition_duration.as_secs_f32();
let nearly_finished = remaining_time < duration;
if nearly_finished {
commands
.entity(action)
.trigger_target(on_end.payload.clone());
}
}
Ok(())
}