1
0
Fork 0
mirror of https://github.com/jugeeya/UltimateTrainingModpack.git synced 2024-11-28 04:44:06 +00:00

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
This commit is contained in:
sidschingis 2020-07-05 22:13:54 +02:00 committed by GitHub
parent a19245e126
commit 0fc8fdf999
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 283 additions and 200 deletions

@ -1 +1 @@
Subproject commit 4393224a14cf6cca1cd1ff7f4e8517ca58071505
Subproject commit 1bede8c899d015e8cad641926680c2da385fd5dd

View file

@ -112,21 +112,6 @@ impl From<i32> for Attack {
}
}
impl Attack {
pub fn into_attack_air_kind(&self) -> Option<i32> {
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<i32> {
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<i32> 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,

View file

@ -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,

View file

@ -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);
}

View file

@ -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<Action> = 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 {
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 {
// 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);
}

View file

@ -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)]
@ -198,43 +182,18 @@ unsafe fn mod_handle_sub_guard_cont(fighter: &mut L2CFighterCommon) {
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) {
let action = mash::get_current_buffer();
if action == Action::Jump {
return true;
}
if attack == Attack::UpB {
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;
}