152 lines
4.8 KiB
Rust
152 lines
4.8 KiB
Rust
//! Everything fighter-shaped: the components that describe a fighter, the
|
|
//! state machine that drives it, and the plugin that schedules it all.
|
|
|
|
use bevy::prelude::*;
|
|
use bevy_egui::EguiPrimaryContextPass;
|
|
use bevy_ggrs::prelude::*;
|
|
|
|
pub mod animation;
|
|
pub mod attributes;
|
|
pub mod collision;
|
|
pub mod debug;
|
|
pub mod ecb;
|
|
pub mod manifest;
|
|
pub mod motion;
|
|
pub mod state;
|
|
pub mod visual;
|
|
|
|
// The fighter components are re-exported so the rest of the crate can say
|
|
// `fighter::FighterVelocity` without caring which file it lives in. Named
|
|
// explicitly rather than globbed, so moving a type between submodules can't
|
|
// silently change what this module exports.
|
|
pub use attributes::FighterAttributes;
|
|
pub use ecb::FighterECB;
|
|
pub use manifest::FighterId;
|
|
pub use motion::{FighterPreviousTranslation, FighterTranslation, FighterVelocity, Grounded};
|
|
|
|
use crate::{
|
|
fighter::{
|
|
animation::animation_init,
|
|
state::{FighterState, fall::FallState},
|
|
visual::FighterAnimations,
|
|
},
|
|
input::FighterInput,
|
|
math::{int::FGi32, vec::FGVec2},
|
|
player::Player,
|
|
schedule::GameplaySet,
|
|
stage::line::StageCollision,
|
|
};
|
|
use state::state_interrupt_system;
|
|
|
|
/// Owns fighter simulation, the rollback registrations for fighter
|
|
/// components, and the fighter debug overlays.
|
|
#[derive(Default)]
|
|
pub struct FighterPlugin;
|
|
|
|
#[derive(Component, Copy, Clone, Hash, Debug, PartialEq, Reflect)]
|
|
pub enum FighterFacingDirection {
|
|
Left,
|
|
Right,
|
|
}
|
|
|
|
impl FighterFacingDirection {
|
|
pub fn to_sign(&self) -> FGi32 {
|
|
match self {
|
|
FighterFacingDirection::Left => FGi32::NEG_ONE,
|
|
FighterFacingDirection::Right => FGi32::ONE,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Component)]
|
|
#[require(Transform)]
|
|
pub struct FighterVisual;
|
|
|
|
impl Plugin for FighterPlugin {
|
|
fn build(&self, app: &mut App) {
|
|
app.add_systems(
|
|
Update,
|
|
(animation_init, animation::setup_fighter_animation_player),
|
|
)
|
|
// Every state gets a chance to interrupt itself, in priority order.
|
|
.add_systems(
|
|
GgrsSchedule,
|
|
(
|
|
ecb::snapshot_fighter_ecb.before(state_interrupt_system),
|
|
state::state_interrupt_system,
|
|
)
|
|
.in_set(GameplaySet::Interrupt),
|
|
)
|
|
.add_systems(
|
|
GgrsSchedule,
|
|
(state::state_update_system,).in_set(GameplaySet::StateUpdate),
|
|
)
|
|
/*.add_systems(
|
|
GgrsSchedule,
|
|
(motion::integrate_gravity, motion::apply_grounded_motion)
|
|
.chain()
|
|
.in_set(GameplaySet::Physics),
|
|
)*/
|
|
.add_systems(
|
|
GgrsSchedule,
|
|
state::state_collision_interrupt_system.in_set(GameplaySet::Collision),
|
|
)
|
|
.add_systems(
|
|
GgrsSchedule,
|
|
(
|
|
animation::apply_fighter_translation_to_visuals,
|
|
animation::apply_animation,
|
|
)
|
|
.chain()
|
|
.in_set(GameplaySet::Animation),
|
|
)
|
|
// Rollback registration lives next to the systems that write these
|
|
// components; a component simulated here but missing from this list
|
|
// is a desync waiting to happen.
|
|
.rollback_component_with_copy::<FighterECB>()
|
|
.rollback_component_with_copy::<ecb::FighterPreviousECB>()
|
|
.rollback_component_with_copy::<FighterVelocity>()
|
|
.rollback_component_with_copy::<FighterTranslation>()
|
|
.rollback_component_with_copy::<FighterPreviousTranslation>()
|
|
.rollback_component_with_copy::<FighterAttributes>()
|
|
.rollback_component_with_copy::<Grounded>()
|
|
.rollback_resource_with_reflect::<StageCollision>()
|
|
// Debug views.
|
|
.add_systems(FixedPostUpdate, debug::debug_draw_ecb)
|
|
.add_systems(EguiPrimaryContextPass, debug::fighter_debug)
|
|
.add_systems(EguiPrimaryContextPass, debug::update_config)
|
|
.rollback_component_with_clone::<FighterState>()
|
|
.checksum_component_with_hash::<FighterState>()
|
|
.register_type::<FighterFacingDirection>();
|
|
}
|
|
}
|
|
|
|
pub fn spawn_fighter(
|
|
commands: &mut Commands,
|
|
player_handle: usize,
|
|
spawn_position: FGVec2,
|
|
attributes: FighterAttributes,
|
|
animations: FighterAnimations,
|
|
visual_root: WorldAssetRoot,
|
|
) {
|
|
commands.spawn((
|
|
Name(format!("Fighter {player_handle}").into()),
|
|
Player {
|
|
handle: player_handle,
|
|
},
|
|
FighterFacingDirection::Right,
|
|
FighterECB {
|
|
vertical_half: FGi32::lit("5.0"),
|
|
horizontal_half: FGi32::lit("2.5"),
|
|
},
|
|
FighterVelocity::default(),
|
|
FighterTranslation(spawn_position),
|
|
attributes,
|
|
visual_root,
|
|
animations,
|
|
FighterState::Fall(FallState),
|
|
FighterVisual,
|
|
FighterInput::default(),
|
|
));
|
|
}
|