Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
856f65b5b5 | ||
|
|
490a4885a4 | ||
|
|
6bab92fe25 |
+2
-5
@@ -7,10 +7,9 @@ authors = [""]
|
||||
[workspace]
|
||||
|
||||
[features]
|
||||
default = ["development", "debug_features", "gamecube-adapter"]
|
||||
default = ["development", "debug_features"]
|
||||
development = ["bevy/dynamic_linking", "bevy/file_watcher", "bevy/embedded_watcher"]
|
||||
debug_features = ["bevy/free_camera"]
|
||||
gamecube-adapter = ["dep:gc-adapter-neo"]
|
||||
|
||||
[dependencies]
|
||||
bevy = { version = "0.19", features = ["asset_processor", "basis-universal", "compressed_image_saver", "bevy_settings"] }
|
||||
@@ -35,6 +34,7 @@ strum = "0.28.0"
|
||||
strum_macros = "0.28.0"
|
||||
bevy_asset_loader = "0.27.0"
|
||||
ron_asset_manager = "0.9.0"
|
||||
gc-adapter-neo = { version = "1.1.1", features = ["libusb"] }
|
||||
rhai = "1.26.0"
|
||||
enum_cycling = "0.2.1"
|
||||
enum_cycling_derive = "0.2.1"
|
||||
@@ -49,6 +49,3 @@ inherits = "release"
|
||||
lto = "thin"
|
||||
codegen-units = 1
|
||||
strip = "symbols"
|
||||
|
||||
[target.'cfg(not(target_os = "windows"))'.dependencies]
|
||||
gc-adapter-neo = { version = "1.1.1", features = ["libusb"], optional = true }
|
||||
|
||||
@@ -107,11 +107,15 @@ impl FighterStateImpl for JumpSquatState {
|
||||
|
||||
fn on_enter(&mut self, state_context: &mut EnterContext) {
|
||||
state_context.play_animation(AnimKind::JumpSquat, false);
|
||||
// Preserve buffered inputs while jump squat makes the transition
|
||||
// temporarily unavailable, matching Ultimate/HDR-style hold input.
|
||||
state_context.input.enable_hold_all(10);
|
||||
state_context.input.clear_command(FighterCommands::Jump);
|
||||
}
|
||||
|
||||
fn on_exit(&mut self, state_context: &mut crate::fighter::state::ExitContext) {
|
||||
// The hold policy is state-scoped and must never leak into the next
|
||||
// state. Command lifetime itself remains independent of this policy.
|
||||
state_context.input.disable_hold_all();
|
||||
}
|
||||
|
||||
|
||||
+10
-1
@@ -1,4 +1,9 @@
|
||||
//! Rollback-safe input history and command buffering.
|
||||
//!
|
||||
//! Raw mapped bindings, button edges, and command lifetimes are deliberately
|
||||
//! separate. This preserves combinations such as pressing a second shield
|
||||
//! binding while the first one is held, while still allowing a command to
|
||||
//! survive after its source button is released.
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_ggrs::{PlayerInputs, Rollback};
|
||||
@@ -271,12 +276,14 @@ impl FighterInput {
|
||||
.saturating_sub(1);
|
||||
}
|
||||
|
||||
/// Holds all inputs for n frames
|
||||
pub fn enable_hold_all(&mut self, max_frames: u16) {
|
||||
self.hold_all = HoldLimit::from_max_frames(max_frames);
|
||||
self.latch_active_commands();
|
||||
}
|
||||
|
||||
/// Stops enrolling commands in the global hold buffer. Commands already
|
||||
/// latched retain their remaining lifetime so they can cross a state
|
||||
/// boundary, such as jump squat into the first airborne frame.
|
||||
pub fn disable_hold_all(&mut self) {
|
||||
self.hold_all = HoldLimit::Disabled;
|
||||
}
|
||||
@@ -546,6 +553,8 @@ pub fn postprocess_input(
|
||||
input.set_attack_command_type(attack_type);
|
||||
}
|
||||
|
||||
// Advance each command's independently latched persistence. Physical
|
||||
// source state is intentionally irrelevant after recognition.
|
||||
for command in FighterCommands::iter() {
|
||||
input.update_hold(command);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@ use bevy::input::gamepad::{
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
|
||||
use super::GcPorts;
|
||||
use super::poll::GcAdapterSnapshot;
|
||||
use super::poll::{GcAdapterSnapshot, GcPortState};
|
||||
|
||||
/// GameCube adapters report 4 ports.
|
||||
pub const GC_PORT_COUNT: usize = 4;
|
||||
@@ -24,6 +23,15 @@ const GC_ADAPTER_PID: u16 = 0x0337;
|
||||
#[derive(Component, Debug, Clone, Copy)]
|
||||
pub struct GcPort(pub u8);
|
||||
|
||||
/// Tracks which entity (if any) represents each of the 4 adapter ports,
|
||||
/// and the last snapshot seen, so both systems below can diff instead of
|
||||
/// re-deriving state every frame.
|
||||
#[derive(Resource, Default)]
|
||||
pub struct GcPorts {
|
||||
pub entities: [Option<Entity>; GC_PORT_COUNT],
|
||||
last: [GcPortState; GC_PORT_COUNT],
|
||||
}
|
||||
|
||||
fn passthrough_gamepad_settings() -> GamepadSettings {
|
||||
let mut settings = GamepadSettings::default();
|
||||
for axis in GamepadAxis::all() {
|
||||
|
||||
+16
-32
@@ -1,23 +1,10 @@
|
||||
#[cfg(all(feature = "gamecube-adapter", not(target_os = "windows")))]
|
||||
pub mod bridge;
|
||||
#[cfg(all(feature = "gamecube-adapter", not(target_os = "windows")))]
|
||||
pub mod poll;
|
||||
|
||||
#[cfg(all(feature = "gamecube-adapter", not(target_os = "windows")))]
|
||||
pub use bridge::{GC_PORT_COUNT, GcPort};
|
||||
#[cfg(all(feature = "gamecube-adapter", not(target_os = "windows")))]
|
||||
pub use bridge::{GC_PORT_COUNT, GcPort, GcPorts};
|
||||
pub use poll::{GcAdapterSnapshot, GcPortState};
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
pub struct GcPorts {
|
||||
pub entities: [Option<Entity>; 4],
|
||||
#[cfg(all(feature = "gamecube-adapter", not(target_os = "windows")))]
|
||||
pub(super) last: [poll::GcPortState; 4],
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "gamecube-adapter", not(target_os = "windows")))]
|
||||
use bevy::input::InputSystems;
|
||||
#[cfg(all(feature = "gamecube-adapter", not(target_os = "windows")))]
|
||||
use bevy::input::gamepad::{gamepad_connection_system, gamepad_event_processing_system};
|
||||
use bevy::prelude::*;
|
||||
|
||||
@@ -28,24 +15,21 @@ impl Plugin for GamecubeAdapterPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_resource::<GcPorts>();
|
||||
|
||||
#[cfg(all(feature = "gamecube-adapter", not(target_os = "windows")))]
|
||||
{
|
||||
let Ok(snapshot) = poll::spawn_poll_thread() else {
|
||||
// No adapter connected; leave GcPorts empty and skip wiring the
|
||||
// polling systems.
|
||||
return;
|
||||
};
|
||||
let Ok(snapshot) = poll::spawn_poll_thread() else {
|
||||
// No adapter connected; leave GcPorts empty and skip wiring the
|
||||
// polling systems.
|
||||
return;
|
||||
};
|
||||
|
||||
app.insert_resource(snapshot).add_systems(
|
||||
PreUpdate,
|
||||
(
|
||||
bridge::gc_connection_system.before(gamepad_connection_system),
|
||||
bridge::gc_event_system
|
||||
.after(gamepad_connection_system)
|
||||
.before(gamepad_event_processing_system),
|
||||
)
|
||||
.in_set(InputSystems),
|
||||
);
|
||||
}
|
||||
app.insert_resource(snapshot).add_systems(
|
||||
PreUpdate,
|
||||
(
|
||||
bridge::gc_connection_system.before(gamepad_connection_system),
|
||||
bridge::gc_event_system
|
||||
.after(gamepad_connection_system)
|
||||
.before(gamepad_event_processing_system),
|
||||
)
|
||||
.in_set(InputSystems),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user