From 0fc8fdf999366977a4dfae92ea690ad4ff43f81a Mon Sep 17 00:00:00 2001 From: sidschingis Date: Sun, 5 Jul 2020 22:13:54 +0200 Subject: [PATCH] Followups (#108) * Fix Late Mash Fixed being a frame late Flag is now immediately returned until the action is performed Also restore Airdodge => Shield when grounded behavior * Update TrainingModpackOverlay * Add Follow Ups WIP Attack followups will currently overwrite the aerial toggle * Simplify Shield Extra attack handling is no longer needed * Apply Action Enum Replacing Mash * Fix Random OOS * Implement Queued Mashes * Update Shield Suspension Removed need for frame counter. Update shield suspension when the mash buffer is updated. Fixes follow ups oos * Cleanup * Cleanup * Update Aerial Flags * Fix Aerial Flag * Fix Copy Paste Error * Use Mash Toggles For Ledge Jump Moved menu logic to buffer_menu_mash --- TrainingModpackOverlay | 2 +- src/common/consts.rs | 95 +++++++++++++++--- src/common/mod.rs | 1 + src/training/ledge.rs | 7 +- src/training/mash.rs | 221 +++++++++++++++++++++++++---------------- src/training/shield.rs | 157 +++++++++++------------------ 6 files changed, 283 insertions(+), 200 deletions(-) diff --git a/TrainingModpackOverlay b/TrainingModpackOverlay index 4393224..1bede8c 160000 --- a/TrainingModpackOverlay +++ b/TrainingModpackOverlay @@ -1 +1 @@ -Subproject commit 4393224a14cf6cca1cd1ff7f4e8517ca58071505 +Subproject commit 1bede8c899d015e8cad641926680c2da385fd5dd diff --git a/src/common/consts.rs b/src/common/consts.rs index 7182fb8..6569593 100644 --- a/src/common/consts.rs +++ b/src/common/consts.rs @@ -112,21 +112,6 @@ impl From for Attack { } } -impl Attack { - pub fn into_attack_air_kind(&self) -> Option { - use Attack::*; - - Some(match self { - Nair => *FIGHTER_COMMAND_ATTACK_AIR_KIND_N, - Fair => *FIGHTER_COMMAND_ATTACK_AIR_KIND_F, - Bair => *FIGHTER_COMMAND_ATTACK_AIR_KIND_B, - Dair => *FIGHTER_COMMAND_ATTACK_AIR_KIND_LW, - UpAir => *FIGHTER_COMMAND_ATTACK_AIR_KIND_HI, - _ => return None, - }) - } -} - // Ledge Option #[repr(i32)] #[derive(Debug, Copy, Clone, PartialEq)] @@ -268,12 +253,92 @@ pub enum OnOff { On = 1, } +#[repr(i32)] +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Action { + Nothing = 0, + Airdodge = 1, + Jump = 2, + Spotdodge = 3, + RollForward = 4, + RollBack = 5, + Nair = 6, + Fair = 7, + Bair = 8, + UpAir = 9, + Dair = 10, + NeutralB = 11, + SideB = 12, + UpB = 13, + DownB = 14, + UpSmash = 15, + FSmash = 16, + DSmash = 17, + Grab = 18, + Jab = 19, + Ftilt = 20, + Utilt = 21, + Dtilt = 22, + Shield = 99, +} + +impl Action { + pub fn into_attack_air_kind(&self) -> Option { + use Action::*; + + Some(match self { + Nair => *FIGHTER_COMMAND_ATTACK_AIR_KIND_N, + Fair => *FIGHTER_COMMAND_ATTACK_AIR_KIND_F, + Bair => *FIGHTER_COMMAND_ATTACK_AIR_KIND_B, + Dair => *FIGHTER_COMMAND_ATTACK_AIR_KIND_LW, + UpAir => *FIGHTER_COMMAND_ATTACK_AIR_KIND_HI, + _ => return None, + }) + } +} + +// To satisfy the unused warning +impl From for Action { + fn from(x: i32) -> Self { + use Action::*; + + match x { + 0 => Nothing, + 1 => Airdodge, + 2 => Jump, + 3 => Spotdodge, + 4 => RollForward, + 5 => RollBack, + 6 => Nair, + 7 => Fair, + 8 => Bair, + 9 => UpAir, + 10 => Dair, + 11 => NeutralB, + 12 => SideB, + 13 => UpB, + 14 => DownB, + 15 => UpSmash, + 16 => FSmash, + 17 => DSmash, + 18 => Grab, + 19 => Jab, + 20 => Ftilt, + 21 => Utilt, + 22 => Dtilt, + 99 => Action::Shield, + _ => Nothing, + } + } +} + #[repr(C)] pub struct TrainingModpackMenu { pub hitbox_vis: HitboxVisualization, pub di_state: Direction, pub left_stick: Direction, // Currently only used for air dodge direction pub mash_attack_state: Attack, + pub follow_up: Action, pub ledge_state: LedgeOption, pub tech_state: TechOption, pub mash_state: Mash, diff --git a/src/common/mod.rs b/src/common/mod.rs index ed345c7..60b5aa7 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -9,6 +9,7 @@ pub static mut MENU_STRUCT: consts::TrainingModpackMenu = consts::TrainingModpac di_state: Direction::None, left_stick: Direction::None, mash_attack_state: Attack::Nair, + follow_up: Action::Nothing, ledge_state: LedgeOption::Random, tech_state: TechOption::Random, mash_state: Mash::None, diff --git a/src/training/ledge.rs b/src/training/ledge.rs index 360f659..0879afe 100644 --- a/src/training/ledge.rs +++ b/src/training/ledge.rs @@ -40,7 +40,12 @@ pub unsafe fn force_option(module_accessor: &mut app::BattleObjectModuleAccessor status = new_status; } - mash::perform_defensive_option(); + match ledge_case { + LedgeOption::Jump => { + mash::buffer_menu_mash(module_accessor); + } + _ => mash::perform_defensive_option(), + } StatusModule::change_status_request_from_script(module_accessor, status, true); } diff --git a/src/training/mash.rs b/src/training/mash.rs index 2bd876d..dc7547c 100644 --- a/src/training/mash.rs +++ b/src/training/mash.rs @@ -6,43 +6,60 @@ use smash::app::{self, lua_bind::*}; use smash::hash40; use smash::lib::lua_const::*; -static mut BUFFERED_ACTION: Mash = Mash::None; -static mut BUFFERED_ATTACK: Attack = Attack::Nair; +static mut CURRENT_AERIAL: Action = Action::Nair; +static mut QUEUE: Vec = vec![]; -pub fn buffer_action(action: Mash) { +pub fn buffer_action(action: Action) { unsafe { - if BUFFERED_ACTION != Mash::None { + if QUEUE.len() > 0 { return; } } unsafe { - BUFFERED_ACTION = action; + QUEUE.insert(0, action); + buffer_follow_up(); } } -pub fn get_current_buffer() -> Mash { - unsafe { BUFFERED_ACTION } -} +pub fn buffer_follow_up() { + let action; -pub fn set_attack(attack: Attack) { unsafe { - if BUFFERED_ATTACK == attack { - return; - } + action = MENU.follow_up; } + + if action == Action::Nothing { + return; + } + unsafe { - BUFFERED_ATTACK = attack; + QUEUE.insert(0, action); } } -pub fn get_current_attack() -> Attack { - unsafe { BUFFERED_ATTACK } +pub fn get_current_buffer() -> Action { + unsafe { + let current = QUEUE.last().unwrap_or(&Action::Nothing); + *current + } } pub fn reset() { unsafe { - BUFFERED_ACTION = Mash::None; + QUEUE.pop(); + } + + shield::suspend_shield(get_current_buffer()); +} + +pub fn set_aerial(attack: Action) { + if !shield::is_aerial(attack) { + return; + } + + unsafe { + CURRENT_AERIAL = attack; } } @@ -57,7 +74,7 @@ pub unsafe fn get_attack_air_kind( return None; } - BUFFERED_ATTACK.into_attack_air_kind() + CURRENT_AERIAL.into_attack_air_kind() } pub unsafe fn get_command_flag_cat( @@ -83,7 +100,7 @@ pub unsafe fn get_command_flag_cat( } unsafe fn check_buffer(module_accessor: &mut app::BattleObjectModuleAccessor) { - if BUFFERED_ACTION != Mash::None { + if QUEUE.len() > 0 { return; } @@ -91,11 +108,41 @@ unsafe fn check_buffer(module_accessor: &mut app::BattleObjectModuleAccessor) { return; } - let mut action = MENU.mash_state; + buffer_menu_mash(module_accessor); +} - if action == Mash::Random { - let mut random_cmds = vec![Mash::Jump, Mash::Attack]; +// Temp Translation +pub fn buffer_menu_mash(module_accessor: &mut app::BattleObjectModuleAccessor) -> Action { + unsafe { + let action; + if MENU.mash_state == Mash::Random { + action = get_random_action(module_accessor); + } else { + action = mash_to_action(MENU.mash_state); + } + buffer_action(action); + action + } +} + +pub fn mash_to_action(mash: Mash) -> Action { + use Action::*; + match mash { + Mash::Airdodge => Airdodge, + Mash::Jump => Jump, + Mash::Spotdodge => Spotdodge, + Mash::RollForward => RollForward, + Mash::RollBack => RollBack, + Mash::Shield => Shield, + Mash::Attack => unsafe { attack_to_action(MENU.mash_attack_state) }, + _ => Nothing, + } +} + +fn get_random_action(module_accessor: &mut app::BattleObjectModuleAccessor) -> Action { + let mut random_cmds = vec![Mash::Jump, Mash::Attack]; + unsafe { if is_airborne(module_accessor) { random_cmds.push(Mash::Airdodge); } @@ -109,20 +156,45 @@ unsafe fn check_buffer(module_accessor: &mut app::BattleObjectModuleAccessor) { let random_cmd_index = app::sv_math::rand(hash40("fighter"), random_cmds.len() as i32) as usize; - action = random_cmds[random_cmd_index]; + mash_to_action(random_cmds[random_cmd_index]) } +} - buffer_action(action); - set_attack(MENU.mash_attack_state); +fn attack_to_action(attack: Attack) -> Action { + use Action::*; + match attack { + Attack::Nair => Nair, + Attack::Fair => Fair, + Attack::Bair => Bair, + Attack::UpAir => UpAir, + Attack::Dair => Dair, + Attack::NeutralB => NeutralB, + Attack::SideB => SideB, + Attack::UpB => UpB, + Attack::DownB => DownB, + Attack::UpSmash => UpSmash, + Attack::FSmash => FSmash, + Attack::DSmash => DSmash, + Attack::Grab => Grab, + Attack::Jab => Jab, + Attack::Ftilt => Ftilt, + Attack::Utilt => Utilt, + Attack::Dtilt => Dtilt, + Attack::Nothing => Nothing, + } } unsafe fn perform_action(module_accessor: &mut app::BattleObjectModuleAccessor) -> i32 { - match BUFFERED_ACTION { - Mash::Airdodge => { + use Action::*; + + let action = get_current_buffer(); + + match action { + Airdodge => { // Shield if grounded instead if is_grounded(module_accessor) { reset(); - buffer_action(Mash::Shield); + buffer_action(Shield); return 0; } @@ -132,50 +204,42 @@ unsafe fn perform_action(module_accessor: &mut app::BattleObjectModuleAccessor) *FIGHTER_PAD_CMD_CAT1_FLAG_AIR_ESCAPE, ); } - Mash::Jump => { + Jump => { return update_jump_flag(module_accessor); } - Mash::Spotdodge => { + Spotdodge => { return get_flag( module_accessor, *FIGHTER_STATUS_KIND_ESCAPE, *FIGHTER_PAD_CMD_CAT1_FLAG_ESCAPE, ); } - Mash::RollForward => { + RollForward => { return get_flag( module_accessor, *FIGHTER_STATUS_KIND_ESCAPE_F, *FIGHTER_PAD_CMD_CAT1_FLAG_ESCAPE_F, ); } - Mash::RollBack => { + RollBack => { return get_flag( module_accessor, *FIGHTER_STATUS_KIND_ESCAPE_B, *FIGHTER_PAD_CMD_CAT1_FLAG_ESCAPE_B, ); } - Mash::Attack => { - return get_attack_flag(module_accessor); - } - Mash::Shield => { + Shield => { /* Doesn't actually cause the shield, but will clear the buffer once shield is possible. Shield hold is performed trough shield::should_hold_shield */ - // return get_flag( - // module_accessor, - // *FIGHTER_STATUS_KIND_GUARD_ON, - // *FIGHTER_PAD_CMD_CAT1_FLAG_AIR_ESCAPE, - // ); return get_flag( module_accessor, *FIGHTER_STATUS_KIND_GUARD_ON, *FIGHTER_PAD_CMD_CAT1_FLAG_AIR_ESCAPE, ); } - _ => return 0, + _ => return get_attack_flag(module_accessor, action), } } @@ -197,15 +261,18 @@ unsafe fn update_jump_flag(module_accessor: &mut app::BattleObjectModuleAccessor ); } -unsafe fn get_attack_flag(module_accessor: &mut app::BattleObjectModuleAccessor) -> i32 { - use Attack::*; +unsafe fn get_attack_flag( + module_accessor: &mut app::BattleObjectModuleAccessor, + action: Action, +) -> i32 { + use Action::*; let action_flag: i32; let status: i32; - match BUFFERED_ATTACK { + match action { Nair | Fair | Bair | UpAir | Dair => { - return get_aerial_flag(module_accessor, BUFFERED_ATTACK); + return get_aerial_flag(module_accessor, action); } NeutralB => { action_flag = *FIGHTER_PAD_CMD_CAT1_FLAG_SPECIAL_N; @@ -268,22 +335,16 @@ unsafe fn get_attack_flag(module_accessor: &mut app::BattleObjectModuleAccessor) unsafe fn get_aerial_flag( module_accessor: &mut app::BattleObjectModuleAccessor, - attack: Attack, + action: Action, ) -> i32 { let mut flag: i32 = 0; // If we are grounded we also need to jump if is_grounded(module_accessor) { - flag += update_jump_flag(module_accessor); - - if flag == 0 { - // Can't jump, return - return 0; - } + flag |= *FIGHTER_PAD_CMD_CAT1_FLAG_JUMP_BUTTON; // Delay attack until we are airborne to get a full hop if MENU.full_hop == OnOff::On { - buffer_action(Mash::Attack); return flag; } } @@ -291,38 +352,27 @@ unsafe fn get_aerial_flag( let status = *FIGHTER_STATUS_KIND_ATTACK_AIR; if MENU.falling_aerials == OnOff::On && !fast_fall::is_falling(module_accessor) { - // Keep Buffering until we are falling - buffer_action(Mash::Attack); return flag; } let action_flag: i32; + use Action::*; - match attack { - Attack::Nair => { - action_flag = *FIGHTER_COMMAND_ATTACK_AIR_KIND_N; - } - Attack::Fair => { - // For some reason the game doesn't trigger the fair correctly - // action_flag = *FIGHTER_COMMAND_ATTACK_AIR_KIND_F; - action_flag = *FIGHTER_COMMAND_ATTACK_AIR_KIND_N; - } - Attack::Bair => { - action_flag = *FIGHTER_COMMAND_ATTACK_AIR_KIND_B; - } - Attack::UpAir => { - // For some reason the game doesn't trigger the uair correctly - // action_flag = *FIGHTER_COMMAND_ATTACK_AIR_KIND_HI; - action_flag = *FIGHTER_COMMAND_ATTACK_AIR_KIND_N; - } - Attack::Dair => { - action_flag = *FIGHTER_COMMAND_ATTACK_AIR_KIND_LW; + /* + * We always trigger attack and change it later into the correct aerial + * @see get_attack_air_kind() + */ + match action { + Nair | Fair | Bair | UpAir | Dair => { + action_flag = *FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_N; } _ => { action_flag = 0; } } + set_aerial(action); + flag |= get_flag(module_accessor, status, action_flag); flag @@ -347,7 +397,7 @@ unsafe fn get_flag( pub unsafe fn perform_defensive_option() { reset(); - let mut shield_suspension_frames = 60; + let action; match MENU.defensive_state { Defensive::Random => { @@ -361,28 +411,27 @@ pub unsafe fn perform_defensive_option() { let random_cmd_index = app::sv_math::rand(hash40("fighter"), random_cmds.len() as i32) as usize; - buffer_action(random_cmds[random_cmd_index]); - set_attack(Attack::Jab); + action = mash_to_action(random_cmds[random_cmd_index]); } Defensive::Roll => { if app::sv_math::rand(hash40("fighter"), 2) == 0 { - buffer_action(Mash::RollForward); + action = Action::RollForward; } else { - buffer_action(Mash::RollBack); + action = Action::RollBack; } } - Defensive::Spotdodge => buffer_action(Mash::Spotdodge), + Defensive::Spotdodge => action = Action::Spotdodge, Defensive::Jab => { - buffer_action(Mash::Attack); - set_attack(Attack::Jab); + action = Action::Jab; } Defensive::Shield => { - shield_suspension_frames = 0; - buffer_action(Mash::Shield); + action = Action::Shield; } - _ => (shield_suspension_frames = 0), + _ => return, } + buffer_action(action); + // Suspend shield hold to allow for other defensive options - shield::suspend_shield(shield_suspension_frames); + shield::suspend_shield(action); } diff --git a/src/training/shield.rs b/src/training/shield.rs index 27c25c9..2bdee2e 100644 --- a/src/training/shield.rs +++ b/src/training/shield.rs @@ -15,15 +15,13 @@ static mut MULTI_HIT_OFFSET: u32 = unsafe { MENU.oos_offset }; // Used to only decrease once per shieldstun change static mut WAS_IN_SHIELDSTUN: bool = false; -static mut FRAME_COUNTER_INDEX: usize = 0; static mut REACTION_INDEX: usize = 0; // For how many frames should the shield hold be overwritten -static mut SHIELD_SUSPEND_FRAMES: u32 = 0; +static mut SUSPEND_SHIELD: bool = false; pub fn init() { unsafe { - FRAME_COUNTER_INDEX = frame_counter::register_counter(); REACTION_INDEX = frame_counter::register_counter(); } } @@ -132,37 +130,23 @@ pub unsafe fn get_param_float( None } -pub unsafe fn should_hold_shield(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { +pub fn should_hold_shield() -> bool { // Mash shield - if mash::get_current_buffer() == Mash::Shield { + if mash::get_current_buffer() == Action::Shield { return true; } + let shield_state; + unsafe { + shield_state = &MENU.shield_state; + } + // We should hold shield if the state requires it - if ![Shield::Hold, Shield::Infinite].contains(&MENU.shield_state) { + if ![Shield::Hold, Shield::Infinite].contains(shield_state) { return false; } - // Hold shield while OOS is not allowed - if !allow_oos() { - return true; - } - - if !was_in_shieldstun(module_accessor) { - return true; - } - - match mash::get_current_buffer() { - Mash::Attack => {} // Handle attack below - // If we are not mashing attack then we will always hold shield - _ => return true, - } - - // We will hold shield if we are in shieldstun and our attack can be performed OOS - match mash::get_current_attack() { - Attack::Grab => return true, // Grab has 4 extra shield frames - _ => return false, - } + true } #[skyline::hook(replace = smash::lua2cpp::L2CFighterCommon_sub_guard_cont)] @@ -194,47 +178,22 @@ unsafe fn mod_handle_sub_guard_cont(fighter: &mut L2CFighterCommon) { return; } - if frame_counter::should_delay(MENU.reaction_time, REACTION_INDEX){ + if frame_counter::should_delay(MENU.reaction_time, REACTION_INDEX) { return; } + let action = mash::buffer_menu_mash(module_accessor); + if handle_escape_option(fighter, module_accessor) { return; } - mash::buffer_action(MENU.mash_state); - mash::set_attack(MENU.mash_attack_state); - if needs_oos_handling_drop_shield() { return; } - // Set shield suspension frames - match MENU.mash_state { - Mash::Attack => match MENU.mash_attack_state { - Attack::UpSmash => {} - Attack::Grab => {} - _ => { - // Force shield drop - suspend_shield(15); - } - }, - - _ => {} - } -} - -// Needed for shield drop options -pub fn suspend_shield(frames: u32) { - if frames <= 0 { - return; - } - - unsafe { - SHIELD_SUSPEND_FRAMES = frames; - frame_counter::reset_frame_count(FRAME_COUNTER_INDEX); - frame_counter::start_counting(FRAME_COUNTER_INDEX); - } + // Set shield suspension + suspend_shield(action); } /** @@ -279,20 +238,20 @@ unsafe fn handle_escape_option( return false; } - match MENU.mash_state { - Mash::Spotdodge => { + match mash::get_current_buffer() { + Action::Spotdodge => { fighter .fighter_base .change_status(FIGHTER_STATUS_KIND_ESCAPE.as_lua_int(), LUA_TRUE); return true; } - Mash::RollForward => { + Action::RollForward => { fighter .fighter_base .change_status(FIGHTER_STATUS_KIND_ESCAPE_F.as_lua_int(), LUA_TRUE); return true; } - Mash::RollBack => { + Action::RollBack => { fighter .fighter_base .change_status(FIGHTER_STATUS_KIND_ESCAPE_B.as_lua_int(), LUA_TRUE); @@ -306,55 +265,59 @@ unsafe fn handle_escape_option( * Needed to allow these attacks to work OOS */ fn needs_oos_handling_drop_shield() -> bool { - match mash::get_current_buffer() { - Mash::Jump => return true, - Mash::Attack => { - let attack = mash::get_current_attack(); - if is_aerial(attack) { - return true; - } + let action = mash::get_current_buffer(); - if attack == Attack::UpB { - return true; - } - } - _ => {} + if action == Action::Jump { + return true; + } + + if is_aerial(action) { + return true; + } + + if action == Action::UpB { + return true; } false } -fn is_aerial(attack: Attack) -> bool { - match attack { - Attack::Nair => return true, - Attack::Fair => return true, - Attack::Bair => return true, - Attack::UpAir => return true, - Attack::Dair => return true, +pub fn is_aerial(action: Action) -> bool { + match action { + Action::Nair => return true, + Action::Fair => return true, + Action::Bair => return true, + Action::UpAir => return true, + Action::Dair => return true, _ => return false, } } +// Needed for shield drop options +pub fn suspend_shield(action: Action) { + unsafe { + SUSPEND_SHIELD = need_suspend_shield(action); + } +} + +fn need_suspend_shield(action: Action) -> bool { + match action { + Action::UpSmash => false, + Action::Grab => false, + Action::Shield => false, + Action::Nothing => false, + _ => { + // Force shield drop + true + } + } +} + /** * Needed for these options to work OOS */ -unsafe fn shield_is_suspended() -> bool { - // Normal behavior when not mashing - if SHIELD_SUSPEND_FRAMES == 0 { - return false; - } - - let resume_normal_behavior = - frame_counter::get_frame_count(FRAME_COUNTER_INDEX) > SHIELD_SUSPEND_FRAMES; - - if resume_normal_behavior { - SHIELD_SUSPEND_FRAMES = 0; - frame_counter::stop_counting(FRAME_COUNTER_INDEX); - - return false; - } - - true +fn shield_is_suspended() -> bool { + unsafe { SUSPEND_SHIELD } } /** @@ -376,7 +339,7 @@ unsafe fn should_return_none_in_check_button( return true; } - if !should_hold_shield(module_accessor) { + if !should_hold_shield() { return true; }