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

Deprecate Web Menu (#472)

* Initial

* Fixes
This commit is contained in:
jugeeya 2023-02-07 06:01:52 -08:00 committed by GitHub
parent bbba8fd3ee
commit d23dcdf968
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 32 additions and 1208 deletions

View file

@ -28,7 +28,7 @@ jobs:
- name: Check - name: Check
run: cargo +nightly check --target=x86_64-unknown-linux-gnu run: cargo +nightly check --target=x86_64-unknown-linux-gnu
- name: Clippy - name: Clippy
run: cargo +nightly clippy --all-targets --all-features --target=x86_64-unknown-linux-gnu run: cargo +nightly clippy --all-targets --all-features --target=x86_64-unknown-linux-gnu -- -D warnings
- name: TUI Test - name: TUI Test
working-directory: training_mod_tui working-directory: training_mod_tui
run: cargo +nightly test run: cargo +nightly test

View file

@ -13,7 +13,7 @@
[![Twitter Follow](https://img.shields.io/twitter/follow/jugeeya?color=brightgreen&logo=twitter&style=for-the-badge)](https://twitter.com/jugeeya) [![Twitter Follow](https://img.shields.io/twitter/follow/jugeeya?color=brightgreen&logo=twitter&style=for-the-badge)](https://twitter.com/jugeeya)
A [Skyline](https://github.com/shadowninja108/Skyline) plugin using [cargo-skyline](https://github.com/jam1garner/cargo-skyline) for adding features to the training mode. It interfaces with [skyline-web](https://github.com/skyline-rs/skyline-web) to provide a menu for customizing training options. A [Skyline](https://github.com/shadowninja108/Skyline) plugin using [cargo-skyline](https://github.com/jam1garner/cargo-skyline) for adding features to the training mode. It uses native Smash UI to provide a menu for customizing training options.
The latest stable release can be found [here](https://github.com/jugeeya/UltimateTrainingModpack/releases/latest). The latest stable release can be found [here](https://github.com/jugeeya/UltimateTrainingModpack/releases/latest).
@ -36,7 +36,6 @@ These are the features that can be found [in the latest beta release](https://gi
### Features ### Features
* Added option for CPU to crouch when grounded - @asimon-1 * Added option for CPU to crouch when grounded - @asimon-1
* Improved web menu UI - @asimon-1, @xhudaman, @jugeeya
* Added Mash Triggers feature, allowing configuration of when the CPU performs mash options - @GradualSyrup * Added Mash Triggers feature, allowing configuration of when the CPU performs mash options - @GradualSyrup
* Add ability to perform mash option after ledge trump, footstool, clatter, hitstun, tumble, and within certain distances - @asimon-1 * Add ability to perform mash option after ledge trump, footstool, clatter, hitstun, tumble, and within certain distances - @asimon-1
* Allow configurable button combinations for save states and opening the menu - @asimon-1 * Allow configurable button combinations for save states and opening the menu - @asimon-1
@ -235,7 +234,6 @@ When multiple options are selected, one of the selected options will be chosen a
| Misc Settings | Hitbox Visualization | Should hitboxes be displayed, hiding other visual effects | Yes, No | | Misc Settings | Hitbox Visualization | Should hitboxes be displayed, hiding other visual effects | Yes, No |
| Misc Settings | Input Delay | Frames to delay player inputs by | 0 to 10 frames (0.167 seconds) | | Misc Settings | Input Delay | Frames to delay player inputs by | 0 to 10 frames (0.167 seconds) |
| Misc Settings | Stage Hazards | Should stage hazards be present | Yes, No | | Misc Settings | Stage Hazards | Should stage hazards be present | Yes, No |
| Misc Settings | Quick Menu | Should use the quick menu instead of the web menu | Yes, No |
<a name="installation"/> <a name="installation"/>
@ -256,8 +254,6 @@ SD Card Root
├── manual_html ├── manual_html
│ └── html-document │ └── html-document
│ └── training_modpack.htdocs │ └── training_modpack.htdocs
│ ├── css
│ │ └── training_modpack.css
│ ├── img │ ├── img
│ │ ├── aerial_delay.svg │ │ ├── aerial_delay.svg
│ │ ├── air_dodge_dir.svg │ │ ├── air_dodge_dir.svg
@ -280,7 +276,6 @@ SD Card Root
│ │ ├── miss_tech_state.svg │ │ ├── miss_tech_state.svg
│ │ ├── oos_offset.svg │ │ ├── oos_offset.svg
│ │ ├── pummel_delay.svg │ │ ├── pummel_delay.svg
│ │ ├── quick_menu.svg
│ │ ├── reaction_time.svg │ │ ├── reaction_time.svg
│ │ ├── save_damage.svg │ │ ├── save_damage.svg
│ │ ├── save_state_enable.svg │ │ ├── save_state_enable.svg
@ -293,8 +288,6 @@ SD Card Root
│ │ ├── tech_state.svg │ │ ├── tech_state.svg
│ │ ├── throw_delay.svg │ │ ├── throw_delay.svg
│ │ └── throw_state.svg │ │ └── throw_state.svg
│ └── js
│ └── training_modpack.js
└── romfs └── romfs
└── skyline └── skyline
└── plugins └── plugins
@ -343,22 +336,20 @@ To install a beta version of the modpack, follow the same procedure using the [l
11. **How do I open the menu?** 11. **How do I open the menu?**
Hold the `SPECIAL` button and press `UP TAUNT` while in training mode. Typically this is `B+DPAD UP`, but do note that these are affected by your control scheme so if you have changed those inputs you need to adjust accordingly. Hold the `SPECIAL` button and press `UP TAUNT` while in training mode. Typically this is `B+DPAD UP`, but do note that these are affected by your control scheme so if you have changed those inputs you need to adjust accordingly.
12. **Why does the menu open slowly?**
The menu uses a built-in web browser for its display, inputs, and styling. It takes about 2 seconds for the browser to open. If you prefer a quicker experience and are frequently opening the menu to adjust your settings, the beta release includes a "Quick Menu" with a different backend. 12. **Why are the save state mirroring positions slightly off on Town and City and Smashville?**
13. **Why are the save state mirroring positions slightly off on Town and City and Smashville?**
These two stages are actually slightly asymmetrical. On Smashville, the left side is 4 units longer than the right side. On Town and City, the left side is 2 units shorter than the right side. This asymmetry is not currently accounted for in the Training Modpack when mirroring save states. These two stages are actually slightly asymmetrical. On Smashville, the left side is 4 units longer than the right side. On Town and City, the left side is 2 units shorter than the right side. This asymmetry is not currently accounted for in the Training Modpack when mirroring save states.
14. **How do I install the Training Modpack?** 13. **How do I install the Training Modpack?**
Full installation instructions are provided in the [#setup-and-download](https://discord.com/channels/407970595418931200/407971997008658432) Discord channel. Full installation instructions are provided in the [#setup-and-download](https://discord.com/channels/407970595418931200/407971997008658432) Discord channel.
15. **How do I install an update to the Training Modpack?** 14. **How do I install an update to the Training Modpack?**
The process for installing and update is very similar to the initial installation. Download the new `.zip` file from the Github releases page, extract the contents, then drag the atmosphere folder to the root of your SD card. Any existing files will be replaced, so there is no need to delete any files manually. The process for installing and update is very similar to the initial installation. Download the new `.zip` file from the Github releases page, extract the contents, then drag the atmosphere folder to the root of your SD card. Any existing files will be replaced, so there is no need to delete any files manually.
16. **How do I install a beta release to the Training Modpack?** 15. **How do I install a beta release to the Training Modpack?**
To install a beta release, follow the same process as updating the Training Modpack. Download the beta `.zip` file from the Github releases page, extract the contents, and drag the atmosphere folder to the root of your SD card. Any existing files will be replaced, so there is no need to delete any files manually. To install a beta release, follow the same process as updating the Training Modpack. Download the beta `.zip` file from the Github releases page, extract the contents, and drag the atmosphere folder to the root of your SD card. Any existing files will be replaced, so there is no need to delete any files manually.
17. **How do I remove the Training Modpack?** 16. **How do I remove the Training Modpack?**
Removing the Training Modpack is as simple as deleting the files and folders that are associated with the modpack, listed below: Removing the Training Modpack is as simple as deleting the files and folders that are associated with the modpack, listed below:
`SD:/atmosphere/contents/01006A800016E000/manual_html/html-document/training_modpack.htdocs/` `SD:/atmosphere/contents/01006A800016E000/manual_html/html-document/training_modpack.htdocs/`
@ -367,45 +358,45 @@ To install a beta version of the modpack, follow the same procedure using the [l
`SD:/atmosphere/contents/01006A800016E000/romfs/skyline/plugins/libparam_hook.nro` `SD:/atmosphere/contents/01006A800016E000/romfs/skyline/plugins/libparam_hook.nro`
`SD:/atmosphere/contents/01006A800016E000/romfs/skyline/plugins/libtraining_modpack.nro` `SD:/atmosphere/contents/01006A800016E000/romfs/skyline/plugins/libtraining_modpack.nro`
`SD:/TrainingModpack/` `SD:/TrainingModpack/`
18. **Can I donate to the Training Modpack?** 17. **Can I donate to the Training Modpack?**
You can find the donation link in the [#faq](https://discord.com/channels/407970595418931200/714960353058095216) Discord channel. We use the money to commission video edits for releases, so thank you if you do end up donating! You can find the donation link in the [#faq](https://discord.com/channels/407970595418931200/714960353058095216) Discord channel. We use the money to commission video edits for releases, so thank you if you do end up donating!
19. **Do I have to repeat the process of installing the mods EVERY time I turn my switch on?** 18. **Do I have to repeat the process of installing the mods EVERY time I turn my switch on?**
The custom firmware Atmosphere stays loaded when the switch goes to sleep, but not if it is fully powered off. So you only need to go through the process of injecting the payload through TegraRCMGui when the switch is fully powered off, such as if the SD card is removed. The custom firmware Atmosphere stays loaded when the switch goes to sleep, but not if it is fully powered off. So you only need to go through the process of injecting the payload through TegraRCMGui when the switch is fully powered off, such as if the SD card is removed.
20. **I've heard about people getting banned while uing this mod online. Can I use this while playing online without getting banned?** 19. **I've heard about people getting banned while uing this mod online. Can I use this while playing online without getting banned?**
The Training Modpack features are only applied while in training mode and do not affect any of the other game modes outside of it, including online play. Smash online is client sided, so only the data that is sent to other players is available for inspection by Nintendo. Since the Training Modpack doesn't affect that data, the Training Modpack is safe to use when playing online. This conclusion is backed up by user experience, where many users have played online with the Training Modpack active and have suffered no adverse consequences. The Training Modpack features are only applied while in training mode and do not affect any of the other game modes outside of it, including online play. Smash online is client sided, so only the data that is sent to other players is available for inspection by Nintendo. Since the Training Modpack doesn't affect that data, the Training Modpack is safe to use when playing online. This conclusion is backed up by user experience, where many users have played online with the Training Modpack active and have suffered no adverse consequences.
*HOWEVER*, please understand that there is inherent risk involved with smash modding, and no guarantee is made that your switch will not be banned. Other wifi-unsafe mods, unsportsmanlike online play, cheating, save editors, online emuMMC, pirating, and other activities may result in a ban. Nintendo has not published a list of ban-worthy activities, nor have they communicated that modding is acceptable, but these are several well-known causes from user experience. It is your responsibility to understand these risks, since it is your switch and your choices. *HOWEVER*, please understand that there is inherent risk involved with smash modding, and no guarantee is made that your switch will not be banned. Other wifi-unsafe mods, unsportsmanlike online play, cheating, save editors, online emuMMC, pirating, and other activities may result in a ban. Nintendo has not published a list of ban-worthy activities, nor have they communicated that modding is acceptable, but these are several well-known causes from user experience. It is your responsibility to understand these risks, since it is your switch and your choices.
21. **Can I change the button combination to bring up the menu?** 20. **Can I change the button combination to bring up the menu?**
This functionality is not supported at this time. However, it is a common request and we are looking into the possibility for the future. This functionality is not supported at this time. However, it is a common request and we are looking into the possibility for the future.
22. **What features does the Training Modpack have? What does each option do?** 21. **What features does the Training Modpack have? What does each option do?**
The Training Modpack has a wide array of features to improve training mode. Some of the more impactful improvements are the ability to save and load fighter positions, practice with hazards off, and adjust CPU behavior in certain scenarios. A full list and explanation of the different settings is available on the Github page here: https://github.com/jugeeya/UltimateTrainingModpack#features The Training Modpack has a wide array of features to improve training mode. Some of the more impactful improvements are the ability to save and load fighter positions, practice with hazards off, and adjust CPU behavior in certain scenarios. A full list and explanation of the different settings is available on the Github page here: https://github.com/jugeeya/UltimateTrainingModpack#features
23. **I think I found a bug in the Training Modpack. How do I report it?** 22. **I think I found a bug in the Training Modpack. How do I report it?**
First check in the #bug-reports channel and Github Issues to see if it has already been reported. If it hasn't, please collect as much information as you can about the bug (including how to reproduce it!) and submit it either in the [#bug-reports](https://discord.com/channels/407970595418931200/407971515171340289) Discord channel or as an issue on Github here: https://github.com/jugeeya/UltimateTrainingModpack/issues First check in the #bug-reports channel and Github Issues to see if it has already been reported. If it hasn't, please collect as much information as you can about the bug (including how to reproduce it!) and submit it either in the [#bug-reports](https://discord.com/channels/407970595418931200/407971515171340289) Discord channel or as an issue on Github here: https://github.com/jugeeya/UltimateTrainingModpack/issues
24. **I have an idea for a new feature in the Training Modpack? How do I suggest it?** 23. **I have an idea for a new feature in the Training Modpack? How do I suggest it?**
Suggestions are always welcome! You can request new features in the [#requests](https://discord.com/channels/407970595418931200/407971627138285579) Discord channel. Please do keep in mind that the developers are volunteers with busy lives, so we may not be able to implement every suggestion, but we do read all requests in that channel and often prioritize features that are highly requested. Suggestions are always welcome! You can request new features in the [#requests](https://discord.com/channels/407970595418931200/407971627138285579) Discord channel. Please do keep in mind that the developers are volunteers with busy lives, so we may not be able to implement every suggestion, but we do read all requests in that channel and often prioritize features that are highly requested.
25. **What happens when I pick multiple options for a setting, such as mash or tech options?** 24. **What happens when I pick multiple options for a setting, such as mash or tech options?**
Some menu settings allow for multiple selections. When there are several settings selected, the CPU will randomly choose between the selections when that setting is triggered. For instance, if you have both "Ledge Jump" and "Neutral Getup" selected under "Ledge Options", then the CPU will randomly select between those two options when deciding what to do when it is on the ledge. Some menu settings allow for multiple selections. When there are several settings selected, the CPU will randomly choose between the selections when that setting is triggered. For instance, if you have both "Ledge Jump" and "Neutral Getup" selected under "Ledge Options", then the CPU will randomly select between those two options when deciding what to do when it is on the ledge.
26. **How do I reset my Training Modpack settings?** 25. **How do I reset my Training Modpack settings?**
If you want to completely reset your menu selections back to the factory default, all you have to do is delete this file: If you want to completely reset your menu selections back to the factory default, all you have to do is delete this file:
`SD:/TrainingModpack/training_modpack_menu.conf` `SD:/TrainingModpack/training_modpack_menu.conf`
27. **What input delay should I pick for practicing online?** 26. **What input delay should I pick for practicing online?**
Good LAN connections can be simulated with an input delay of 3-5 frames. Poorer Wifi connections can be up to 6-8 frames. Good LAN connections can be simulated with an input delay of 3-5 frames. Poorer Wifi connections can be up to 6-8 frames.
28. **How do I install other mods, like skins or stages?** 27. **How do I install other mods, like skins or stages?**
You will need to use the Arcropolis mod manager to enable other types of mods. This discord is focused on the Training Modpack; more information on installation of other mods can be found in the general SSBU modding Discord, which is linked in the [#welcome](https://discord.com/channels/407970595418931200/721077130456203335) channel. You will need to use the Arcropolis mod manager to enable other types of mods. This discord is focused on the Training Modpack; more information on installation of other mods can be found in the general SSBU modding Discord, which is linked in the [#welcome](https://discord.com/channels/407970595418931200/721077130456203335) channel.
29. **Can I put the Training Modpack under my `SD:/ultimate/mods` folder?** 28. **Can I put the Training Modpack under my `SD:/ultimate/mods` folder?**
No, the Training Modpack is not supported in a chainloading configuration. Please only install in the recommended location. No, the Training Modpack is not supported in a chainloading configuration. Please only install in the recommended location.
30. **Are there any known mods that conflict with the Training Modpack?** 29. **Are there any known mods that conflict with the Training Modpack?**
Currently the known conflicts are: Currently the known conflicts are:
@ -413,11 +404,11 @@ To install a beta version of the modpack, follow the same procedure using the [l
* Minecraft Skins * Minecraft Skins
* Blujay's Hitbox Visualizer * Blujay's Hitbox Visualizer
31. **Who created the Training Modpack?** 30. **Who created the Training Modpack?**
jugeeya is the creator of the Training Modpack, along with its predecessor in Smash 4. There are a number of developers who contribute new features and bugfixes as well, listed on the contributors page: https://github.com/jugeeya/UltimateTrainingModpack/graphs/contributors jugeeya is the creator of the Training Modpack, along with its predecessor in Smash 4. There are a number of developers who contribute new features and bugfixes as well, listed on the contributors page: https://github.com/jugeeya/UltimateTrainingModpack/graphs/contributors
32. **I want to contribute as a developer to the Training Modpack. How do I get started?** 31. **I want to contribute as a developer to the Training Modpack. How do I get started?**
If you'd like to help out as a developer, we welcome any contributions! The Training Modpack is written in Rust, and uses the Skyline framework to hook into existing Smash functions to add functionality to training mode. If you aren't already familiar with Rust, the Rust book is a great place to start familiarizing yourself with the syntax and structure: https://doc.rust-lang.org/book/ . You can also take a look through the existing codebase on Github to check out how everything works right now. It's all open source! If you'd like to help out as a developer, we welcome any contributions! The Training Modpack is written in Rust, and uses the Skyline framework to hook into existing Smash functions to add functionality to training mode. If you aren't already familiar with Rust, the Rust book is a great place to start familiarizing yourself with the syntax and structure: https://doc.rust-lang.org/book/ . You can also take a look through the existing codebase on Github to check out how everything works right now. It's all open source!

View file

@ -3,13 +3,7 @@ use crate::events::{Event, EVENT_QUEUE};
use crate::logging::*; use crate::logging::*;
use crate::training::frame_counter; use crate::training::frame_counter;
use ramhorns::Template;
use skyline::info::get_program_id;
use skyline::nn::hid::NpadGcState; use skyline::nn::hid::NpadGcState;
use skyline::nn::web::WebSessionBootMode;
use skyline_web::{Background, BootDisplay, WebSession, Webpage};
use std::fs;
use std::path::Path;
use training_mod_consts::MenuJsonStruct; use training_mod_consts::MenuJsonStruct;
static mut FRAME_COUNTER_INDEX: usize = 0; static mut FRAME_COUNTER_INDEX: usize = 0;
@ -21,9 +15,6 @@ pub fn init() {
unsafe { unsafe {
FRAME_COUNTER_INDEX = frame_counter::register_counter(); FRAME_COUNTER_INDEX = frame_counter::register_counter();
QUICK_MENU_FRAME_COUNTER_INDEX = frame_counter::register_counter(); QUICK_MENU_FRAME_COUNTER_INDEX = frame_counter::register_counter();
if !is_emulator() {
write_web_menu_file();
}
} }
} }
@ -45,32 +36,13 @@ pub unsafe fn menu_condition(module_accessor: &mut smash::app::BattleObjectModul
} }
} }
pub unsafe fn write_web_menu_file() {
let tpl = Template::new(include_str!("../templates/menu.html")).unwrap();
let overall_menu = ui_menu(MENU);
let data = tpl.render(&overall_menu);
// Now that we have the html, write it to file
// From skyline-web
let program_id = get_program_id();
let htdocs_dir = "training_modpack";
let menu_html_path = Path::new("sd:/atmosphere/contents")
.join(format!("{program_id:016X}"))
.join(format!("manual_html/html-document/{htdocs_dir}.htdocs/"))
.join("training_menu.html");
fs::write(menu_html_path, data).expect("Failed to write menu HTML file");
}
const MENU_CONF_PATH: &str = "sd:/TrainingModpack/training_modpack_menu.json"; const MENU_CONF_PATH: &str = "sd:/TrainingModpack/training_modpack_menu.json";
pub unsafe fn set_menu_from_json(message: &str) { pub unsafe fn set_menu_from_json(message: &str) {
let web_response = serde_json::from_str::<MenuJsonStruct>(message); let response = serde_json::from_str::<MenuJsonStruct>(message);
info!("Received menu message: {message}"); info!("Received menu message: {message}");
if let Ok(message_json) = web_response { if let Ok(message_json) = response {
// Includes both MENU and DEFAULTS_MENU // Includes both MENU and DEFAULTS_MENU
// From Web Applet
MENU = message_json.menu; MENU = message_json.menu;
DEFAULTS_MENU = message_json.defaults_menu; DEFAULTS_MENU = message_json.defaults_menu;
std::fs::write( std::fs::write(
@ -85,14 +57,6 @@ pub unsafe fn set_menu_from_json(message: &str) {
&format!("{message:#?}\0") &format!("{message:#?}\0")
); );
}; };
if MENU.quick_menu == OnOff::Off && is_emulator() {
skyline::error::show_error(
0x69,
"Cannot use web menu on emulator.\n\0",
"Only the quick menu is runnable via emulator currently.\n\0",
);
MENU.quick_menu = OnOff::On;
}
} }
pub fn spawn_menu() { pub fn spawn_menu() {
@ -102,17 +66,6 @@ pub fn spawn_menu() {
frame_counter::reset_frame_count(QUICK_MENU_FRAME_COUNTER_INDEX); frame_counter::reset_frame_count(QUICK_MENU_FRAME_COUNTER_INDEX);
frame_counter::start_counting(QUICK_MENU_FRAME_COUNTER_INDEX); frame_counter::start_counting(QUICK_MENU_FRAME_COUNTER_INDEX);
if MENU.quick_menu == OnOff::Off {
#[cfg(feature = "web_session_preload")]
{
WEB_MENU_ACTIVE = true;
}
#[cfg(not(feature = "web_session_preload"))]
{
spawn_web_session(new_web_session(false));
}
} else {
let mut app = QUICK_MENU_APP.lock(); let mut app = QUICK_MENU_APP.lock();
*app = training_mod_tui::App::new( *app = training_mod_tui::App::new(
ui_menu(MENU), ui_menu(MENU),
@ -120,7 +73,6 @@ pub fn spawn_menu() {
drop(app); drop(app);
QUICK_MENU_ACTIVE = true; QUICK_MENU_ACTIVE = true;
} }
}
} }
pub struct ButtonPresses { pub struct ButtonPresses {
@ -372,72 +324,3 @@ pub unsafe fn quick_menu_loop() {
} }
} }
} }
static mut WEB_MENU_ACTIVE: bool = false;
unsafe fn spawn_web_session(session: WebSession) {
info!("Opening menu session...");
let loaded_msg = session.recv();
info!("Received loaded message from web: {}", &loaded_msg);
let message_send = MenuJsonStruct {
menu: MENU,
defaults_menu: DEFAULTS_MENU,
};
session.send_json(&message_send);
let message_recv = session.recv();
info!("Tearing down Training Modpack menu session");
session.exit();
session.wait_for_exit();
set_menu_from_json(&message_recv);
EVENT_QUEUE.push(Event::menu_open(message_recv));
}
unsafe fn new_web_session(hidden: bool) -> WebSession {
Webpage::new()
.background(Background::BlurredScreenshot)
.boot_icon(true)
.boot_display(BootDisplay::BlurredScreenshot)
.htdocs_dir("training_modpack")
.start_page("training_menu.html")
.open_session(if hidden {
WebSessionBootMode::InitiallyHidden
} else {
WebSessionBootMode::Default
})
.unwrap()
}
pub unsafe fn web_session_loop() {
// Don't query the FighterManager too early otherwise it will crash...
std::thread::sleep(std::time::Duration::new(30, 0)); // sleep for 30 secs on bootup
let mut web_session: Option<WebSession> = None;
loop {
std::thread::sleep(std::time::Duration::from_millis(100));
if (is_ready_go() || entry_count() > 0) && is_training_mode() {
if web_session.is_some() {
if WEB_MENU_ACTIVE {
spawn_web_session(web_session.unwrap());
web_session = None;
WEB_MENU_ACTIVE = false;
}
} else {
// TODO
// Starting a new session causes some ingame lag.
// Investigate whether we can minimize this lag by
// waiting until the player is idle or using CPU boost mode
info!("Starting new menu session...");
web_session = Some(new_web_session(true));
}
} else {
// No longer in training mode, tear down the session.
// This will avoid conflicts with other web plugins, and helps with stability.
// Having the session open too long, especially if the switch has been put to sleep, can cause freezes
if let Some(web_session_to_kill) = web_session {
info!("Tearing down Training Modpack menu session");
web_session_to_kill.exit();
web_session_to_kill.wait_for_exit();
}
web_session = None;
}
}
}

View file

@ -3,7 +3,6 @@
#![feature(exclusive_range_pattern)] #![feature(exclusive_range_pattern)]
#![feature(once_cell)] #![feature(once_cell)]
#![feature(c_variadic)] #![feature(c_variadic)]
#![deny(warnings)]
#![allow( #![allow(
clippy::borrow_interior_mutable_const, clippy::borrow_interior_mutable_const,
clippy::declare_interior_mutable_const, clippy::declare_interior_mutable_const,
@ -33,9 +32,7 @@ use std::fs;
use crate::logging::*; use crate::logging::*;
use crate::menu::quick_menu_loop; use crate::menu::quick_menu_loop;
#[cfg(feature = "web_session_preload")] use training_mod_consts::MenuJsonStruct;
use crate::menu::web_session_loop;
use training_mod_consts::{MenuJsonStruct, OnOff};
use crate::training::ui::notifications::notification; use crate::training::ui::notifications::notification;
fn nro_main(nro: &NroInfo<'_>) { fn nro_main(nro: &NroInfo<'_>) {
@ -145,14 +142,6 @@ pub fn main() {
button_config::save_all_btn_config_from_defaults(); button_config::save_all_btn_config_from_defaults();
} }
if is_emulator() {
unsafe {
DEFAULTS_MENU.quick_menu = OnOff::On;
MENU.quick_menu = OnOff::On;
BASE_MENU.quick_menu = OnOff::On;
}
}
std::thread::spawn(|| loop { std::thread::spawn(|| loop {
std::thread::sleep(std::time::Duration::from_secs(10)); std::thread::sleep(std::time::Duration::from_secs(10));
unsafe { unsafe {
@ -174,9 +163,4 @@ pub fn main() {
}); });
std::thread::spawn(|| unsafe { quick_menu_loop() }); std::thread::spawn(|| unsafe { quick_menu_loop() });
#[cfg(feature = "web_session_preload")]
if !is_emulator() {
std::thread::spawn(|| unsafe { web_session_loop() });
}
} }

View file

@ -1 +0,0 @@
.noUi-target,.noUi-target *{-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;-ms-touch-action:none;touch-action:none;-ms-user-select:none;-moz-user-select:none;user-select:none;-moz-box-sizing:border-box;box-sizing:border-box}.noUi-target{position:relative}.noUi-base,.noUi-connects{width:100%;height:100%;position:relative;z-index:1}.noUi-connects{overflow:hidden;z-index:0}.noUi-connect,.noUi-origin{will-change:transform;position:absolute;z-index:1;top:0;right:0;height:100%;width:100%;-ms-transform-origin:0 0;-webkit-transform-origin:0 0;-webkit-transform-style:preserve-3d;transform-origin:0 0;transform-style:flat}.noUi-txt-dir-rtl.noUi-horizontal .noUi-origin{left:0;right:auto}.noUi-vertical .noUi-origin{top:-100%;width:0}.noUi-horizontal .noUi-origin{height:0}.noUi-handle{-webkit-backface-visibility:hidden;backface-visibility:hidden;position:absolute}.noUi-touch-area{height:100%;width:100%}.noUi-state-tap .noUi-connect,.noUi-state-tap .noUi-origin{-webkit-transition:transform .3s;transition:transform .3s}.noUi-state-drag *{cursor:inherit!important}.noUi-horizontal{height:18px}.noUi-horizontal .noUi-handle{width:34px;height:28px;right:-17px;top:-6px}.noUi-vertical{width:18px}.noUi-vertical .noUi-handle{width:28px;height:34px;right:-6px;bottom:-17px}.noUi-txt-dir-rtl.noUi-horizontal .noUi-handle{left:-17px;right:auto}.noUi-target{background:#FAFAFA;border-radius:4px;border:1px solid #D3D3D3;box-shadow:inset 0 1px 1px #F0F0F0,0 3px 6px -5px #BBB}.noUi-connects{border-radius:3px}.noUi-connect{background:#3FB8AF}.noUi-draggable{cursor:ew-resize}.noUi-vertical .noUi-draggable{cursor:ns-resize}.noUi-handle{border:1px solid #D9D9D9;border-radius:3px;background:#FFF;cursor:default;box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #EBEBEB,0 3px 6px -3px #BBB}.noUi-active{box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #DDD,0 3px 6px -3px #BBB}.noUi-handle:after,.noUi-handle:before{content:"";display:block;position:absolute;height:14px;width:1px;background:#E8E7E6;left:14px;top:6px}.noUi-handle:after{left:17px}.noUi-vertical .noUi-handle:after,.noUi-vertical .noUi-handle:before{width:14px;height:1px;left:6px;top:14px}.noUi-vertical .noUi-handle:after{top:17px}[disabled] .noUi-connect{background:#B8B8B8}[disabled] .noUi-handle,[disabled].noUi-handle,[disabled].noUi-target{cursor:not-allowed}.noUi-pips,.noUi-pips *{-moz-box-sizing:border-box;box-sizing:border-box}.noUi-pips{position:absolute;color:#999}.noUi-value{position:absolute;white-space:nowrap;text-align:center}.noUi-value-sub{color:#ccc;font-size:10px}.noUi-marker{position:absolute;background:#CCC}.noUi-marker-sub{background:#AAA}.noUi-marker-large{background:#AAA}.noUi-pips-horizontal{padding:10px 0;height:80px;top:100%;left:0;width:100%}.noUi-value-horizontal{-webkit-transform:translate(-50%,50%);transform:translate(-50%,50%)}.noUi-rtl .noUi-value-horizontal{-webkit-transform:translate(50%,50%);transform:translate(50%,50%)}.noUi-marker-horizontal.noUi-marker{margin-left:-1px;width:2px;height:5px}.noUi-marker-horizontal.noUi-marker-sub{height:10px}.noUi-marker-horizontal.noUi-marker-large{height:15px}.noUi-pips-vertical{padding:0 10px;height:100%;top:0;left:100%}.noUi-value-vertical{-webkit-transform:translate(0,-50%);transform:translate(0,-50%);padding-left:25px}.noUi-rtl .noUi-value-vertical{-webkit-transform:translate(0,50%);transform:translate(0,50%)}.noUi-marker-vertical.noUi-marker{width:5px;height:2px;margin-top:-1px}.noUi-marker-vertical.noUi-marker-sub{width:10px}.noUi-marker-vertical.noUi-marker-large{width:15px}.noUi-tooltip{display:block;position:absolute;border:1px solid #D9D9D9;border-radius:3px;background:#fff;color:#000;padding:5px;text-align:center;white-space:nowrap}.noUi-horizontal .noUi-tooltip{-webkit-transform:translate(-50%,0);transform:translate(-50%,0);left:50%;bottom:120%}.noUi-vertical .noUi-tooltip{-webkit-transform:translate(0,-50%);transform:translate(0,-50%);top:50%;right:120%}.noUi-horizontal .noUi-origin>.noUi-tooltip{-webkit-transform:translate(50%,0);transform:translate(50%,0);left:auto;bottom:10px}.noUi-vertical .noUi-origin>.noUi-tooltip{-webkit-transform:translate(0,-18px);transform:translate(0,-18px);top:auto;right:28px}

View file

@ -1,342 +0,0 @@
button {
padding: unset;
margin: unset;
}
@-moz-document url-prefix() {
@font-face {
font-family: 'nintendo_udsgr_std_003';
src: url('../fonts/nintendo_udsgr_std_003.ttf');
}
@font-face {
font-family: 'nintendo_ext_003';
src: url('../fonts/nintendo_ext_003.ttf');
}
}
@keyframes background-slide {
0% {
background-position-x: 0px;
}
100% {
background-position-x: 422.4px;
}
}
.tab-list-container {
overflow: hidden;
background-color: #555;
display: flex;
justify-content: flex-start;
width: 100%;
align-items: center;
}
.tab-list-container p {
color: #fff;
width: 130px;
height: fit-content;
margin: 0px 10px 0px 10px;
padding: 0px;
}
.tab-list {
overflow: hidden;
display: flex;
justify-content: flex-start;
width: 100%;
}
.tab-list button {
background-color: inherit;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
color: #fff;
margin: 5px 0px 0px 8px;
border-radius: 8px 8px 0px 0px;
font-size: large;
}
.tab-list button:hover {
background: #797979;
}
.tab-list button.active {
color: #000;
background: #ccc;
}
body {
background: none;
font-family: 'nintendo_ext_003', 'nintendo_udsgr_std_003';
margin: 0;
}
.header {
display: flex;
justify-content: space-between;
position: relative;
width: 100%;
height: 80px;
z-index: 100;
background: #000;
box-shadow: 0px 1px 1px #000;
}
.header-title {
color: #f46264;
font-size: 26px;
line-height: 2.5;
margin: 0px;
padding: 0px;
word-break: normal;
}
.header-label {
display: flex;
flex-direction: column;
justify-content: center;
align-items: end;
margin-right: 15px;
}
.header-label > p {
color: #fff;
margin: 0;
padding: 0;
}
.return-icon-container {
width: 101px;
height: 65px;
padding-left: 21px;
background: #a80114;
border-radius: 0px 0px 15px 0px;
}
.return-icon {
width: 58px;
height: 58px;
padding-left: 7px;
filter: drop-shadow(3px 5px 2px rgba(0, 0, 0, 0.8));
}
/* Center Icons */
.question::before {
width: 70px;
}
/* Footer */
.footer {
position: fixed;
bottom: 0px;
left: 0px;
background: #000;
display: flex;
justify-content: center;
align-items: center;
height: 73px;
width: 100%;
color: #fff;
z-index: 100;
}
/* Save Defaults Container */
.defaults-checkbox-container {
position: fixed;
right: 50px;
margin-top: 10px;
display: flex;
justify-content: center;
flex-direction: column;
}
/* Checkbox element (hidden) */
#saveDefaults {
position: absolute;
left: -100vw;
}
.checkbox-display {
margin: 10px 70px;
}
/* Displayed Checkbox (unchecked) */
.checkbox-display::after {
content: '\E14C';
color: white;
}
/* Displayed Checkbox (checked) */
#saveDefaults:checked ~ .checkbox-display::after {
content: '\E14B';
}
.hide {
display: none !important;
}
.hidden {
visibility: hidden;
}
:focus:not(.noUi-handle),
.handleSelected,
.noUi-connect
{
background: rgb(255, 70, 2);
background: linear-gradient(
45deg,
rgba(255, 70, 2, 1) 20%,
rgba(255, 215, 0, 1) 46%,
rgba(255, 215, 0, 1) 54%,
rgba(255, 70, 2, 1) 80%
);
background-size: 500% 100%;
will-change: animation;
animation: translate-anim 5s infinite linear;
}
:focus > div {
color: #fff;
text-shadow: -1px -1px 1px #000, 1px -1px 1px #000, -1px 1px 1px #000, 1px 1px 1px #000;
}
@keyframes translate-anim {
0% {
background-position: 0% 0%;
}
100% {
background-position: 100% 0%;
}
}
.tab-content-container {
height: 513px;
}
.tab-content {
display: flex;
flex-wrap: wrap;
height: 100%;
}
.tab-content-wrapper,
.modal {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
height: 100%;
}
.modal {
position: fixed;
height: 513px;
width: 100%;
}
.menu-item,
.modal-button {
flex-basis: 23%;
margin: 0 1%;
height: 84px;
display: flex;
justify-content: center;
align-items: center;
}
.modal-button {
flex-basis: 20%;
height: 50px;
border-color: black;
border-radius: 0.5rem;
border-style: solid;
border-width: 0.25rem 0.75rem;
justify-content: flex-start;
padding: 0 0.5rem;
}
.modal-button-title {
margin: 0 auto;
font-size: large;
}
.menu-button-content,
.modal-button-content {
display: flex;
justify-content: flex-start;
align-items: center;
width: 100%;
height: 100%;
}
.menu-button {
display: flex;
justify-content: flex-start;
align-items: center;
height: 100%;
width: 100%;
padding: 0.5rem;
border-color: black;
border-radius: 0.5rem;
border-style: solid;
border-width: 0.25rem 1.25rem;
}
.menu-icon {
width: 25%;
height: 100%;
}
.menu-icon img {
width: 100%;
height: 100%;
}
.menu-title {
padding: 0 0.025rem;
margin: 0 auto;
text-align: center;
font-size: large;
}
.modal-slider {
width: 70%;
margin-bottom: 170px;
}
.modal-slider-label {
width: 100%;
display: flex;
justify-content: center;
}
.modal-slider-label > p {
font-size: 26px;
padding: 0.5rem;
background: #ccc;
border-color: black;
border-radius: 0.5rem;
border-style: solid;
border-width: 0.25rem;
}
.noUi-value, .noUi-pips {
margin-top: 12px;
color: #000;
}
.noUi-marker, .noUi-marker-large {
background: #000;
}
.noUi-tooltip {
padding: 12px;
font-size: 1.5rem;
}

File diff suppressed because one or more lines are too long

View file

@ -1,521 +0,0 @@
// Polyfill for NodeList.forEach.
// Allows forEach to be called directly on a node list (return type of doucment.querySelectorAll)
if (window.NodeList && !NodeList.prototype.forEach) {
NodeList.prototype.forEach = Array.prototype.forEach;
}
// Polyfill for Object.entries
// Iterates on an object and returns an array containing arrays of key/value pairs ([key, value])
// for each pair in the object
if (!Object.entries) {
Object.entries = function (obj) {
var ownProps = Object.keys(obj),
i = ownProps.length,
resArray = new Array(i); // preallocate the Array
while (i--) resArray[i] = [ownProps[i], obj[ownProps[i]]];
return resArray;
};
}
const isNx = typeof window.nx !== 'undefined';
var DEFAULTS_PREFIX = '__';
// Set input handlers
if (isNx) {
window.nx.footer.setAssign('A', '', function () { select(document.activeElement) }, { se: '' });
window.nx.footer.setAssign('B', '', closeOrExit, { se: '' });
window.nx.footer.setAssign('X', '', resetCurrentMenu, { se: '' });
window.nx.footer.setAssign('L', '', resetAllMenus, { se: '' });
window.nx.footer.setAssign('R', '', saveDefaults, { se: '' });
window.nx.footer.setAssign('ZR', '', cycleNextTab, { se: '' });
window.nx.footer.setAssign('ZL', '', cyclePrevTab, { se: '' });
window.nx.addEventListener("message", function (msg) { setSettingsFromJSON(msg.data) });
document.addEventListener('keydown', (event) => {
switch (event.keyCode) {
case 37: // Control stick left
decreaseSelectedHandle();
break;
case 39: // Control stick right
increaseSelectedHandle();
break;
}
})
} else {
document.addEventListener('keydown', (event) => {
switch (event.key) {
case 'a':
console.log('a');
select(document.activeElement);
break;
case 'b':
console.log('b');
closeOrExit();
break;
case 'x':
console.log('x');
resetCurrentMenu();
break;
case 'l':
console.log('l');
resetAllMenus();
break;
case 'r':
console.log('r');
saveDefaults();
break;
case 'p':
console.log('p');
cycleNextTab();
break;
case 'o':
console.log('o');
cyclePrevTab();
break;
case 'ArrowLeft':
console.log('ArrowLeft');
decreaseSelectedHandle();
break;
case 'ArrowRight':
console.log('ArrowRight');
increaseSelectedHandle();
break;
}
});
}
const onLoad = () => {
// Activate the first tab
openTab(document.querySelector('button.tab-button'));
initializeAllSliders();
if (isNx) {
window.nx.sendMessage("loaded");
} else {
settings = {};
setSettingsFromJSON("{\"menu\":{\"aerial_delay\":0,\"air_dodge_dir\":0,\"attack_angle\":0,\"buff_state\":0,\"character_item\":0,\"clatter_strength\":0,\"crouch\":0,\"di_state\":0,\"falling_aerials\":0,\"fast_fall_delay\":0,\"fast_fall\":0,\"follow_up\":0,\"frame_advantage\":0,\"full_hop\":0,\"hitbox_vis\":1,\"input_delay\":1,\"ledge_delay\":0,\"ledge_state\":31,\"mash_state\":0,\"mash_triggers\":131,\"miss_tech_state\":15,\"oos_offset\":0,\"pummel_delay\":0,\"quick_menu\":0,\"reaction_time\":0,\"save_damage\":4,\"save_damage_limits\":[63,106],\"save_state_autoload\":1,\"save_state_enable\":1,\"save_state_mirroring\":1,\"sdi_state\":0,\"sdi_strength\":0,\"shield_state\":0,\"shield_tilt\":0,\"stage_hazards\":0,\"tech_state\":15,\"throw_delay\":0,\"throw_state\":1},\"defaults_menu\":{\"aerial_delay\":0,\"air_dodge_dir\":0,\"attack_angle\":0,\"buff_state\":0,\"character_item\":0,\"clatter_strength\":0,\"crouch\":0,\"di_state\":0,\"falling_aerials\":0,\"fast_fall_delay\":0,\"fast_fall\":0,\"follow_up\":0,\"frame_advantage\":0,\"full_hop\":0,\"hitbox_vis\":1,\"input_delay\":1,\"ledge_delay\":0,\"ledge_state\":31,\"mash_state\":0,\"mash_triggers\":131,\"miss_tech_state\":15,\"oos_offset\":0,\"pummel_delay\":0,\"quick_menu\":0,\"reaction_time\":0,\"save_damage\":4,\"save_damage_limits\":[41,118],\"save_state_autoload\":1,\"save_state_enable\":1,\"save_state_mirroring\":1,\"sdi_state\":0,\"sdi_strength\":0,\"shield_state\":0,\"shield_tilt\":0,\"stage_hazards\":0,\"tech_state\":15,\"throw_delay\":0,\"throw_state\":1}}");
}
};
window.onload = onLoad;
var settings;
var defaultSettings;
var lastFocusedItem = document.querySelector('.menu-item > button');
var selectedSliderHandle = -1;
const currentTabContent = () => {
const currentActiveTab = document.querySelector('.tab-button.active');
var currentActiveTabContent = document.querySelector(`#${currentActiveTab.id.replace('button', 'tab')}`);
return currentActiveTabContent;
};
const openTab = (eventTarget) => {
playSound('SeWebZoomIn');
const selectedTab = document.getElementById(eventTarget.id.replace('button', 'tab'));
const activeTabContent = document.querySelector('.tab-content:not(.hide)');
const activeTab = document.querySelector('.tab-button.active');
// Hide content of current active tab
if (activeTabContent) {
activeTabContent.classList.add('hide');
}
closeAllActiveModals();
// Remove "active" class from current active tab
if (activeTab) {
activeTab.classList.remove('active');
}
// Show the new current tab, and add an "active" class to the button that opened the tab
eventTarget.classList.add('active');
selectedTab.classList.remove('hide');
selectedTab.querySelector('button').focus();
};
const openMenuItem = (eventTarget) => {
playSound('SeWebMenuListOpen');
var { target } = eventTarget.dataset;
const modal = document.querySelector(`.modal[data-id=${target}]`);
currentTabContent().classList.toggle('hide');
modal.classList.toggle('hide');
elem = modal.querySelector('button');
if (!elem) {
elem = modal.querySelector('.noUi-handle-lower')
}
elem.focus();
lastFocusedItem = eventTarget;
};
const closeAllActiveModals = () => {
document.querySelectorAll('.modal:not(.hide)').forEach((modal) => {
modal.classList.add('hide');
});
lastFocusedItem.focus();
};
const toggleOption = (element) => {
playSound('SeSelectCheck');
if (element.parentElement.classList.contains('single-option')) {
selectSingleOption(element);
return;
}
const img = element.querySelector('img');
const previouslySelected = !img.classList.contains('hidden');
const menuId = element.parentElement.dataset.id;
const toggleValue = parseInt(img.dataset.val);
settings[menuId] = previouslySelected ? settings[menuId] - toggleValue : settings[menuId] + toggleValue;
element.querySelector('img').classList.toggle('hidden');
};
// Add this later
// function toggleSingleOption(element) {
// selectSingleOption(element);
// }
const closestClass = (element, class_) => {
if (!element) {
return null;
}
if (element.classList.contains(class_)) {
return element;
}
// Didn't find it, go up a level
return closestClass(element.parentElement, class_);
};
function playSound(label) {
//** Valid labels **//
// SeToggleBtnFocus, SeToggleBtnOn, SeToggleBtnOff, SeCheckboxFocus, SeCheckboxOn
// SeCheckboxOff, SeRadioBtnFocus, SeRadioBtnOn, SeSelectCheck, SeSelectUncheck, SeBtnDecide
// SeTouchUnfocus, SeBtnFocus, SeKeyError, SeDialogOpen, SeWebZoomOut, SeWebZoomIn, SeWebNaviFocus
// SeWebPointerFocus, SeFooterFocus, SeFooterDecideBack, SeFooterDecideFinish, SeWebChangeCursorPointer
// SeWebTouchFocus, SeWebLinkDecide, SeWebTextboxStartEdit, SeWebButtonDecide, SeWebRadioBtnOn
// SeWebCheckboxUncheck, SeWebCheckboxCheck, SeWebMenuListOpen
if (isNx) {
window.nx.playSystemSe(label);
} else {
console.log('Sound Effect: ' + label);
}
}
const exit = () => {
playSound('SeFooterDecideBack');
const messageObject = {
menu: settings,
defaults_menu: defaultSettings
}
if (isNx) {
window.nx.sendMessage(
JSON.stringify(messageObject)
);
} else {
console.log(JSON.stringify(messageObject));
}
};
function closeOrExit() {
// Deselect any sliders
handlesWereSelected = deselectSliderHandles();
if (handlesWereSelected) {return}
selectedHandles = document.querySelectorAll(".handleSelected");
if (selectedHandles.length) {
console.log("Found selected handles");
selectedHandles.forEach((handle) => {
handle.classList.remove("handleSelected");
});
return;
}
// Close any open menus
if (document.querySelector('.modal:not(.hide)')) {
console.log('Closing Items');
closeAllActiveModals();
currentTabContent().classList.remove('hide');
lastFocusedItem.focus();
return;
}
console.log('Exiting');
exit();
}
function setSettingsFromJSON(msg) {
// Receive a menu message and set settings
var msg_json = JSON.parse(msg);
settings = msg_json["menu"];
defaultSettings = msg_json["defaults_menu"];
populateMenuFromSettings();
}
function selectSingleOption(eventTarget) {
// Deselect all options in the submenu
const parent = eventTarget.parentElement;
parent.querySelectorAll('.menu-icon:not(.hidden)').forEach((sibling) => {
sibling.classList.add('hidden');
settings[parent.dataset.id] = settings[parent.dataset.id] - parseInt(sibling.dataset.val);
});
eventTarget.querySelector('.menu-icon').classList.remove('hidden');
const value = parseInt(eventTarget.querySelector('.menu-icon').dataset.val);
settings[parent.dataset.id] = settings[parent.dataset.id] + value;
}
const isValueInBitmask = (value, mask) => (mask & value) != 0;
const setOptionsForMenu = (menuId) => {
const modal = document.querySelector(`.modal[data-id="${menuId}"]`);
if (modal.querySelector('.modal-button')) {
// Toggle menu
modal.querySelectorAll('.menu-icon').forEach(function (toggle) {
if (isValueInBitmask(toggle.dataset.val, settings[menuId])) {
toggle.classList.remove('hidden');
} else {
toggle.classList.add('hidden');
}
});
if (modal.classList.contains('single-option')) {
// If no option is selected default to the first option
if (modal.querySelectorAll('.menu-icon:not(.hidden)').length === 0) {
selectSingleOption(modal.querySelector('button'));
}
}
} else {
// Slider menu
slider = modal.querySelector('.modal-slider');
setSliderVals(slider, settings[menuId]);
}
};
function populateMenuFromSettings() {
document.querySelectorAll('.menu-item').forEach((item) => setOptionsForMenu(item.id));
}
function getSettingsValFromMenuID(id) {
const modal = document.querySelector(`.modal[data-id='${id}']`);
if (modal.querySelector('.modal-button')) {
// Toggle menu
// Return value is a bitmask
var value = 0;
const options = modal.querySelectorAll('img:not(.hidden)');
options.forEach(function (toggle) {
value += parseInt(toggle.dataset.val);
});
return value;
} else {
// Slider menu
// Return value is a [lower,upper] array
slider = modal.querySelector('.modal-slider');
return getSliderVals(slider);
}
}
function resetCurrentMenu() {
playSound('SeWebTextboxStartEdit');
const menu = document.querySelector('.modal:not(.hide)');
const menuId = menu.dataset.id;
const defaultSubmenuSetting = defaultSettings[menuId];
settings[menuId] = defaultSubmenuSetting;
deselectSliderHandles();
populateMenuFromSettings();
}
function resetAllMenus() {
// Resets all submenus to the default values
if (confirm('Are you sure that you want to reset all menu settings to the default?')) {
document.querySelectorAll('.menu-item').forEach(function (item) {
const defaultMenuId = item.id;
const defaultSubmenuSetting = defaultSettings[defaultMenuId];
settings[item.id] = defaultSubmenuSetting;
deselectSliderHandles();
populateMenuFromSettings();
});
}
}
function setHelpText(text) {
document.getElementById('help-text').innerText = text;
}
function saveDefaults() {
if (confirm('Are you sure that you want to change the default menu settings to the current selections?')) {
document.querySelectorAll('.menu-item').forEach((item) => {
const menu = item.id;
defaultSettings[menu] = getSettingsValFromMenuID(item.id);
});
}
}
function cycleNextTab() {
deselectSliderHandles();
// Cycle to the next tab
const activeTab = document.querySelector('.tab-button.active');
var nextTab = activeTab.nextElementSibling;
if (!nextTab) {
// On the last tab - set the next tab as the first tab in the list
nextTab = document.querySelector('.tab-button');
}
openTab(nextTab);
}
function cyclePrevTab() {
deselectSliderHandles();
// Cycle to the previous tab
const activeTab = document.querySelector('.tab-button.active');
var previousTab = activeTab.previousElementSibling;
if (!previousTab) {
// On the first tab - set the next tab as the last tab in the list
tabs = document.querySelectorAll('.tab-button');
previousTab = tabs[tabs.length - 1];
}
openTab(previousTab);
}
function getSliderVals(slider) {
var arr = slider.noUiSlider.get();
return [parseFloat(arr[0]), parseFloat(arr[1])]
}
function setSliderVals(slider, vals) {
slider.noUiSlider.set(vals);
}
function setSettingsFromSlider(slider) {
menuId = closestClass(slider, "modal").dataset.id;
settings[menuId] = getSliderVals(slider)
}
function initializeSlider(slider) {
noUiSlider.create(
slider,
{
start: [
parseFloat(slider.dataset.selectedMin),
parseFloat(slider.dataset.selectedMax),
],
connect: true,
range: {
'min': parseFloat(slider.dataset.absMin),
'max': parseFloat(slider.dataset.absMax),
},
step: 1,
tooltips: [
{ to: function (value) { return value.toFixed(0) + '%'; } },
{ to: function (value) { return value.toFixed(0) + '%'; } },
],
pips: {
mode: 'range',
density: 10,
},
keyboardMultiplier: 0, // Prevents doublestepping with custom implementation
}
);
slider.noUiSlider.on('set', function () { setSettingsFromSlider(slider) });
}
function initializeAllSliders() {
document.querySelectorAll(".modal-slider").forEach((item) => {
initializeSlider(item);
});
}
function select(element) {
if (element.classList.contains("noUi-handle")) {
element.classList.toggle("handleSelected");
}
element.click();
}
function increaseSelectedHandle() {
// Increments the selected slider handle, if one is selected
// Won't go past the slider limit
handle = document.querySelector(".noUi-handle.handleSelected");
if (handle) {
slider = closestClass(handle, "modal-slider");
isLowerHandle = handle.classList.contains("noUi-handle-lower");
step = slider.noUiSlider.options.step;
currentVals = getSliderVals(slider);
if (isLowerHandle) {
setSliderVals(
slider,
[currentVals[0] + step, null]
);
} else {
setSliderVals(
slider,
[null, currentVals[1] + step]
);
}
// Refocus the handle, since the native navigation might focus the other handle
// TODO: Is there a more elegant way to do this?
setTimeout( function() {handle.focus() }, 15);
}
}
function decreaseSelectedHandle() {
// Decrements the selected slider handle, if one is selected
// Won't go past the slider limit
handle = document.querySelector(".noUi-handle.handleSelected");
if (handle) {
slider = closestClass(handle, "modal-slider");
isLowerHandle = handle.classList.contains("noUi-handle-lower");
step = slider.noUiSlider.options.step;
currentVals = getSliderVals(slider);
if (isLowerHandle) {
setSliderVals(
slider,
[currentVals[0] - step, null]
);
} else {
setSliderVals(
slider,
[null, currentVals[1] - step]
);
}
// Refocus the handle, since the native navigation might focus the other handle
// TODO: Is there a more elegant way to do this?
setTimeout( function() {handle.focus() }, 15);
}
}
function deselectSliderHandles() {
/// Returns true if any slider handles were changed from selected -> deselected
/// Returns false if there were no selected slider handles to begin with
selectedHandles = document.querySelectorAll(".handleSelected");
if (selectedHandles.length) {
selectedHandles.forEach((handle) => {
handle.classList.remove("handleSelected");
});
return true;
} else {
return false;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

View file

@ -1,83 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Modpack Menu</title>
<link rel="stylesheet" href="./css/nouislider.min.css">
<link rel="stylesheet" href="./css/training_modpack.css" />
</head>
<body>
<script defer src="./js/nouislider.min.js"></script>
<script defer src="./js/training_modpack.js"></script>
<div class="header">
<a id="ret-button" tabindex="-1" class="return-icon-container" onclick="closeOrExit()">
<img class="return-icon" src="./img/m_retnormal.svg">
</a>
<p class="header-title">Ultimate Training Modpack Menu</p>
<div class="header-label">
<p class="header-desc">Reset Current Menu: &#xE0A2;</p>
<p class="header-desc">Reset All Menus: &#xE0A4;</p>
<p class="header-desc">Save Defaults: &#xE0A5;</p>
</div>
</div>
<div class="tab-list-container">
<p>Prev Tab: &#xE0A6;</p>
<div class="tab-list">
{{#tabs}}
<button class="tab-button" id="{{tab_id}}_button" onclick="openTab(this)" tabindex="-1">{{tab_title}}</button>
{{/tabs}}
</div>
<p>Next Tab: &#xE0A7;</p>
</div>
<div class="tab-content-container">
{{#tabs}}
{{#tab_submenus}}
<div class="hide modal{{#is_single_option}} single-option{{/is_single_option}}" data-id="{{submenu_id}}">
{{#toggles}}
<button class="modal-button" onclick="toggleOption(this)">
<div class="modal-button-content">
<img class="hidden menu-icon" src="./img/check.svg" data-val="{{toggle_value}}" />
<p class="modal-button-title">{{toggle_title}}</p>
</div>
</button>
{{/toggles}}
{{#slider}}
<div class="modal-slider-label">
<p>{{submenu_title}}</p>
</div>
<div
id="{{submenu_id}}-slider"
data-selected-min="{{selected_min}}"
data-selected-max="{{selected_max}}"
data-abs-min="{{abs_min}}"
data-abs-max="{{abs_max}}"
class="modal-slider"
>
</div>
{{/slider}}
</div>
{{/tab_submenus}}
<div id="{{tab_id}}_tab" class="tab-content hide">
<div class="tab-content-wrapper">
{{#tab_submenus}}
<div class="menu-item" id="{{submenu_id}}">
<button class="menu-button" onfocus="setHelpText('{{help_text}}')" onclick="openMenuItem(this)" data-target="{{submenu_id}}">
<div class="menu-button-content">
<img src="./img/{{submenu_id}}.svg" class="menu-icon" />
<p class="menu-title">{{submenu_title}}</p>
</div>
</button>
</div>
{{/tab_submenus}}
</div>
</div>
{{/tabs}}
</div>
<footer id="footer" class="footer">
<p id="help-text" class="header-desc"></p>
</footer>
</body>
</html>

View file

@ -1,77 +0,0 @@
var slider = document.getElementById('slider');
const STEP = 5
function checkGamepad(index, gamepad) {
// Gets the current value of the sliders
var current_value = slider.noUiSlider.get();
// Displays it on the HTML page
document.getElementById("input").innerHTML = current_value;
// Checks to see if the L-button is pressed
if(gamepad.buttons[4].pressed){
// If the right-side of the slider is focused on, subtract STEP from the current value
if(document.activeElement.classList.contains("noUi-handle-upper")){
slider.noUiSlider.set(
[
null,
parseInt(current_value[1]) - STEP
]);
}
// If the left-side of the slider is focused on, subtract STEP from the current value
else if(document.activeElement.classList.contains("noUi-handle-lower")){
slider.noUiSlider.set(
[
parseInt(current_value[0]) - STEP,
null
]);
}
}
// Checks to see if the R-button is pressed
else if(gamepad.buttons[5].pressed){
// If the right-side of the slider is focused on, add STEP to the current value
if(document.activeElement.classList.contains("noUi-handle-upper")){
slider.noUiSlider.set(
[
null,
parseInt(current_value[1]) + STEP
]);
}
// If the left-side of the slider is focused on, add STEP to the current value
else if(document.activeElement.classList.contains("noUi-handle-lower")){
slider.noUiSlider.set(
[
parseInt(current_value[0]) + STEP,
null
]);
}
}
};
window.onload = function(){
// Creates the slider
noUiSlider.create(slider, {
start: [20, 80],
connect: true,
range: {
'min': 0,
'max': 100
}
});
// Listens to see if the gamepad is connected
window.addEventListener("gamepadconnected", function (e) {
document.getElementById("input").innerHTML = "Gamepad Connected!";
});
// Sets an interval and runs a function every 100 seconds to check the gamepads
setInterval(function () {
var gpl = navigator.getGamepads();
if (gpl.length > 0) {
for (var i = 0; i < gpl.length; i++) {
checkGamepad(i, gpl[i]);
}
}
}, 100);
}

View file

@ -1131,7 +1131,6 @@ pub struct TrainingModpackMenu {
pub miss_tech_state: MissTechFlags, pub miss_tech_state: MissTechFlags,
pub oos_offset: Delay, pub oos_offset: Delay,
pub pummel_delay: MedDelay, pub pummel_delay: MedDelay,
pub quick_menu: OnOff,
pub reaction_time: Delay, pub reaction_time: Delay,
pub save_damage_cpu: SaveDamage, pub save_damage_cpu: SaveDamage,
pub save_damage_limits_cpu: DamagePercent, pub save_damage_limits_cpu: DamagePercent,
@ -1219,7 +1218,6 @@ pub static DEFAULTS_MENU: TrainingModpackMenu = TrainingModpackMenu {
miss_tech_state: MissTechFlags::all(), miss_tech_state: MissTechFlags::all(),
oos_offset: Delay::empty(), oos_offset: Delay::empty(),
pummel_delay: MedDelay::empty(), pummel_delay: MedDelay::empty(),
quick_menu: OnOff::Off,
reaction_time: Delay::empty(), reaction_time: Delay::empty(),
save_damage_cpu: SaveDamage::DEFAULT, save_damage_cpu: SaveDamage::DEFAULT,
save_damage_limits_cpu: DamagePercent::default(), save_damage_limits_cpu: DamagePercent::default(),
@ -1680,13 +1678,6 @@ pub unsafe fn ui_menu(menu: TrainingModpackMenu) -> UiMenu<'static> {
true, true,
&(menu.stage_hazards as u32), &(menu.stage_hazards as u32),
); );
misc_tab.add_submenu_with_toggles::<OnOff>(
"Quick Menu",
"quick_menu",
"Quick Menu: Should use quick or web menu",
true,
&(menu.quick_menu as u32),
);
misc_tab.add_submenu_with_toggles::<OnOff>( misc_tab.add_submenu_with_toggles::<OnOff>(
"HUD", "HUD",
"hud", "hud",