Compare commits

..
Author SHA1 Message Date
EIREXE 2c68e1fb55 Fix fighter visuals after landing snap
CI / build-and-test (pull_request) Successful in 1m56s
CI / production-build (pull_request) Successful in 10m36s
2026-09-26 17:40:52 +02:00
8 changed files with 18 additions and 189 deletions
-13
View File
@@ -109,7 +109,6 @@ impl FighterDebugRowKind {
enum DebugFlag { enum DebugFlag {
Network, Network,
Ecb, Ecb,
BlastZones,
StageCollision, StageCollision,
AnimationBones, AnimationBones,
AttackHitboxes, AttackHitboxes,
@@ -151,10 +150,6 @@ const FIGHTER_MENU: &[DebugMenuEntry] = &[
label: "ECB", label: "ECB",
flag: DebugFlag::Ecb, flag: DebugFlag::Ecb,
}, },
DebugMenuEntry::Toggle {
label: "Blast Zones",
flag: DebugFlag::BlastZones,
},
DebugMenuEntry::Toggle { DebugMenuEntry::Toggle {
label: "Animation bones", label: "Animation bones",
flag: DebugFlag::AnimationBones, flag: DebugFlag::AnimationBones,
@@ -248,7 +243,6 @@ pub struct DebugSettings {
pub frame_stepping: bool, pub frame_stepping: bool,
pub network: bool, pub network: bool,
pub ecb: bool, pub ecb: bool,
pub blast_zones: bool,
pub stage_collision: bool, pub stage_collision: bool,
pub animation_bones: bool, pub animation_bones: bool,
pub attack_hitboxes: bool, pub attack_hitboxes: bool,
@@ -270,7 +264,6 @@ impl Default for DebugSettings {
frame_stepping: false, frame_stepping: false,
network: false, network: false,
ecb: false, ecb: false,
blast_zones: true,
stage_collision: false, stage_collision: false,
animation_bones: false, animation_bones: false,
attack_hitboxes: false, attack_hitboxes: false,
@@ -335,7 +328,6 @@ impl DebugSettings {
match flag { match flag {
DebugFlag::Network => self.network, DebugFlag::Network => self.network,
DebugFlag::Ecb => self.ecb, DebugFlag::Ecb => self.ecb,
DebugFlag::BlastZones => self.blast_zones,
DebugFlag::StageCollision => self.stage_collision, DebugFlag::StageCollision => self.stage_collision,
DebugFlag::AnimationBones => self.animation_bones, DebugFlag::AnimationBones => self.animation_bones,
DebugFlag::AttackHitboxes => self.attack_hitboxes, DebugFlag::AttackHitboxes => self.attack_hitboxes,
@@ -350,7 +342,6 @@ impl DebugSettings {
match flag { match flag {
DebugFlag::Network => self.network = !self.network, DebugFlag::Network => self.network = !self.network,
DebugFlag::Ecb => self.ecb = !self.ecb, DebugFlag::Ecb => self.ecb = !self.ecb,
DebugFlag::BlastZones => self.blast_zones = !self.blast_zones,
DebugFlag::StageCollision => self.stage_collision = !self.stage_collision, DebugFlag::StageCollision => self.stage_collision = !self.stage_collision,
DebugFlag::AnimationBones => self.animation_bones = !self.animation_bones, DebugFlag::AnimationBones => self.animation_bones = !self.animation_bones,
DebugFlag::AttackHitboxes => self.attack_hitboxes = !self.attack_hitboxes, DebugFlag::AttackHitboxes => self.attack_hitboxes = !self.attack_hitboxes,
@@ -893,10 +884,6 @@ pub fn stage_collision_enabled(settings: Res<DebugSettings>) -> bool {
settings.stage_collision settings.stage_collision
} }
pub fn blast_zones_enabled(settings: Res<DebugSettings>) -> bool {
settings.blast_zones
}
pub fn animation_bones_enabled(settings: Res<DebugSettings>) -> bool { pub fn animation_bones_enabled(settings: Res<DebugSettings>) -> bool {
settings.animation_bones settings.animation_bones
} }
+3 -59
View File
@@ -1,14 +1,9 @@
//! Rollback-simulated state for an active match. //! Rollback-simulated state for an active match.
use bevy::input::InputSystems;
use bevy::prelude::*; use bevy::prelude::*;
use bevy_ggrs::{RunGgrsSystems, prelude::*}; use bevy_ggrs::prelude::*;
use crate::{ use crate::match_loading::{MatchConfig, MatchRules};
AppState,
match_loading::{MatchConfig, MatchRules},
netcode::session::SessionConfig,
};
#[derive(Resource, Clone, Debug, Hash, PartialEq, Eq)] #[derive(Resource, Clone, Debug, Hash, PartialEq, Eq)]
pub struct MatchState { pub struct MatchState {
@@ -24,11 +19,6 @@ pub struct PlayerMatchState {
pub deaths: u32, pub deaths: u32,
} }
#[derive(Resource, Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct PauseState {
pub paused: bool,
}
impl MatchState { impl MatchState {
pub fn from_config(config: &MatchConfig) -> Self { pub fn from_config(config: &MatchConfig) -> Self {
let stocks_remaining = match &config.rules { let stocks_remaining = match &config.rules {
@@ -56,56 +46,10 @@ pub struct MatchRuntimePlugin;
impl Plugin for MatchRuntimePlugin { impl Plugin for MatchRuntimePlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.rollback_resource_with_clone::<MatchState>() app.rollback_resource_with_clone::<MatchState>()
.checksum_resource_with_hash::<MatchState>() .checksum_resource_with_hash::<MatchState>();
.init_resource::<PauseState>()
.configure_sets(PreUpdate, RunGgrsSystems.run_if(ggrs_should_run))
.add_systems(OnEnter(AppState::InMatch), reset_pause)
.add_systems(OnExit(AppState::InMatch), reset_pause)
.add_systems(
PreUpdate,
toggle_pause
.after(InputSystems)
.before(RunGgrsSystems)
.run_if(in_state(AppState::InMatch)),
);
} }
} }
pub fn ggrs_should_run(pause: Option<Res<PauseState>>) -> bool {
!pause.is_some_and(|pause| pause.paused)
}
fn toggle_pause(
key: Res<ButtonInput<KeyCode>>,
gamepads: Query<&Gamepad>,
session: Res<SessionConfig>,
mut pause: ResMut<PauseState>,
mut virtual_time: ResMut<Time<Virtual>>,
) {
if session.is_multiplayer() {
return;
}
let pressed = key.just_pressed(KeyCode::Escape)
|| gamepads
.iter()
.any(|gamepad| gamepad.just_pressed(GamepadButton::Start));
if !pressed {
return;
}
pause.paused = !pause.paused;
virtual_time.toggle();
}
fn reset_pause(
mut pause: ResMut<PauseState>,
mut virtual_time: ResMut<Time<Virtual>>,
) {
pause.paused = false;
virtual_time.unpause();
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
+1 -1
View File
@@ -226,7 +226,7 @@ fn gamepad_input(
input.accept.accumulate(&status); input.accept.accumulate(&status);
} }
MenuInputMapAction::Start => { MenuInputMapAction::Start => {
input.start.accumulate(&status); input.accept.accumulate(&status);
} }
} }
} }
-17
View File
@@ -42,15 +42,6 @@ pub enum SessionConfigError {
} }
impl SessionConfig { impl SessionConfig {
/// Returns whether this match has more than one remote participant.
pub fn is_multiplayer(&self) -> bool {
self.players.len() > 1
|| self
.players
.iter()
.any(|player| matches!(player, PlayerConnection::Remote(_)))
}
pub fn from_args(args: &Args, player_count: usize) -> Result<Self, SessionConfigError> { pub fn from_args(args: &Args, player_count: usize) -> Result<Self, SessionConfigError> {
if player_count == 0 { if player_count == 0 {
return Err(SessionConfigError::NoPlayers); return Err(SessionConfigError::NoPlayers);
@@ -205,13 +196,6 @@ mod tests {
config.players, config.players,
[PlayerConnection::Local, PlayerConnection::Local] [PlayerConnection::Local, PlayerConnection::Local]
); );
assert!(config.is_multiplayer());
}
#[test]
fn single_local_player_is_not_multiplayer() {
let config = SessionConfig::from_args(&args(&["game", "--players", "1"]), 1).unwrap();
assert!(!config.is_multiplayer());
} }
#[test] #[test]
@@ -223,7 +207,6 @@ mod tests {
.unwrap(); .unwrap();
assert!(matches!(config.players[0], PlayerConnection::Local)); assert!(matches!(config.players[0], PlayerConnection::Local));
assert!(matches!(config.players[1], PlayerConnection::Remote(_))); assert!(matches!(config.players[1], PlayerConnection::Remote(_)));
assert!(config.is_multiplayer());
} }
#[test] #[test]
+1 -29
View File
@@ -1,12 +1,6 @@
use bevy::{color::palettes::css::RED, prelude::*}; use bevy::{color::palettes::css::RED, prelude::*};
use crate::{ use crate::stage::line::StageCollision;
match_loading::MatchConfig,
stage::{
line::StageCollision,
manifest::{StageManifest, StageManifestRegistry},
},
};
pub fn debug_draw_scene(mut gizmos: Gizmos, stage_collision: Res<StageCollision>) { pub fn debug_draw_scene(mut gizmos: Gizmos, stage_collision: Res<StageCollision>) {
for poly in stage_collision.stage_polys.iter() { for poly in stage_collision.stage_polys.iter() {
@@ -22,25 +16,3 @@ pub fn debug_draw_scene(mut gizmos: Gizmos, stage_collision: Res<StageCollision>
} }
} }
} }
pub fn debug_draw_blast_zones(
mut gizmos: Gizmos,
stage_registry: Res<StageManifestRegistry>,
stage_manifests: Res<Assets<StageManifest>>,
match_config: Res<MatchConfig>,
) {
let Some(stage_manifest_handle) = stage_registry.get(match_config.stage) else {
return;
};
let Some(stage_manifest) = stage_manifests.get(&stage_manifest_handle) else {
return;
};
let top_left = stage_manifest.blast_zone_top_left.to_vec2();
let bottom_right = stage_manifest.blast_zone_bottom_right.to_vec2();
let center = (top_left + bottom_right) / 2.0;
let size = (bottom_right - top_left).abs();
gizmos.rect(Vec3::new(center.x, center.y, 0.0), size, bevy::color::palettes::css::BLUE);
}
+6 -7
View File
@@ -7,10 +7,14 @@ use strum::IntoEnumIterator;
use strum_macros::EnumIter; use strum_macros::EnumIter;
use crate::{ use crate::{
AppState, game_settings::GameSettings, math::{int::FGi32, vec::FGVec2}, stage::{ AppState,
game_settings::GameSettings,
math::vec::FGVec2,
stage::{
StagePoly, StagePoly,
line::{StageCollision, StagePolyLineSegmentType, StagePolyType}, line::{StageCollision, StagePolyLineSegmentType, StagePolyType},
}, texture_reference::TextureReference, },
texture_reference::TextureReference,
}; };
use bevy::prelude::*; use bevy::prelude::*;
@@ -83,11 +87,6 @@ pub struct StageManifest {
pub icon: TextureReference, pub icon: TextureReference,
pub camera: StageCameraProfile, pub camera: StageCameraProfile,
pub polygons: Vec<StagePolyDefinition>, pub polygons: Vec<StagePolyDefinition>,
#[reflect(ignore)]
pub blast_zone_top_left: FGVec2,
#[reflect(ignore)]
pub blast_zone_bottom_right: FGVec2,
} }
#[derive(Resource)] #[derive(Resource)]
+4 -9
View File
@@ -10,7 +10,7 @@ pub mod scene;
pub use line::{StagePlaneIntersectResult, StagePoly}; pub use line::{StagePlaneIntersectResult, StagePoly};
use crate::{match_loading::MatchConfig, stage::{line::StageCollision, manifest::StageCameraProfile}}; use crate::stage::{line::StageCollision, manifest::StageCameraProfile};
/// Owns the stage geometry: which scene gets spawned and how it is drawn. /// Owns the stage geometry: which scene gets spawned and how it is drawn.
#[derive(Default)] #[derive(Default)]
@@ -20,14 +20,9 @@ impl Plugin for StagePlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems( app.add_systems(
Update, Update,
( debug::debug_draw_scene
(debug::debug_draw_scene .run_if(resource_exists::<line::StageCollision>)
.run_if(resource_exists::<line::StageCollision>) .run_if(crate::debug_tools::stage_collision_enabled),
.run_if(crate::debug_tools::stage_collision_enabled)),
(debug::debug_draw_blast_zones
.run_if(resource_exists::<MatchConfig>)
.run_if(crate::debug_tools::blast_zones_enabled),),
),
); );
} }
} }
+3 -54
View File
@@ -3,7 +3,7 @@ use bevy::prelude::*;
use crate::{ use crate::{
AppState, AppState,
match_loading::{MatchConfig, MatchRules}, match_loading::{MatchConfig, MatchRules},
match_runtime::{MatchState, PauseState}, match_runtime::MatchState,
}; };
pub mod battle_ui; pub mod battle_ui;
@@ -11,9 +11,6 @@ pub mod errors;
pub mod fighter_info; pub mod fighter_info;
pub mod stock_icon; pub mod stock_icon;
#[derive(Component)]
pub struct PauseOverlay;
#[derive(Default)] #[derive(Default)]
pub struct InGameUIPlugin; pub struct InGameUIPlugin;
@@ -33,56 +30,8 @@ impl Plugin for InGameUIPlugin {
} else { } else {
false false
} }
}), }),
), ),
) );
.add_systems(Update, sync_pause_overlay);
}
}
pub fn sync_pause_overlay(
pause: Res<PauseState>,
overlay: Query<Entity, With<PauseOverlay>>,
mut commands: Commands,
) {
if !pause.is_changed() {
return;
}
if pause.paused {
if overlay.is_empty() {
commands.spawn((
PauseOverlay,
Node {
position_type: PositionType::Absolute,
left: px(0),
right: px(0),
top: px(0),
bottom: px(0),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
flex_direction: FlexDirection::Column,
..default()
},
BackgroundColor(Color::srgba(0.0, 0.0, 0.0, 0.7)),
ZIndex(100),
children![
(
Text::new("Paused"),
TextFont::from_font_size(crate::menus::style::H1_SIZE as f32),
TextColor(Color::WHITE),
),
(
Text::new("Press Esc or Start to resume"),
TextFont::from_font_size(crate::menus::style::TEXT_SIZE as f32),
TextColor(Color::WHITE),
),
],
));
}
} else {
for entity in &overlay {
commands.entity(entity).despawn();
}
} }
} }