Initial work on rollback and refactor

This commit is contained in:
2026-08-12 12:27:35 +02:00
parent ad6c1e17ac
commit a21f24b2d9
45 changed files with 1451 additions and 717 deletions
+130
View File
@@ -0,0 +1,130 @@
//! Per-fighter input state: the current and previous frame, plus the buffered
//! commands (smash inputs and friends) that states query when deciding whether
//! to interrupt.
use std::ops::{Index, IndexMut};
use bevy::prelude::*;
use bevy_ggrs::{PlayerInputs, Rollback};
use crate::game_settings::GameSettings;
use crate::input::FighterInputFrame;
use crate::netcode::GGRSCfg;
use crate::player::Player;
struct FighterInputBufferedActions {}
pub enum FighterCommands {
SmashMoveLeft,
SmashMoveRight,
}
/// 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)]
pub struct FighterCommandLifetimes {
smash_move_left: u8,
smash_move_right: u8,
}
impl Index<FighterCommands> for FighterCommandLifetimes {
type Output = u8;
fn index(&self, command: FighterCommands) -> &Self::Output {
match command {
FighterCommands::SmashMoveLeft => &self.smash_move_left,
FighterCommands::SmashMoveRight => &self.smash_move_right,
}
}
}
impl IndexMut<FighterCommands> for FighterCommandLifetimes {
fn index_mut(&mut self, command: FighterCommands) -> &mut Self::Output {
match command {
FighterCommands::SmashMoveLeft => &mut self.smash_move_left,
FighterCommands::SmashMoveRight => &mut self.smash_move_right,
}
}
}
#[derive(Component, Default, Clone, Copy)]
#[require(Rollback)]
pub struct FighterInput {
command_lifetimes: FighterCommandLifetimes,
prev_frame: FighterInputFrame,
current_frame: FighterInputFrame,
frames_in_smash_move_deadzone: u8,
}
impl FighterInput {
pub fn get_last_frame(&self) -> FighterInputFrame {
self.current_frame
}
pub fn push_input_frame(&mut self, frame: FighterInputFrame) {
self.prev_frame = self.current_frame;
self.current_frame = frame;
}
pub fn clear_buffer(&mut self) {
self.command_lifetimes = FighterCommandLifetimes::default()
}
pub fn clear_command(&mut self, command: FighterCommands) {
self.command_lifetimes[command] = 0;
}
pub fn set_lifetime(&mut self, command: FighterCommands, lifetime: u8) {
self.command_lifetimes[command] = lifetime;
}
pub fn has_command(&self, command: FighterCommands) -> bool {
self.command_lifetimes[command] > 0
}
}
/// Turns this frame's rolled-back inputs into per-fighter [`FighterInput`]
/// state. Runs in `GameplaySet::Input`, before any state looks at it.
pub fn postprocess_input(
query: Query<(&mut FighterInput, &Player)>,
inputs: Res<PlayerInputs<GGRSCfg>>,
game_settings: Res<GameSettings>,
) {
for (mut input, player) in query {
input.push_input_frame(inputs[player.handle].0);
// Directional smash detection
let x_abs = input.current_frame.movement.x.abs();
let reset_deadzone = game_settings.input_common.smash_input_reset_axis_threshold;
let axis_threshold = game_settings.input_common.smash_input_axis_threshold;
let frame_threshold = game_settings.input_common.smash_input_frame_threshold;
if x_abs < reset_deadzone {
input.frames_in_smash_move_deadzone = 0xFE;
} else {
let prev_x_abs = input.prev_frame.movement.x.abs();
if prev_x_abs < reset_deadzone
|| input.prev_frame.movement.x.signum() != input.current_frame.movement.x.signum()
{
input.frames_in_smash_move_deadzone = 0;
} else {
input.frames_in_smash_move_deadzone += 1;
}
}
if x_abs >= axis_threshold && input.frames_in_smash_move_deadzone <= frame_threshold {
input.clear_command(FighterCommands::SmashMoveLeft);
input.clear_command(FighterCommands::SmashMoveRight);
let command_to_set = if input.current_frame.movement.x > 0.0 {
FighterCommands::SmashMoveRight
} else {
FighterCommands::SmashMoveLeft
};
input.set_lifetime(command_to_set, game_settings.input_common.input_buffer_size);
if input.current_frame.movement.x > 0.0 {}
}
}
}