diff --git a/README.md b/README.md index fc5c7d3..dd7952e 100644 --- a/README.md +++ b/README.md @@ -357,7 +357,7 @@ To install a beta version of the modpack, follow the same procedure using the [l `SD:/atmosphere/contents/01006A800016E000/romfs/skyline/plugins/libnro_hook.nro` `SD:/atmosphere/contents/01006A800016E000/romfs/skyline/plugins/libparam_hook.nro` `SD:/atmosphere/contents/01006A800016E000/romfs/skyline/plugins/libtraining_modpack.nro` - `SD:/TrainingModpack/` + `SD:/ultimate/TrainingModpack/` 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! @@ -386,7 +386,7 @@ To install a beta version of the modpack, follow the same procedure using the [l 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: - `SD:/TrainingModpack/training_modpack_menu.conf` + `SD:/ultimate/TrainingModpack/training_modpack_menu.conf` 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. diff --git a/src/common/button_config.rs b/src/common/button_config.rs index 82a4b0d..9d8e5a5 100644 --- a/src/common/button_config.rs +++ b/src/common/button_config.rs @@ -1,6 +1,8 @@ use std::collections::HashMap; use std::fs; +use crate::consts::TRAINING_MODPACK_TOML_PATH; + use lazy_static::lazy_static; use log::info; use serde::Deserialize; @@ -78,13 +80,13 @@ pub struct TopLevelBtnComboConfig { } pub fn load_from_file() { - let combo_path = "sd:/TrainingModpack/training_modpack.toml"; - info!("Checking for previous button combo settings in training_modpack.toml..."); + let combo_path = TRAINING_MODPACK_TOML_PATH; + info!("Checking for previous button combo settings in {TRAINING_MODPACK_TOML_PATH}..."); let mut valid_button_config = false; if fs::metadata(combo_path).is_ok() { info!("Previous button combo settings found. Loading..."); - let combo_conf = - fs::read_to_string(combo_path).unwrap_or_else(|_| panic!("Could not read {}", combo_path)); + let combo_conf = fs::read_to_string(combo_path) + .unwrap_or_else(|_| panic!("Could not read {}", combo_path)); let conf: Result = toml::from_str(&combo_conf); if let Ok(conf) = conf { if validate_config(conf) { @@ -96,13 +98,11 @@ pub fn load_from_file() { if !valid_button_config { info!("No previous button combo file found. Creating..."); - fs::write(combo_path, DEFAULT_BTN_CONFIG) - .expect("Failed to write button config conf file"); + fs::write(combo_path, DEFAULT_BTN_CONFIG).expect("Failed to write button config conf file"); save_all_btn_config_from_defaults(); } } - fn save_all_btn_config_from_defaults() { let conf = TopLevelBtnComboConfig { button_config: BtnComboConfig { @@ -146,8 +146,13 @@ fn save_all_btn_config_from_toml(data: &str) { fn validate_config(conf: TopLevelBtnComboConfig) -> bool { let conf = conf.button_config; - let configs = [conf.open_menu, conf.save_state, conf.load_state, - conf.previous_save_state_slot, conf.next_save_state_slot]; + let configs = [ + conf.open_menu, + conf.save_state, + conf.load_state, + conf.previous_save_state_slot, + conf.next_save_state_slot, + ]; let bad_keys = configs .iter() .flat_map(|btn_list| { @@ -164,8 +169,9 @@ fn validate_config(conf: TopLevelBtnComboConfig) -> bool { 0x71, "Training Modpack custom button\nconfiguration is invalid!\0", &format!( - "The following keys are invalid in\nsd:/TrainingModpack/training_modpack.toml:\n\ + "The following keys are invalid in\n{}:\n\ {:?}\n\nPossible Keys: {:#?}\0", + TRAINING_MODPACK_TOML_PATH, &bad_keys, BUTTON_MAPPING.keys() ), @@ -207,13 +213,14 @@ fn combo_passes( ) -> bool { unsafe { let (hold, press) = get_combo_keys(combo); - let this_combo_passes = hold.iter() + let this_combo_passes = hold + .iter() .map(|hold| *BUTTON_MAPPING.get(&*hold.to_uppercase()).unwrap()) .all(|hold| ControlModule::check_button_on(module_accessor, hold)) && press - .iter() - .map(|press| *BUTTON_MAPPING.get(&*press.to_uppercase()).unwrap()) - .all(|press| ControlModule::check_button_trigger(module_accessor, press)); + .iter() + .map(|press| *BUTTON_MAPPING.get(&*press.to_uppercase()).unwrap()) + .all(|press| ControlModule::check_button_trigger(module_accessor, press)); this_combo_passes } diff --git a/src/common/dev_config.rs b/src/common/dev_config.rs index 96c091c..9d67453 100644 --- a/src/common/dev_config.rs +++ b/src/common/dev_config.rs @@ -6,12 +6,13 @@ use serde::Deserialize; use skyline::nn::hid::NpadGcState; use toml; +use crate::consts::DEV_TOML_PATH; use crate::logging::info; /// Hot-reloadable configs for quicker development /// /// In game, press L+R+A at any point to reread these configs from -/// the file in sd:/TrainingModpack/dev.toml +/// the file in DEV_TOML_PATH on the SD card /// /// Example usage: /// @@ -42,7 +43,7 @@ lazy_static! { impl DevConfig { fn load_from_toml() -> DevConfig { - let dev_path = "sd:/TrainingModpack/dev.toml"; + let dev_path = DEV_TOML_PATH; if fs::metadata(dev_path).is_ok() { info!("Loading dev.toml configs..."); let dev_config_str = fs::read_to_string(dev_path).unwrap_or_else(|_| panic!("Could not read {}", dev_path)); diff --git a/src/common/menu.rs b/src/common/menu.rs index 9ebbc3c..fcd25fd 100644 --- a/src/common/menu.rs +++ b/src/common/menu.rs @@ -8,6 +8,7 @@ use training_mod_consts::MenuJsonStruct; use training_mod_tui::AppPage; use crate::common::*; +use crate::consts::MENU_OPTIONS_PATH; use crate::events::{Event, EVENT_QUEUE}; use crate::logging::*; @@ -22,14 +23,11 @@ pub unsafe fn menu_condition(module_accessor: &mut app::BattleObjectModuleAccess button_config::combo_passes_exclusive(module_accessor, button_config::ButtonCombo::OpenMenu) } -const MENU_CONF_PATH: &str = "sd:/TrainingModpack/training_modpack_menu.json"; - pub fn load_from_file() { - let menu_conf_path = "sd:/TrainingModpack/training_modpack_menu.json"; - info!("Checking for previous menu in training_modpack_menu.json..."); - if fs::metadata(menu_conf_path).is_ok() { - let menu_conf = fs::read_to_string(menu_conf_path) - .unwrap_or_else(|_| panic!("Could not remove {}", menu_conf_path)); + info!("Checking for previous menu in {MENU_OPTIONS_PATH}..."); + if fs::metadata(MENU_OPTIONS_PATH).is_ok() { + let menu_conf = fs::read_to_string(MENU_OPTIONS_PATH) + .unwrap_or_else(|_| panic!("Could not remove {}", MENU_OPTIONS_PATH)); if let Ok(menu_conf_json) = serde_json::from_str::(&menu_conf) { unsafe { MENU = menu_conf_json.menu; @@ -38,10 +36,10 @@ pub fn load_from_file() { } } else { warn!("Previous menu found but is invalid. Deleting..."); - fs::remove_file(menu_conf_path).unwrap_or_else(|_| { + fs::remove_file(MENU_OPTIONS_PATH).unwrap_or_else(|_| { panic!( "{} has invalid schema but could not be deleted!", - menu_conf_path + MENU_OPTIONS_PATH ) }); } @@ -58,7 +56,7 @@ pub unsafe fn set_menu_from_json(message: &str) { MENU = message_json.menu; DEFAULTS_MENU = message_json.defaults_menu; fs::write( - MENU_CONF_PATH, + MENU_OPTIONS_PATH, serde_json::to_string_pretty(&message_json).unwrap(), ) .expect("Failed to write menu settings file"); @@ -208,7 +206,7 @@ pub fn handle_get_npad_state(state: *mut NpadGcState, _controller_id: *const u32 BUTTON_PRESSES.r.is_pressed = true; } // Special case for frame-by-frame - if FRAME_COUNTER < MENU_INPUT_WAIT_FRAMES && (*state).Buttons & (1 << 8) > 0 { + if FRAME_COUNTER > MENU_INPUT_WAIT_FRAMES && (*state).Buttons & (1 << 8) > 0 { BUTTON_PRESSES.zl.is_pressed = true; } if (*state).Buttons & (1 << 9) > 0 { @@ -223,7 +221,10 @@ pub fn handle_get_npad_state(state: *mut NpadGcState, _controller_id: *const u32 if (*state).Buttons & ((1 << 15) | (1 << 19)) > 0 { BUTTON_PRESSES.down.is_pressed = true; } - if (*state).Buttons & ((1 << 13) | (1 << 17)) > 0 { + // Special case for "UP" in menu open button combo + if FRAME_COUNTER > MENU_INPUT_WAIT_FRAMES + && (*state).Buttons & ((1 << 13) | (1 << 17)) > 0 + { BUTTON_PRESSES.up.is_pressed = true; } diff --git a/src/common/release.rs b/src/common/release.rs index b5cdc74..0c63d4d 100644 --- a/src/common/release.rs +++ b/src/common/release.rs @@ -2,10 +2,12 @@ use std::fs; use skyline_web::DialogOk; +use crate::consts::{ + LEGACY_MENU_OPTIONS_PATH, MENU_DEFAULT_OPTIONS_PATH, MENU_OPTIONS_PATH, VERSION_TXT_PATH, +}; use crate::logging::*; pub const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION"); -const VERSION_FILE_PATH: &str = "sd:/TrainingModpack/version.txt"; enum VersionCheck { Current, @@ -33,7 +35,7 @@ fn record_current_version(fpath: &str) { } pub fn version_check() { - match is_current_version(VERSION_FILE_PATH) { + match is_current_version(VERSION_TXT_PATH) { VersionCheck::Current => { // Version is current, no need to take any action } @@ -47,13 +49,16 @@ pub fn version_check() { ) ); // Remove old menu selections, silently ignoring errors (i.e. if the file doesn't exist) - fs::remove_file("sd:/TrainingModpack/training_modpack_menu.conf") - .unwrap_or_else(|_| error!("Couldn't remove training_modpack_menu.conf")); - fs::remove_file("sd:/TrainingModpack/training_modpack_menu.json") - .unwrap_or_else(|_| error!("Couldn't remove training_modpack_menu.json")); - fs::remove_file("sd:/TrainingModpack/training_modpack_menu_defaults.conf") - .unwrap_or_else(|_| error!("Couldn't remove training_modpack_menu_defaults.conf")); - record_current_version(VERSION_FILE_PATH); + [ + MENU_OPTIONS_PATH, + MENU_DEFAULT_OPTIONS_PATH, + LEGACY_MENU_OPTIONS_PATH, + ] + .iter() + .for_each(|path| { + fs::remove_file(path).unwrap_or_else(|_| error!("Couldn't remove {path}")) + }); + record_current_version(VERSION_TXT_PATH); } VersionCheck::NoFile => { // Display dialog box on fresh installation @@ -63,7 +68,7 @@ pub fn version_check() { Please refer to the Github page and the Discord server for a full list of features and instructions on how to utilize the improved Training Mode." ) ); - record_current_version(VERSION_FILE_PATH); + record_current_version(VERSION_TXT_PATH); } } } diff --git a/src/lib.rs b/src/lib.rs index dfbaa91..42cb9f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,23 +4,25 @@ #![feature(once_cell)] #![feature(c_variadic)] #![allow( -clippy::borrow_interior_mutable_const, -clippy::declare_interior_mutable_const, -clippy::not_unsafe_ptr_arg_deref, -clippy::missing_safety_doc, -clippy::wrong_self_convention, -clippy::option_map_unit_fn, -clippy::fn_null_check, -clippy::transmute_num_to_bytes + clippy::borrow_interior_mutable_const, + clippy::declare_interior_mutable_const, + clippy::not_unsafe_ptr_arg_deref, + clippy::missing_safety_doc, + clippy::wrong_self_convention, + clippy::option_map_unit_fn, + clippy::fn_null_check, + clippy::transmute_num_to_bytes )] use std::fs; +use std::path::PathBuf; -use skyline::libc::mkdir; use skyline::nro::{self, NroInfo}; +use training_mod_consts::LEGACY_TRAINING_MODPACK_ROOT; -use crate::common::*; use crate::common::events::events_loop; +use crate::common::*; +use crate::consts::TRAINING_MODPACK_ROOT; use crate::events::{Event, EVENT_QUEUE}; use crate::logging::*; use crate::menu::quick_menu_loop; @@ -47,12 +49,6 @@ fn nro_main(nro: &NroInfo<'_>) { } } -macro_rules! c_str { - ($l:tt) => { - [$l.as_bytes(), "\u{0}".as_bytes()].concat().as_ptr() - }; -} - #[skyline::main(name = "training_modpack")] pub fn main() { std::panic::set_hook(Box::new(|info| { @@ -79,8 +75,16 @@ pub fn main() { unsafe { EVENT_QUEUE.push(Event::smash_open()); notification("Training Modpack".to_string(), "Welcome!".to_string(), 60); - notification("Open Menu".to_string(), "Special + Uptaunt".to_string(), 120); - notification("Save State".to_string(), "Grab + Downtaunt".to_string(), 120); + notification( + "Open Menu".to_string(), + "Special + Uptaunt".to_string(), + 120, + ); + notification( + "Save State".to_string(), + "Grab + Downtaunt".to_string(), + 120, + ); notification("Load State".to_string(), "Grab + Uptaunt".to_string(), 120); } @@ -89,14 +93,21 @@ pub fn main() { training::training_mods(); nro::add_hook(nro_main).unwrap(); - unsafe { - mkdir(c_str!("sd:/TrainingModpack/"), 777); - } + fs::create_dir_all(TRAINING_MODPACK_ROOT) + .expect("Could not create Training Modpack root folder!"); - let ovl_path = "sd:/switch/.overlays/ovlTrainingModpack.ovl"; - if fs::metadata(ovl_path).is_ok() { - warn!("Removing ovlTrainingModpack.ovl..."); - fs::remove_file(ovl_path).unwrap_or_else(|_| panic!("Could not remove {}", ovl_path)) + // Migrate legacy if exists + if fs::metadata(LEGACY_TRAINING_MODPACK_ROOT).is_ok() { + for entry in fs::read_dir(LEGACY_TRAINING_MODPACK_ROOT).unwrap() { + let entry = entry.unwrap(); + let src_path = &entry.path(); + let dest_path = &PathBuf::from(TRAINING_MODPACK_ROOT).join(entry.file_name()); + fs::rename(src_path, dest_path).unwrap_or_else(|e| { + error!("Could not move file from {src_path:#?} to {dest_path:#?} with error {e}") + }); + } + fs::remove_dir_all(LEGACY_TRAINING_MODPACK_ROOT) + .expect("Could not delete legacy Training Modpack folder!"); } info!("Performing version check..."); diff --git a/src/training/save_states.rs b/src/training/save_states.rs index ce8b48e..68696b8 100644 --- a/src/training/save_states.rs +++ b/src/training/save_states.rs @@ -17,6 +17,7 @@ use crate::common::consts::get_random_int; use crate::common::consts::FighterId; use crate::common::consts::OnOff; use crate::common::consts::SaveStateMirroring; +use crate::common::consts::SAVE_STATES_TOML_PATH; use crate::common::is_dead; use crate::common::MENU; use crate::is_operation_cpu; @@ -114,14 +115,13 @@ pub fn load_from_file() -> SaveStateSlots { cpu: [default_save_state!(); NUM_SAVE_STATE_SLOTS], }; - let save_states_path = "sd:/TrainingModpack/save_states.toml"; - info!("Checking for previous save state settings in save_states.toml..."); - if std::fs::metadata(save_states_path).is_err() { + info!("Checking for previous save state settings in {SAVE_STATES_TOML_PATH}..."); + if std::fs::metadata(SAVE_STATES_TOML_PATH).is_err() { return defaults; } info!("Previous save state settings found. Loading..."); - if let Ok(data) = std::fs::read_to_string(save_states_path) { + if let Ok(data) = std::fs::read_to_string(SAVE_STATES_TOML_PATH) { let input_slots = toml::from_str::(&data); if let Ok(input_slots) = input_slots { return input_slots; @@ -134,7 +134,7 @@ pub fn load_from_file() -> SaveStateSlots { pub unsafe fn save_to_file() { let save_states_str = toml::to_string_pretty(&*SAVE_STATE_SLOTS.data_ptr()) .expect("Error serializing save state information"); - std::fs::write("sd:/TrainingModpack/save_states.toml", save_states_str) + std::fs::write(SAVE_STATES_TOML_PATH, save_states_str) .expect("Could not write save state information to file"); } @@ -596,6 +596,12 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor) MIRROR_STATE = 1.0; save_state_player().state = Save; save_state_cpu().state = Save; + notifications::clear_notifications("Save State"); + notifications::notification( + "Save State".to_string(), + format!("Saved Slot {SAVE_STATE_SLOT}"), + 120, + ); } if save_state.state == Save && !fighter_is_nana { diff --git a/src/training/ui/mod.rs b/src/training/ui/mod.rs index 6049638..d8a2cab 100644 --- a/src/training/ui/mod.rs +++ b/src/training/ui/mod.rs @@ -1,8 +1,10 @@ use sarc::SarcFile; use skyline::nn::ui2d::*; -use training_mod_consts::{MENU, OnOff}; +use training_mod_consts::{OnOff, MENU}; use crate::common::{is_ready_go, is_training_mode}; +#[cfg(feature = "layout_arc_from_file")] +use crate::consts::LAYOUT_ARC_PATH; mod damage; mod display; @@ -37,8 +39,7 @@ pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64) static mut LAYOUT_ARC: &mut [u8; 600000] = &mut [0u8; 600000]; /// We are editing the info_training/layout.arc and replacing the original file with our -/// modified version from `sd://TrainingModpack/layout.arc` or, in the case of Ryujinx for the cool -/// kids `${RYUJINX_DIR}/sdcard/TrainingModpack/layout.arc` +/// modified version from `LAYOUT_ARC_PATH` /// /// When we edit the layout we are doing two things. /// @@ -72,9 +73,7 @@ static mut LAYOUT_ARC: &mut [u8; 600000] = &mut [0u8; 600000]; /// label_material.set_black_res_color(LABEL_BLACK_SELECTED_COLOR); /// ``` #[skyline::hook(offset = 0x37730d4, inline)] -unsafe fn handle_layout_arc_malloc( - ctx: &mut skyline::hooks::InlineCtx -) { +unsafe fn handle_layout_arc_malloc(ctx: &mut skyline::hooks::InlineCtx) { if !is_training_mode() { return; } @@ -82,9 +81,11 @@ unsafe fn handle_layout_arc_malloc( let decompressed_file = *ctx.registers[21].x.as_ref() as *const u8; let decompressed_size = *ctx.registers[1].x.as_ref() as usize; - let layout_arc = SarcFile::read( - std::slice::from_raw_parts(decompressed_file, decompressed_size) - ).unwrap(); + let layout_arc = SarcFile::read(std::slice::from_raw_parts( + decompressed_file, + decompressed_size, + )) + .unwrap(); let training_layout = layout_arc.files.iter().find(|f| { f.name.is_some() && f.name.as_ref().unwrap() == &String::from("blyt/info_training.bflyt") }); @@ -95,8 +96,9 @@ unsafe fn handle_layout_arc_malloc( let inject_arc; let inject_arc_size: u64; - #[cfg(feature = "layout_arc_from_file")] { - let inject_arc_from_file = std::fs::read("sd:/TrainingModpack/layout.arc").unwrap(); + #[cfg(feature = "layout_arc_from_file")] + { + let inject_arc_from_file = std::fs::read(LAYOUT_ARC_PATH).unwrap(); inject_arc_size = inject_arc_from_file.len() as u64; // Copy read file to global @@ -107,7 +109,8 @@ unsafe fn handle_layout_arc_malloc( inject_arc = LAYOUT_ARC.as_ptr(); } - #[cfg(not(feature = "layout_arc_from_file"))] { + #[cfg(not(feature = "layout_arc_from_file"))] + { include_flate::flate!(static INJECT_ARC_FROM_FILE: [u8] from "src/static/layout.arc"); inject_arc = INJECT_ARC_FROM_FILE.as_ptr(); @@ -125,8 +128,5 @@ unsafe fn handle_layout_arc_malloc( } pub fn init() { - skyline::install_hooks!( - handle_draw, - handle_layout_arc_malloc - ); + skyline::install_hooks!(handle_draw, handle_layout_arc_malloc); } diff --git a/training_mod_consts/src/files.rs b/training_mod_consts/src/files.rs new file mode 100644 index 0000000..7bcbc99 --- /dev/null +++ b/training_mod_consts/src/files.rs @@ -0,0 +1,14 @@ +pub const ULTIMATE_ROOT: &str = "sd:/ultimate/"; + +pub const LEGACY_TRAINING_MODPACK_ROOT: &str = "sd:/TrainingModpack"; +pub const TRAINING_MODPACK_ROOT: &str = "sd:/ultimate/TrainingModpack"; +pub const TRAINING_MODPACK_TOML_PATH: &str = "sd:/ultimate/TrainingModpack/training_modpack.toml"; +pub const SAVE_STATES_TOML_PATH: &str = "sd:/ultimate/TrainingModpack/save_states.toml"; +pub const DEV_TOML_PATH: &str = "sd:/ultimate/TrainingModpack/dev.toml"; +pub const VERSION_TXT_PATH: &str = "sd:/ultimate/TrainingModpack/version.txt"; +pub const LAYOUT_ARC_PATH: &str = "sd:/ultimate/TrainingModpack/layout.arc"; +pub const MENU_OPTIONS_PATH: &str = "sd:/ultimate/TrainingModpack/training_modpack_menu.conf"; +pub const LEGACY_MENU_OPTIONS_PATH: &str = + "sd:/ultimate/TrainingModpack/training_modpack_menu.json"; +pub const MENU_DEFAULT_OPTIONS_PATH: &str = + "sd:/ultimate/TrainingModpack/training_modpack_menu_defaults.conf"; diff --git a/training_mod_consts/src/lib.rs b/training_mod_consts/src/lib.rs index 560837f..72f6478 100644 --- a/training_mod_consts/src/lib.rs +++ b/training_mod_consts/src/lib.rs @@ -7,1101 +7,12 @@ extern crate bitflags_serde_shim; #[macro_use] extern crate num_derive; -use core::f64::consts::PI; use serde::{Deserialize, Serialize}; -use serde_repr::{Deserialize_repr, Serialize_repr}; -#[cfg(feature = "smash")] -use smash::lib::lua_const::*; -use strum::IntoEnumIterator; -use strum_macros::EnumIter; -pub trait ToggleTrait { - fn to_toggle_strs() -> Vec<&'static str>; - fn to_toggle_vals() -> Vec; -} - -pub trait SliderTrait { - fn get_limits() -> (u32, u32); -} - -// bitflag helper function macro -macro_rules! extra_bitflag_impls { - ($e:ty) => { - impl core::fmt::Display for $e { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - core::fmt::Debug::fmt(self, f) - } - } - - impl $e { - pub fn to_vec(&self) -> Vec::<$e> { - let mut vec = Vec::<$e>::new(); - let mut field = <$e>::from_bits_truncate(self.bits); - while !field.is_empty() { - let flag = <$e>::from_bits(1u32 << field.bits.trailing_zeros()).unwrap(); - field -= flag; - vec.push(flag); - } - return vec; - } - - pub fn to_index(&self) -> u32 { - if self.bits == 0 { - 0 - } else { - self.bits.trailing_zeros() - } - } - - pub fn get_random(&self) -> $e { - let options = self.to_vec(); - match options.len() { - 0 => { - return <$e>::empty(); - } - 1 => { - return options[0]; - } - _ => { - return *random_option(&options); - } - } - } - } - impl ToggleTrait for $e { - fn to_toggle_strs() -> Vec<&'static str> { - let all_options = <$e>::all().to_vec(); - all_options.iter().map(|i| i.as_str().unwrap_or("")).collect() - } - - fn to_toggle_vals() -> Vec { - let all_options = <$e>::all().to_vec(); - all_options.iter().map(|i| i.bits() as u32).collect() - } - } - } -} - -pub fn get_random_int(_max: i32) -> i32 { - #[cfg(feature = "smash")] - unsafe { - smash::app::sv_math::rand(smash::hash40("fighter"), _max) - } - - #[cfg(not(feature = "smash"))] - 0 -} - -/// Generate a random float between _min and _max. -/// Note that (_min <= _max) is not enforced. -pub fn get_random_float(_min: f32, _max: f32) -> f32 { - #[cfg(feature = "smash")] - unsafe { - _min + smash::app::sv_math::randf(smash::hash40("fighter"), _max - _min) - } - - #[cfg(not(feature = "smash"))] - _min -} - -pub fn random_option(arg: &[T]) -> &T { - &arg[get_random_int(arg.len() as i32) as usize] -} - -// DI -/* - 0, 0.785398, 1.570796, 2.356194, -3.14159, -2.356194, -1.570796, -0.785398 - 0, pi/4, pi/2, 3pi/4, pi, 5pi/4, 3pi/2, 7pi/4 -*/ - -// DI / Left stick -bitflags! { - pub struct Direction : u32 { - const OUT = 0x1; - const UP_OUT = 0x2; - const UP = 0x4; - const UP_IN = 0x8; - const IN = 0x10; - const DOWN_IN = 0x20; - const DOWN = 0x40; - const DOWN_OUT = 0x80; - const NEUTRAL = 0x100; - const LEFT = 0x200; - const RIGHT = 0x400; - } -} - -impl Direction { - pub fn into_angle(self) -> Option { - let index = self.into_index(); - - if index == 0 { - None - } else { - Some((index as i32 - 1) as f64 * PI / 4.0) - } - } - fn into_index(self) -> i32 { - match self { - Direction::OUT => 1, - Direction::UP_OUT => 2, - Direction::UP => 3, - Direction::UP_IN => 4, - Direction::IN => 5, - Direction::DOWN_IN => 6, - Direction::DOWN => 7, - Direction::DOWN_OUT => 8, - Direction::NEUTRAL => 0, - Direction::LEFT => 5, - Direction::RIGHT => 1, - _ => 0, - } - } - - fn as_str(self) -> Option<&'static str> { - Some(match self { - Direction::OUT => "Away", - Direction::UP_OUT => "Up and Away", - Direction::UP => "Up", - Direction::UP_IN => "Up and In", - Direction::IN => "In", - Direction::DOWN_IN => "Down and In", - Direction::DOWN => "Down", - Direction::DOWN_OUT => "Down and Away", - Direction::NEUTRAL => "Neutral", - Direction::LEFT => "Left", - Direction::RIGHT => "Right", - _ => return None, - }) - } -} - -extra_bitflag_impls! {Direction} -impl_serde_for_bitflags!(Direction); - -// Ledge Option -bitflags! { - pub struct LedgeOption : u32 - { - const NEUTRAL = 0x1; - const ROLL = 0x2; - const JUMP = 0x4; - const ATTACK = 0x8; - const WAIT = 0x10; - } -} - -impl LedgeOption { - pub fn into_status(self) -> Option { - #[cfg(feature = "smash")] - { - Some(match self { - LedgeOption::NEUTRAL => *FIGHTER_STATUS_KIND_CLIFF_CLIMB, - LedgeOption::ROLL => *FIGHTER_STATUS_KIND_CLIFF_ESCAPE, - LedgeOption::JUMP => *FIGHTER_STATUS_KIND_CLIFF_JUMP1, - LedgeOption::ATTACK => *FIGHTER_STATUS_KIND_CLIFF_ATTACK, - LedgeOption::WAIT => *FIGHTER_STATUS_KIND_CLIFF_WAIT, - _ => return None, - }) - } - - #[cfg(not(feature = "smash"))] - None - } - - fn as_str(self) -> Option<&'static str> { - Some(match self { - LedgeOption::NEUTRAL => "Neutral Getup", - LedgeOption::ROLL => "Roll", - LedgeOption::JUMP => "Jump", - LedgeOption::ATTACK => "Getup Attack", - LedgeOption::WAIT => "Wait", - _ => return None, - }) - } - const fn default() -> LedgeOption { - // Neutral,Roll,Jump,Attack (everything except wait) - LedgeOption::NEUTRAL - .union(LedgeOption::ROLL) - .union(LedgeOption::JUMP) - .union(LedgeOption::ATTACK) - } -} - -extra_bitflag_impls! {LedgeOption} -impl_serde_for_bitflags!(LedgeOption); - -// Tech options -bitflags! { - pub struct TechFlags : u32 { - const NO_TECH = 0x1; - const ROLL_F = 0x2; - const ROLL_B = 0x4; - const IN_PLACE = 0x8; - } -} - -impl TechFlags { - fn as_str(self) -> Option<&'static str> { - Some(match self { - TechFlags::NO_TECH => "No Tech", - TechFlags::ROLL_F => "Roll Forwards", - TechFlags::ROLL_B => "Roll Backwards", - TechFlags::IN_PLACE => "Tech In Place", - _ => return None, - }) - } -} - -extra_bitflag_impls! {TechFlags} -impl_serde_for_bitflags!(TechFlags); - -// Missed Tech Options -bitflags! { - pub struct MissTechFlags : u32 { - const GETUP = 0x1; - const ATTACK = 0x2; - const ROLL_F = 0x4; - const ROLL_B = 0x8; - } -} - -impl MissTechFlags { - fn as_str(self) -> Option<&'static str> { - Some(match self { - MissTechFlags::GETUP => "Neutral Getup", - MissTechFlags::ATTACK => "Getup Attack", - MissTechFlags::ROLL_F => "Roll Forwards", - MissTechFlags::ROLL_B => "Roll Backwards", - _ => return None, - }) - } -} - -extra_bitflag_impls! {MissTechFlags} -impl_serde_for_bitflags!(MissTechFlags); - -/// Shield States -#[repr(i32)] -#[derive( - Debug, Clone, Copy, PartialEq, FromPrimitive, EnumIter, Serialize_repr, Deserialize_repr, -)] -pub enum Shield { - None = 0x0, - Infinite = 0x1, - Hold = 0x2, - Constant = 0x4, -} - -impl Shield { - pub fn as_str(self) -> Option<&'static str> { - Some(match self { - Shield::None => "None", - Shield::Infinite => "Infinite", - Shield::Hold => "Hold", - Shield::Constant => "Constant", - }) - } -} - -impl ToggleTrait for Shield { - fn to_toggle_strs() -> Vec<&'static str> { - Shield::iter().map(|i| i.as_str().unwrap_or("")).collect() - } - - fn to_toggle_vals() -> Vec { - Shield::iter().map(|i| i as u32).collect() - } -} - -// Save State Mirroring -#[repr(i32)] -#[derive( - Debug, Clone, Copy, PartialEq, FromPrimitive, EnumIter, Serialize_repr, Deserialize_repr, -)] -pub enum SaveStateMirroring { - None = 0x0, - Alternate = 0x1, - Random = 0x2, -} - -impl SaveStateMirroring { - pub fn as_str(self) -> Option<&'static str> { - Some(match self { - SaveStateMirroring::None => "None", - SaveStateMirroring::Alternate => "Alternate", - SaveStateMirroring::Random => "Random", - }) - } -} - -impl ToggleTrait for SaveStateMirroring { - fn to_toggle_strs() -> Vec<&'static str> { - SaveStateMirroring::iter() - .map(|i| i.as_str().unwrap_or("")) - .collect() - } - - fn to_toggle_vals() -> Vec { - SaveStateMirroring::iter().map(|i| i as u32).collect() - } -} - -#[repr(i32)] -#[derive(Debug, Clone, Copy, PartialEq, Serialize_repr, Deserialize_repr)] -pub enum OnOff { - Off = 0, - On = 1, -} - -impl OnOff { - pub fn from_val(val: u32) -> Option { - match val { - 1 => Some(OnOff::On), - 0 => Some(OnOff::Off), - _ => None, - } - } - - pub fn as_str(self) -> Option<&'static str> { - Some(match self { - OnOff::Off => "Off", - OnOff::On => "On", - }) - } -} - -impl ToggleTrait for OnOff { - fn to_toggle_strs() -> Vec<&'static str> { - vec!["Off", "On"] - } - fn to_toggle_vals() -> Vec { - vec![0, 1] - } -} - -bitflags! { - pub struct Action : u32 { - const AIR_DODGE = 0x1; - const JUMP = 0x2; - const SHIELD = 0x4; - const SPOT_DODGE = 0x8; - const ROLL_F = 0x10; - const ROLL_B = 0x20; - const NAIR = 0x40; - const FAIR = 0x80; - const BAIR = 0x100; - const UAIR = 0x200; - const DAIR = 0x400; - const NEUTRAL_B = 0x800; - const SIDE_B = 0x1000; - const UP_B = 0x2000; - const DOWN_B = 0x4000; - const F_SMASH = 0x8000; - const U_SMASH = 0x10000; - const D_SMASH = 0x20000; - const JAB = 0x40000; - const F_TILT = 0x80000; - const U_TILT = 0x0010_0000; - const D_TILT = 0x0020_0000; - const GRAB = 0x0040_0000; - // TODO: Make work - const DASH = 0x0080_0000; - const DASH_ATTACK = 0x0100_0000; - } -} - -impl Action { - pub fn into_attack_air_kind(self) -> Option { - #[cfg(feature = "smash")] - { - Some(match self { - Action::NAIR => *FIGHTER_COMMAND_ATTACK_AIR_KIND_N, - Action::FAIR => *FIGHTER_COMMAND_ATTACK_AIR_KIND_F, - Action::BAIR => *FIGHTER_COMMAND_ATTACK_AIR_KIND_B, - Action::DAIR => *FIGHTER_COMMAND_ATTACK_AIR_KIND_LW, - Action::UAIR => *FIGHTER_COMMAND_ATTACK_AIR_KIND_HI, - _ => return None, - }) - } - - #[cfg(not(feature = "smash"))] - None - } - - pub fn as_str(self) -> Option<&'static str> { - Some(match self { - Action::AIR_DODGE => "Airdodge", - Action::JUMP => "Jump", - Action::SHIELD => "Shield", - Action::SPOT_DODGE => "Spotdodge", - Action::ROLL_F => "Roll Forwards", - Action::ROLL_B => "Roll Backwards", - Action::NAIR => "Neutral Aerial", - Action::FAIR => "Forward Aerial", - Action::BAIR => "Backward Aerial", - Action::UAIR => "Up Aerial", - Action::DAIR => "Down Aerial", - Action::NEUTRAL_B => "Neutral Special", - Action::SIDE_B => "Side Special", - Action::UP_B => "Up Special", - Action::DOWN_B => "Down Special", - Action::F_SMASH => "Forward Smash", - Action::U_SMASH => "Up Smash", - Action::D_SMASH => "Down Smash", - Action::JAB => "Jab", - Action::F_TILT => "Forward Tilt", - Action::U_TILT => "Up Tilt", - Action::D_TILT => "Down Tilt", - Action::GRAB => "Grab", - Action::DASH => "Dash", - Action::DASH_ATTACK => "Dash Attack", - _ => return None, - }) - } -} - -extra_bitflag_impls! {Action} -impl_serde_for_bitflags!(Action); - -bitflags! { - pub struct AttackAngle : u32 { - const NEUTRAL = 0x1; - const UP = 0x2; - const DOWN = 0x4; - } -} - -impl AttackAngle { - pub fn as_str(self) -> Option<&'static str> { - Some(match self { - AttackAngle::NEUTRAL => "Neutral", - AttackAngle::UP => "Up", - AttackAngle::DOWN => "Down", - _ => return None, - }) - } -} - -extra_bitflag_impls! {AttackAngle} -impl_serde_for_bitflags!(AttackAngle); - -bitflags! { - pub struct Delay : u32 { - const D0 = 0x1; - const D1 = 0x2; - const D2 = 0x4; - const D3 = 0x8; - const D4 = 0x10; - const D5 = 0x20; - const D6 = 0x40; - const D7 = 0x80; - const D8 = 0x100; - const D9 = 0x200; - const D10 = 0x400; - const D11 = 0x800; - const D12 = 0x1000; - const D13 = 0x2000; - const D14 = 0x4000; - const D15 = 0x8000; - const D16 = 0x10000; - const D17 = 0x20000; - const D18 = 0x40000; - const D19 = 0x80000; - const D20 = 0x0010_0000; - const D21 = 0x0020_0000; - const D22 = 0x0040_0000; - const D23 = 0x0080_0000; - const D24 = 0x0100_0000; - const D25 = 0x0200_0000; - const D26 = 0x0400_0000; - const D27 = 0x0800_0000; - const D28 = 0x1000_0000; - const D29 = 0x2000_0000; - const D30 = 0x4000_0000; - } -} - -// Throw Option -bitflags! { - pub struct ThrowOption : u32 - { - const NONE = 0x1; - const FORWARD = 0x2; - const BACKWARD = 0x4; - const UP = 0x8; - const DOWN = 0x10; - } -} - -impl ThrowOption { - pub fn into_cmd(self) -> Option { - #[cfg(feature = "smash")] - { - Some(match self { - ThrowOption::NONE => 0, - ThrowOption::FORWARD => *FIGHTER_PAD_CMD_CAT2_FLAG_THROW_F, - ThrowOption::BACKWARD => *FIGHTER_PAD_CMD_CAT2_FLAG_THROW_B, - ThrowOption::UP => *FIGHTER_PAD_CMD_CAT2_FLAG_THROW_HI, - ThrowOption::DOWN => *FIGHTER_PAD_CMD_CAT2_FLAG_THROW_LW, - _ => return None, - }) - } - - #[cfg(not(feature = "smash"))] - None - } - - pub fn as_str(self) -> Option<&'static str> { - Some(match self { - ThrowOption::NONE => "None", - ThrowOption::FORWARD => "Forward Throw", - ThrowOption::BACKWARD => "Back Throw", - ThrowOption::UP => "Up Throw", - ThrowOption::DOWN => "Down Throw", - _ => return None, - }) - } -} - -extra_bitflag_impls! {ThrowOption} -impl_serde_for_bitflags!(ThrowOption); - -// Buff Option -bitflags! { - pub struct BuffOption : u32 - { - const ACCELERATLE = 0x1; - const OOMPH = 0x2; - const PSYCHE = 0x4; - const BOUNCE = 0x8; - const ARSENE = 0x10; - const BREATHING = 0x20; - const LIMIT = 0x40; - const KO = 0x80; - const WING = 0x100; - } -} - -impl BuffOption { - pub fn into_int(self) -> Option { - #[cfg(feature = "smash")] - { - Some(match self { - BuffOption::ACCELERATLE => *FIGHTER_BRAVE_SPECIAL_LW_COMMAND11_SPEED_UP, - BuffOption::OOMPH => *FIGHTER_BRAVE_SPECIAL_LW_COMMAND12_ATTACK_UP, - BuffOption::PSYCHE => *FIGHTER_BRAVE_SPECIAL_LW_COMMAND21_CHARGE, - BuffOption::BOUNCE => *FIGHTER_BRAVE_SPECIAL_LW_COMMAND13_REFLECT, - BuffOption::BREATHING => 1, - BuffOption::ARSENE => 1, - BuffOption::LIMIT => 1, - BuffOption::KO => 1, - BuffOption::WING => 1, - _ => return None, - }) - } - - #[cfg(not(feature = "smash"))] - None - } - - fn as_str(self) -> Option<&'static str> { - Some(match self { - BuffOption::ACCELERATLE => "Acceleratle", - BuffOption::OOMPH => "Oomph", - BuffOption::BOUNCE => "Bounce", - BuffOption::PSYCHE => "Psyche Up", - BuffOption::BREATHING => "Deep Breathing", - BuffOption::ARSENE => "Arsene", - BuffOption::LIMIT => "Limit Break", - BuffOption::KO => "KO Punch", - BuffOption::WING => "1-Winged Angel", - _ => return None, - }) - } -} - -extra_bitflag_impls! {BuffOption} -impl_serde_for_bitflags!(BuffOption); - -impl Delay { - pub fn as_str(self) -> Option<&'static str> { - Some(match self { - Delay::D0 => "0", - Delay::D1 => "1", - Delay::D2 => "2", - Delay::D3 => "3", - Delay::D4 => "4", - Delay::D5 => "5", - Delay::D6 => "6", - Delay::D7 => "7", - Delay::D8 => "8", - Delay::D9 => "9", - Delay::D10 => "10", - Delay::D11 => "11", - Delay::D12 => "12", - Delay::D13 => "13", - Delay::D14 => "14", - Delay::D15 => "15", - Delay::D16 => "16", - Delay::D17 => "17", - Delay::D18 => "18", - Delay::D19 => "19", - Delay::D20 => "20", - Delay::D21 => "21", - Delay::D22 => "22", - Delay::D23 => "23", - Delay::D24 => "24", - Delay::D25 => "25", - Delay::D26 => "26", - Delay::D27 => "27", - Delay::D28 => "28", - Delay::D29 => "29", - Delay::D30 => "30", - _ => return None, - }) - } - - pub fn into_delay(&self) -> u32 { - self.to_index() - } -} - -extra_bitflag_impls! {Delay} -impl_serde_for_bitflags!(Delay); - -bitflags! { - pub struct MedDelay : u32 { - const D0 = 0x1; - const D5 = 0x2; - const D10 = 0x4; - const D15 = 0x8; - const D20 = 0x10; - const D25 = 0x20; - const D30 = 0x40; - const D35 = 0x80; - const D40 = 0x100; - const D45 = 0x200; - const D50 = 0x400; - const D55 = 0x800; - const D60 = 0x1000; - const D65 = 0x2000; - const D70 = 0x4000; - const D75 = 0x8000; - const D80 = 0x10000; - const D85 = 0x20000; - const D90 = 0x40000; - const D95 = 0x80000; - const D100 = 0x0010_0000; - const D105 = 0x0020_0000; - const D110 = 0x0040_0000; - const D115 = 0x0080_0000; - const D120 = 0x0100_0000; - const D125 = 0x0200_0000; - const D130 = 0x0400_0000; - const D135 = 0x0800_0000; - const D140 = 0x1000_0000; - const D145 = 0x2000_0000; - const D150 = 0x4000_0000; - } -} - -impl MedDelay { - pub fn as_str(self) -> Option<&'static str> { - Some(match self { - MedDelay::D0 => "0", - MedDelay::D5 => "5", - MedDelay::D10 => "10", - MedDelay::D15 => "15", - MedDelay::D20 => "20", - MedDelay::D25 => "25", - MedDelay::D30 => "30", - MedDelay::D35 => "35", - MedDelay::D40 => "40", - MedDelay::D45 => "45", - MedDelay::D50 => "50", - MedDelay::D55 => "55", - MedDelay::D60 => "60", - MedDelay::D65 => "65", - MedDelay::D70 => "70", - MedDelay::D75 => "75", - MedDelay::D80 => "80", - MedDelay::D85 => "85", - MedDelay::D90 => "90", - MedDelay::D95 => "95", - MedDelay::D100 => "100", - MedDelay::D105 => "105", - MedDelay::D110 => "110", - MedDelay::D115 => "115", - MedDelay::D120 => "120", - MedDelay::D125 => "125", - MedDelay::D130 => "130", - MedDelay::D135 => "135", - MedDelay::D140 => "140", - MedDelay::D145 => "145", - MedDelay::D150 => "150", - _ => return None, - }) - } - - pub fn into_meddelay(&self) -> u32 { - self.to_index() * 5 - } -} - -extra_bitflag_impls! {MedDelay} -impl_serde_for_bitflags!(MedDelay); - -bitflags! { - pub struct LongDelay : u32 { - const D0 = 0x1; - const D10 = 0x2; - const D20 = 0x4; - const D30 = 0x8; - const D40 = 0x10; - const D50 = 0x20; - const D60 = 0x40; - const D70 = 0x80; - const D80 = 0x100; - const D90 = 0x200; - const D100 = 0x400; - const D110 = 0x800; - const D120 = 0x1000; - const D130 = 0x2000; - const D140 = 0x4000; - const D150 = 0x8000; - const D160 = 0x10000; - const D170 = 0x20000; - const D180 = 0x40000; - const D190 = 0x80000; - const D200 = 0x0010_0000; - const D210 = 0x0020_0000; - const D220 = 0x0040_0000; - const D230 = 0x0080_0000; - const D240 = 0x0100_0000; - const D250 = 0x0200_0000; - const D260 = 0x0400_0000; - const D270 = 0x0800_0000; - const D280 = 0x1000_0000; - const D290 = 0x2000_0000; - const D300 = 0x4000_0000; - } -} - -impl LongDelay { - pub fn as_str(self) -> Option<&'static str> { - Some(match self { - LongDelay::D0 => "0", - LongDelay::D10 => "10", - LongDelay::D20 => "20", - LongDelay::D30 => "30", - LongDelay::D40 => "40", - LongDelay::D50 => "50", - LongDelay::D60 => "60", - LongDelay::D70 => "70", - LongDelay::D80 => "80", - LongDelay::D90 => "90", - LongDelay::D100 => "100", - LongDelay::D110 => "110", - LongDelay::D120 => "120", - LongDelay::D130 => "130", - LongDelay::D140 => "140", - LongDelay::D150 => "150", - LongDelay::D160 => "160", - LongDelay::D170 => "170", - LongDelay::D180 => "180", - LongDelay::D190 => "190", - LongDelay::D200 => "200", - LongDelay::D210 => "210", - LongDelay::D220 => "220", - LongDelay::D230 => "230", - LongDelay::D240 => "240", - LongDelay::D250 => "250", - LongDelay::D260 => "260", - LongDelay::D270 => "270", - LongDelay::D280 => "280", - LongDelay::D290 => "290", - LongDelay::D300 => "300", - _ => return None, - }) - } - - pub fn into_longdelay(&self) -> u32 { - self.to_index() * 10 - } -} - -extra_bitflag_impls! {LongDelay} -impl_serde_for_bitflags!(LongDelay); - -bitflags! { - pub struct BoolFlag : u32 { - const TRUE = 0x1; - const FALSE = 0x2; - } -} - -extra_bitflag_impls! {BoolFlag} -impl_serde_for_bitflags!(BoolFlag); - -impl BoolFlag { - pub fn into_bool(self) -> bool { - matches!(self, BoolFlag::TRUE) - } - - pub fn as_str(self) -> Option<&'static str> { - Some(match self { - BoolFlag::TRUE => "True", - _ => "False", - }) - } -} - -#[repr(u32)] -#[derive( - Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, EnumIter, Serialize_repr, Deserialize_repr, -)] -pub enum SdiFrequency { - None = 0, - Normal = 1, - Medium = 2, - High = 4, -} - -impl SdiFrequency { - pub fn into_u32(self) -> u32 { - match self { - SdiFrequency::None => u32::MAX, - SdiFrequency::Normal => 8, - SdiFrequency::Medium => 6, - SdiFrequency::High => 4, - } - } - - pub fn as_str(self) -> Option<&'static str> { - Some(match self { - SdiFrequency::None => "None", - SdiFrequency::Normal => "Normal", - SdiFrequency::Medium => "Medium", - SdiFrequency::High => "High", - }) - } -} - -impl ToggleTrait for SdiFrequency { - fn to_toggle_strs() -> Vec<&'static str> { - SdiFrequency::iter() - .map(|i| i.as_str().unwrap_or("")) - .collect() - } - - fn to_toggle_vals() -> Vec { - SdiFrequency::iter().map(|i| i as u32).collect() - } -} - -#[repr(u32)] -#[derive( - Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, EnumIter, Serialize_repr, Deserialize_repr, -)] -pub enum ClatterFrequency { - None = 0, - Normal = 1, - Medium = 2, - High = 4, -} - -impl ClatterFrequency { - pub fn into_u32(self) -> u32 { - match self { - ClatterFrequency::None => u32::MAX, - ClatterFrequency::Normal => 8, - ClatterFrequency::Medium => 5, - ClatterFrequency::High => 2, - } - } - - pub fn as_str(self) -> Option<&'static str> { - Some(match self { - ClatterFrequency::None => "None", - ClatterFrequency::Normal => "Normal", - ClatterFrequency::Medium => "Medium", - ClatterFrequency::High => "High", - }) - } -} - -impl ToggleTrait for ClatterFrequency { - fn to_toggle_strs() -> Vec<&'static str> { - ClatterFrequency::iter() - .map(|i| i.as_str().unwrap_or("")) - .collect() - } - - fn to_toggle_vals() -> Vec { - ClatterFrequency::iter().map(|i| i as u32).collect() - } -} - -/// Item Selections -#[repr(i32)] -#[derive( - Debug, Clone, Copy, PartialEq, FromPrimitive, EnumIter, Serialize_repr, Deserialize_repr, -)] -pub enum CharacterItem { - None = 0, - PlayerVariation1 = 0x1, - PlayerVariation2 = 0x2, - PlayerVariation3 = 0x4, - PlayerVariation4 = 0x8, - PlayerVariation5 = 0x10, - PlayerVariation6 = 0x20, - PlayerVariation7 = 0x40, - PlayerVariation8 = 0x80, - CpuVariation1 = 0x100, - CpuVariation2 = 0x200, - CpuVariation3 = 0x400, - CpuVariation4 = 0x800, - CpuVariation5 = 0x1000, - CpuVariation6 = 0x2000, - CpuVariation7 = 0x4000, - CpuVariation8 = 0x8000, -} - -impl CharacterItem { - pub fn as_idx(self) -> u32 { - log_2(self as i32 as u32) - } - - pub fn as_str(self) -> Option<&'static str> { - Some(match self { - CharacterItem::PlayerVariation1 => "Player 1st Var.", - CharacterItem::PlayerVariation2 => "Player 2nd Var.", - CharacterItem::PlayerVariation3 => "Player 3rd Var.", - CharacterItem::PlayerVariation4 => "Player 4th Var.", - CharacterItem::PlayerVariation5 => "Player 5th Var.", - CharacterItem::PlayerVariation6 => "Player 6th Var.", - CharacterItem::PlayerVariation7 => "Player 7th Var.", - CharacterItem::PlayerVariation8 => "Player 8th Var.", - CharacterItem::CpuVariation1 => "CPU 1st Var.", - CharacterItem::CpuVariation2 => "CPU 2nd Var.", - CharacterItem::CpuVariation3 => "CPU 3rd Var.", - CharacterItem::CpuVariation4 => "CPU 4th Var.", - CharacterItem::CpuVariation5 => "CPU 5th Var.", - CharacterItem::CpuVariation6 => "CPU 6th Var.", - CharacterItem::CpuVariation7 => "CPU 7th Var.", - CharacterItem::CpuVariation8 => "CPU 8th Var.", - _ => "None", - }) - } -} - -impl ToggleTrait for CharacterItem { - fn to_toggle_strs() -> Vec<&'static str> { - CharacterItem::iter() - .map(|i| i.as_str().unwrap_or("")) - .collect() - } - - fn to_toggle_vals() -> Vec { - CharacterItem::iter().map(|i| i as u32).collect() - } -} - -bitflags! { - pub struct MashTrigger : u32 { - const HIT = 0b0000_0000_0000_0000_0001; - const BLOCK = 0b0000_0000_0000_0000_0010; - const PARRY = 0b0000_0000_0000_0000_0100; - const TUMBLE = 0b0000_0000_0000_0000_1000; - const LANDING = 0b0000_0000_0000_0001_0000; - const TRUMP = 0b0000_0000_0000_0010_0000; - const FOOTSTOOL = 0b0000_0000_0000_0100_0000; - const CLATTER = 0b0000_0000_0000_1000_0000; - const LEDGE = 0b0000_0000_0001_0000_0000; - const TECH = 0b0000_0000_0010_0000_0000; - const MISTECH = 0b0000_0000_0100_0000_0000; - const GROUNDED = 0b0000_0000_1000_0000_0000; - const AIRBORNE = 0b0000_0001_0000_0000_0000; - const DISTANCE_CLOSE = 0b0000_0010_0000_0000_0000; - const DISTANCE_MID = 0b0000_0100_0000_0000_0000; - const DISTANCE_FAR = 0b0000_1000_0000_0000_0000; - const ALWAYS = 0b0001_0000_0000_0000_0000; - } -} - -impl MashTrigger { - pub fn as_str(self) -> Option<&'static str> { - Some(match self { - MashTrigger::HIT => "Hitstun", - MashTrigger::BLOCK => "Shieldstun", - MashTrigger::PARRY => "Parry", - MashTrigger::TUMBLE => "Tumble", - MashTrigger::LANDING => "Landing", - MashTrigger::TRUMP => "Ledge Trump", - MashTrigger::FOOTSTOOL => "Footstool", - MashTrigger::CLATTER => "Clatter", - MashTrigger::LEDGE => "Ledge Option", - MashTrigger::TECH => "Tech Option", - MashTrigger::MISTECH => "Mistech Option", - MashTrigger::GROUNDED => "Grounded", - MashTrigger::AIRBORNE => "Airborne", - MashTrigger::DISTANCE_CLOSE => "Distance: Close", - MashTrigger::DISTANCE_MID => "Distance: Mid", - MashTrigger::DISTANCE_FAR => "Distance: Far", - MashTrigger::ALWAYS => "Always", - _ => return None, - }) - } - - const fn default() -> MashTrigger { - // Hit, block, clatter - MashTrigger::HIT - .union(MashTrigger::BLOCK) - .union(MashTrigger::CLATTER) - } -} - -extra_bitflag_impls! {MashTrigger} -impl_serde_for_bitflags!(MashTrigger); - -#[derive(Clone, Copy, Serialize, Deserialize, Debug)] -pub struct DamagePercent(pub u32, pub u32); - -impl SliderTrait for DamagePercent { - fn get_limits() -> (u32, u32) { - (0, 150) - } -} - -impl DamagePercent { - const fn default() -> DamagePercent { - DamagePercent(0, 150) - } -} - -bitflags! { - pub struct SaveDamage : u32 - { - const DEFAULT = 0b001; - const SAVED = 0b010; - const RANDOM = 0b100; - } -} - -impl SaveDamage { - fn as_str(self) -> Option<&'static str> { - Some(match self { - SaveDamage::DEFAULT => "Default", - SaveDamage::SAVED => "Save State", - SaveDamage::RANDOM => "Random Value", - _ => return None, - }) - } -} - -extra_bitflag_impls! {SaveDamage} -impl_serde_for_bitflags!(SaveDamage); +pub mod options; +pub use options::*; +pub mod files; +pub use files::*; #[repr(C)] #[derive(Clone, Copy, Serialize, Deserialize, Debug)] @@ -1148,18 +59,6 @@ pub struct TrainingModpackMenu { pub throw_state: ThrowOption, } -const fn num_bits() -> u32 { - (std::mem::size_of::() * 8) as u32 -} - -fn log_2(x: u32) -> u32 { - if x == 0 { - 0 - } else { - num_bits::() - x.leading_zeros() - 1 - } -} - #[repr(C)] #[derive(Debug, Serialize, Deserialize)] pub struct MenuJsonStruct { @@ -1276,7 +175,7 @@ impl<'a> SubMenu<'a> { submenu_id: &'a str, help_text: &'a str, is_single_option: bool, - initial_value: &u32 + initial_value: &u32, ) -> SubMenu<'a> { let mut instance = SubMenu { submenu_title: submenu_title, @@ -1291,8 +190,8 @@ impl<'a> SubMenu<'a> { let values = T::to_toggle_vals(); let titles = T::to_toggle_strs(); for i in 0..values.len() { - let checked: bool = (values[i] & initial_value) > 0 - || (!values[i] == 0 && initial_value == &0); + let checked: bool = + (values[i] & initial_value) > 0 || (!values[i] == 0 && initial_value == &0); instance.add_toggle(values[i], titles[i], checked); } // Select the first option if there's nothing selected atm but it's a single option submenu @@ -1387,98 +286,98 @@ pub unsafe fn ui_menu(menu: TrainingModpackMenu) -> UiMenu<'static> { "mash_state", "Mash Toggles: Actions to be performed as soon as possible", false, - &(menu.mash_state.bits as u32), + &(menu.mash_state.bits()), ); mash_tab.add_submenu_with_toggles::( "Followup Toggles", "follow_up", "Followup Toggles: Actions to be performed after the Mash option", false, - &(menu.follow_up.bits as u32), + &(menu.follow_up.bits()), ); mash_tab.add_submenu_with_toggles::( "Mash Triggers", "mash_triggers", "Mash triggers: When the Mash Option will be performed", false, - &(menu.mash_triggers.bits as u32), + &(menu.mash_triggers.bits()), ); mash_tab.add_submenu_with_toggles::( "Attack Angle", "attack_angle", "Attack Angle: For attacks that can be angled, such as some forward tilts", false, - &(menu.attack_angle.bits as u32), + &(menu.attack_angle.bits()), ); mash_tab.add_submenu_with_toggles::( "Throw Options", "throw_state", "Throw Options: Throw to be performed when a grab is landed", false, - &(menu.throw_state.bits as u32), + &(menu.throw_state.bits()), ); mash_tab.add_submenu_with_toggles::( "Throw Delay", "throw_delay", "Throw Delay: How many frames to delay the throw option", false, - &(menu.throw_delay.bits as u32), + &(menu.throw_delay.bits()), ); mash_tab.add_submenu_with_toggles::( "Pummel Delay", "pummel_delay", "Pummel Delay: How many frames after a grab to wait before starting to pummel", false, - &(menu.pummel_delay.bits as u32), + &(menu.pummel_delay.bits()), ); mash_tab.add_submenu_with_toggles::( "Falling Aerials", "falling_aerials", "Falling Aerials: Should aerials be performed when rising or when falling", false, - &(menu.falling_aerials.bits as u32), + &(menu.falling_aerials.bits()), ); mash_tab.add_submenu_with_toggles::( "Full Hop", "full_hop", "Full Hop: Should the CPU perform a full hop or a short hop", false, - &(menu.full_hop.bits as u32), + &(menu.full_hop.bits()), ); mash_tab.add_submenu_with_toggles::( "Aerial Delay", "aerial_delay", "Aerial Delay: How long to delay a Mash aerial attack", false, - &(menu.aerial_delay.bits as u32), + &(menu.aerial_delay.bits()), ); mash_tab.add_submenu_with_toggles::( "Fast Fall", "fast_fall", "Fast Fall: Should the CPU fastfall during a jump", false, - &(menu.fast_fall.bits as u32), + &(menu.fast_fall.bits()), ); mash_tab.add_submenu_with_toggles::( "Fast Fall Delay", "fast_fall_delay", "Fast Fall Delay: How many frames the CPU should delay their fastfall", false, - &(menu.fast_fall_delay.bits as u32), + &(menu.fast_fall_delay.bits()), ); mash_tab.add_submenu_with_toggles::( "OoS Offset", "oos_offset", "OoS Offset: How many times the CPU shield can be hit before performing a Mash option", false, - &(menu.oos_offset.bits as u32), + &(menu.oos_offset.bits()), ); mash_tab.add_submenu_with_toggles::( "Reaction Time", "reaction_time", "Reaction Time: How many frames to delay before performing a mash option", false, - &(menu.reaction_time.bits as u32), + &(menu.reaction_time.bits()), ); overall_menu.tabs.push(mash_tab); @@ -1492,21 +391,21 @@ pub unsafe fn ui_menu(menu: TrainingModpackMenu) -> UiMenu<'static> { "air_dodge_dir", "Airdodge Direction: Direction to angle airdodges", false, - &(menu.air_dodge_dir.bits as u32), + &(menu.air_dodge_dir.bits()), ); defensive_tab.add_submenu_with_toggles::( "DI Direction", "di_state", "DI Direction: Direction to angle the directional influence during hitlag", false, - &(menu.di_state.bits as u32), + &(menu.di_state.bits()), ); defensive_tab.add_submenu_with_toggles::( "SDI Direction", "sdi_state", "SDI Direction: Direction to angle the smash directional influence during hitlag", false, - &(menu.sdi_state.bits as u32), + &(menu.sdi_state.bits()), ); defensive_tab.add_submenu_with_toggles::( "SDI Strength", @@ -1527,28 +426,28 @@ pub unsafe fn ui_menu(menu: TrainingModpackMenu) -> UiMenu<'static> { "ledge_state", "Ledge Options: Actions to be taken when on the ledge", false, - &(menu.ledge_state.bits as u32), + &(menu.ledge_state.bits()), ); defensive_tab.add_submenu_with_toggles::( "Ledge Delay", "ledge_delay", "Ledge Delay: How many frames to delay the ledge option", false, - &(menu.ledge_delay.bits as u32), + &(menu.ledge_delay.bits()), ); defensive_tab.add_submenu_with_toggles::( "Tech Options", "tech_state", "Tech Options: Actions to take when slammed into a hard surface", false, - &(menu.tech_state.bits as u32), + &(menu.tech_state.bits()), ); defensive_tab.add_submenu_with_toggles::( "Mistech Options", "miss_tech_state", "Mistech Options: Actions to take after missing a tech", false, - &(menu.miss_tech_state.bits as u32), + &(menu.miss_tech_state.bits()), ); defensive_tab.add_submenu_with_toggles::( "Shield Toggles", @@ -1562,7 +461,7 @@ pub unsafe fn ui_menu(menu: TrainingModpackMenu) -> UiMenu<'static> { "shield_tilt", "Shield Tilt: Direction to tilt the shield", false, // TODO: Should this be true? - &(menu.shield_tilt.bits as u32), + &(menu.shield_tilt.bits()), ); defensive_tab.add_submenu_with_toggles::( @@ -1598,7 +497,7 @@ pub unsafe fn ui_menu(menu: TrainingModpackMenu) -> UiMenu<'static> { "save_damage_cpu", "Save Damage: Should save states retain CPU damage", true, - &(menu.save_damage_cpu.bits as u32), + &(menu.save_damage_cpu.bits()), ); save_state_tab.add_submenu_with_slider::( "Dmg Range (CPU)", @@ -1612,7 +511,7 @@ pub unsafe fn ui_menu(menu: TrainingModpackMenu) -> UiMenu<'static> { "save_damage_player", "Save Damage: Should save states retain player damage", true, - &(menu.save_damage_player.bits as u32), + &(menu.save_damage_player.bits() as u32), ); save_state_tab.add_submenu_with_slider::( "Dmg Range (Player)", @@ -1640,7 +539,7 @@ pub unsafe fn ui_menu(menu: TrainingModpackMenu) -> UiMenu<'static> { "buff_state", "Buff Options: Buff(s) to be applied to respective character when loading save states", false, - &(menu.buff_state.bits as u32), + &(menu.buff_state.bits()), ); overall_menu.tabs.push(save_state_tab); @@ -1668,7 +567,7 @@ pub unsafe fn ui_menu(menu: TrainingModpackMenu) -> UiMenu<'static> { "input_delay", "Input Delay: Frames to delay player inputs by", true, - &(menu.input_delay.bits as u32), + &(menu.input_delay.bits()), ); misc_tab.add_submenu_with_toggles::( "Stage Hazards", diff --git a/training_mod_consts/src/options.rs b/training_mod_consts/src/options.rs new file mode 100644 index 0000000..e92181a --- /dev/null +++ b/training_mod_consts/src/options.rs @@ -0,0 +1,1108 @@ +use core::f64::consts::PI; +use serde::{Deserialize, Serialize}; +use serde_repr::{Deserialize_repr, Serialize_repr}; +#[cfg(feature = "smash")] +use smash::lib::lua_const::*; +use strum::IntoEnumIterator; +use strum_macros::EnumIter; + + +const fn num_bits() -> u32 { + (std::mem::size_of::() * 8) as u32 +} + +fn log_2(x: u32) -> u32 { + if x == 0 { + 0 + } else { + num_bits::() - x.leading_zeros() - 1 + } +} + +pub trait ToggleTrait { + fn to_toggle_strs() -> Vec<&'static str>; + fn to_toggle_vals() -> Vec; +} + +pub trait SliderTrait { + fn get_limits() -> (u32, u32); +} + +// bitflag helper function macro +macro_rules! extra_bitflag_impls { + ($e:ty) => { + impl core::fmt::Display for $e { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::Debug::fmt(self, f) + } + } + + impl $e { + pub fn to_vec(&self) -> Vec::<$e> { + let mut vec = Vec::<$e>::new(); + let mut field = <$e>::from_bits_truncate(self.bits); + while !field.is_empty() { + let flag = <$e>::from_bits(1u32 << field.bits.trailing_zeros()).unwrap(); + field -= flag; + vec.push(flag); + } + return vec; + } + + pub fn to_index(&self) -> u32 { + if self.bits == 0 { + 0 + } else { + self.bits.trailing_zeros() + } + } + + pub fn get_random(&self) -> $e { + let options = self.to_vec(); + match options.len() { + 0 => { + return <$e>::empty(); + } + 1 => { + return options[0]; + } + _ => { + return *random_option(&options); + } + } + } + } + impl ToggleTrait for $e { + fn to_toggle_strs() -> Vec<&'static str> { + let all_options = <$e>::all().to_vec(); + all_options.iter().map(|i| i.as_str().unwrap_or("")).collect() + } + + fn to_toggle_vals() -> Vec { + let all_options = <$e>::all().to_vec(); + all_options.iter().map(|i| i.bits() as u32).collect() + } + } + } +} + +pub fn get_random_int(_max: i32) -> i32 { + #[cfg(feature = "smash")] + unsafe { + smash::app::sv_math::rand(smash::hash40("fighter"), _max) + } + + #[cfg(not(feature = "smash"))] + 0 +} + +/// Generate a random float between _min and _max. +/// Note that (_min <= _max) is not enforced. +pub fn get_random_float(_min: f32, _max: f32) -> f32 { + #[cfg(feature = "smash")] + unsafe { + _min + smash::app::sv_math::randf(smash::hash40("fighter"), _max - _min) + } + + #[cfg(not(feature = "smash"))] + _min +} + +pub fn random_option(arg: &[T]) -> &T { + &arg[get_random_int(arg.len() as i32) as usize] +} + +// DI +/* + 0, 0.785398, 1.570796, 2.356194, -3.14159, -2.356194, -1.570796, -0.785398 + 0, pi/4, pi/2, 3pi/4, pi, 5pi/4, 3pi/2, 7pi/4 +*/ + +// DI / Left stick +bitflags! { + pub struct Direction : u32 { + const OUT = 0x1; + const UP_OUT = 0x2; + const UP = 0x4; + const UP_IN = 0x8; + const IN = 0x10; + const DOWN_IN = 0x20; + const DOWN = 0x40; + const DOWN_OUT = 0x80; + const NEUTRAL = 0x100; + const LEFT = 0x200; + const RIGHT = 0x400; + } +} + +impl Direction { + pub fn into_angle(self) -> Option { + let index = self.into_index(); + + if index == 0 { + None + } else { + Some((index as i32 - 1) as f64 * PI / 4.0) + } + } + fn into_index(self) -> i32 { + match self { + Direction::OUT => 1, + Direction::UP_OUT => 2, + Direction::UP => 3, + Direction::UP_IN => 4, + Direction::IN => 5, + Direction::DOWN_IN => 6, + Direction::DOWN => 7, + Direction::DOWN_OUT => 8, + Direction::NEUTRAL => 0, + Direction::LEFT => 5, + Direction::RIGHT => 1, + _ => 0, + } + } + + fn as_str(self) -> Option<&'static str> { + Some(match self { + Direction::OUT => "Away", + Direction::UP_OUT => "Up and Away", + Direction::UP => "Up", + Direction::UP_IN => "Up and In", + Direction::IN => "In", + Direction::DOWN_IN => "Down and In", + Direction::DOWN => "Down", + Direction::DOWN_OUT => "Down and Away", + Direction::NEUTRAL => "Neutral", + Direction::LEFT => "Left", + Direction::RIGHT => "Right", + _ => return None, + }) + } +} + +extra_bitflag_impls! {Direction} +impl_serde_for_bitflags!(Direction); + +// Ledge Option +bitflags! { + pub struct LedgeOption : u32 + { + const NEUTRAL = 0x1; + const ROLL = 0x2; + const JUMP = 0x4; + const ATTACK = 0x8; + const WAIT = 0x10; + } +} + +impl LedgeOption { + pub fn into_status(self) -> Option { + #[cfg(feature = "smash")] + { + Some(match self { + LedgeOption::NEUTRAL => *FIGHTER_STATUS_KIND_CLIFF_CLIMB, + LedgeOption::ROLL => *FIGHTER_STATUS_KIND_CLIFF_ESCAPE, + LedgeOption::JUMP => *FIGHTER_STATUS_KIND_CLIFF_JUMP1, + LedgeOption::ATTACK => *FIGHTER_STATUS_KIND_CLIFF_ATTACK, + LedgeOption::WAIT => *FIGHTER_STATUS_KIND_CLIFF_WAIT, + _ => return None, + }) + } + + #[cfg(not(feature = "smash"))] + None + } + + fn as_str(self) -> Option<&'static str> { + Some(match self { + LedgeOption::NEUTRAL => "Neutral Getup", + LedgeOption::ROLL => "Roll", + LedgeOption::JUMP => "Jump", + LedgeOption::ATTACK => "Getup Attack", + LedgeOption::WAIT => "Wait", + _ => return None, + }) + } + pub const fn default() -> LedgeOption { + // Neutral,Roll,Jump,Attack (everything except wait) + LedgeOption::NEUTRAL + .union(LedgeOption::ROLL) + .union(LedgeOption::JUMP) + .union(LedgeOption::ATTACK) + } +} + +extra_bitflag_impls! {LedgeOption} +impl_serde_for_bitflags!(LedgeOption); + +// Tech options +bitflags! { + pub struct TechFlags : u32 { + const NO_TECH = 0x1; + const ROLL_F = 0x2; + const ROLL_B = 0x4; + const IN_PLACE = 0x8; + } +} + +impl TechFlags { + fn as_str(self) -> Option<&'static str> { + Some(match self { + TechFlags::NO_TECH => "No Tech", + TechFlags::ROLL_F => "Roll Forwards", + TechFlags::ROLL_B => "Roll Backwards", + TechFlags::IN_PLACE => "Tech In Place", + _ => return None, + }) + } +} + +extra_bitflag_impls! {TechFlags} +impl_serde_for_bitflags!(TechFlags); + +// Missed Tech Options +bitflags! { + pub struct MissTechFlags : u32 { + const GETUP = 0x1; + const ATTACK = 0x2; + const ROLL_F = 0x4; + const ROLL_B = 0x8; + } +} + +impl MissTechFlags { + fn as_str(self) -> Option<&'static str> { + Some(match self { + MissTechFlags::GETUP => "Neutral Getup", + MissTechFlags::ATTACK => "Getup Attack", + MissTechFlags::ROLL_F => "Roll Forwards", + MissTechFlags::ROLL_B => "Roll Backwards", + _ => return None, + }) + } +} + +extra_bitflag_impls! {MissTechFlags} +impl_serde_for_bitflags!(MissTechFlags); + +/// Shield States +#[repr(i32)] +#[derive( + Debug, Clone, Copy, PartialEq, FromPrimitive, EnumIter, Serialize_repr, Deserialize_repr, +)] +pub enum Shield { + None = 0x0, + Infinite = 0x1, + Hold = 0x2, + Constant = 0x4, +} + +impl Shield { + pub fn as_str(self) -> Option<&'static str> { + Some(match self { + Shield::None => "None", + Shield::Infinite => "Infinite", + Shield::Hold => "Hold", + Shield::Constant => "Constant", + }) + } +} + +impl ToggleTrait for Shield { + fn to_toggle_strs() -> Vec<&'static str> { + Shield::iter().map(|i| i.as_str().unwrap_or("")).collect() + } + + fn to_toggle_vals() -> Vec { + Shield::iter().map(|i| i as u32).collect() + } +} + +// Save State Mirroring +#[repr(i32)] +#[derive( + Debug, Clone, Copy, PartialEq, FromPrimitive, EnumIter, Serialize_repr, Deserialize_repr, +)] +pub enum SaveStateMirroring { + None = 0x0, + Alternate = 0x1, + Random = 0x2, +} + +impl SaveStateMirroring { + pub fn as_str(self) -> Option<&'static str> { + Some(match self { + SaveStateMirroring::None => "None", + SaveStateMirroring::Alternate => "Alternate", + SaveStateMirroring::Random => "Random", + }) + } +} + +impl ToggleTrait for SaveStateMirroring { + fn to_toggle_strs() -> Vec<&'static str> { + SaveStateMirroring::iter() + .map(|i| i.as_str().unwrap_or("")) + .collect() + } + + fn to_toggle_vals() -> Vec { + SaveStateMirroring::iter().map(|i| i as u32).collect() + } +} + +#[repr(i32)] +#[derive(Debug, Clone, Copy, PartialEq, Serialize_repr, Deserialize_repr)] +pub enum OnOff { + Off = 0, + On = 1, +} + +impl OnOff { + pub fn from_val(val: u32) -> Option { + match val { + 1 => Some(OnOff::On), + 0 => Some(OnOff::Off), + _ => None, + } + } + + pub fn as_str(self) -> Option<&'static str> { + Some(match self { + OnOff::Off => "Off", + OnOff::On => "On", + }) + } +} + +impl ToggleTrait for OnOff { + fn to_toggle_strs() -> Vec<&'static str> { + vec!["Off", "On"] + } + fn to_toggle_vals() -> Vec { + vec![0, 1] + } +} + +bitflags! { + pub struct Action : u32 { + const AIR_DODGE = 0x1; + const JUMP = 0x2; + const SHIELD = 0x4; + const SPOT_DODGE = 0x8; + const ROLL_F = 0x10; + const ROLL_B = 0x20; + const NAIR = 0x40; + const FAIR = 0x80; + const BAIR = 0x100; + const UAIR = 0x200; + const DAIR = 0x400; + const NEUTRAL_B = 0x800; + const SIDE_B = 0x1000; + const UP_B = 0x2000; + const DOWN_B = 0x4000; + const F_SMASH = 0x8000; + const U_SMASH = 0x10000; + const D_SMASH = 0x20000; + const JAB = 0x40000; + const F_TILT = 0x80000; + const U_TILT = 0x0010_0000; + const D_TILT = 0x0020_0000; + const GRAB = 0x0040_0000; + // TODO: Make work + const DASH = 0x0080_0000; + const DASH_ATTACK = 0x0100_0000; + } +} + +impl Action { + pub fn into_attack_air_kind(self) -> Option { + #[cfg(feature = "smash")] + { + Some(match self { + Action::NAIR => *FIGHTER_COMMAND_ATTACK_AIR_KIND_N, + Action::FAIR => *FIGHTER_COMMAND_ATTACK_AIR_KIND_F, + Action::BAIR => *FIGHTER_COMMAND_ATTACK_AIR_KIND_B, + Action::DAIR => *FIGHTER_COMMAND_ATTACK_AIR_KIND_LW, + Action::UAIR => *FIGHTER_COMMAND_ATTACK_AIR_KIND_HI, + _ => return None, + }) + } + + #[cfg(not(feature = "smash"))] + None + } + + pub fn as_str(self) -> Option<&'static str> { + Some(match self { + Action::AIR_DODGE => "Airdodge", + Action::JUMP => "Jump", + Action::SHIELD => "Shield", + Action::SPOT_DODGE => "Spotdodge", + Action::ROLL_F => "Roll Forwards", + Action::ROLL_B => "Roll Backwards", + Action::NAIR => "Neutral Aerial", + Action::FAIR => "Forward Aerial", + Action::BAIR => "Backward Aerial", + Action::UAIR => "Up Aerial", + Action::DAIR => "Down Aerial", + Action::NEUTRAL_B => "Neutral Special", + Action::SIDE_B => "Side Special", + Action::UP_B => "Up Special", + Action::DOWN_B => "Down Special", + Action::F_SMASH => "Forward Smash", + Action::U_SMASH => "Up Smash", + Action::D_SMASH => "Down Smash", + Action::JAB => "Jab", + Action::F_TILT => "Forward Tilt", + Action::U_TILT => "Up Tilt", + Action::D_TILT => "Down Tilt", + Action::GRAB => "Grab", + Action::DASH => "Dash", + Action::DASH_ATTACK => "Dash Attack", + _ => return None, + }) + } +} + +extra_bitflag_impls! {Action} +impl_serde_for_bitflags!(Action); + +bitflags! { + pub struct AttackAngle : u32 { + const NEUTRAL = 0x1; + const UP = 0x2; + const DOWN = 0x4; + } +} + +impl AttackAngle { + pub fn as_str(self) -> Option<&'static str> { + Some(match self { + AttackAngle::NEUTRAL => "Neutral", + AttackAngle::UP => "Up", + AttackAngle::DOWN => "Down", + _ => return None, + }) + } +} + +extra_bitflag_impls! {AttackAngle} +impl_serde_for_bitflags!(AttackAngle); + +bitflags! { + pub struct Delay : u32 { + const D0 = 0x1; + const D1 = 0x2; + const D2 = 0x4; + const D3 = 0x8; + const D4 = 0x10; + const D5 = 0x20; + const D6 = 0x40; + const D7 = 0x80; + const D8 = 0x100; + const D9 = 0x200; + const D10 = 0x400; + const D11 = 0x800; + const D12 = 0x1000; + const D13 = 0x2000; + const D14 = 0x4000; + const D15 = 0x8000; + const D16 = 0x10000; + const D17 = 0x20000; + const D18 = 0x40000; + const D19 = 0x80000; + const D20 = 0x0010_0000; + const D21 = 0x0020_0000; + const D22 = 0x0040_0000; + const D23 = 0x0080_0000; + const D24 = 0x0100_0000; + const D25 = 0x0200_0000; + const D26 = 0x0400_0000; + const D27 = 0x0800_0000; + const D28 = 0x1000_0000; + const D29 = 0x2000_0000; + const D30 = 0x4000_0000; + } +} + +// Throw Option +bitflags! { + pub struct ThrowOption : u32 + { + const NONE = 0x1; + const FORWARD = 0x2; + const BACKWARD = 0x4; + const UP = 0x8; + const DOWN = 0x10; + } +} + +impl ThrowOption { + pub fn into_cmd(self) -> Option { + #[cfg(feature = "smash")] + { + Some(match self { + ThrowOption::NONE => 0, + ThrowOption::FORWARD => *FIGHTER_PAD_CMD_CAT2_FLAG_THROW_F, + ThrowOption::BACKWARD => *FIGHTER_PAD_CMD_CAT2_FLAG_THROW_B, + ThrowOption::UP => *FIGHTER_PAD_CMD_CAT2_FLAG_THROW_HI, + ThrowOption::DOWN => *FIGHTER_PAD_CMD_CAT2_FLAG_THROW_LW, + _ => return None, + }) + } + + #[cfg(not(feature = "smash"))] + None + } + + pub fn as_str(self) -> Option<&'static str> { + Some(match self { + ThrowOption::NONE => "None", + ThrowOption::FORWARD => "Forward Throw", + ThrowOption::BACKWARD => "Back Throw", + ThrowOption::UP => "Up Throw", + ThrowOption::DOWN => "Down Throw", + _ => return None, + }) + } +} + +extra_bitflag_impls! {ThrowOption} +impl_serde_for_bitflags!(ThrowOption); + +// Buff Option +bitflags! { + pub struct BuffOption : u32 + { + const ACCELERATLE = 0x1; + const OOMPH = 0x2; + const PSYCHE = 0x4; + const BOUNCE = 0x8; + const ARSENE = 0x10; + const BREATHING = 0x20; + const LIMIT = 0x40; + const KO = 0x80; + const WING = 0x100; + } +} + +impl BuffOption { + pub fn into_int(self) -> Option { + #[cfg(feature = "smash")] + { + Some(match self { + BuffOption::ACCELERATLE => *FIGHTER_BRAVE_SPECIAL_LW_COMMAND11_SPEED_UP, + BuffOption::OOMPH => *FIGHTER_BRAVE_SPECIAL_LW_COMMAND12_ATTACK_UP, + BuffOption::PSYCHE => *FIGHTER_BRAVE_SPECIAL_LW_COMMAND21_CHARGE, + BuffOption::BOUNCE => *FIGHTER_BRAVE_SPECIAL_LW_COMMAND13_REFLECT, + BuffOption::BREATHING => 1, + BuffOption::ARSENE => 1, + BuffOption::LIMIT => 1, + BuffOption::KO => 1, + BuffOption::WING => 1, + _ => return None, + }) + } + + #[cfg(not(feature = "smash"))] + None + } + + fn as_str(self) -> Option<&'static str> { + Some(match self { + BuffOption::ACCELERATLE => "Acceleratle", + BuffOption::OOMPH => "Oomph", + BuffOption::BOUNCE => "Bounce", + BuffOption::PSYCHE => "Psyche Up", + BuffOption::BREATHING => "Deep Breathing", + BuffOption::ARSENE => "Arsene", + BuffOption::LIMIT => "Limit Break", + BuffOption::KO => "KO Punch", + BuffOption::WING => "1-Winged Angel", + _ => return None, + }) + } +} + +extra_bitflag_impls! {BuffOption} +impl_serde_for_bitflags!(BuffOption); + +impl Delay { + pub fn as_str(self) -> Option<&'static str> { + Some(match self { + Delay::D0 => "0", + Delay::D1 => "1", + Delay::D2 => "2", + Delay::D3 => "3", + Delay::D4 => "4", + Delay::D5 => "5", + Delay::D6 => "6", + Delay::D7 => "7", + Delay::D8 => "8", + Delay::D9 => "9", + Delay::D10 => "10", + Delay::D11 => "11", + Delay::D12 => "12", + Delay::D13 => "13", + Delay::D14 => "14", + Delay::D15 => "15", + Delay::D16 => "16", + Delay::D17 => "17", + Delay::D18 => "18", + Delay::D19 => "19", + Delay::D20 => "20", + Delay::D21 => "21", + Delay::D22 => "22", + Delay::D23 => "23", + Delay::D24 => "24", + Delay::D25 => "25", + Delay::D26 => "26", + Delay::D27 => "27", + Delay::D28 => "28", + Delay::D29 => "29", + Delay::D30 => "30", + _ => return None, + }) + } + + pub fn into_delay(&self) -> u32 { + self.to_index() + } +} + +extra_bitflag_impls! {Delay} +impl_serde_for_bitflags!(Delay); + +bitflags! { + pub struct MedDelay : u32 { + const D0 = 0x1; + const D5 = 0x2; + const D10 = 0x4; + const D15 = 0x8; + const D20 = 0x10; + const D25 = 0x20; + const D30 = 0x40; + const D35 = 0x80; + const D40 = 0x100; + const D45 = 0x200; + const D50 = 0x400; + const D55 = 0x800; + const D60 = 0x1000; + const D65 = 0x2000; + const D70 = 0x4000; + const D75 = 0x8000; + const D80 = 0x10000; + const D85 = 0x20000; + const D90 = 0x40000; + const D95 = 0x80000; + const D100 = 0x0010_0000; + const D105 = 0x0020_0000; + const D110 = 0x0040_0000; + const D115 = 0x0080_0000; + const D120 = 0x0100_0000; + const D125 = 0x0200_0000; + const D130 = 0x0400_0000; + const D135 = 0x0800_0000; + const D140 = 0x1000_0000; + const D145 = 0x2000_0000; + const D150 = 0x4000_0000; + } +} + +impl MedDelay { + pub fn as_str(self) -> Option<&'static str> { + Some(match self { + MedDelay::D0 => "0", + MedDelay::D5 => "5", + MedDelay::D10 => "10", + MedDelay::D15 => "15", + MedDelay::D20 => "20", + MedDelay::D25 => "25", + MedDelay::D30 => "30", + MedDelay::D35 => "35", + MedDelay::D40 => "40", + MedDelay::D45 => "45", + MedDelay::D50 => "50", + MedDelay::D55 => "55", + MedDelay::D60 => "60", + MedDelay::D65 => "65", + MedDelay::D70 => "70", + MedDelay::D75 => "75", + MedDelay::D80 => "80", + MedDelay::D85 => "85", + MedDelay::D90 => "90", + MedDelay::D95 => "95", + MedDelay::D100 => "100", + MedDelay::D105 => "105", + MedDelay::D110 => "110", + MedDelay::D115 => "115", + MedDelay::D120 => "120", + MedDelay::D125 => "125", + MedDelay::D130 => "130", + MedDelay::D135 => "135", + MedDelay::D140 => "140", + MedDelay::D145 => "145", + MedDelay::D150 => "150", + _ => return None, + }) + } + + pub fn into_meddelay(&self) -> u32 { + self.to_index() * 5 + } +} + +extra_bitflag_impls! {MedDelay} +impl_serde_for_bitflags!(MedDelay); + +bitflags! { + pub struct LongDelay : u32 { + const D0 = 0x1; + const D10 = 0x2; + const D20 = 0x4; + const D30 = 0x8; + const D40 = 0x10; + const D50 = 0x20; + const D60 = 0x40; + const D70 = 0x80; + const D80 = 0x100; + const D90 = 0x200; + const D100 = 0x400; + const D110 = 0x800; + const D120 = 0x1000; + const D130 = 0x2000; + const D140 = 0x4000; + const D150 = 0x8000; + const D160 = 0x10000; + const D170 = 0x20000; + const D180 = 0x40000; + const D190 = 0x80000; + const D200 = 0x0010_0000; + const D210 = 0x0020_0000; + const D220 = 0x0040_0000; + const D230 = 0x0080_0000; + const D240 = 0x0100_0000; + const D250 = 0x0200_0000; + const D260 = 0x0400_0000; + const D270 = 0x0800_0000; + const D280 = 0x1000_0000; + const D290 = 0x2000_0000; + const D300 = 0x4000_0000; + } +} + +impl LongDelay { + pub fn as_str(self) -> Option<&'static str> { + Some(match self { + LongDelay::D0 => "0", + LongDelay::D10 => "10", + LongDelay::D20 => "20", + LongDelay::D30 => "30", + LongDelay::D40 => "40", + LongDelay::D50 => "50", + LongDelay::D60 => "60", + LongDelay::D70 => "70", + LongDelay::D80 => "80", + LongDelay::D90 => "90", + LongDelay::D100 => "100", + LongDelay::D110 => "110", + LongDelay::D120 => "120", + LongDelay::D130 => "130", + LongDelay::D140 => "140", + LongDelay::D150 => "150", + LongDelay::D160 => "160", + LongDelay::D170 => "170", + LongDelay::D180 => "180", + LongDelay::D190 => "190", + LongDelay::D200 => "200", + LongDelay::D210 => "210", + LongDelay::D220 => "220", + LongDelay::D230 => "230", + LongDelay::D240 => "240", + LongDelay::D250 => "250", + LongDelay::D260 => "260", + LongDelay::D270 => "270", + LongDelay::D280 => "280", + LongDelay::D290 => "290", + LongDelay::D300 => "300", + _ => return None, + }) + } + + pub fn into_longdelay(&self) -> u32 { + self.to_index() * 10 + } +} + +extra_bitflag_impls! {LongDelay} +impl_serde_for_bitflags!(LongDelay); + +bitflags! { + pub struct BoolFlag : u32 { + const TRUE = 0x1; + const FALSE = 0x2; + } +} + +extra_bitflag_impls! {BoolFlag} +impl_serde_for_bitflags!(BoolFlag); + +impl BoolFlag { + pub fn into_bool(self) -> bool { + matches!(self, BoolFlag::TRUE) + } + + pub fn as_str(self) -> Option<&'static str> { + Some(match self { + BoolFlag::TRUE => "True", + _ => "False", + }) + } +} + +#[repr(u32)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, EnumIter, Serialize_repr, Deserialize_repr, +)] +pub enum SdiFrequency { + None = 0, + Normal = 1, + Medium = 2, + High = 4, +} + +impl SdiFrequency { + pub fn into_u32(self) -> u32 { + match self { + SdiFrequency::None => u32::MAX, + SdiFrequency::Normal => 8, + SdiFrequency::Medium => 6, + SdiFrequency::High => 4, + } + } + + pub fn as_str(self) -> Option<&'static str> { + Some(match self { + SdiFrequency::None => "None", + SdiFrequency::Normal => "Normal", + SdiFrequency::Medium => "Medium", + SdiFrequency::High => "High", + }) + } +} + +impl ToggleTrait for SdiFrequency { + fn to_toggle_strs() -> Vec<&'static str> { + SdiFrequency::iter() + .map(|i| i.as_str().unwrap_or("")) + .collect() + } + + fn to_toggle_vals() -> Vec { + SdiFrequency::iter().map(|i| i as u32).collect() + } +} + +#[repr(u32)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, EnumIter, Serialize_repr, Deserialize_repr, +)] +pub enum ClatterFrequency { + None = 0, + Normal = 1, + Medium = 2, + High = 4, +} + +impl ClatterFrequency { + pub fn into_u32(self) -> u32 { + match self { + ClatterFrequency::None => u32::MAX, + ClatterFrequency::Normal => 8, + ClatterFrequency::Medium => 5, + ClatterFrequency::High => 2, + } + } + + pub fn as_str(self) -> Option<&'static str> { + Some(match self { + ClatterFrequency::None => "None", + ClatterFrequency::Normal => "Normal", + ClatterFrequency::Medium => "Medium", + ClatterFrequency::High => "High", + }) + } +} + +impl ToggleTrait for ClatterFrequency { + fn to_toggle_strs() -> Vec<&'static str> { + ClatterFrequency::iter() + .map(|i| i.as_str().unwrap_or("")) + .collect() + } + + fn to_toggle_vals() -> Vec { + ClatterFrequency::iter().map(|i| i as u32).collect() + } +} + +/// Item Selections +#[repr(i32)] +#[derive( + Debug, Clone, Copy, PartialEq, FromPrimitive, EnumIter, Serialize_repr, Deserialize_repr, +)] +pub enum CharacterItem { + None = 0, + PlayerVariation1 = 0x1, + PlayerVariation2 = 0x2, + PlayerVariation3 = 0x4, + PlayerVariation4 = 0x8, + PlayerVariation5 = 0x10, + PlayerVariation6 = 0x20, + PlayerVariation7 = 0x40, + PlayerVariation8 = 0x80, + CpuVariation1 = 0x100, + CpuVariation2 = 0x200, + CpuVariation3 = 0x400, + CpuVariation4 = 0x800, + CpuVariation5 = 0x1000, + CpuVariation6 = 0x2000, + CpuVariation7 = 0x4000, + CpuVariation8 = 0x8000, +} + +impl CharacterItem { + pub fn as_idx(self) -> u32 { + log_2(self as i32 as u32) + } + + pub fn as_str(self) -> Option<&'static str> { + Some(match self { + CharacterItem::PlayerVariation1 => "Player 1st Var.", + CharacterItem::PlayerVariation2 => "Player 2nd Var.", + CharacterItem::PlayerVariation3 => "Player 3rd Var.", + CharacterItem::PlayerVariation4 => "Player 4th Var.", + CharacterItem::PlayerVariation5 => "Player 5th Var.", + CharacterItem::PlayerVariation6 => "Player 6th Var.", + CharacterItem::PlayerVariation7 => "Player 7th Var.", + CharacterItem::PlayerVariation8 => "Player 8th Var.", + CharacterItem::CpuVariation1 => "CPU 1st Var.", + CharacterItem::CpuVariation2 => "CPU 2nd Var.", + CharacterItem::CpuVariation3 => "CPU 3rd Var.", + CharacterItem::CpuVariation4 => "CPU 4th Var.", + CharacterItem::CpuVariation5 => "CPU 5th Var.", + CharacterItem::CpuVariation6 => "CPU 6th Var.", + CharacterItem::CpuVariation7 => "CPU 7th Var.", + CharacterItem::CpuVariation8 => "CPU 8th Var.", + _ => "None", + }) + } +} + +impl ToggleTrait for CharacterItem { + fn to_toggle_strs() -> Vec<&'static str> { + CharacterItem::iter() + .map(|i| i.as_str().unwrap_or("")) + .collect() + } + + fn to_toggle_vals() -> Vec { + CharacterItem::iter().map(|i| i as u32).collect() + } +} + +bitflags! { + pub struct MashTrigger : u32 { + const HIT = 0b0000_0000_0000_0000_0001; + const BLOCK = 0b0000_0000_0000_0000_0010; + const PARRY = 0b0000_0000_0000_0000_0100; + const TUMBLE = 0b0000_0000_0000_0000_1000; + const LANDING = 0b0000_0000_0000_0001_0000; + const TRUMP = 0b0000_0000_0000_0010_0000; + const FOOTSTOOL = 0b0000_0000_0000_0100_0000; + const CLATTER = 0b0000_0000_0000_1000_0000; + const LEDGE = 0b0000_0000_0001_0000_0000; + const TECH = 0b0000_0000_0010_0000_0000; + const MISTECH = 0b0000_0000_0100_0000_0000; + const GROUNDED = 0b0000_0000_1000_0000_0000; + const AIRBORNE = 0b0000_0001_0000_0000_0000; + const DISTANCE_CLOSE = 0b0000_0010_0000_0000_0000; + const DISTANCE_MID = 0b0000_0100_0000_0000_0000; + const DISTANCE_FAR = 0b0000_1000_0000_0000_0000; + const ALWAYS = 0b0001_0000_0000_0000_0000; + } +} + +impl MashTrigger { + pub fn as_str(self) -> Option<&'static str> { + Some(match self { + MashTrigger::HIT => "Hitstun", + MashTrigger::BLOCK => "Shieldstun", + MashTrigger::PARRY => "Parry", + MashTrigger::TUMBLE => "Tumble", + MashTrigger::LANDING => "Landing", + MashTrigger::TRUMP => "Ledge Trump", + MashTrigger::FOOTSTOOL => "Footstool", + MashTrigger::CLATTER => "Clatter", + MashTrigger::LEDGE => "Ledge Option", + MashTrigger::TECH => "Tech Option", + MashTrigger::MISTECH => "Mistech Option", + MashTrigger::GROUNDED => "Grounded", + MashTrigger::AIRBORNE => "Airborne", + MashTrigger::DISTANCE_CLOSE => "Distance: Close", + MashTrigger::DISTANCE_MID => "Distance: Mid", + MashTrigger::DISTANCE_FAR => "Distance: Far", + MashTrigger::ALWAYS => "Always", + _ => return None, + }) + } + + pub const fn default() -> MashTrigger { + // Hit, block, clatter + MashTrigger::HIT + .union(MashTrigger::BLOCK) + .union(MashTrigger::CLATTER) + } +} + +extra_bitflag_impls! {MashTrigger} +impl_serde_for_bitflags!(MashTrigger); + +#[derive(Clone, Copy, Serialize, Deserialize, Debug)] +pub struct DamagePercent(pub u32, pub u32); + +impl SliderTrait for DamagePercent { + fn get_limits() -> (u32, u32) { + (0, 150) + } +} + +impl DamagePercent { + pub const fn default() -> DamagePercent { + DamagePercent(0, 150) + } +} + +bitflags! { + pub struct SaveDamage : u32 + { + const DEFAULT = 0b001; + const SAVED = 0b010; + const RANDOM = 0b100; + } +} + +impl SaveDamage { + fn as_str(self) -> Option<&'static str> { + Some(match self { + SaveDamage::DEFAULT => "Default", + SaveDamage::SAVED => "Save State", + SaveDamage::RANDOM => "Random Value", + _ => return None, + }) + } +} + +extra_bitflag_impls! {SaveDamage} +impl_serde_for_bitflags!(SaveDamage); \ No newline at end of file