use bevy::prelude::*; use strum_macros::EnumIter; use crate::fighter::{FighterFacingDirection, FighterHitstop, FighterVisual}; /// The simulation-owned animation clock. Rendering may interpolate freely, but /// gameplay samples baked data only with this rollback-tracked value. #[derive(Component, Clone, Copy, Debug, Hash, PartialEq, Eq)] pub struct FighterAnimationFrame { pub kind: AnimKind, pub frame: u32, pub repeat: bool, } impl FighterAnimationFrame { pub const fn new(kind: AnimKind, repeat: bool) -> Self { Self { kind, frame: 0, repeat, } } pub fn reset(&mut self, kind: AnimKind, repeat: bool) { self.kind = kind; self.frame = 0; self.repeat = repeat; } pub fn advance(&mut self) { self.frame = self.frame.saturating_add(1); } } pub fn advance_fighter_animation_frames( mut fighters: Query<(&mut FighterAnimationFrame, &FighterHitstop)>, ) { for (mut animation, hitstop) in &mut fighters { if hitstop.remaining_frames == 0 { animation.advance(); } } } pub fn apply_animation( children: Query< ( &mut Transform, &FighterFacingDirection, &FighterAnimationFrame, ), With, >, ) { for (mut trf, facing_direction, frame) in children { trf.rotation = Quat::IDENTITY; if let AnimKind::Turn = frame.kind { trf.rotate_local_y( std::f32::consts::PI * -0.5 * facing_direction.to_sign().to_num::(), ); } else { trf.rotate_local_y( std::f32::consts::PI * 0.5 * facing_direction.to_sign().to_num::(), ); } } } #[derive( Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize, Reflect, EnumIter, )] pub enum AnimKind { Wait, Walk, Run, Dash, JumpSquat, JumpForward, JumpBack, DoubleJumpForward, DoubleJumpBack, Landing, LandingSpecial, AirDodge, Fall, Turn, CrouchEnter, CrouchIdle, CrouchExit, LedgeWait, LedgeJump, LedgeGetUp, LedgeGrab, PlatformPass, FlinchGround, FlinchAir, // Attacks AttackJab1, AttackUpTilt, AttackForwardTilt, AttackDownTilt, AttackNeutralAir, AttackForwardAir, AttackDash, AttackUpSmash, }