Rework input buffer to match HDR closer #79
@@ -45,6 +45,9 @@ impl FighterStateImpl for AirAttackState {
|
||||
}
|
||||
|
||||
fn on_enter(&mut self, state_context: &mut EnterContext) {
|
||||
state_context
|
||||
.input
|
||||
.consume_command(crate::input::FighterCommands::Attack);
|
||||
state_context.play_animation(self.attack_kind.get_animation(), false);
|
||||
let script = state_context.attack_script_assets.scripts[&self.attack_kind].clone();
|
||||
state_context.hitboxes.0 = Some(crate::fighter::FighterActiveAttack {
|
||||
|
||||
@@ -42,6 +42,9 @@ impl FighterStateImpl for GroundAttackState {
|
||||
}
|
||||
|
||||
fn on_enter(&mut self, state_context: &mut EnterContext) {
|
||||
state_context
|
||||
.input
|
||||
.consume_command(crate::input::FighterCommands::Attack);
|
||||
state_context.play_animation(self.attack_kind.get_animation(), false);
|
||||
let script = state_context.attack_script_assets.scripts[&self.attack_kind].clone();
|
||||
state_context.hitboxes.0 = Some(crate::fighter::FighterActiveAttack {
|
||||
|
||||
+72
-20
@@ -101,6 +101,8 @@ impl FighterAttackCommandType {
|
||||
struct CommandSlot {
|
||||
lifetime: u8,
|
||||
age: u16,
|
||||
hold_remaining: u16,
|
||||
hold_latched: bool,
|
||||
consumed: bool,
|
||||
}
|
||||
|
||||
@@ -109,6 +111,8 @@ impl Default for CommandSlot {
|
||||
Self {
|
||||
lifetime: 0,
|
||||
age: 0,
|
||||
hold_remaining: 0,
|
||||
hold_latched: false,
|
||||
consumed: false,
|
||||
}
|
||||
}
|
||||
@@ -220,6 +224,8 @@ impl FighterInput {
|
||||
pub fn clear_command(&mut self, command: FighterCommands) {
|
||||
let slot = &mut self.command_slots[command.index()];
|
||||
slot.lifetime = 0;
|
||||
slot.hold_remaining = 0;
|
||||
slot.hold_latched = false;
|
||||
slot.consumed = true;
|
||||
}
|
||||
|
||||
@@ -236,7 +242,10 @@ impl FighterInput {
|
||||
let slot = &mut self.command_slots[command.index()];
|
||||
slot.lifetime = lifetime;
|
||||
slot.age = 0;
|
||||
slot.hold_remaining = 0;
|
||||
slot.hold_latched = false;
|
||||
slot.consumed = false;
|
||||
self.latch_if_hold_enabled(command);
|
||||
}
|
||||
|
||||
pub fn has_command(&self, command: FighterCommands) -> bool {
|
||||
@@ -252,8 +261,9 @@ impl FighterInput {
|
||||
pub fn enable_hold_all(&mut self, max_frames: u16) {
|
||||
self.hold_all = HoldLimit {
|
||||
enabled: true,
|
||||
max_frames,
|
||||
max_frames: if max_frames == 0 { u16::MAX } else { max_frames },
|
||||
};
|
||||
self.latch_active_commands();
|
||||
}
|
||||
|
||||
pub fn disable_hold_all(&mut self) {
|
||||
@@ -263,14 +273,35 @@ impl FighterInput {
|
||||
pub fn enable_hold(&mut self, command: FighterCommands, max_frames: u16) {
|
||||
self.hold_limits[command.index()] = HoldLimit {
|
||||
enabled: true,
|
||||
max_frames,
|
||||
max_frames: if max_frames == 0 { u16::MAX } else { max_frames },
|
||||
};
|
||||
if self.has_command(command) {
|
||||
self.latch_if_hold_enabled(command);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disable_hold(&mut self, command: FighterCommands) {
|
||||
self.hold_limits[command.index()] = HoldLimit::default();
|
||||
}
|
||||
|
||||
fn latch_if_hold_enabled(&mut self, command: FighterCommands) {
|
||||
let limit = self.hold_limit(command);
|
||||
if !limit.enabled {
|
||||
return;
|
||||
}
|
||||
let slot = &mut self.command_slots[command.index()];
|
||||
slot.hold_latched = true;
|
||||
slot.hold_remaining = limit.max_frames;
|
||||
}
|
||||
|
||||
fn latch_active_commands(&mut self) {
|
||||
for command in FighterCommands::iter() {
|
||||
if self.has_command(command) {
|
||||
self.latch_if_hold_enabled(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_pressed(&self, action: DigitalAction) -> bool {
|
||||
self.current_frame
|
||||
.bindings_for(action)
|
||||
@@ -317,15 +348,19 @@ impl FighterInput {
|
||||
}
|
||||
}
|
||||
|
||||
fn update_hold(&mut self, command: FighterCommands, source_held: bool) {
|
||||
fn update_hold(&mut self, command: FighterCommands) {
|
||||
let index = command.index();
|
||||
let limit = self.hold_limit(command);
|
||||
let slot = &mut self.command_slots[index];
|
||||
slot.age = slot.age.saturating_add(1);
|
||||
if slot.lifetime == 0 && source_held && !slot.consumed {
|
||||
if limit.enabled && (limit.max_frames == 0 || slot.age < limit.max_frames) {
|
||||
if slot.hold_latched && slot.hold_remaining != 0 {
|
||||
if slot.hold_remaining != u16::MAX {
|
||||
slot.hold_remaining = slot.hold_remaining.saturating_sub(1);
|
||||
}
|
||||
if slot.lifetime == 0 {
|
||||
slot.lifetime = 1;
|
||||
}
|
||||
} else if slot.hold_latched {
|
||||
slot.hold_latched = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,17 +498,14 @@ pub fn postprocess_input(
|
||||
|
||||
// Extend only commands whose source is still held. Recognition above
|
||||
// has already reset age and cleared the consumed marker for new edges.
|
||||
let jump_held = input.current_frame.is_held(DigitalAction::Jump);
|
||||
let shield_held = input.current_frame.is_held(DigitalAction::Shield);
|
||||
let attack_held = input.current_frame.is_held(DigitalAction::Attack);
|
||||
input.update_hold(FighterCommands::Jump, jump_held);
|
||||
input.update_hold(FighterCommands::Shield, shield_held);
|
||||
input.update_hold(FighterCommands::GuardHold, shield_now.count() >= 2);
|
||||
input.update_hold(FighterCommands::Attack, attack_held);
|
||||
input.update_hold(FighterCommands::SmashMoveUp, false);
|
||||
input.update_hold(FighterCommands::SmashMoveDown, false);
|
||||
input.update_hold(FighterCommands::SmashMoveLeft, false);
|
||||
input.update_hold(FighterCommands::SmashMoveRight, false);
|
||||
input.update_hold(FighterCommands::Jump);
|
||||
input.update_hold(FighterCommands::Shield);
|
||||
input.update_hold(FighterCommands::GuardHold);
|
||||
input.update_hold(FighterCommands::Attack);
|
||||
input.update_hold(FighterCommands::SmashMoveUp);
|
||||
input.update_hold(FighterCommands::SmashMoveDown);
|
||||
input.update_hold(FighterCommands::SmashMoveLeft);
|
||||
input.update_hold(FighterCommands::SmashMoveRight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,12 +543,32 @@ mod tests {
|
||||
let mut input = FighterInput::default();
|
||||
input.enable_hold(FighterCommands::Shield, 10);
|
||||
input.set_lifetime(FighterCommands::Shield, 1);
|
||||
input.update_hold(FighterCommands::Shield, true);
|
||||
input.update_hold(FighterCommands::Shield);
|
||||
input.reduce_command_lifetime(FighterCommands::Shield);
|
||||
input.update_hold(FighterCommands::Shield, true);
|
||||
input.update_hold(FighterCommands::Shield);
|
||||
assert!(input.has_command(FighterCommands::Shield));
|
||||
input.consume_command(FighterCommands::Shield);
|
||||
input.update_hold(FighterCommands::Shield, true);
|
||||
input.update_hold(FighterCommands::Shield);
|
||||
assert!(!input.has_command(FighterCommands::Shield));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hold_buffer_keeps_released_attack_through_jump_squat() {
|
||||
let mut input = FighterInput::default();
|
||||
input.set_lifetime(FighterCommands::Attack, 3);
|
||||
input.enable_hold_all(10);
|
||||
|
||||
// The command remains latched even though its physical source is no
|
||||
// longer held. Five simulation ticks cover the test fighter's squat.
|
||||
for _ in 0..5 {
|
||||
for command in FighterCommands::iter() {
|
||||
input.reduce_command_lifetime(command);
|
||||
}
|
||||
input.update_hold(FighterCommands::Attack);
|
||||
}
|
||||
|
||||
assert!(input.has_command(FighterCommands::Attack));
|
||||
input.disable_hold_all();
|
||||
assert!(input.has_command(FighterCommands::Attack));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user