1
0
Fork 0
mirror of https://github.com/jugeeya/UltimateTrainingModpack.git synced 2024-11-27 20:34:03 +00:00

Open Menu with Start Press (#598)

* Initial

* Another fix

* Don't allow opening our menu when vanilla menu is active

* Add menu option ; remove delay to close menu

* Fix issues when closing menu

* Show button config first, log fade

* Fix test

* Fix test again

* Fix initial notification, turn off hitbox visualization by default
This commit is contained in:
jugeeya 2023-08-22 16:12:12 -07:00 committed by GitHub
parent 58f699b285
commit 00f2284397
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 221 additions and 87 deletions

2
ryujinx_build.ps1 vendored
View file

@ -7,7 +7,7 @@ $LOCAL_LAYOUT_ARC_PATH="C:\Users\Josh\Documents\Games\UltimateTrainingModpack\sr
$RYUJINX_PLUGIN_PATH="C:\Users\Josh\AppData\Roaming\Ryujinx\mods\contents\01006a800016e000\romfs\skyline\plugins\libtraining_modpack.nro"
$LOCAL_PLUGIN_PATH="C:\Users\Josh\Documents\Games\UltimateTrainingModpack\target\aarch64-skyline-switch\release\libtraining_modpack.nro"
$RYUJINX_EXE_PATH="C:\Users\Josh\Documents\Games\Ryujinx\publish\Ryujinx.exe"
$RYUJINX_EXE_PATH="C:\Users\Josh\Documents\Games\Ryujinx\ryujinx-1.1.999-win_x64\publish\Ryujinx.exe"
$SMASH_NSP_PATH='C:\Users\Josh\Documents\Games\ROMs\Super Smash Bros Ultimate [Base Game]\Super Smash Bros Ultimate[01006A800016E000][US][v0].nsp'

View file

@ -2,12 +2,16 @@ use std::collections::HashMap;
use crate::common::*;
use crate::input::{ControllerStyle::*, *};
use crate::training::frame_counter;
use crate::training::ui::menu::VANILLA_MENU_ACTIVE;
use lazy_static::lazy_static;
use parking_lot::Mutex;
use strum::IntoEnumIterator;
use strum_macros::EnumIter;
use super::menu::QUICK_MENU_ACTIVE;
pub fn button_mapping(
button_config: ButtonConfig,
style: ControllerStyle,
@ -55,9 +59,12 @@ pub enum ButtonCombo {
InputPlayback,
}
pub const DEFAULT_OPEN_MENU_CONFIG: ButtonConfig = ButtonConfig::B.union(ButtonConfig::DPAD_UP);
unsafe fn get_combo_keys(combo: ButtonCombo) -> ButtonConfig {
match combo {
ButtonCombo::OpenMenu => MENU.menu_open,
// For OpenMenu, have a default in addition to accepting start press
ButtonCombo::OpenMenu => DEFAULT_OPEN_MENU_CONFIG,
ButtonCombo::SaveState => MENU.save_state_save,
ButtonCombo::LoadState => MENU.save_state_load,
ButtonCombo::InputRecord => MENU.input_record,
@ -74,6 +81,7 @@ lazy_static! {
(ButtonCombo::InputRecord, false),
(ButtonCombo::InputPlayback, false),
]));
static ref START_HOLD_FRAMES: Mutex<u32> = Mutex::new(0);
}
fn combo_passes(p1_controller: Controller, combo: ButtonCombo) -> bool {
@ -124,15 +132,53 @@ pub fn combo_passes_exclusive(combo: ButtonCombo) -> bool {
}
}
pub fn handle_final_input_mapping(player_idx: i32, controller_struct: &SomeControllerStruct) {
pub fn handle_final_input_mapping(player_idx: i32, controller_struct: &mut SomeControllerStruct) {
if player_idx == 0 {
let p1_controller = *controller_struct.controller;
let p1_controller = &mut *controller_struct.controller;
let mut start_menu_request = false;
let menu_close_wait_frame =
unsafe { frame_counter::get_frame_count(menu::FRAME_COUNTER_INDEX) };
if unsafe { MENU.menu_open_start_press == OnOff::On } {
let start_hold_frames = &mut *START_HOLD_FRAMES.lock();
if p1_controller.current_buttons.plus() {
*start_hold_frames += 1;
p1_controller.previous_buttons.set_plus(false);
p1_controller.current_buttons.set_plus(false);
p1_controller.just_down.set_plus(false);
p1_controller.just_release.set_plus(false);
} else {
if *start_hold_frames > 0 && menu_close_wait_frame == 0 {
// Here, we just finished holding start
if *start_hold_frames < 10
&& unsafe { !VANILLA_MENU_ACTIVE }
&& menu_close_wait_frame == 0
{
// If we held for fewer than 10 frames, let's open the training mod menu
start_menu_request = true;
} else if unsafe { !QUICK_MENU_ACTIVE } {
// Otherwise, let's let the game know that we had pressed start
// So long as our menu isn't active
p1_controller.current_buttons.set_plus(true);
p1_controller.just_down.set_plus(true);
unsafe {
VANILLA_MENU_ACTIVE = true;
}
}
}
*start_hold_frames = 0;
}
}
let button_combo_requests = &mut *BUTTON_COMBO_REQUESTS.lock();
button_combo_requests
.iter_mut()
.for_each(|(combo, is_request)| {
if !*is_request {
*is_request = _combo_passes_exclusive(p1_controller, *combo);
*is_request = _combo_passes_exclusive(*p1_controller, *combo);
if *combo == button_config::ButtonCombo::OpenMenu && start_menu_request {
*is_request = true;
}
}
})
}

View file

@ -267,7 +267,7 @@ pub struct SomeControllerStruct {
}
// Define struct used for final controller inputs
#[derive(Copy, Clone)]
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub struct MappedInputs {
pub buttons: Buttons,

View file

@ -13,13 +13,18 @@ use crate::consts::MENU_OPTIONS_PATH;
use crate::events::{Event, EVENT_QUEUE};
use crate::input::*;
use crate::logging::*;
use crate::training::frame_counter;
// This is a special frame counter that will tick on draw()
// We'll count how long the menu has been open
pub static mut FRAME_COUNTER: u32 = 0;
const MENU_CLOSE_WAIT_FRAMES: u32 = 60;
pub static mut FRAME_COUNTER_INDEX: usize = 0;
pub const MENU_CLOSE_WAIT_FRAMES: u32 = 15;
pub static mut QUICK_MENU_ACTIVE: bool = false;
pub fn init() {
unsafe {
FRAME_COUNTER_INDEX = frame_counter::register_counter_no_reset();
}
}
pub unsafe fn menu_condition() -> bool {
button_config::combo_passes_exclusive(button_config::ButtonCombo::OpenMenu)
}
@ -72,23 +77,18 @@ pub unsafe fn set_menu_from_json(message: &str) {
pub fn spawn_menu() {
unsafe {
FRAME_COUNTER = 0;
QUICK_MENU_ACTIVE = true;
}
}
#[derive(Eq, PartialEq, Hash, Copy, Clone)]
enum DirectionButton {
DpadLeft,
LLeft,
RLeft,
DpadDown,
LDown,
RDown,
DpadRight,
LRight,
RRight,
DpadUp,
LUp,
RUp,
}
@ -107,16 +107,12 @@ lazy_static! {
static ref DIRECTION_HOLD_FRAMES: Mutex<HashMap<DirectionButton, u32>> = {
use DirectionButton::*;
Mutex::new(HashMap::from([
(DpadLeft, 0),
(LLeft, 0),
(RLeft, 0),
(DpadDown, 0),
(LDown, 0),
(RDown, 0),
(DpadRight, 0),
(LRight, 0),
(RRight, 0),
(DpadUp, 0),
(LUp, 0),
(RUp, 0),
]))
@ -125,13 +121,28 @@ lazy_static! {
pub fn handle_final_input_mapping(
player_idx: i32,
controller_struct: &SomeControllerStruct,
controller_struct: &mut SomeControllerStruct,
out: *mut MappedInputs,
) {
unsafe {
if player_idx == 0 {
let p1_controller = *controller_struct.controller;
let p1_controller = &mut *controller_struct.controller;
*P1_CONTROLLER_STYLE.lock() = p1_controller.style;
if frame_counter::get_frame_count(FRAME_COUNTER_INDEX) > 0
&& frame_counter::get_frame_count(FRAME_COUNTER_INDEX) < MENU_CLOSE_WAIT_FRAMES
{
// If we just closed the menu, kill all inputs to avoid accidental presses
*out = MappedInputs::empty();
p1_controller.current_buttons = ButtonBitfield::default();
p1_controller.previous_buttons = ButtonBitfield::default();
p1_controller.just_down = ButtonBitfield::default();
p1_controller.just_release = ButtonBitfield::default();
} else if frame_counter::get_frame_count(FRAME_COUNTER_INDEX) >= MENU_CLOSE_WAIT_FRAMES
{
frame_counter::reset_frame_count(FRAME_COUNTER_INDEX);
frame_counter::stop_counting(FRAME_COUNTER_INDEX);
}
if QUICK_MENU_ACTIVE {
// If we're here, remove all other presses
*out = MappedInputs::empty();
@ -161,16 +172,12 @@ pub fn handle_final_input_mapping(
.iter_mut()
.for_each(|(direction, frames)| {
let still_held = match direction {
DpadLeft => button_current_held.dpad_left(),
LLeft => button_current_held.l_left(),
RLeft => button_current_held.r_left(),
DpadDown => button_current_held.dpad_down(),
LDown => button_current_held.l_down(),
RDown => button_current_held.r_down(),
DpadRight => button_current_held.dpad_right(),
LRight => button_current_held.l_right(),
RRight => button_current_held.r_right(),
DpadUp => button_current_held.dpad_up(),
LUp => button_current_held.l_up(),
RUp => button_current_held.r_up(),
};
@ -190,15 +197,24 @@ pub fn handle_final_input_mapping(
received_input = true;
if app.page != AppPage::SUBMENU {
app.on_b()
} else if FRAME_COUNTER > MENU_CLOSE_WAIT_FRAMES {
} else {
// Leave menu.
frame_counter::start_counting(FRAME_COUNTER_INDEX);
QUICK_MENU_ACTIVE = false;
FRAME_COUNTER = 0;
let menu_json = app.get_menu_selections();
set_menu_from_json(&menu_json);
EVENT_QUEUE.push(Event::menu_open(menu_json));
}
});
button_mapping(ButtonConfig::PLUS, style, button_presses).then(|| {
received_input = true;
// Leave menu.
frame_counter::start_counting(FRAME_COUNTER_INDEX);
QUICK_MENU_ACTIVE = false;
let menu_json = app.get_menu_selections();
set_menu_from_json(&menu_json);
EVENT_QUEUE.push(Event::menu_open(menu_json));
});
button_mapping(ButtonConfig::X, style, button_presses).then(|| {
app.save_defaults();
received_input = true;
@ -227,7 +243,7 @@ pub fn handle_final_input_mapping(
(button_presses.dpad_left()
|| button_presses.l_left()
|| button_presses.r_left()
|| [DpadLeft, LLeft, RLeft].iter().any(hold_condition))
|| [LLeft, RLeft].iter().any(hold_condition))
.then(|| {
received_input = true;
app.on_left();
@ -235,7 +251,7 @@ pub fn handle_final_input_mapping(
(button_presses.dpad_right()
|| button_presses.l_right()
|| button_presses.r_right()
|| [DpadRight, LRight, RRight].iter().any(hold_condition))
|| [LRight, RRight].iter().any(hold_condition))
.then(|| {
received_input = true;
app.on_right();
@ -243,7 +259,7 @@ pub fn handle_final_input_mapping(
(button_presses.dpad_up()
|| button_presses.l_up()
|| button_presses.r_up()
|| [DpadUp, LUp, RUp].iter().any(hold_condition))
|| [LUp, RUp].iter().any(hold_condition))
.then(|| {
received_input = true;
app.on_up();
@ -251,7 +267,7 @@ pub fn handle_final_input_mapping(
(button_presses.dpad_down()
|| button_presses.l_down()
|| button_presses.r_down()
|| [DpadDown, LDown, RDown].iter().any(hold_condition))
|| [LDown, RDown].iter().any(hold_condition))
.then(|| {
received_input = true;
app.on_down();

View file

@ -18,8 +18,9 @@ use std::fs;
use std::path::PathBuf;
use skyline::nro::{self, NroInfo};
use training_mod_consts::LEGACY_TRAINING_MODPACK_ROOT;
use training_mod_consts::{OnOff, LEGACY_TRAINING_MODPACK_ROOT};
use crate::common::button_config::DEFAULT_OPEN_MENU_CONFIG;
use crate::common::events::events_loop;
use crate::common::*;
use crate::consts::TRAINING_MODPACK_ROOT;
@ -117,7 +118,15 @@ pub fn main() {
unsafe {
notification("Training Modpack".to_string(), "Welcome!".to_string(), 60);
notification("Open Menu".to_string(), MENU.menu_open.to_string(), 120);
notification(
"Open Menu".to_string(),
if MENU.menu_open_start_press == OnOff::On {
"Start".to_string()
} else {
DEFAULT_OPEN_MENU_CONFIG.to_string()
},
120,
);
notification(
"Save State".to_string(),
MENU.save_state_save.to_string(),

View file

@ -1,17 +1,27 @@
static mut SHOULD_COUNT: Vec<bool> = vec![];
static mut NO_RESET: Vec<bool> = vec![];
static mut COUNTERS: Vec<u32> = vec![];
pub fn register_counter() -> usize {
fn _register_counter(no_reset: bool) -> usize {
unsafe {
let index = COUNTERS.len();
COUNTERS.push(0);
SHOULD_COUNT.push(false);
NO_RESET.push(no_reset);
index
}
}
pub fn register_counter_no_reset() -> usize {
_register_counter(true)
}
pub fn register_counter() -> usize {
_register_counter(false)
}
pub fn start_counting(index: usize) {
unsafe {
SHOULD_COUNT[index] = true;
@ -81,6 +91,9 @@ pub fn tick() {
pub fn reset_all() {
unsafe {
for (index, _frame) in COUNTERS.iter().enumerate() {
if NO_RESET[index] {
continue;
}
full_reset(index);
}
}

View file

@ -701,8 +701,8 @@ unsafe fn handle_final_input_mapping(
if !is_training_mode() {
return;
}
button_config::handle_final_input_mapping(player_idx, controller_struct);
menu::handle_final_input_mapping(player_idx, controller_struct, out);
button_config::handle_final_input_mapping(player_idx, controller_struct);
dev_config::handle_final_input_mapping(player_idx, controller_struct);
input_delay::handle_final_input_mapping(player_idx, out);
input_record::handle_final_input_mapping(player_idx, out);
@ -800,4 +800,5 @@ pub fn training_mods() {
tech::init();
input_record::init();
ui::init();
menu::init();
}

View file

@ -6,8 +6,12 @@ use smash::ui2d::{SmashPane, SmashTextBox};
use training_mod_tui::gauge::GaugeState;
use training_mod_tui::{App, AppPage, NUM_LISTS};
use crate::common::menu::{self, MENU_CLOSE_WAIT_FRAMES};
use crate::training::frame_counter;
use crate::{common, common::menu::QUICK_MENU_ACTIVE, input::*};
use super::fade_out;
pub static NUM_MENU_TEXT_OPTIONS: usize = 32;
pub static _NUM_MENU_TABS: usize = 3;
@ -53,6 +57,8 @@ const BG_LEFT_SELECTED_WHITE_COLOR: ResColor = ResColor {
a: 255,
};
pub static mut VANILLA_MENU_ACTIVE: bool = false;
lazy_static! {
static ref GCC_BUTTON_MAPPING: HashMap<&'static str, u16> = HashMap::from([
("L", 0xE204),
@ -346,6 +352,15 @@ unsafe fn render_slider_page(app: &App, root_pane: &Pane) {
}
pub unsafe fn draw(root_pane: &Pane) {
// Determine if we're in the menu by seeing if the "help" footer has
// begun moving upward. It starts at -80 and moves to 0 over 10 frames
// in info_training_in_menu.bflan
VANILLA_MENU_ACTIVE = root_pane
.find_pane_by_name_recursive("L_staying_help")
.unwrap()
.pos_y
!= -80.0;
// Update menu display
// Grabbing lock as read-only, essentially
let app = &*crate::common::menu::QUICK_MENU_APP.data_ptr();
@ -357,12 +372,21 @@ pub unsafe fn draw(root_pane: &Pane) {
}
}
root_pane
.find_pane_by_name_recursive("TrModMenu")
.unwrap()
.set_visible(QUICK_MENU_ACTIVE);
let overall_parent_pane = root_pane.find_pane_by_name_recursive("TrModMenu").unwrap();
overall_parent_pane.set_visible(true);
let menu_close_wait_frame = frame_counter::get_frame_count(menu::FRAME_COUNTER_INDEX);
if QUICK_MENU_ACTIVE {
common::menu::FRAME_COUNTER += 1;
overall_parent_pane.alpha = 255;
overall_parent_pane.global_alpha = 255;
} else if menu_close_wait_frame > 0 {
fade_out(
overall_parent_pane,
MENU_CLOSE_WAIT_FRAMES - menu_close_wait_frame,
MENU_CLOSE_WAIT_FRAMES,
);
} else {
overall_parent_pane.alpha = 0;
overall_parent_pane.global_alpha = 0;
}
// Make all invisible first

View file

@ -10,9 +10,27 @@ use crate::consts::LAYOUT_ARC_PATH;
mod damage;
mod display;
mod menu;
pub mod menu;
pub mod notifications;
pub fn fade_out(pane: &mut Pane, current_frame: u32, total_frames: u32) {
if current_frame < total_frames {
// Logarithmic fade out
let alpha = ((255.0 / (total_frames as f32 + 1.0).log10())
* (current_frame as f32 + 1.0).log10()) as u8;
pane.alpha = alpha;
pane.global_alpha = alpha;
// Linear fade out
// let alpha = ((current_frame as f32 / 100.0) * 255.0) as u8;
// pane.alpha = alpha;
// pane.global_alpha = alpha;
} else {
pane.alpha = 0;
pane.global_alpha = 0;
}
}
#[skyline::hook(offset = 0x4b620)]
pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64) {
let layout_name = skyline::from_c_str((*layout).layout_name);

View file

@ -83,7 +83,7 @@ pub struct TrainingModpackMenu {
pub hitstun_playback: HitstunPlayback,
pub playback_mash: OnOff,
pub playback_loop: OnOff,
pub menu_open: ButtonConfig,
pub menu_open_start_press: OnOff,
pub save_state_save: ButtonConfig,
pub save_state_load: ButtonConfig,
pub input_record: ButtonConfig,
@ -139,7 +139,7 @@ pub static DEFAULTS_MENU: TrainingModpackMenu = TrainingModpackMenu {
follow_up: Action::empty(),
frame_advantage: OnOff::Off,
full_hop: BoolFlag::TRUE,
hitbox_vis: OnOff::On,
hitbox_vis: OnOff::Off,
hud: OnOff::On,
input_delay: Delay::D0,
ledge_delay: LongDelay::empty(),
@ -188,7 +188,7 @@ pub static DEFAULTS_MENU: TrainingModpackMenu = TrainingModpackMenu {
hitstun_playback: HitstunPlayback::Hitstun,
playback_mash: OnOff::On,
playback_loop: OnOff::Off,
menu_open: ButtonConfig::B.union(ButtonConfig::DPAD_UP),
menu_open_start_press: OnOff::On,
save_state_save: ButtonConfig::ZL.union(ButtonConfig::DPAD_DOWN),
save_state_load: ButtonConfig::ZL.union(ButtonConfig::DPAD_UP),
input_record: ButtonConfig::ZR.union(ButtonConfig::DPAD_DOWN),
@ -339,6 +339,48 @@ pub struct UiMenu {
pub unsafe fn ui_menu(menu: TrainingModpackMenu) -> UiMenu {
let mut overall_menu = UiMenu { tabs: Vec::new() };
let mut button_tab = Tab {
tab_id: "button".to_string(),
tab_title: "Button Config".to_string(),
tab_submenus: Vec::new(),
};
button_tab.add_submenu_with_toggles::<OnOff>(
"Menu Open Start Press".to_string(),
"menu_open_start_press".to_string(),
"Menu Open Start Press: Press start to open the mod menu. To open the original menu, hold start.\nThe default menu open option is always available as Hold B + Press DPad Up.".to_string(),
true,
&(menu.menu_open_start_press as u32),
);
button_tab.add_submenu_with_toggles::<ButtonConfig>(
"Save State Save".to_string(),
"save_state_save".to_string(),
"Save State Save: Hold any one button and press the others to trigger".to_string(),
false,
&(menu.save_state_save.bits() as u32),
);
button_tab.add_submenu_with_toggles::<ButtonConfig>(
"Save State Load".to_string(),
"save_state_load".to_string(),
"Save State Load: Hold any one button and press the others to trigger".to_string(),
false,
&(menu.save_state_load.bits() as u32),
);
button_tab.add_submenu_with_toggles::<ButtonConfig>(
"Input Record".to_string(),
"input_record".to_string(),
"Input Record: Hold any one button and press the others to trigger".to_string(),
false,
&(menu.input_record.bits() as u32),
);
button_tab.add_submenu_with_toggles::<ButtonConfig>(
"Input Playback".to_string(),
"input_playback".to_string(),
"Input Playback: Hold any one button and press the others to trigger".to_string(),
false,
&(menu.input_playback.bits() as u32),
);
overall_menu.tabs.push(button_tab);
let mut mash_tab = Tab {
tab_id: "mash".to_string(),
tab_title: "Mash Settings".to_string(),
@ -851,48 +893,5 @@ pub unsafe fn ui_menu(menu: TrainingModpackMenu) -> UiMenu {
);
overall_menu.tabs.push(input_tab);
let mut button_tab = Tab {
tab_id: "button".to_string(),
tab_title: "Button Config".to_string(),
tab_submenus: Vec::new(),
};
button_tab.add_submenu_with_toggles::<ButtonConfig>(
"Menu Open".to_string(),
"menu_open".to_string(),
"Menu Open: Hold: Hold any one button and press the others to trigger".to_string(),
false,
&(menu.menu_open.bits() as u32),
);
button_tab.add_submenu_with_toggles::<ButtonConfig>(
"Save State Save".to_string(),
"save_state_save".to_string(),
"Save State Save: Hold any one button and press the others to trigger".to_string(),
false,
&(menu.save_state_save.bits() as u32),
);
button_tab.add_submenu_with_toggles::<ButtonConfig>(
"Save State Load".to_string(),
"save_state_load".to_string(),
"Save State Load: Hold any one button and press the others to trigger".to_string(),
false,
&(menu.save_state_load.bits() as u32),
);
button_tab.add_submenu_with_toggles::<ButtonConfig>(
"Input Record".to_string(),
"input_record".to_string(),
"Input Record: Hold any one button and press the others to trigger".to_string(),
false,
&(menu.input_record.bits() as u32),
);
button_tab.add_submenu_with_toggles::<ButtonConfig>(
"Input Playback".to_string(),
"input_playback".to_string(),
"Input Playback: Hold any one button and press the others to trigger".to_string(),
false,
&(menu.input_playback.bits() as u32),
);
overall_menu.tabs.push(button_tab);
overall_menu
}

View file

@ -48,6 +48,8 @@ fn test_set_airdodge() -> Result<(), Box<dyn Error>> {
}
let (_terminal, mut app) = test_backend_setup(menu, menu_defaults)?;
// Enter Mash Section
app.next_tab();
// Enter Mash Toggles
app.on_a();
// Set Mash Airdodge
@ -103,6 +105,8 @@ fn test_save_and_reset_defaults() -> Result<(), Box<dyn Error>> {
let (_terminal, mut app) = test_backend_setup(menu, menu_defaults)?;
// Enter Mash Section
app.next_tab();
// Enter Mash Toggles
app.on_a();
// Set Mash Airdodge
@ -146,6 +150,8 @@ fn test_save_and_reset_defaults() -> Result<(), Box<dyn Error>> {
"The menu should have Mash Airdodge"
);
// Enter Mash Section
app.next_tab();
// Enter Mash Toggles
app.on_a();
// Unset Mash Airdodge
@ -216,6 +222,8 @@ fn test_toggle_naming() -> Result<(), Box<dyn Error>> {
}
let (mut terminal, mut app) = test_backend_setup(menu, menu_defaults)?;
// Enter Mash Section
app.next_tab();
// Enter Mash Toggles
app.on_a();
// Set Mash Airdodge