diff --git a/src/fighter/animation.rs b/src/fighter/animation.rs index 71ba4d7..6e63bd0 100644 --- a/src/fighter/animation.rs +++ b/src/fighter/animation.rs @@ -1,4 +1,5 @@ use bevy::prelude::*; +use strum_macros::EnumIter; use crate::fighter::{FighterFacingDirection, FighterVisual}; @@ -63,7 +64,7 @@ pub fn apply_animation( } #[derive( - Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize, Reflect, + Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize, Reflect, EnumIter )] pub enum AnimKind { Wait, diff --git a/src/fighter/attack.rs b/src/fighter/attack.rs index 4a5b70a..3eba7ac 100644 --- a/src/fighter/attack.rs +++ b/src/fighter/attack.rs @@ -175,7 +175,7 @@ pub fn update_active_hitbox_list(script: &FighterAttackScript, list: &mut Vec>, } -#[derive(Component, Clone, Debug, Default)] +#[derive(Component, Clone, Debug, Default, Hash)] pub struct FighterSolvedHurtboxes(pub Vec); pub fn solve_hurtboxes( diff --git a/src/fighter/baked_animation.rs b/src/fighter/baked_animation.rs index 9e3d29b..1973b17 100644 --- a/src/fighter/baked_animation.rs +++ b/src/fighter/baked_animation.rs @@ -38,7 +38,7 @@ pub const BAKED_ANIMATION_FPS: u32 = 60; /// Parent transforms are composed after /// quantization, which makes every runtime sample platform-independent. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)] pub struct FixedMat4 { pub cols: [[FGi32; 4]; 4], } diff --git a/src/fighter/collision.rs b/src/fighter/collision.rs index eff1bd4..d485a80 100644 --- a/src/fighter/collision.rs +++ b/src/fighter/collision.rs @@ -30,7 +30,7 @@ pub fn collide_fighter_with_scene( } } */ - +#[derive(Clone)] pub struct AirCollisionWithStageResult { pub line_id: StageLineID, pub hit_position: FGVec2, @@ -45,9 +45,11 @@ pub fn air_collide_with_stage( state_context.translation.0 + state_context.ecb.get_bottom_point(), ); + let mut intersect_results = vec![]; + for (poly_idx, poly) in state_context.stage_collision.stage_polys.iter().enumerate() { if let Some(intersect_result) = poly.intersect_ray(ray_segment) { - return Some(AirCollisionWithStageResult { + intersect_results.push((AirCollisionWithStageResult { line_id: { StageLineID { polygon: poly_idx, @@ -56,9 +58,14 @@ pub fn air_collide_with_stage( }, hit_position: intersect_result.position, hit_normal: intersect_result.normal, - }); + }, intersect_result.position.distance_squared(ray_segment.point2()))); } } + intersect_results.sort_by(| (_, a), (_, b) | { + a.cmp(b) + }); - None + intersect_results.iter().last().map(|(val, _)| { + val.to_owned().clone() + }) } diff --git a/src/fighter/ecb.rs b/src/fighter/ecb.rs index b05527b..c8bae0e 100644 --- a/src/fighter/ecb.rs +++ b/src/fighter/ecb.rs @@ -5,14 +5,14 @@ use crate::math::{int::FGi32, vec::FGVec2}; /// Environment collision box: the diamond used to resolve a fighter against /// the stage. Purely geometry — the collision response lives in /// [`super::collision`]. -#[derive(Component, Debug, Clone, Copy)] +#[derive(Component, Debug, Clone, Copy, Hash)] #[require(FighterPreviousECB)] pub struct FighterECB { pub vertical_half: FGi32, pub horizontal_half: FGi32, } -#[derive(Component, Debug, Clone, Copy, Default)] +#[derive(Component, Debug, Clone, Copy, Default, Hash)] pub struct FighterPreviousECB { pub vertical_half: FGi32, pub horizontal_half: FGi32, diff --git a/src/fighter/hurtbox.rs b/src/fighter/hurtbox.rs index 9024787..45f69b7 100644 --- a/src/fighter/hurtbox.rs +++ b/src/fighter/hurtbox.rs @@ -10,7 +10,7 @@ use crate::{ /// A capsule after an arbitrary affine transform. Non-uniform scale turns the /// spherical caps and circular cylinder into ellipsoidal geometry. -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Hash)] pub struct FixedAffineCapsule { pub transform: FixedMat4, pub half_length: FGi32, diff --git a/src/fighter/manifest.rs b/src/fighter/manifest.rs index 9b368f6..d2224c7 100644 --- a/src/fighter/manifest.rs +++ b/src/fighter/manifest.rs @@ -109,13 +109,17 @@ pub fn prepare_fighter_manifests( ); return; } - if !manifest.animations.contains_key(&AnimKind::Wait) { - fail( - &mut next_state, - &format!("{:?} has no Wait animation", manifest.id), - ); - return; + + for anim_kind in AnimKind::iter() { + if !manifest.animations.contains_key(&anim_kind) { + fail( + &mut next_state, + &format!("{:?} has no {:?} animation", manifest.id, anim_kind), + ); + return; + } } + if !manifest.camera.is_valid() { fail( &mut next_state, diff --git a/src/fighter/mod.rs b/src/fighter/mod.rs index 06fef19..9c1b7ce 100644 --- a/src/fighter/mod.rs +++ b/src/fighter/mod.rs @@ -144,13 +144,19 @@ impl Plugin for FighterPlugin { // components; a component simulated here but missing from this list // is a desync waiting to happen. .rollback_component_with_copy::() + .checksum_component_with_hash::() .rollback_component_with_copy::() + .checksum_component_with_hash::() .rollback_component_with_copy::() + .checksum_component_with_hash::() .rollback_component_with_copy::() + .checksum_component_with_hash::() .rollback_component_with_copy::() + .checksum_component_with_hash::() .rollback_component_with_copy::() .checksum_component_with_hash::() .rollback_component_with_copy::() + .checksum_component_with_hash::() .rollback_component_with_copy::() .rollback_resource_with_reflect::() // Debug views. @@ -176,14 +182,16 @@ impl Plugin for FighterPlugin { ) .rollback_component_with_clone::() .rollback_component_with_clone::() + .checksum_component_with_hash::() .rollback_component_with_clone::() + .checksum_component_with_hash::() .checksum_component_with_hash::() .checksum_component_with_hash::() .register_type::(); } } -#[derive(Component, Clone)] +#[derive(Component, Clone, Hash)] pub struct FighterHitboxes { pub attack_script: Option>, pub active_hitboxes: Vec, diff --git a/src/fighter/motion.rs b/src/fighter/motion.rs index 10b32f2..e01889e 100644 --- a/src/fighter/motion.rs +++ b/src/fighter/motion.rs @@ -10,16 +10,16 @@ use crate::{ math::{int::FGi32, vec::FGVec2, vec3::FGVec3}, }; -#[derive(Component, Clone, Copy)] +#[derive(Component, Clone, Copy, Hash)] pub struct Grounded; -#[derive(Component, Deref, DerefMut, Default, Debug, Clone, Copy)] +#[derive(Component, Deref, DerefMut, Default, Debug, Clone, Copy, Hash)] pub struct FighterVelocity(pub FGVec2); -#[derive(Component, Deref, DerefMut, Default, Clone, Copy)] +#[derive(Component, Deref, DerefMut, Default, Clone, Copy, Hash)] pub struct FighterPreviousTranslation(pub FGVec2); -#[derive(Component, Deref, DerefMut, Default, Clone, Copy)] +#[derive(Component, Deref, DerefMut, Default, Clone, Copy, Hash)] #[require(FighterPreviousTranslation)] pub struct FighterTranslation(pub FGVec2); diff --git a/src/fighter/state/jump.rs b/src/fighter/state/jump.rs index ead92a6..44f7187 100644 --- a/src/fighter/state/jump.rs +++ b/src/fighter/state/jump.rs @@ -221,11 +221,6 @@ impl FighterStateImpl for JumpState { }; state_context.velocity.y = vertical_vel; - state_context.velocity.x += state_context - .fighter_manifest - .attributes - .jump_horizontal_velocity - * movement_stick_x; } fn check_collision_interrupt( diff --git a/src/fighter/state/mod.rs b/src/fighter/state/mod.rs index 544410b..b17e62a 100644 --- a/src/fighter/state/mod.rs +++ b/src/fighter/state/mod.rs @@ -120,7 +120,7 @@ impl FighterStateContext<'_> { anims_data .frame_count(self.animation_frame.kind) .expect("Current animation should have baked frames") - < self.animation_frame.frame + <= self.animation_frame.frame } } diff --git a/src/input/buffer.rs b/src/input/buffer.rs index b964065..0c95838 100644 --- a/src/input/buffer.rs +++ b/src/input/buffer.rs @@ -25,7 +25,7 @@ pub enum FighterCommands { /// Frames of life remaining for each buffered command. Indexed by /// [`FighterCommands`] so adding a command is a two-line change here. -#[derive(Default, Clone, Copy)] +#[derive(Default, Clone, Copy, Hash)] pub struct FighterCommandLifetimes { smash_move_left: u8, smash_move_right: u8, @@ -60,7 +60,7 @@ impl IndexMut for FighterCommandLifetimes { } } -#[derive(Component, Default, Clone, Copy)] +#[derive(Component, Default, Clone, Copy, Hash)] #[require(Rollback)] pub struct FighterInput { command_lifetimes: FighterCommandLifetimes, diff --git a/src/input/frame.rs b/src/input/frame.rs index ef926d2..3422f67 100644 --- a/src/input/frame.rs +++ b/src/input/frame.rs @@ -4,7 +4,7 @@ use crate::math::vec::FGVec2; /// One frame of a player's raw input. This is the type GGRS serializes and /// sends over the wire, so keep it small and keep it `PartialEq`. -#[derive(Copy, Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, PartialEq, Default, Serialize, Deserialize, Hash)] pub struct FighterInputFrame { pub movement: FGVec2, pub directional_attack: FGVec2, diff --git a/src/input/mod.rs b/src/input/mod.rs index b51f647..d7deb86 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -108,7 +108,8 @@ impl Plugin for FighterInputPlugin { GgrsSchedule, buffer::postprocess_input.in_set(GameplaySet::Input), ) - .rollback_component_with_clone::(); + .rollback_component_with_clone::() + .checksum_component_with_hash::(); } } diff --git a/src/math/vec.rs b/src/math/vec.rs index d33fe14..6186b44 100644 --- a/src/math/vec.rs +++ b/src/math/vec.rs @@ -118,6 +118,12 @@ impl FGVec2 { (to - self).length() } + #[inline] + #[must_use] + pub fn distance_squared(self, to: Self) -> FGWide { + (to - self).length_squared() + } + #[inline] pub fn normalize(&self) -> Option { let x = I32F32::from_num(self.x);