1
0
mirror of https://github.com/jugeeya/UltimateTrainingModpack.git synced 2026-02-07 18:02:26 +00:00

Quick Menu + Ryujinx Compatibility (#313)

* Initial commit

* Format Rust code using rustfmt

* Add back fs calls

* working with drawing

* wow we're almost there

* multi-lists working, selection within tui working

* working with tabs

* working with smash

* amend warnings, fix menu actually saving inputs

* small refactors

* Fully working!

* Fix warnings

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
jugeeya
2022-03-20 11:09:25 -07:00
committed by GitHub
parent c6c4105fc3
commit e4e2de0a79
17 changed files with 1916 additions and 774 deletions

View File

@@ -5,8 +5,13 @@ extern crate bitflags;
extern crate num_derive;
use core::f64::consts::PI;
#[cfg(feature = "smash")]
use smash::lib::lua_const::*;
use strum_macros::EnumIter;
use serde::{Serialize, Deserialize};
use ramhorns::Content;
use strum::IntoEnumIterator;
use std::ops::BitOr;
// bitflag helper function macro
macro_rules! extra_bitflag_impls {
@@ -72,8 +77,12 @@ macro_rules! extra_bitflag_impls {
}
}
pub fn get_random_int(max: i32) -> i32 {
unsafe { smash::app::sv_math::rand(smash::hash40("fighter"), max) }
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
}
pub fn random_option<T>(arg: &[T]) -> &T {
@@ -88,8 +97,8 @@ pub fn random_option<T>(arg: &[T]) -> &T {
// DI / Left stick
bitflags! {
pub struct Direction : u32
{
#[derive(Serialize, Deserialize)]
pub struct Direction : u32 {
const OUT = 0x1;
const UP_OUT = 0x2;
const UP = 0x4;
@@ -153,6 +162,7 @@ extra_bitflag_impls! {Direction}
// Ledge Option
bitflags! {
#[derive(Serialize, Deserialize)]
pub struct LedgeOption : u32
{
const NEUTRAL = 0x1;
@@ -165,14 +175,19 @@ bitflags! {
impl LedgeOption {
pub fn into_status(self) -> Option<i32> {
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(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> {
@@ -191,6 +206,7 @@ extra_bitflag_impls! {LedgeOption}
// Tech options
bitflags! {
#[derive(Serialize, Deserialize)]
pub struct TechFlags : u32 {
const NO_TECH = 0x1;
const ROLL_F = 0x2;
@@ -215,6 +231,7 @@ extra_bitflag_impls! {TechFlags}
// Missed Tech Options
bitflags! {
#[derive(Serialize, Deserialize)]
pub struct MissTechFlags : u32 {
const GETUP = 0x1;
const ATTACK = 0x2;
@@ -239,7 +256,7 @@ extra_bitflag_impls! {MissTechFlags}
/// Shield States
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, FromPrimitive, EnumIter)]
#[derive(Debug, Clone, Copy, PartialEq, FromPrimitive, EnumIter, Serialize, Deserialize)]
pub enum Shield {
None = 0,
Infinite = 1,
@@ -264,7 +281,7 @@ impl Shield {
// Save State Mirroring
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, FromPrimitive, EnumIter)]
#[derive(Debug, Clone, Copy, PartialEq, FromPrimitive, EnumIter, Serialize, Deserialize)]
pub enum SaveStateMirroring {
None = 0,
Alternate = 1,
@@ -287,6 +304,7 @@ impl SaveStateMirroring {
// Defensive States
bitflags! {
#[derive(Serialize, Deserialize)]
pub struct Defensive : u32 {
const SPOT_DODGE = 0x1;
const ROLL_F = 0x2;
@@ -312,7 +330,7 @@ impl Defensive {
extra_bitflag_impls! {Defensive}
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum OnOff {
Off = 0,
On = 1,
@@ -340,6 +358,7 @@ impl OnOff {
}
bitflags! {
#[derive(Serialize, Deserialize)]
pub struct Action : u32 {
const AIR_DODGE = 0x1;
const JUMP = 0x2;
@@ -372,14 +391,19 @@ bitflags! {
impl Action {
pub fn into_attack_air_kind(self) -> Option<i32> {
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(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> {
@@ -417,6 +441,7 @@ impl Action {
extra_bitflag_impls! {Action}
bitflags! {
#[derive(Serialize, Deserialize)]
pub struct AttackAngle : u32 {
const NEUTRAL = 0x1;
const UP = 0x2;
@@ -438,6 +463,7 @@ impl AttackAngle {
extra_bitflag_impls! {AttackAngle}
bitflags! {
#[derive(Serialize, Deserialize)]
pub struct Delay : u32 {
const D0 = 0x1;
const D1 = 0x2;
@@ -475,6 +501,7 @@ bitflags! {
// Throw Option
bitflags! {
#[derive(Serialize, Deserialize)]
pub struct ThrowOption : u32
{
const NONE = 0x1;
@@ -487,14 +514,19 @@ bitflags! {
impl ThrowOption {
pub fn into_cmd(self) -> Option<i32> {
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(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> {
@@ -513,6 +545,7 @@ extra_bitflag_impls! {ThrowOption}
// Buff Option
bitflags! {
#[derive(Serialize, Deserialize)]
pub struct BuffOption : u32
{
const ACCELERATLE = 0x1;
@@ -529,18 +562,23 @@ bitflags! {
impl BuffOption {
pub fn into_int(self) -> Option<i32> {
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(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> {
@@ -607,6 +645,7 @@ impl Delay {
extra_bitflag_impls! {Delay}
bitflags! {
#[derive(Serialize, Deserialize)]
pub struct MedDelay : u32 {
const D0 = 0x1;
const D5 = 0x2;
@@ -688,6 +727,7 @@ impl MedDelay {
extra_bitflag_impls! {MedDelay}
bitflags! {
#[derive(Serialize, Deserialize)]
pub struct LongDelay : u32 {
const D0 = 0x1;
const D10 = 0x2;
@@ -769,6 +809,7 @@ impl LongDelay {
extra_bitflag_impls! {LongDelay}
bitflags! {
#[derive(Serialize, Deserialize)]
pub struct BoolFlag : u32 {
const TRUE = 0x1;
const FALSE = 0x2;
@@ -791,7 +832,7 @@ impl BoolFlag {
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, EnumIter)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, EnumIter, Serialize, Deserialize)]
pub enum SdiStrength {
Normal = 0,
Medium = 1,
@@ -834,11 +875,13 @@ impl ToUrlParam for i32 {
// Macro to build the url parameter string
macro_rules! url_params {
(
#[repr(C)]
#[derive($($trait_name:ident, )*)]
pub struct $e:ident {
$(pub $field_name:ident: $field_type:ty,)*
}
) => {
#[repr(C)]
#[derive($($trait_name, )*)]
pub struct $e {
$(pub $field_name: $field_type,)*
@@ -859,9 +902,9 @@ macro_rules! url_params {
}
}
#[repr(C)]
url_params! {
#[derive(Clone, Copy, )]
#[repr(C)]
#[derive(Clone, Copy, Serialize, Deserialize, Debug, )]
pub struct TrainingModpackMenu {
pub hitbox_vis: OnOff,
pub stage_hazards: OnOff,
@@ -896,6 +939,7 @@ url_params! {
pub throw_delay: MedDelay,
pub pummel_delay: MedDelay,
pub buff_state: BuffOption,
pub quick_menu: OnOff,
}
}
@@ -947,6 +991,7 @@ impl TrainingModpackMenu {
throw_delay = MedDelay::from_bits(val),
pummel_delay = MedDelay::from_bits(val),
buff_state = BuffOption::from_bits(val),
quick_menu = OnOff::from_val(val),
);
}
}
@@ -958,3 +1003,612 @@ pub enum FighterId {
Player = 0,
CPU = 1,
}
#[derive(Content, Clone)]
pub struct Slider {
pub min: usize,
pub max: usize,
pub index: usize,
pub value: usize,
}
#[derive(Content, Clone)]
pub struct Toggle<'a> {
pub title: &'a str,
pub checked: &'a str,
pub index: usize,
pub value: usize,
pub default: &'a str,
}
#[derive(Content, Clone)]
pub struct OnOffSelector<'a> {
pub title: &'a str,
pub checked: &'a str,
pub default: &'a str,
}
#[derive(Clone)]
pub enum SubMenuType {
TOGGLE,
SLIDER,
ONOFF,
}
impl SubMenuType {
pub fn from_str(s : &str) -> SubMenuType {
match s {
"toggle" => SubMenuType::TOGGLE,
"slider" => SubMenuType::SLIDER,
"onoff" => SubMenuType::ONOFF,
_ => panic!("Unexpected SubMenuType!")
}
}
}
#[derive(Content, Clone)]
pub struct SubMenu<'a> {
pub title: &'a str,
pub id: &'a str,
pub _type: &'a str,
pub toggles: Vec<Toggle<'a>>,
pub sliders: Vec<Slider>,
pub onoffselector: Vec<OnOffSelector<'a>>,
pub index: usize,
pub check_against: usize,
pub is_single_option: Option<bool>,
pub help_text: &'a str,
}
impl<'a> SubMenu<'a> {
pub fn max_idx(&self) -> usize {
self.toggles
.iter()
.max_by(|t1, t2| t1.index.cmp(&t2.index))
.map(|t| t.index)
.unwrap_or(self.index)
}
pub fn add_toggle(&mut self, title: &'a str, checked: bool, value: usize, default: bool) {
self.toggles.push(Toggle {
title,
checked: if checked { "is-appear" } else { "is-hidden" },
index: self.max_idx() + 1,
value,
default: if default { "is-appear" } else { "is-hidden" },
});
}
pub fn add_slider(&mut self, min: usize, max: usize, value: usize) {
self.sliders.push(Slider {
min,
max,
index: self.max_idx() + 1,
value,
});
}
pub fn add_onoffselector(&mut self, title: &'a str, checked: bool, default: bool) {
// TODO: Is there a more elegant way to do this?
// The HTML only supports a single onoffselector but the SubMenu stores it as a Vec
self.onoffselector.push(OnOffSelector {
title,
checked: if checked { "is-appear" } else { "is-hidden" },
default: if default { "is-appear" } else { "is-hidden" },
});
}
}
pub static DEFAULT_MENU: TrainingModpackMenu = TrainingModpackMenu {
hitbox_vis: OnOff::On,
stage_hazards: OnOff::Off,
di_state: Direction::empty(),
sdi_state: Direction::empty(),
sdi_strength: SdiStrength::Normal,
air_dodge_dir: Direction::empty(),
mash_state: Action::empty(),
follow_up: Action::empty(),
attack_angle: AttackAngle::empty(),
ledge_state: LedgeOption::all(),
ledge_delay: LongDelay::empty(),
tech_state: TechFlags::all(),
miss_tech_state: MissTechFlags::all(),
shield_state: Shield::None,
defensive_state: Defensive::all(),
oos_offset: Delay::empty(),
shield_tilt: Direction::empty(),
reaction_time: Delay::empty(),
mash_in_neutral: OnOff::Off,
fast_fall: BoolFlag::empty(),
fast_fall_delay: Delay::empty(),
falling_aerials: BoolFlag::empty(),
aerial_delay: Delay::empty(),
full_hop: BoolFlag::empty(),
input_delay: 0,
save_damage: OnOff::On,
save_state_mirroring: SaveStateMirroring::None,
frame_advantage: OnOff::Off,
save_state_enable: OnOff::On,
throw_state: ThrowOption::NONE,
throw_delay: MedDelay::empty(),
pummel_delay: MedDelay::empty(),
buff_state: BuffOption::empty(),
quick_menu: OnOff::On,
};
pub static mut MENU: TrainingModpackMenu = DEFAULT_MENU;
#[derive(Content, Clone)]
pub struct Menu<'a> {
pub sub_menus: Vec<SubMenu<'a>>,
}
impl<'a> Menu<'a> {
pub fn max_idx(&self) -> usize {
self.sub_menus
.iter()
.max_by(|x, y| x.max_idx().cmp(&y.max_idx()))
.map(|sub_menu| sub_menu.max_idx())
.unwrap_or(0)
}
pub fn add_sub_menu(
&mut self,
title: &'a str,
id: &'a str,
_type: &'a str,
check_against: usize,
toggles: Vec<(&'a str, usize)>,
sliders: Vec<(usize, usize, usize)>,
defaults: usize,
help_text: &'a str,
) {
let mut sub_menu = SubMenu {
title,
id,
_type,
toggles: Vec::new(),
sliders: Vec::new(),
onoffselector: Vec::new(),
index: self.max_idx() + 1,
check_against,
is_single_option: Some(true),
help_text,
};
for toggle in toggles {
sub_menu.add_toggle(
toggle.0,
(check_against & toggle.1) != 0,
toggle.1,
(defaults & toggle.1) != 0,
)
}
for slider in sliders {
sub_menu.add_slider(slider.0, slider.1, slider.2);
}
self.sub_menus.push(sub_menu);
}
pub fn add_sub_menu_sep(
&mut self,
title: &'a str,
id: &'a str,
_type: &'a str,
check_against: usize,
strs: Vec<&'a str>,
vals: Vec<usize>,
defaults: usize,
help_text: &'a str,
) {
let mut sub_menu = SubMenu {
title,
id,
_type,
toggles: Vec::new(),
sliders: Vec::new(),
onoffselector: Vec::new(),
index: self.max_idx() + 1,
check_against,
is_single_option: None,
help_text,
};
for i in 0..strs.len() {
sub_menu.add_toggle(
strs[i],
(check_against & vals[i]) != 0,
vals[i],
(defaults & vals[i]) != 0,
)
}
// TODO: add sliders?
self.sub_menus.push(sub_menu);
}
pub fn add_sub_menu_onoff(
&mut self,
title: &'a str,
id: &'a str,
_type: &'a str,
check_against: usize,
checked: bool,
default: usize,
help_text: &'a str,
) {
let mut sub_menu = SubMenu {
title,
id,
_type,
toggles: Vec::new(),
sliders: Vec::new(),
onoffselector: Vec::new(),
index: self.max_idx() + 1,
check_against,
is_single_option: None,
help_text,
};
sub_menu.add_onoffselector(title, checked, (default & OnOff::On as usize) != 0);
self.sub_menus.push(sub_menu);
}
}
macro_rules! add_bitflag_submenu {
($menu:ident, $title:literal, $id:ident, $e:ty, $help_text:literal) => {
paste::paste!{
let [<$id _strs>] = <$e>::to_toggle_strs();
let [<$id _vals>] = <$e>::to_toggle_vals();
$menu.add_sub_menu_sep(
$title,
stringify!($id),
"toggle",
MENU.$id.bits() as usize,
[<$id _strs>],
[<$id _vals>],
DEFAULT_MENU.$id.bits() as usize,
stringify!($help_text),
);
}
}
}
macro_rules! add_single_option_submenu {
($menu:ident, $title:literal, $id:ident, $e:ty, $help_text:literal) => {
paste::paste!{
let mut [<$id _toggles>] = Vec::new();
for val in [<$e>]::iter() {
[<$id _toggles>].push((val.as_str().unwrap_or(""), val as usize));
}
$menu.add_sub_menu(
$title,
stringify!($id),
"toggle",
MENU.$id as usize,
[<$id _toggles>],
[].to_vec(),
DEFAULT_MENU.$id as usize,
stringify!($help_text),
);
}
}
}
macro_rules! add_onoff_submenu {
($menu:ident, $title:literal, $id:ident, $help_text:literal) => {
paste::paste! {
$menu.add_sub_menu_onoff(
$title,
stringify!($id),
"onoff",
MENU.$id as usize,
(MENU.$id as usize & OnOff::On as usize) != 0,
DEFAULT_MENU.$id as usize,
stringify!($help_text),
);
}
};
}
pub unsafe fn get_menu() -> Menu<'static> {
let mut overall_menu = Menu {
sub_menus: Vec::new(),
};
// Toggle/bitflag menus
add_bitflag_submenu!(
overall_menu,
"Mash Toggles",
mash_state,
Action,
"Mash Toggles: Actions to be performed as soon as possible"
);
add_bitflag_submenu!(
overall_menu,
"Followup Toggles",
follow_up,
Action,
"Followup Toggles: Actions to be performed after the Mash option"
);
add_bitflag_submenu!(
overall_menu,
"Attack Angle",
attack_angle,
AttackAngle,
"Attack Angle: For attacks that can be angled, such as some forward tilts"
);
add_bitflag_submenu!(
overall_menu,
"Ledge Options",
ledge_state,
LedgeOption,
"Ledge Options: Actions to be taken when on the ledge"
);
add_bitflag_submenu!(
overall_menu,
"Ledge Delay",
ledge_delay,
LongDelay,
"Ledge Delay: How many frames to delay the ledge option"
);
add_bitflag_submenu!(
overall_menu,
"Tech Options",
tech_state,
TechFlags,
"Tech Options: Actions to take when slammed into a hard surface"
);
add_bitflag_submenu!(
overall_menu,
"Miss Tech Options",
miss_tech_state,
MissTechFlags,
"Miss Tech Options: Actions to take after missing a tech"
);
add_bitflag_submenu!(
overall_menu,
"Defensive Options",
defensive_state,
Defensive,
"Defensive Options: Actions to take after a ledge option, tech option, or miss tech option"
);
add_bitflag_submenu!(
overall_menu,
"Aerial Delay",
aerial_delay,
Delay,
"Aerial Delay: How long to delay a Mash aerial attack"
);
add_bitflag_submenu!(
overall_menu,
"OoS Offset",
oos_offset,
Delay,
"OoS Offset: How many times the CPU shield can be hit before performing a Mash option"
);
add_bitflag_submenu!(
overall_menu,
"Reaction Time",
reaction_time,
Delay,
"Reaction Time: How many frames to delay before performing an option out of shield"
);
add_bitflag_submenu!(
overall_menu,
"Fast Fall",
fast_fall,
BoolFlag,
"Fast Fall: Should the CPU fastfall during a jump"
);
add_bitflag_submenu!(
overall_menu,
"Fast Fall Delay",
fast_fall_delay,
Delay,
"Fast Fall Delay: How many frames the CPU should delay their fastfall"
);
add_bitflag_submenu!(
overall_menu,
"Falling Aerials",
falling_aerials,
BoolFlag,
"Falling Aerials: Should aerials be performed when rising or when falling"
);
add_bitflag_submenu!(
overall_menu,
"Full Hop",
full_hop,
BoolFlag,
"Full Hop: Should the CPU perform a full hop or a short hop"
);
add_bitflag_submenu!(
overall_menu,
"Shield Tilt",
shield_tilt,
Direction,
"Shield Tilt: Direction to tilt the shield"
);
add_bitflag_submenu!(
overall_menu,
"DI Direction",
di_state,
Direction,
"DI Direction: Direction to angle the directional influence during hitlag"
);
add_bitflag_submenu!(
overall_menu,
"SDI Direction",
sdi_state,
Direction,
"SDI Direction: Direction to angle the smash directional influence during hitlag"
);
add_bitflag_submenu!(
overall_menu,
"Airdodge Direction",
air_dodge_dir,
Direction,
"Airdodge Direction: Direction to angle airdodges"
);
add_single_option_submenu!(
overall_menu,
"SDI Strength",
sdi_strength,
SdiStrength,
"SDI Strength: Relative strength of the smash directional influence inputs"
);
add_single_option_submenu!(
overall_menu,
"Shield Toggles",
shield_state,
Shield,
"Shield Toggles: CPU Shield Behavior"
);
add_single_option_submenu!(
overall_menu,
"Mirroring",
save_state_mirroring,
SaveStateMirroring,
"Mirroring: Flips save states in the left-right direction across the stage center"
);
add_bitflag_submenu!(
overall_menu,
"Throw Options",
throw_state,
ThrowOption,
"Throw Options: Throw to be performed when a grab is landed"
);
add_bitflag_submenu!(
overall_menu,
"Throw Delay",
throw_delay,
MedDelay,
"Throw Delay: How many frames to delay the throw option"
);
add_bitflag_submenu!(
overall_menu,
"Pummel Delay",
pummel_delay,
MedDelay,
"Pummel Delay: How many frames after a grab to wait before starting to pummel"
);
add_bitflag_submenu!(
overall_menu,
"Buff Options",
buff_state,
BuffOption,
"Buff Options: Buff(s) to be applied to respective character when loading save states"
);
// Slider menus
overall_menu.add_sub_menu(
"Input Delay",
"input_delay",
// unnecessary for slider?
"toggle",
0,
[
("0", 0),
("1", 1),
("2", 2),
("3", 3),
("4", 4),
("5", 5),
("6", 6),
("7", 7),
("8", 8),
("9", 9),
("10", 10),
]
.to_vec(),
[].to_vec(), //(0, 10, MENU.input_delay as usize)
DEFAULT_MENU.input_delay as usize,
stringify!("Input Delay: Frames to delay player inputs by"),
);
add_onoff_submenu!(
overall_menu,
"Save States",
save_state_enable,
"Save States: Enable save states! Save a state with Grab+Down Taunt, load it with Grab+Up Taunt."
);
add_onoff_submenu!(
overall_menu,
"Save Damage",
save_damage,
"Save Damage: Should save states retain player/CPU damage"
);
add_onoff_submenu!(
overall_menu,
"Hitbox Visualization",
hitbox_vis,
"Hitbox Visualization: Should hitboxes be displayed, hiding other visual effects"
);
add_onoff_submenu!(
overall_menu,
"Stage Hazards",
stage_hazards,
"Stage Hazards: Should stage hazards be present"
);
add_onoff_submenu!(
overall_menu,
"Frame Advantage",
frame_advantage,
"Frame Advantage: Display the time difference between when the player is actionable and the CPU is actionable");
add_onoff_submenu!(
overall_menu,
"Mash In Neutral",
mash_in_neutral,
"Mash In Neutral: Should Mash options be performed repeatedly or only when the CPU is hit"
);
add_onoff_submenu!(
overall_menu,
"Quick Menu",
quick_menu,
"Quick Menu: Whether to use Quick Menu or Web Menu"
);
overall_menu
}
pub fn get_menu_from_url(mut menu: TrainingModpackMenu, s: &str) -> TrainingModpackMenu {
let base_url_len = "http://localhost/?".len();
let total_len = s.len();
let ss: String = s
.chars()
.skip(base_url_len)
.take(total_len - base_url_len)
.collect();
for toggle_values in ss.split('&') {
let toggle_value_split = toggle_values.split('=').collect::<Vec<&str>>();
let toggle = toggle_value_split[0];
if toggle.is_empty() {
continue;
}
let toggle_vals = toggle_value_split[1];
let bitwise_or = <u32 as BitOr<u32>>::bitor;
let bits = toggle_vals
.split(',')
.filter(|val| !val.is_empty())
.map(|val| val.parse().unwrap())
.fold(0, bitwise_or);
menu.set(toggle, bits);
}
menu
}