From 153ce48f853b09d8621d90d71edc86cfdaed313c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Rom=C3=A0n?= Date: Tue, 11 Aug 2026 01:11:09 +0200 Subject: [PATCH] Implement short hop aerial macro in CSS menu --- dynamic/src/ext.rs | 1 + .../common/src/function_hooks/controls.rs | 12 ++++ .../common/src/general_statuses/jumpsquat.rs | 14 +++- src/controls/submenu.rs | 69 ++++++++++++++++++- src/lua.rs | 7 ++ 5 files changed, 98 insertions(+), 5 deletions(-) diff --git a/dynamic/src/ext.rs b/dynamic/src/ext.rs index d92bfbd6bb..d453195afa 100644 --- a/dynamic/src/ext.rs +++ b/dynamic/src/ext.rs @@ -328,6 +328,7 @@ bitflags! { const CStickOverride = 0x200000; const RivalsWallJump = 0x400000; const TreadJump = 0x800000; + const ShortHopAerialMacro = 0x1000000; const SpecialAll = 0x20802; const AttackAll = 0x201; diff --git a/fighters/common/src/function_hooks/controls.rs b/fighters/common/src/function_hooks/controls.rs index 55d6baf9b4..8e60465589 100644 --- a/fighters/common/src/function_hooks/controls.rs +++ b/fighters/common/src/function_hooks/controls.rs @@ -891,10 +891,22 @@ unsafe fn map_controls_hook( _ => (*mappings).pro_absmash & 4 != 0, }; + let is_short_hop_aerial_macro = match controller.style { + ControllerStyle::GCController => (*mappings).gc_absmash & 8 != 0, + ControllerStyle::LeftJoycon | ControllerStyle::RightJoycon => { + (*mappings).joy_absmash & 8 != 0 + } + _ => (*mappings).pro_absmash & 8 != 0, + }; + if is_rivals_walljump { (*out).buttons |= Buttons::RivalsWallJump; } + if is_short_hop_aerial_macro { + (*out).buttons |= Buttons::ShortHopAerialMacro; + } + let (parry_manual, hold) = if is_parry_taunt { (Buttons::AppealAll, Buttons::Special) } else { diff --git a/fighters/common/src/general_statuses/jumpsquat.rs b/fighters/common/src/general_statuses/jumpsquat.rs index 43b325e03b..e266122ed1 100644 --- a/fighters/common/src/general_statuses/jumpsquat.rs +++ b/fighters/common/src/general_statuses/jumpsquat.rs @@ -236,6 +236,10 @@ unsafe fn status_JumpSquat_common(fighter: &mut L2CFighterCommon, lr_update: L2C } // if we are doing a buffered aerial we want to disable all of the other transitions if WorkModule::is_flag(fighter.module_accessor, *FIGHTER_INSTANCE_WORK_ID_FLAG_JUMP_MINI_ATTACK) { + // short hop aerial macro: buffering an aerial out of jumpsquat always short hops + if fighter.is_button_on(Buttons::ShortHopAerialMacro) { + WorkModule::on_flag(fighter.module_accessor, *FIGHTER_STATUS_WORK_ID_FLAG_RESERVE_JUMP_MINI); + } WorkModule::on_flag(fighter.module_accessor, *FIGHTER_STATUS_JUMP_FLAG_RESERVE_ATTACK_BUTTON_ON); for x in potential_enables.iter() { WorkModule::unable_transition_term(fighter.module_accessor, *x); @@ -364,8 +368,10 @@ unsafe fn sub_jump_squat_uniq_check_sub(fighter: &mut L2CFighterCommon, flag: L2 if ControlModule::check_button_trigger(fighter.module_accessor, *CONTROL_PAD_BUTTON_CSTICK_ON) && ControlModule::check_button_trigger(fighter.module_accessor, *CONTROL_PAD_BUTTON_ATTACK) && ControlModule::check_button_off(fighter.module_accessor, *CONTROL_PAD_BUTTON_SPECIAL) { - // uncomment for short hop aerial only - // WorkModule::on_flag(fighter.module_accessor, *FIGHTER_STATUS_WORK_ID_FLAG_RESERVE_JUMP_MINI); + // short hop aerial only, when the macro is enabled + if fighter.is_button_on(Buttons::ShortHopAerialMacro) { + WorkModule::on_flag(fighter.module_accessor, *FIGHTER_STATUS_WORK_ID_FLAG_RESERVE_JUMP_MINI); + } } } } @@ -404,6 +410,10 @@ unsafe fn sub_jump_squat_uniq_check_sub_mini_attack(fighter: &mut L2CFighterComm WorkModule::unable_transition_term(fighter.module_accessor, *x); } WorkModule::on_flag(fighter.module_accessor, *FIGHTER_INSTANCE_WORK_ID_FLAG_JUMP_MINI_ATTACK); + // short hop aerial macro: pressing attack during jumpsquat always short hops + if fighter.is_button_on(Buttons::ShortHopAerialMacro) { + WorkModule::on_flag(fighter.module_accessor, *FIGHTER_STATUS_WORK_ID_FLAG_RESERVE_JUMP_MINI); + } } } } diff --git a/src/controls/submenu.rs b/src/controls/submenu.rs index 57ecddd6e4..5b08530624 100644 --- a/src/controls/submenu.rs +++ b/src/controls/submenu.rs @@ -193,7 +193,8 @@ impl GamecubeMenu { const STICK_SENS: usize = 14; const RUMBLE: usize = 15; const AB_SMASH: usize = 16; - const COUNT: usize = 17; + const SH_AERIAL_MACRO: usize = 17; + const COUNT: usize = 18; } impl TagSubMenu for GamecubeMenu { @@ -351,6 +352,18 @@ impl TagSubMenu for GamecubeMenu { start_button: Some(index), })) } + Self::SH_AERIAL_MACRO => { + let mut controls = unsafe { get_ptr_to_controls(self.controls_id) }; + if controls.controls_mut().gc_absmash & 8 != 0 { + controls.controls_mut().gc_absmash &= !8; + } else { + controls.controls_mut().gc_absmash |= 8; + } + Some(Box::new(Self { + controls_id: self.controls_id, + start_button: Some(index), + })) + } _ => Some(Box::new(self.clone())), } } @@ -420,6 +433,14 @@ impl TagSubMenu for GamecubeMenu { b"A+B Smash: Off" } } + Self::SH_AERIAL_MACRO => { + let mut controls = unsafe { get_ptr_to_controls(self.controls_id) }; + if controls.controls_mut().gc_absmash & 8 != 0 { + b"SH Aerial Macro: On" + } else { + b"SH Aerial Macro: Off" + } + } _ => return None, }; @@ -452,7 +473,8 @@ impl ProControllerMenu { const STICK_SENS: usize = 15; const RUMBLE: usize = 16; const AB_SMASH: usize = 17; - const COUNT: usize = 18; + const SH_AERIAL_MACRO: usize = 18; + const COUNT: usize = 19; } impl TagSubMenu for ProControllerMenu { @@ -616,6 +638,18 @@ impl TagSubMenu for ProControllerMenu { start_button: Some(index), })) } + Self::SH_AERIAL_MACRO => { + let mut controls = unsafe { get_ptr_to_controls(self.controls_id) }; + if controls.controls_mut().pro_absmash & 8 != 0 { + controls.controls_mut().pro_absmash &= !8; + } else { + controls.controls_mut().pro_absmash |= 8; + } + Some(Box::new(Self { + controls_id: self.controls_id, + start_button: Some(index), + })) + } _ => Some(Box::new(self.clone())), } } @@ -686,6 +720,14 @@ impl TagSubMenu for ProControllerMenu { b"A+B Smash: Off" } } + Self::SH_AERIAL_MACRO => { + let mut controls = unsafe { get_ptr_to_controls(self.controls_id) }; + if controls.controls_mut().pro_absmash & 8 != 0 { + b"SH Aerial Macro: On" + } else { + b"SH Aerial Macro: Off" + } + } _ => return None, }; @@ -714,7 +756,8 @@ impl JoyConMenu { const STICK_SENS: usize = 11; const RUMBLE: usize = 12; const AB_SMASH: usize = 13; - const COUNT: usize = 14; + const SH_AERIAL_MACRO: usize = 14; + const COUNT: usize = 15; } impl TagSubMenu for JoyConMenu { @@ -849,6 +892,18 @@ impl TagSubMenu for JoyConMenu { start_button: Some(index), })) } + Self::SH_AERIAL_MACRO => { + let mut controls = unsafe { get_ptr_to_controls(self.controls_id) }; + if controls.controls_mut().joy_absmash & 8 != 0 { + controls.controls_mut().joy_absmash &= !8; + } else { + controls.controls_mut().joy_absmash |= 8; + } + Some(Box::new(Self { + controls_id: self.controls_id, + start_button: Some(index), + })) + } _ => Some(Box::new(self.clone())), } } @@ -915,6 +970,14 @@ impl TagSubMenu for JoyConMenu { b"A+B Smash: Off" } } + Self::SH_AERIAL_MACRO => { + let mut controls = unsafe { get_ptr_to_controls(self.controls_id) }; + if controls.controls_mut().joy_absmash & 8 != 0 { + b"SH Aerial Macro: On" + } else { + b"SH Aerial Macro: Off" + } + } _ => return None, }; diff --git a/src/lua.rs b/src/lua.rs index 27c02131fd..f4dd600325 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -264,6 +264,7 @@ unsafe fn frank_talk_think_tankk(ctx: &mut skyline::hooks::InlineCtx) { static mut CURRENT_UI_PARRY_TOGGLE: bool = false; static mut CURRENT_UI_RIVALS_JUMP: bool = false; +static mut CURRENT_UI_SH_AERIAL_MACRO: bool = false; #[skyline::hook(offset = 0x1d30a18, inline)] unsafe fn get_on_value_for_custom(ctx: &mut skyline::hooks::InlineCtx) { @@ -271,6 +272,8 @@ unsafe fn get_on_value_for_custom(ctx: &mut skyline::hooks::InlineCtx) { ctx.registers[19].set_x(CURRENT_UI_PARRY_TOGGLE as u64); } else if ctx.registers[1].x() == 6 { ctx.registers[19].set_x(CURRENT_UI_RIVALS_JUMP as u64); + } else if ctx.registers[1].x() == 7 { + ctx.registers[19].set_x(CURRENT_UI_SH_AERIAL_MACRO as u64); } } @@ -354,6 +357,7 @@ unsafe fn init_ui_state(ctx: &mut skyline::hooks::InlineCtx) { let ptr = (ctx.registers[8].x() as *mut u8).add(1); CURRENT_UI_PARRY_TOGGLE = (*ptr >> 1) & 1 != 0; CURRENT_UI_RIVALS_JUMP = (*ptr >> 2) & 1 != 0; + CURRENT_UI_SH_AERIAL_MACRO = (*ptr >> 3) & 1 != 0; *ptr &= 1; } @@ -362,6 +366,7 @@ unsafe fn exit_gc(ctx: &mut skyline::hooks::InlineCtx) { let ptr = (ctx.registers[20].x() as *mut u8).add(0xC4); *ptr |= (CURRENT_UI_PARRY_TOGGLE as u8) << 1; *ptr |= (CURRENT_UI_RIVALS_JUMP as u8) << 2; + *ptr |= (CURRENT_UI_SH_AERIAL_MACRO as u8) << 3; IS_IN_UI = false; } @@ -370,6 +375,7 @@ unsafe fn exit_fk(ctx: &mut skyline::hooks::InlineCtx) { let ptr = (ctx.registers[20].x() as *mut u8).add(0xE0); *ptr |= (CURRENT_UI_PARRY_TOGGLE as u8) << 1; *ptr |= (CURRENT_UI_RIVALS_JUMP as u8) << 2; + *ptr |= (CURRENT_UI_SH_AERIAL_MACRO as u8) << 3; IS_IN_UI = false; } @@ -378,6 +384,7 @@ unsafe fn exit_jc(ctx: &mut skyline::hooks::InlineCtx) { let ptr = (ctx.registers[20].x() as *mut u8).add(0xD4); *ptr |= (CURRENT_UI_PARRY_TOGGLE as u8) << 1; *ptr |= (CURRENT_UI_RIVALS_JUMP as u8) << 2; + *ptr |= (CURRENT_UI_SH_AERIAL_MACRO as u8) << 3; IS_IN_UI = false; }