From 884c00b069ae3cb2ed0e5bd17d582e2069b79d52 Mon Sep 17 00:00:00 2001 From: jugeeya <jugeeya@live.com> Date: Sun, 20 Feb 2022 19:14:22 -0800 Subject: [PATCH] Metrics Pipeline (#278) * initial commit * plotting working * allow plotting * Change spacing in workflow YML * Fix get_random_option calls * Format Rust code using rustfmt * Amend removed feature Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .github/workflows/rust.yml | 240 +++--- Cargo.toml | 101 +-- src/common/consts.rs | 952 +---------------------- src/common/events.rs | 359 ++++----- src/common/menu.rs | 1246 +++++++++++++++--------------- src/common/mod.rs | 343 ++++---- src/common/raygun_printer.rs | 486 ++++++------ src/common/release.rs | 72 +- src/lib.rs | 276 ++++--- src/templates/buff_state.svg | 164 ++-- src/templates/menu.html | 974 +++++++++++------------ src/templates/pummel_delay.svg | 232 +++--- src/templates/throw_delay.svg | 254 +++--- src/templates/throw_state.svg | 402 +++++----- src/training/buff.rs | 492 ++++++------ src/training/mod.rs | 1070 ++++++++++++------------- src/training/reset.rs | 98 +-- src/training/save_states.rs | 3 +- src/training/tech.rs | 544 ++++++------- src/training/throw.rs | 308 ++++---- training_mod_consts/Cargo.toml | 13 + training_mod_consts/src/lib.rs | 960 +++++++++++++++++++++++ training_mod_metrics/Cargo.toml | 12 + training_mod_metrics/src/main.rs | 144 ++++ 24 files changed, 4958 insertions(+), 4787 deletions(-) create mode 100644 training_mod_consts/Cargo.toml create mode 100644 training_mod_consts/src/lib.rs create mode 100644 training_mod_metrics/Cargo.toml create mode 100644 training_mod_metrics/src/main.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 284014d..4d880db 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,120 +1,120 @@ -name: Rust - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - workflow_dispatch: - -jobs: - clippy_pr: - runs-on: ubuntu-latest - container: jugeeya/cargo-skyline:2.1.0-dkp - steps: - - uses: actions/checkout@v2 - - name: Setup PATH - run: export PATH=$PATH:/root/.cargo/bin:/opt/devkitpro/devkitA64/bin - - name: Install minimal nightly rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly-2021-06-01 - components: rustfmt, clippy - default: true - target: x86_64-unknown-linux-gnu - - uses: actions-rs/clippy-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - toolchain: nightly-2021-06-01 - args: --all-features --target=x86_64-unknown-linux-gnu - - uses: mbrobbel/rustfmt-check@master - with: - token: ${{ secrets.GITHUB_TOKEN }} - plugin: - runs-on: ubuntu-latest - container: - image: jugeeya/cargo-skyline:2.1.0-dkp - steps: - - uses: actions/checkout@v2 - - name: Build release NRO - run: | - PATH=$PATH:/root/.cargo/bin:/opt/devkitpro/devkitA64/bin /root/.cargo/bin/cargo-skyline skyline build --release - env: - HOME: /root - - name: Upload plugin artifact - uses: actions/upload-artifact@v2 - with: - name: plugin - path: target/aarch64-skyline-switch/release/libtraining_modpack.nro - - name: Upload menu icons - uses: actions/upload-artifact@v2 - with: - name: svg - path: src/templates/*.svg - plugin_outside_training_mode: - if: github.ref == 'refs/heads/master' - runs-on: ubuntu-latest - container: - image: jugeeya/cargo-skyline:2.1.0-dkp - steps: - - uses: actions/checkout@v2 - - name: Build outside_training_mode NRO - run: | - PATH=$PATH:/root/.cargo/bin:/opt/devkitpro/devkitA64/bin /root/.cargo/bin/cargo-skyline skyline build --release --features outside_training_mode - env: - HOME: /root - - name: Upload plugin (outside training mode) artifact - uses: actions/upload-artifact@v2 - with: - name: plugin_outside_training_mode - path: target/aarch64-skyline-switch/release/libtraining_modpack.nro - upload: - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/master' - needs: - - plugin - steps: - - name: Download all artifacts - uses: actions/download-artifact@v2 - - name: Prepare zip - env: - SKYLINE_DIR: atmosphere/contents/01006A800016E000 - SMASH_PLUGIN_DIR: atmosphere/contents/01006A800016E000/romfs/skyline/plugins - SMASH_WEB_DIR: atmosphere/contents/01006A800016E000/manual_html/html-document/contents.htdocs - run: | - mkdir -p ${{env.SKYLINE_DIR}} - mkdir -p ${{env.SMASH_PLUGIN_DIR}} - mkdir -p ${{env.SMASH_WEB_DIR}} - wget https://github.com/skyline-dev/skyline/releases/download/beta/skyline.zip - unzip skyline.zip - mv exefs ${{env.SKYLINE_DIR}} - cp plugin/libtraining_modpack.nro ${{env.SMASH_PLUGIN_DIR}}/libtraining_modpack.nro - wget https://github.com/ultimate-research/params-hook-plugin/releases/download/v0.1.1/libparam_hook.nro - wget https://github.com/ultimate-research/nro-hook-plugin/releases/download/v0.3.0/libnro_hook.nro - wget https://github.com/jugeeya/nn-hid-hook/releases/download/beta/libnn_hid_hook.nro - cp libparam_hook.nro ${{env.SMASH_PLUGIN_DIR}}/libparam_hook.nro - cp libnro_hook.nro ${{env.SMASH_PLUGIN_DIR}}/libnro_hook.nro - cp libnn_hid_hook.nro ${{env.SMASH_PLUGIN_DIR}}/libnn_hid_hook.nro - ls -1 svg/*.svg | xargs -n 1 basename | xargs -L1 -I{} cp svg/{} ${{env.SMASH_WEB_DIR}}/{} - zip -r training_modpack_beta.zip atmosphere - - name: Update Release - uses: meeDamian/github-release@2.0 - with: - token: ${{ secrets.GITHUB_TOKEN }} - prerelease: true - allow_override: true - gzip: false - tag: beta - name: beta - body: > - Beta built off of the latest code in the repository. - - Install the same way you would install a full release. - files: > - training_modpack_beta.zip - - name: Upload zip as artifact - uses: actions/upload-artifact@v1 - with: - name: full_build - path: training_modpack_beta.zip +name: Rust + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + workflow_dispatch: + +jobs: + clippy_pr: + runs-on: ubuntu-latest + container: jugeeya/cargo-skyline:2.1.0-dkp + steps: + - uses: actions/checkout@v2 + - name: Setup PATH + run: export PATH=$PATH:/root/.cargo/bin:/opt/devkitpro/devkitA64/bin + - name: Install minimal nightly rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly-2021-06-01 + components: rustfmt, clippy + default: true + target: x86_64-unknown-linux-gnu + - uses: actions-rs/clippy-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + toolchain: nightly-2021-06-01 + args: --all-features --target=x86_64-unknown-linux-gnu + - uses: mbrobbel/rustfmt-check@master + with: + token: ${{ secrets.GITHUB_TOKEN }} + plugin: + runs-on: ubuntu-latest + container: + image: jugeeya/cargo-skyline:2.1.0-dkp + steps: + - uses: actions/checkout@v2 + - name: Build release NRO + run: | + PATH=$PATH:/root/.cargo/bin:/opt/devkitpro/devkitA64/bin /root/.cargo/bin/cargo-skyline skyline build --release + env: + HOME: /root + - name: Upload plugin artifact + uses: actions/upload-artifact@v2 + with: + name: plugin + path: target/aarch64-skyline-switch/release/libtraining_modpack.nro + - name: Upload menu icons + uses: actions/upload-artifact@v2 + with: + name: svg + path: src/templates/*.svg + plugin_outside_training_mode: + if: github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + container: + image: jugeeya/cargo-skyline:2.1.0-dkp + steps: + - uses: actions/checkout@v2 + - name: Build outside_training_mode NRO + run: | + PATH=$PATH:/root/.cargo/bin:/opt/devkitpro/devkitA64/bin /root/.cargo/bin/cargo-skyline skyline build --release --features outside_training_mode + env: + HOME: /root + - name: Upload plugin (outside training mode) artifact + uses: actions/upload-artifact@v2 + with: + name: plugin_outside_training_mode + path: target/aarch64-skyline-switch/release/libtraining_modpack.nro + upload: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/master' + needs: + - plugin + steps: + - name: Download all artifacts + uses: actions/download-artifact@v2 + - name: Prepare zip + env: + SKYLINE_DIR: atmosphere/contents/01006A800016E000 + SMASH_PLUGIN_DIR: atmosphere/contents/01006A800016E000/romfs/skyline/plugins + SMASH_WEB_DIR: atmosphere/contents/01006A800016E000/manual_html/html-document/contents.htdocs + run: | + mkdir -p ${{env.SKYLINE_DIR}} + mkdir -p ${{env.SMASH_PLUGIN_DIR}} + mkdir -p ${{env.SMASH_WEB_DIR}} + wget https://github.com/skyline-dev/skyline/releases/download/beta/skyline.zip + unzip skyline.zip + mv exefs ${{env.SKYLINE_DIR}} + cp plugin/libtraining_modpack.nro ${{env.SMASH_PLUGIN_DIR}}/libtraining_modpack.nro + wget https://github.com/ultimate-research/params-hook-plugin/releases/download/v0.1.1/libparam_hook.nro + wget https://github.com/ultimate-research/nro-hook-plugin/releases/download/v0.3.0/libnro_hook.nro + wget https://github.com/jugeeya/nn-hid-hook/releases/download/beta/libnn_hid_hook.nro + cp libparam_hook.nro ${{env.SMASH_PLUGIN_DIR}}/libparam_hook.nro + cp libnro_hook.nro ${{env.SMASH_PLUGIN_DIR}}/libnro_hook.nro + cp libnn_hid_hook.nro ${{env.SMASH_PLUGIN_DIR}}/libnn_hid_hook.nro + ls -1 svg/*.svg | xargs -n 1 basename | xargs -L1 -I{} cp svg/{} ${{env.SMASH_WEB_DIR}}/{} + zip -r training_modpack_beta.zip atmosphere + - name: Update Release + uses: meeDamian/github-release@2.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + prerelease: true + allow_override: true + gzip: false + tag: beta + name: beta + body: > + Beta built off of the latest code in the repository. + + Install the same way you would install a full release. + files: > + training_modpack_beta.zip + - name: Upload zip as artifact + uses: actions/upload-artifact@v1 + with: + name: full_build + path: training_modpack_beta.zip \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index da0cc30..61508f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,50 +1,51 @@ -[package] -name = "training_modpack" -version = "3.2.0" -authors = ["jugeeya <jugeeya@live.com>"] -edition = "2018" - -[lib] -crate-type = ["cdylib"] - -[dependencies] -skyline = { git = "https://github.com/ultimate-research/skyline-rs.git" } -skyline_smash = { git = "https://github.com/ultimate-research/skyline-smash.git" } -skyline-web = { git = "https://github.com/skyline-rs/skyline-web.git" } -bitflags = "1.2.1" -parking_lot = { version = "0.11.1", features = ["nightly"] } -lazy_static = "1.4.0" -owo-colors = "1.1.3" -ramhorns = "0.10.1" -paste = "1.0" -num = "0.3.0" -num-derive = "0.3" -num-traits = "0.2" -wsl = "0.1.0" -strum = "0.21.0" -strum_macros = "0.21.0" -minreq = { version = "=2.2.1", features = ["https", "json-using-serde"] } -serde = { version = "1", features = ["derive"] } -serde_json = "1" - -[patch.crates-io] -ring = { git = "https://github.com/skyline-rs/ring", branch = "0.16.20" } -webpki = { git = "https://github.com/skyline-rs/webpki" } - -[profile.dev] -panic = "abort" - -[profile.release] -panic = "abort" -lto = true - -[package.metadata.skyline] -titleid = "01006A800016E000" -plugin-dependencies = [ - { name = "libnro_hook.nro", url = "https://github.com/ultimate-research/nro-hook-plugin/releases/download/v0.3.0/libnro_hook.nro" }, - { name = "libparam_hook.nro", url = "https://github.com/ultimate-research/params-hook-plugin/releases/download/v0.1.1/libparam_hook.nro" }, - { name = "libnn_hid_hook.nro", url = "https://github.com/jugeeya/nn-hid-hook/releases/download/beta/libnn_hid_hook.nro" } -] - -[features] -outside_training_mode = [] +[package] +name = "training_modpack" +version = "3.2.0" +authors = ["jugeeya <jugeeya@live.com>"] +edition = "2018" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +skyline = { git = "https://github.com/ultimate-research/skyline-rs.git" } +skyline_smash = { git = "https://github.com/ultimate-research/skyline-smash.git" } +skyline-web = { git = "https://github.com/skyline-rs/skyline-web.git" } +bitflags = "1.2.1" +parking_lot = { version = "0.11.1", features = ["nightly"] } +lazy_static = "1.4.0" +owo-colors = "2.1.0" +ramhorns = "0.12.0" +paste = "1.0" +num = "0.4.0" +num-derive = "0.3" +num-traits = "0.2" +wsl = "0.1.0" +strum = "0.21.0" +strum_macros = "0.21.0" +minreq = { version = "=2.2.1", features = ["https", "json-using-serde"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +training_mod_consts = { path = "training_mod_consts" } + +[patch.crates-io] +ring = { git = "https://github.com/skyline-rs/ring", branch = "0.16.20" } +webpki = { git = "https://github.com/skyline-rs/webpki" } + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" +lto = true + +[package.metadata.skyline] +titleid = "01006A800016E000" +plugin-dependencies = [ + { name = "libnro_hook.nro", url = "https://github.com/ultimate-research/nro-hook-plugin/releases/download/v0.3.0/libnro_hook.nro" }, + { name = "libparam_hook.nro", url = "https://github.com/ultimate-research/params-hook-plugin/releases/download/v0.1.1/libparam_hook.nro" }, + { name = "libnn_hid_hook.nro", url = "https://github.com/jugeeya/nn-hid-hook/releases/download/beta/libnn_hid_hook.nro" } +] + +[features] +outside_training_mode = [] diff --git a/src/common/consts.rs b/src/common/consts.rs index 55fd853..8c33c17 100644 --- a/src/common/consts.rs +++ b/src/common/consts.rs @@ -1,951 +1 @@ -use crate::common::get_random_int; -use core::f64::consts::PI; -use smash::lib::lua_const::*; -use strum_macros::EnumIter; - -// 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); - } - } - } - - pub 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() - } - - pub fn to_toggle_vals() -> Vec<usize> { - let all_options = <$e>::all().to_vec(); - all_options.iter().map(|i| i.bits() as usize).collect() - } - pub fn to_url_param(&self) -> String { - self.to_vec() - .into_iter() - .map(|field| field.bits().to_string()) - .collect::<Vec<_>>() - .join(",") - } - } - } -} - -pub fn random_option<T>(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<f64> { - 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} - -// 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<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, - }) - } - - 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, - }) - } -} - -extra_bitflag_impls! {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} - -// 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} - -/// Shield States -#[repr(i32)] -#[derive(Debug, Clone, Copy, PartialEq, FromPrimitive, EnumIter)] -pub enum Shield { - None = 0, - Infinite = 1, - Hold = 2, - Constant = 3, -} - -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", - }) - } - - pub fn to_url_param(&self) -> String { - (*self as i32).to_string() - } -} - -// Save State Mirroring -#[repr(i32)] -#[derive(Debug, Clone, Copy, PartialEq, FromPrimitive, EnumIter)] -pub enum SaveStateMirroring { - None = 0, - Alternate = 1, - Random = 2, -} - -impl SaveStateMirroring { - pub fn as_str(self) -> Option<&'static str> { - Some(match self { - SaveStateMirroring::None => "None", - SaveStateMirroring::Alternate => "Alternate", - SaveStateMirroring::Random => "Random", - }) - } - - fn to_url_param(&self) -> String { - (*self as i32).to_string() - } -} - -// Defensive States -bitflags! { - pub struct Defensive : u32 { - const SPOT_DODGE = 0x1; - const ROLL_F = 0x2; - const ROLL_B = 0x4; - const JAB = 0x8; - const SHIELD = 0x10; - } -} - -impl Defensive { - fn as_str(self) -> Option<&'static str> { - Some(match self { - Defensive::SPOT_DODGE => "Spotdodge", - Defensive::ROLL_F => "Roll Forwards", - Defensive::ROLL_B => "Roll Backwards", - Defensive::JAB => "Jab", - Defensive::SHIELD => "Shield", - _ => return None, - }) - } -} - -extra_bitflag_impls! {Defensive} - -#[repr(i32)] -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum OnOff { - Off = 0, - On = 1, -} - -impl OnOff { - pub fn from_val(val: u32) -> Option<Self> { - 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", - }) - } - - pub fn to_url_param(&self) -> String { - (*self as i32).to_string() - } -} - -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<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, - }) - } - - 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} - -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} - -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<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, - }) - } - - 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} - -// 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<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, - }) - } - - 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 => "One-Winged Angel", - _ => return None, - }) - } -} - -extra_bitflag_impls! {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} - -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} - -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} - -bitflags! { - pub struct BoolFlag : u32 { - const TRUE = 0x1; - const FALSE = 0x2; - } -} - -extra_bitflag_impls! {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)] -pub enum SdiStrength { - Normal = 0, - Medium = 1, - High = 2, -} - -impl SdiStrength { - pub fn into_u32(self) -> u32 { - match self { - SdiStrength::Normal => 8, - SdiStrength::Medium => 6, - SdiStrength::High => 4, - } - } - - pub fn as_str(self) -> Option<&'static str> { - Some(match self { - SdiStrength::Normal => "Normal", - SdiStrength::Medium => "Medium", - SdiStrength::High => "High", - }) - } - - pub fn to_url_param(&self) -> String { - (*self as u32).to_string() - } -} - -// For input delay -trait ToUrlParam { - fn to_url_param(&self) -> String; -} - -impl ToUrlParam for i32 { - fn to_url_param(&self) -> String { - self.to_string() - } -} - -// Macro to build the url parameter string -macro_rules! url_params { - ( - #[derive($($trait_name:ident, )*)] - pub struct $e:ident { - $(pub $field_name:ident: $field_type:ty,)* - } - ) => { - #[derive($($trait_name, )*)] - pub struct $e { - $(pub $field_name: $field_type,)* - } - impl $e { - pub fn to_url_params(&self) -> String { - let mut s = "?".to_string(); - $( - s.push_str(stringify!($field_name)); - s.push_str(&"="); - s.push_str(&self.$field_name.to_url_param()); - s.push_str(&"&"); - )* - s.pop(); - s - } - } - } -} - -#[repr(C)] -url_params! { - #[derive(Clone, Copy, )] - pub struct TrainingModpackMenu { - pub hitbox_vis: OnOff, - pub stage_hazards: OnOff, - pub di_state: Direction, - pub sdi_state: Direction, - pub sdi_strength: SdiStrength, - pub air_dodge_dir: Direction, - pub mash_state: Action, - pub follow_up: Action, - pub attack_angle: AttackAngle, - pub ledge_state: LedgeOption, - pub ledge_delay: LongDelay, - pub tech_state: TechFlags, - pub miss_tech_state: MissTechFlags, - pub shield_state: Shield, - pub defensive_state: Defensive, - pub oos_offset: Delay, - pub reaction_time: Delay, - pub shield_tilt: Direction, - pub mash_in_neutral: OnOff, - pub fast_fall: BoolFlag, - pub fast_fall_delay: Delay, - pub falling_aerials: BoolFlag, - pub aerial_delay: Delay, - pub full_hop: BoolFlag, - pub input_delay: i32, - pub save_damage: OnOff, - pub save_state_mirroring: SaveStateMirroring, - pub frame_advantage: OnOff, - pub save_state_enable: OnOff, - pub throw_state: ThrowOption, - pub throw_delay: MedDelay, - pub pummel_delay: MedDelay, - pub buff_state: BuffOption, - } -} - -macro_rules! set_by_str { - ($obj:ident, $s:ident, $($field:ident = $rhs:expr,)*) => { - $( - if $s == stringify!($field) { - $obj.$field = $rhs.unwrap(); - } - )* - } -} - -impl TrainingModpackMenu { - pub fn set(&mut self, s: &str, val: u32) { - set_by_str!( - self, - s, - aerial_delay = Delay::from_bits(val), - air_dodge_dir = Direction::from_bits(val), - attack_angle = AttackAngle::from_bits(val), - defensive_state = Defensive::from_bits(val), - di_state = Direction::from_bits(val), - falling_aerials = BoolFlag::from_bits(val), - fast_fall_delay = Delay::from_bits(val), - fast_fall = BoolFlag::from_bits(val), - follow_up = Action::from_bits(val), - full_hop = BoolFlag::from_bits(val), - hitbox_vis = OnOff::from_val(val), - input_delay = Some(val as i32), - ledge_delay = LongDelay::from_bits(val), - ledge_state = LedgeOption::from_bits(val), - mash_in_neutral = OnOff::from_val(val), - mash_state = Action::from_bits(val), - miss_tech_state = MissTechFlags::from_bits(val), - oos_offset = Delay::from_bits(val), - reaction_time = Delay::from_bits(val), - sdi_state = Direction::from_bits(val), - sdi_strength = num::FromPrimitive::from_u32(val), - shield_state = num::FromPrimitive::from_u32(val), - shield_tilt = Direction::from_bits(val), - stage_hazards = OnOff::from_val(val), - tech_state = TechFlags::from_bits(val), - save_damage = OnOff::from_val(val), - frame_advantage = OnOff::from_val(val), - save_state_mirroring = num::FromPrimitive::from_u32(val), - save_state_enable = OnOff::from_val(val), - throw_state = ThrowOption::from_bits(val), - throw_delay = MedDelay::from_bits(val), - pummel_delay = MedDelay::from_bits(val), - buff_state = BuffOption::from_bits(val), - ); - } -} - -// Fighter Ids -#[repr(i32)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum FighterId { - Player = 0, - CPU = 1, -} +pub use training_mod_consts::*; diff --git a/src/common/events.rs b/src/common/events.rs index 623c36e..343069b 100644 --- a/src/common/events.rs +++ b/src/common/events.rs @@ -1,179 +1,180 @@ -use core::lazy::OnceCell; -use serde::{Deserialize, Serialize}; -use skyline::libc::c_void; -use skyline::nn::{account, crypto, oe, time}; -use std::convert::TryInto; -use std::time::{SystemTime, UNIX_EPOCH}; - -use crate::common::release::CURRENT_VERSION; - -pub static mut EVENT_QUEUE: Vec<Event> = vec![]; -static mut SESSION_ID: OnceCell<String> = OnceCell::new(); -static mut DEVICE_ID: OnceCell<String> = OnceCell::new(); -static mut USER_ID: OnceCell<String> = OnceCell::new(); - -#[derive(Debug, Default, Deserialize, Serialize)] -pub struct Event { - pub event_name: String, - pub user_id: String, - pub device_id: String, - pub event_time: u128, - pub session_id: String, - pub menu_settings: String, - pub mod_version: String, - pub smash_version: String, -} - -extern "C" { - #[link_name = "\u{1}_ZN2nn2oe17GetPseudoDeviceIdEPNS_4util4UuidE"] - pub fn GetPseudoDeviceId(arg1: *mut Uuid); -} - -#[derive(Debug)] -pub struct Uuid { - size: u32, - string_size: u32, - data: [u8; 16], -} - -impl Uuid { - pub fn to_str(&self) -> String { - self.data - .iter() - .map(|i| format!("{:02x}", i)) - .collect::<String>() - } -} - -struct Sha256Hash { - hash: [u8; 0x20], -} - -impl Event { - pub fn new() -> Event { - let mut device_uuid = Uuid { - size: 16, - string_size: 300, - data: [0u8; 16], - }; - unsafe { - GetPseudoDeviceId(&mut device_uuid as *mut Uuid); - } - - unsafe { - time::Initialize(); - let event_time = SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("Time went backwards") - .as_millis(); - - if SESSION_ID.get().is_none() { - account::Initialize(); - let mut user_uid = account::Uid::new(); - account::GetLastOpenedUser(&mut user_uid); - - let mut user_id_hash = Sha256Hash { hash: [0; 0x20] }; - crypto::GenerateSha256Hash( - &mut user_id_hash as *mut _ as *mut c_void, - 0x20 * 8, - user_uid.id.as_ptr() as *const c_void, - 16 * 8, - ); - - USER_ID - .set( - user_uid - .id - .iter() - .map(|i| format!("{:02x}", i)) - .collect::<Vec<String>>() - .join(""), - ) - .unwrap(); - - let mut device_id_hash = Sha256Hash { hash: [0; 0x20] }; - crypto::GenerateSha256Hash( - &mut device_id_hash as *mut _ as *mut c_void, - 0x20 * 8, - device_uuid.data.as_ptr() as *const c_void, - 64 * 2, - ); - DEVICE_ID - .set( - device_uuid - .data - .iter() - .map(|i| format!("{:02x}", i)) - .collect::<Vec<String>>() - .join(""), - ) - .unwrap(); - - let mut session_id_hash = Sha256Hash { hash: [0; 0x20] }; - // let mut device_id_0_bytes : [u8; 8] = Default::default(); - // device_id_0_bytes.copy_from_slice(&device_uuid.data[0..8]); - // let mut device_id_1_bytes : [u8; 8] = Default::default(); - // device_id_1_bytes.copy_from_slice(&device_uuid.data[8..16]); - let event_time_bytes: [u8; 16] = std::mem::transmute(event_time.to_be()); - let session_id_bytes: [u8; 32] = [event_time_bytes, device_uuid.data] - .concat() - .try_into() - .unwrap(); - - crypto::GenerateSha256Hash( - &mut session_id_hash as *mut _ as *mut c_void, - 0x20 * 8, - session_id_bytes.as_ptr() as *const c_void, - 32 * 8, - ); - SESSION_ID - .set( - session_id_hash - .hash - .iter() - .map(|i| format!("{:02x}", i)) - .collect::<Vec<String>>() - .join(""), - ) - .unwrap(); - } - - Event { - user_id: USER_ID.get().unwrap().to_string(), - device_id: DEVICE_ID.get().unwrap().to_string(), - event_time, - session_id: SESSION_ID.get().unwrap().to_string(), - mod_version: CURRENT_VERSION.to_string(), - smash_version: smash_version(), - ..Default::default() - } - } - } - - pub fn smash_open() -> Event { - Event { - event_name: "SMASH_OPEN".to_string(), - ..Event::new() - } - } - - pub fn menu_open(menu_settings: String) -> Event { - Event { - event_name: "MENU_OPEN".to_string(), - menu_settings, - ..Event::new() - } - } -} - -fn smash_version() -> String { - let mut smash_version = oe::DisplayVersion { name: [0; 16] }; - - unsafe { - oe::GetDisplayVersion(&mut smash_version); - - std::ffi::CStr::from_ptr(smash_version.name.as_ptr() as *const i8) - .to_string_lossy() - .into_owned() - } -} +use core::lazy::OnceCell; +use serde::{Deserialize, Serialize}; +use skyline::libc::c_void; +use skyline::nn::{account, crypto, oe, time}; +use std::convert::TryInto; +use std::time::{SystemTime, UNIX_EPOCH}; + +use crate::common::release::CURRENT_VERSION; + +pub static mut EVENT_QUEUE: Vec<Event> = vec![]; +static mut SESSION_ID: OnceCell<String> = OnceCell::new(); +static mut DEVICE_ID: OnceCell<String> = OnceCell::new(); +static mut USER_ID: OnceCell<String> = OnceCell::new(); + +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct Event { + pub event_name: String, + pub user_id: String, + pub device_id: String, + pub event_time: u128, + pub session_id: String, + pub menu_settings: String, + pub mod_version: String, + pub smash_version: String, +} + +extern "C" { + #[link_name = "\u{1}_ZN2nn2oe17GetPseudoDeviceIdEPNS_4util4UuidE"] + pub fn GetPseudoDeviceId(arg1: *mut Uuid); +} + +#[derive(Debug)] +#[repr(C)] +pub struct Uuid { + size: u32, + string_size: u32, + data: [u8; 16], +} + +impl Uuid { + pub fn to_str(&self) -> String { + self.data + .iter() + .map(|i| format!("{:02x}", i)) + .collect::<String>() + } +} + +struct Sha256Hash { + hash: [u8; 0x20], +} + +impl Event { + pub fn new() -> Event { + let mut device_uuid = Uuid { + size: 16, + string_size: 300, + data: [0u8; 16], + }; + unsafe { + GetPseudoDeviceId(&mut device_uuid as *mut Uuid); + } + + unsafe { + time::Initialize(); + let event_time = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("Time went backwards") + .as_millis(); + + if SESSION_ID.get().is_none() { + account::Initialize(); + let mut user_uid = account::Uid::new(); + account::GetLastOpenedUser(&mut user_uid); + + let mut user_id_hash = Sha256Hash { hash: [0; 0x20] }; + crypto::GenerateSha256Hash( + &mut user_id_hash as *mut _ as *mut c_void, + 0x20 * 8, + user_uid.id.as_ptr() as *const c_void, + 16 * 8, + ); + + USER_ID + .set( + user_uid + .id + .iter() + .map(|i| format!("{:02x}", i)) + .collect::<Vec<String>>() + .join(""), + ) + .unwrap(); + + let mut device_id_hash = Sha256Hash { hash: [0; 0x20] }; + crypto::GenerateSha256Hash( + &mut device_id_hash as *mut _ as *mut c_void, + 0x20 * 8, + device_uuid.data.as_ptr() as *const c_void, + 64 * 2, + ); + DEVICE_ID + .set( + device_uuid + .data + .iter() + .map(|i| format!("{:02x}", i)) + .collect::<Vec<String>>() + .join(""), + ) + .unwrap(); + + let mut session_id_hash = Sha256Hash { hash: [0; 0x20] }; + // let mut device_id_0_bytes : [u8; 8] = Default::default(); + // device_id_0_bytes.copy_from_slice(&device_uuid.data[0..8]); + // let mut device_id_1_bytes : [u8; 8] = Default::default(); + // device_id_1_bytes.copy_from_slice(&device_uuid.data[8..16]); + let event_time_bytes: [u8; 16] = std::mem::transmute(event_time.to_be()); + let session_id_bytes: [u8; 32] = [event_time_bytes, device_uuid.data] + .concat() + .try_into() + .unwrap(); + + crypto::GenerateSha256Hash( + &mut session_id_hash as *mut _ as *mut c_void, + 0x20 * 8, + session_id_bytes.as_ptr() as *const c_void, + 32 * 8, + ); + SESSION_ID + .set( + session_id_hash + .hash + .iter() + .map(|i| format!("{:02x}", i)) + .collect::<Vec<String>>() + .join(""), + ) + .unwrap(); + } + + Event { + user_id: USER_ID.get().unwrap().to_string(), + device_id: DEVICE_ID.get().unwrap().to_string(), + event_time, + session_id: SESSION_ID.get().unwrap().to_string(), + mod_version: CURRENT_VERSION.to_string(), + smash_version: smash_version(), + ..Default::default() + } + } + } + + pub fn smash_open() -> Event { + Event { + event_name: "SMASH_OPEN".to_string(), + ..Event::new() + } + } + + pub fn menu_open(menu_settings: String) -> Event { + Event { + event_name: "MENU_OPEN".to_string(), + menu_settings, + ..Event::new() + } + } +} + +fn smash_version() -> String { + let mut smash_version = oe::DisplayVersion { name: [0; 16] }; + + unsafe { + oe::GetDisplayVersion(&mut smash_version); + + std::ffi::CStr::from_ptr(smash_version.name.as_ptr() as *const i8) + .to_string_lossy() + .into_owned() + } +} diff --git a/src/common/menu.rs b/src/common/menu.rs index d623ee0..77a30dd 100644 --- a/src/common/menu.rs +++ b/src/common/menu.rs @@ -1,623 +1,623 @@ -use crate::common::*; -use crate::events::{Event, EVENT_QUEUE}; -use crate::training::frame_counter; -use ramhorns::{Content, Template}; -use skyline::info::get_program_id; -use skyline_web::{Background, BootDisplay, Webpage}; -use smash::lib::lua_const::*; -use std::fs; -use std::ops::BitOr; -use std::path::Path; -use strum::IntoEnumIterator; - -static mut FRAME_COUNTER_INDEX: usize = 0; -const MENU_LOCKOUT_FRAMES: u32 = 15; - -pub fn init() { - unsafe { - FRAME_COUNTER_INDEX = frame_counter::register_counter(); - write_menu(); - } -} - -#[derive(Content)] -struct Slider { - min: usize, - max: usize, - index: usize, - value: usize, -} - -#[derive(Content)] -struct Toggle<'a> { - title: &'a str, - checked: &'a str, - index: usize, - value: usize, - default: &'a str, -} - -#[derive(Content)] -struct OnOffSelector<'a> { - title: &'a str, - checked: &'a str, - default: &'a str, -} - -#[derive(Content)] -struct SubMenu<'a> { - title: &'a str, - id: &'a str, - toggles: Vec<Toggle<'a>>, - sliders: Vec<Slider>, - onoffselector: Vec<OnOffSelector<'a>>, - index: usize, - check_against: usize, - is_single_option: Option<bool>, - 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" }, - }); - } -} - -#[derive(Content)] -struct Menu<'a> { - 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, - 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, - 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, - check_against: usize, - strs: Vec<&'a str>, - vals: Vec<usize>, - defaults: usize, - help_text: &'a str, - ) { - let mut sub_menu = SubMenu { - title, - id, - 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, - check_against: usize, - checked: bool, - default: usize, - help_text: &'a str, - ) { - let mut sub_menu = SubMenu { - title, - id, - 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), - 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), - 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), - MENU.$id as usize, - (MENU.$id as usize & OnOff::On as usize) != 0, - DEFAULT_MENU.$id as usize, - stringify!($help_text), - ); - } - }; -} - -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 -} - -pub unsafe fn menu_condition(module_accessor: &mut smash::app::BattleObjectModuleAccessor) -> bool { - // Only check for button combination if the counter is 0 (not locked out) - match frame_counter::get_frame_count(FRAME_COUNTER_INDEX) { - 0 => { - ControlModule::check_button_on(module_accessor, *CONTROL_PAD_BUTTON_SPECIAL) - && ControlModule::check_button_on_trriger( - module_accessor, - *CONTROL_PAD_BUTTON_APPEAL_HI, - ) - } - 1..MENU_LOCKOUT_FRAMES => false, - _ => { - // Waited longer than the lockout time, reset the counter so the menu can be opened again - frame_counter::full_reset(FRAME_COUNTER_INDEX); - false - } - } -} - -pub unsafe fn write_menu() { - let tpl = Template::new(include_str!("../templates/menu.html")).unwrap(); - - 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? - MENU.input_delay as usize, - [ - ("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" - ); - - let data = tpl.render(&overall_menu); - - // Now that we have the html, write it to file - // From skyline-web - let program_id = get_program_id(); - let htdocs_dir = "contents"; - let path = Path::new("sd:/atmosphere/contents") - .join(&format!("{:016X}", program_id)) - .join(&format!("manual_html/html-document/{}.htdocs/", htdocs_dir)) - .join("training_menu.html"); - fs::write(path, data).unwrap(); -} - -const MENU_CONF_PATH: &str = "sd:/TrainingModpack/training_modpack_menu.conf"; - -pub fn spawn_menu() { - unsafe { - frame_counter::reset_frame_count(FRAME_COUNTER_INDEX); - frame_counter::start_counting(FRAME_COUNTER_INDEX); - } - - let fname = "training_menu.html"; - let params = unsafe { MENU.to_url_params() }; - let page_response = Webpage::new() - .background(Background::BlurredScreenshot) - .htdocs_dir("contents") - .boot_display(BootDisplay::BlurredScreenshot) - .boot_icon(true) - .start_page(&format!("{}{}", fname, params)) - .open() - .unwrap(); - - let orig_last_url = page_response.get_last_url().unwrap(); - let last_url = &orig_last_url.replace("&save_defaults=1", ""); - unsafe { - MENU = get_menu_from_url(MENU, last_url); - } - if last_url.len() != orig_last_url.len() { - // Save as default - unsafe { - DEFAULT_MENU = get_menu_from_url(DEFAULT_MENU, last_url); - write_menu(); - } - let menu_defaults_conf_path = "sd:/TrainingModpack/training_modpack_menu_defaults.conf"; - std::fs::write(menu_defaults_conf_path, last_url) - .expect("Failed to write default menu conf file"); - } - - std::fs::write(MENU_CONF_PATH, last_url).expect("Failed to write menu conf file"); - unsafe { - EVENT_QUEUE.push(Event::menu_open(last_url.to_string())); - } -} +use crate::common::*; +use crate::events::{Event, EVENT_QUEUE}; +use crate::training::frame_counter; +use ramhorns::{Content, Template}; +use skyline::info::get_program_id; +use skyline_web::{Background, BootDisplay, Webpage}; +use smash::lib::lua_const::*; +use std::fs; +use std::ops::BitOr; +use std::path::Path; +use strum::IntoEnumIterator; + +static mut FRAME_COUNTER_INDEX: usize = 0; +const MENU_LOCKOUT_FRAMES: u32 = 15; + +pub fn init() { + unsafe { + FRAME_COUNTER_INDEX = frame_counter::register_counter(); + write_menu(); + } +} + +#[derive(Content)] +struct Slider { + min: usize, + max: usize, + index: usize, + value: usize, +} + +#[derive(Content)] +struct Toggle<'a> { + title: &'a str, + checked: &'a str, + index: usize, + value: usize, + default: &'a str, +} + +#[derive(Content)] +struct OnOffSelector<'a> { + title: &'a str, + checked: &'a str, + default: &'a str, +} + +#[derive(Content)] +struct SubMenu<'a> { + title: &'a str, + id: &'a str, + toggles: Vec<Toggle<'a>>, + sliders: Vec<Slider>, + onoffselector: Vec<OnOffSelector<'a>>, + index: usize, + check_against: usize, + is_single_option: Option<bool>, + 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" }, + }); + } +} + +#[derive(Content)] +struct Menu<'a> { + 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, + 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, + 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, + check_against: usize, + strs: Vec<&'a str>, + vals: Vec<usize>, + defaults: usize, + help_text: &'a str, + ) { + let mut sub_menu = SubMenu { + title, + id, + 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, + check_against: usize, + checked: bool, + default: usize, + help_text: &'a str, + ) { + let mut sub_menu = SubMenu { + title, + id, + 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), + 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), + 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), + MENU.$id as usize, + (MENU.$id as usize & OnOff::On as usize) != 0, + DEFAULT_MENU.$id as usize, + stringify!($help_text), + ); + } + }; +} + +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 +} + +pub unsafe fn menu_condition(module_accessor: &mut smash::app::BattleObjectModuleAccessor) -> bool { + // Only check for button combination if the counter is 0 (not locked out) + match frame_counter::get_frame_count(FRAME_COUNTER_INDEX) { + 0 => { + ControlModule::check_button_on(module_accessor, *CONTROL_PAD_BUTTON_SPECIAL) + && ControlModule::check_button_on_trriger( + module_accessor, + *CONTROL_PAD_BUTTON_APPEAL_HI, + ) + } + 1..MENU_LOCKOUT_FRAMES => false, + _ => { + // Waited longer than the lockout time, reset the counter so the menu can be opened again + frame_counter::full_reset(FRAME_COUNTER_INDEX); + false + } + } +} + +pub unsafe fn write_menu() { + let tpl = Template::new(include_str!("../templates/menu.html")).unwrap(); + + 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? + MENU.input_delay as usize, + [ + ("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" + ); + + let data = tpl.render(&overall_menu); + + // Now that we have the html, write it to file + // From skyline-web + let program_id = get_program_id(); + let htdocs_dir = "contents"; + let path = Path::new("sd:/atmosphere/contents") + .join(&format!("{:016X}", program_id)) + .join(&format!("manual_html/html-document/{}.htdocs/", htdocs_dir)) + .join("training_menu.html"); + fs::write(path, data).unwrap(); +} + +const MENU_CONF_PATH: &str = "sd:/TrainingModpack/training_modpack_menu.conf"; + +pub fn spawn_menu() { + unsafe { + frame_counter::reset_frame_count(FRAME_COUNTER_INDEX); + frame_counter::start_counting(FRAME_COUNTER_INDEX); + } + + let fname = "training_menu.html"; + let params = unsafe { MENU.to_url_params() }; + let page_response = Webpage::new() + .background(Background::BlurredScreenshot) + .htdocs_dir("contents") + .boot_display(BootDisplay::BlurredScreenshot) + .boot_icon(true) + .start_page(&format!("{}{}", fname, params)) + .open() + .unwrap(); + + let orig_last_url = page_response.get_last_url().unwrap(); + let last_url = &orig_last_url.replace("&save_defaults=1", ""); + unsafe { + MENU = get_menu_from_url(MENU, last_url); + } + if last_url.len() != orig_last_url.len() { + // Save as default + unsafe { + DEFAULT_MENU = get_menu_from_url(DEFAULT_MENU, last_url); + write_menu(); + } + let menu_defaults_conf_path = "sd:/TrainingModpack/training_modpack_menu_defaults.conf"; + std::fs::write(menu_defaults_conf_path, last_url) + .expect("Failed to write default menu conf file"); + } + + std::fs::write(MENU_CONF_PATH, last_url).expect("Failed to write menu conf file"); + unsafe { + EVENT_QUEUE.push(Event::menu_open(last_url.to_string())); + } +} diff --git a/src/common/mod.rs b/src/common/mod.rs index 59f74f6..8158d3f 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,174 +1,169 @@ -pub mod consts; -pub mod events; -pub mod menu; -pub mod raygun_printer; -pub mod release; - -use crate::common::consts::*; -use smash::app::{self, lua_bind::*}; -use smash::hash40; -use smash::lib::lua_const::*; - -pub static BASE_MENU: consts::TrainingModpackMenu = consts::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(), -}; - -pub static mut DEFAULT_MENU: TrainingModpackMenu = BASE_MENU; -pub static mut MENU: TrainingModpackMenu = BASE_MENU; -pub static mut FIGHTER_MANAGER_ADDR: usize = 0; -pub static mut STAGE_MANAGER_ADDR: usize = 0; - -#[cfg(not(feature = "outside_training_mode"))] -extern "C" { - #[link_name = "\u{1}_ZN3app9smashball16is_training_modeEv"] - pub fn is_training_mode() -> bool; -} - -#[cfg(feature = "outside_training_mode")] -pub fn is_training_mode() -> bool { - return true; -} - -pub fn get_category(module_accessor: &mut app::BattleObjectModuleAccessor) -> i32 { - (module_accessor.info >> 28) as u8 as i32 -} - -pub fn get_module_accessor(fighter_id: FighterId) -> *mut app::BattleObjectModuleAccessor { - let entry_id_int = fighter_id as i32; - let entry_id = app::FighterEntryID(entry_id_int); - unsafe { - let mgr = *(FIGHTER_MANAGER_ADDR as *mut *mut app::FighterManager); - let fighter_entry = - FighterManager::get_fighter_entry(mgr, entry_id) as *mut app::FighterEntry; - let current_fighter_id = FighterEntry::current_fighter_id(fighter_entry); - app::sv_battle_object::module_accessor(current_fighter_id as u32) - } -} - -pub fn is_fighter(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { - get_category(module_accessor) == BATTLE_OBJECT_CATEGORY_FIGHTER -} - -pub fn is_operation_cpu(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { - unsafe { - if !is_fighter(module_accessor) { - return false; - } - - let entry_id_int = - WorkModule::get_int(module_accessor, *FIGHTER_INSTANCE_WORK_ID_INT_ENTRY_ID) as i32; - - if entry_id_int == 0 { - return false; - } - - let entry_id = app::FighterEntryID(entry_id_int); - let mgr = *(FIGHTER_MANAGER_ADDR as *mut *mut app::FighterManager); - let fighter_information = - FighterManager::get_fighter_information(mgr, entry_id) as *mut app::FighterInformation; - - FighterInformation::is_operation_cpu(fighter_information) - } -} - -pub fn is_grounded(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { - let situation_kind = unsafe { StatusModule::situation_kind(module_accessor) as i32 }; - - situation_kind == SITUATION_KIND_GROUND -} - -pub fn is_airborne(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { - let situation_kind = unsafe { StatusModule::situation_kind(module_accessor) as i32 }; - - situation_kind == SITUATION_KIND_AIR -} - -pub fn is_idle(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { - let status_kind = unsafe { StatusModule::status_kind(module_accessor) }; - - status_kind == FIGHTER_STATUS_KIND_WAIT -} - -pub fn is_in_hitstun(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { - let status_kind = unsafe { StatusModule::status_kind(module_accessor) }; - - (*FIGHTER_STATUS_KIND_DAMAGE..*FIGHTER_STATUS_KIND_DAMAGE_FALL).contains(&status_kind) -} -pub fn is_in_footstool(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { - let status_kind = unsafe { StatusModule::status_kind(module_accessor) }; - - (*FIGHTER_STATUS_KIND_TREAD_DAMAGE..=*FIGHTER_STATUS_KIND_TREAD_FALL).contains(&status_kind) -} - -pub fn is_shielding(module_accessor: *mut app::BattleObjectModuleAccessor) -> bool { - let status_kind = unsafe { StatusModule::status_kind(module_accessor) as i32 }; - - (*FIGHTER_STATUS_KIND_GUARD_ON..=*FIGHTER_STATUS_KIND_GUARD_DAMAGE).contains(&status_kind) -} - -pub fn is_in_shieldstun(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { - let status_kind = unsafe { StatusModule::status_kind(module_accessor) }; - let prev_status = unsafe { StatusModule::prev_status_kind(module_accessor, 0) }; - - // If we are taking shield damage or we are droping shield from taking shield damage we are in hitstun - status_kind == FIGHTER_STATUS_KIND_GUARD_DAMAGE - || (prev_status == FIGHTER_STATUS_KIND_GUARD_DAMAGE - && status_kind == FIGHTER_STATUS_KIND_GUARD_OFF) -} - -pub fn get_random_int(max: i32) -> i32 { - unsafe { app::sv_math::rand(hash40("fighter"), max) } -} - -pub unsafe fn is_dead(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { - let fighter_kind = app::utility::get_kind(module_accessor); - let fighter_is_ptrainer = [ - *FIGHTER_KIND_PZENIGAME, - *FIGHTER_KIND_PFUSHIGISOU, - *FIGHTER_KIND_PLIZARDON, - ] - .contains(&fighter_kind); - let status_kind = StatusModule::status_kind(module_accessor) as i32; - let prev_status_kind = StatusModule::prev_status_kind(module_accessor, 0); - // Pokemon trainer enters FIGHTER_STATUS_KIND_WAIT for one frame during their respawn animation - // And the previous status is FIGHTER_STATUS_NONE - if fighter_is_ptrainer { - [*FIGHTER_STATUS_KIND_DEAD, *FIGHTER_STATUS_KIND_STANDBY].contains(&status_kind) - || (status_kind == FIGHTER_STATUS_KIND_WAIT - && prev_status_kind == FIGHTER_STATUS_KIND_NONE) - } else { - [*FIGHTER_STATUS_KIND_DEAD, *FIGHTER_STATUS_KIND_STANDBY].contains(&status_kind) - } -} +pub mod consts; +pub mod events; +pub mod menu; +pub mod raygun_printer; +pub mod release; + +use crate::common::consts::*; +use smash::app::{self, lua_bind::*}; +use smash::lib::lua_const::*; + +pub static BASE_MENU: consts::TrainingModpackMenu = consts::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(), +}; + +pub static mut DEFAULT_MENU: TrainingModpackMenu = BASE_MENU; +pub static mut MENU: TrainingModpackMenu = BASE_MENU; +pub static mut FIGHTER_MANAGER_ADDR: usize = 0; +pub static mut STAGE_MANAGER_ADDR: usize = 0; + +#[cfg(not(feature = "outside_training_mode"))] +extern "C" { + #[link_name = "\u{1}_ZN3app9smashball16is_training_modeEv"] + pub fn is_training_mode() -> bool; +} + +#[cfg(feature = "outside_training_mode")] +pub fn is_training_mode() -> bool { + return true; +} + +pub fn get_category(module_accessor: &mut app::BattleObjectModuleAccessor) -> i32 { + (module_accessor.info >> 28) as u8 as i32 +} + +pub fn get_module_accessor(fighter_id: FighterId) -> *mut app::BattleObjectModuleAccessor { + let entry_id_int = fighter_id as i32; + let entry_id = app::FighterEntryID(entry_id_int); + unsafe { + let mgr = *(FIGHTER_MANAGER_ADDR as *mut *mut app::FighterManager); + let fighter_entry = + FighterManager::get_fighter_entry(mgr, entry_id) as *mut app::FighterEntry; + let current_fighter_id = FighterEntry::current_fighter_id(fighter_entry); + app::sv_battle_object::module_accessor(current_fighter_id as u32) + } +} + +pub fn is_fighter(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { + get_category(module_accessor) == BATTLE_OBJECT_CATEGORY_FIGHTER +} + +pub fn is_operation_cpu(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { + unsafe { + if !is_fighter(module_accessor) { + return false; + } + + let entry_id_int = + WorkModule::get_int(module_accessor, *FIGHTER_INSTANCE_WORK_ID_INT_ENTRY_ID) as i32; + + if entry_id_int == 0 { + return false; + } + + let entry_id = app::FighterEntryID(entry_id_int); + let mgr = *(FIGHTER_MANAGER_ADDR as *mut *mut app::FighterManager); + let fighter_information = + FighterManager::get_fighter_information(mgr, entry_id) as *mut app::FighterInformation; + + FighterInformation::is_operation_cpu(fighter_information) + } +} + +pub fn is_grounded(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { + let situation_kind = unsafe { StatusModule::situation_kind(module_accessor) as i32 }; + + situation_kind == SITUATION_KIND_GROUND +} + +pub fn is_airborne(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { + let situation_kind = unsafe { StatusModule::situation_kind(module_accessor) as i32 }; + + situation_kind == SITUATION_KIND_AIR +} + +pub fn is_idle(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { + let status_kind = unsafe { StatusModule::status_kind(module_accessor) }; + + status_kind == FIGHTER_STATUS_KIND_WAIT +} + +pub fn is_in_hitstun(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { + let status_kind = unsafe { StatusModule::status_kind(module_accessor) }; + + (*FIGHTER_STATUS_KIND_DAMAGE..*FIGHTER_STATUS_KIND_DAMAGE_FALL).contains(&status_kind) +} +pub fn is_in_footstool(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { + let status_kind = unsafe { StatusModule::status_kind(module_accessor) }; + + (*FIGHTER_STATUS_KIND_TREAD_DAMAGE..=*FIGHTER_STATUS_KIND_TREAD_FALL).contains(&status_kind) +} + +pub fn is_shielding(module_accessor: *mut app::BattleObjectModuleAccessor) -> bool { + let status_kind = unsafe { StatusModule::status_kind(module_accessor) as i32 }; + + (*FIGHTER_STATUS_KIND_GUARD_ON..=*FIGHTER_STATUS_KIND_GUARD_DAMAGE).contains(&status_kind) +} + +pub fn is_in_shieldstun(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { + let status_kind = unsafe { StatusModule::status_kind(module_accessor) }; + let prev_status = unsafe { StatusModule::prev_status_kind(module_accessor, 0) }; + + // If we are taking shield damage or we are droping shield from taking shield damage we are in hitstun + status_kind == FIGHTER_STATUS_KIND_GUARD_DAMAGE + || (prev_status == FIGHTER_STATUS_KIND_GUARD_DAMAGE + && status_kind == FIGHTER_STATUS_KIND_GUARD_OFF) +} + +pub unsafe fn is_dead(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { + let fighter_kind = app::utility::get_kind(module_accessor); + let fighter_is_ptrainer = [ + *FIGHTER_KIND_PZENIGAME, + *FIGHTER_KIND_PFUSHIGISOU, + *FIGHTER_KIND_PLIZARDON, + ] + .contains(&fighter_kind); + let status_kind = StatusModule::status_kind(module_accessor) as i32; + let prev_status_kind = StatusModule::prev_status_kind(module_accessor, 0); + // Pokemon trainer enters FIGHTER_STATUS_KIND_WAIT for one frame during their respawn animation + // And the previous status is FIGHTER_STATUS_NONE + if fighter_is_ptrainer { + [*FIGHTER_STATUS_KIND_DEAD, *FIGHTER_STATUS_KIND_STANDBY].contains(&status_kind) + || (status_kind == FIGHTER_STATUS_KIND_WAIT + && prev_status_kind == FIGHTER_STATUS_KIND_NONE) + } else { + [*FIGHTER_STATUS_KIND_DEAD, *FIGHTER_STATUS_KIND_STANDBY].contains(&status_kind) + } +} diff --git a/src/common/raygun_printer.rs b/src/common/raygun_printer.rs index 20b6e45..3df34d3 100644 --- a/src/common/raygun_printer.rs +++ b/src/common/raygun_printer.rs @@ -1,243 +1,243 @@ -use std::ops::Neg; - -use smash::app; -use smash::phx::{Hash40, Vector3f}; - -pub const RAYGUN_LENGTH: f32 = 8.0; -pub const RAYGUN_HEIGHT: f32 = 6.0; -pub const RAYGUN_HORIZ_OFFSET: f32 = 2.0; - -/* - segment data list : {Z, Y, X, ZRot, Size} - segment labels : - _ - |_| from top to top left, clockwise: a->f + g mid + \|/ from top mid to top left, clockwise: h->m + --two half g's: n, o - |_| /|\ -*/ - -pub static SEGMENT_DICT: [[f32; 5]; 15] = [ - [0.0, RAYGUN_HEIGHT * 2.0, 0.0, 0.0, 0.25], // a - [0.0, RAYGUN_HEIGHT, RAYGUN_LENGTH, 90.0, 0.25], // b - [0.0, 0.0, RAYGUN_LENGTH, 90.0, 0.25], // c - [0.0, 0.0, 0.0, 0.0, 0.25], // d - [0.0, 0.0, 0.0, 90.0, 0.25], // e - [0.0, RAYGUN_HEIGHT, 0.0, 90.0, 0.25], // f - [0.0, RAYGUN_HEIGHT, 0.0, 0.0, 0.25], // g mid - [0.0, RAYGUN_HEIGHT, RAYGUN_LENGTH / 2.0, 90.0, 0.25], // h - [0.0, RAYGUN_HEIGHT, RAYGUN_LENGTH / 2.0, 52.0, 0.2], // i - [0.0, RAYGUN_HEIGHT, RAYGUN_LENGTH / 2.0, -52.0, 0.2], // j - [0.0, 0.0, RAYGUN_LENGTH / 2.0, 90.0, 0.25], // k - [ - 0.0, - RAYGUN_HEIGHT / 2.0, - RAYGUN_LENGTH * 3.0 / 16.0, - 52.0, - 0.2, - ], // l - [ - 0.0, - RAYGUN_HEIGHT * 3.0 / 2.0, - RAYGUN_LENGTH * 3.0 / 16.0, - -52.0, - 0.2, - ], // m - [0.0, RAYGUN_HEIGHT, 0.0, 0.0, 0.15], // n - [0.0, RAYGUN_HEIGHT, RAYGUN_LENGTH / 2.0, 0.0, 0.15], // o -]; - -/* - Segments making up each character, each index corresponding to: - 'A' through 'Z', '0' through '9', ' ', '-', '+', '#' (where '#' is all segments) -*/ -pub static ALPHABET: [&str; 40] = [ - "abcefg", - "adefijn", - "adef", - "eflm", - "adefn", - "aefn", - "acdefo", - "bcefg", - "adhk", - "bcd", - "efnij", - "def", - "bcefim", - "bcefjm", - "abcdef", - "abefg", - "abcdefj", - "aefijn", - "acdfg", - "ahk", - "bcdef", - "efil", - "bcefjl", - "ijlm", - "ikm", - "adil", - "abcdef", - "ef", - "abdeg", - "abcdg", - "bcfg", - "acdfg", - "acdefg", - "abc", - "abcdefg", - "abcdfg", - "", - "g", - "ghk", - "abcdefhijklmno", -]; - -// Each index is a segment's corresponding flipped segment, for when facing left -pub static SEGMENT_REV: [char; 15] = [ - 'a', 'f', 'e', 'd', 'c', 'b', 'g', 'h', 'm', 'l', 'k', 'j', 'i', 'o', 'n', -]; - -fn show_segment( - module_accessor: &mut app::BattleObjectModuleAccessor, - z: f32, - y: f32, - x: f32, - zrot: f32, - size: f32, -) { - let pos = Vector3f { x, y, z }; - let rot = Vector3f { - x: 0.0, - y: 90.0, - z: zrot, - }; - let random = Vector3f { - x: 0.0, - y: 0.0, - z: 0.0, - }; - - unsafe { - app::lua_bind::EffectModule::req_on_joint( - module_accessor, - Hash40::new("sys_raygun_bullet"), - Hash40::new("top"), - &pos, - &rot, - size, - &random, - &random, - false, - 0, - 0, - 0, - ); - } -} - -fn alphabet_index(to_print: char) -> i32 { - match to_print { - 'A'..='Z' => to_print as i32 - 'A' as i32, - '0'..='9' => to_print as i32 - '0' as i32 + 'Z' as i32 - 'A' as i32 + 1, - ' ' => 36, - '-' => 37, - '+' => 38, - '#' => 39, - _ => -1, - } -} - -fn print_char( - module_accessor: &mut app::BattleObjectModuleAccessor, - to_print: char, - line_num: i32, - horiz_offset: f32, - facing_left: i32, -) { - let is_facing_left = facing_left == -1; - let x_direction = facing_left as f32; - - let alph_index = alphabet_index(to_print); - if !(0..40).contains(&alph_index) { - return; - } - let segment_str = ALPHABET[alph_index as usize]; - - let line_offset = 40.0 - ((line_num as f32) * 16.0); - - for segment_char in segment_str.chars() { - let mut index = segment_char as i32 - 'a' as i32; - - let segment: [f32; 5]; - if is_facing_left { - index = SEGMENT_REV[index as usize] as i32 - 'a' as i32; - } - segment = SEGMENT_DICT[index as usize]; - - const SIZE_MULT: f32 = 0.5; - - let x = ((segment[2] + horiz_offset) * SIZE_MULT) + (x_direction * 5.0); - let y = ((segment[1] + line_offset) * SIZE_MULT) + 5.0; - let z = segment[0] * SIZE_MULT; - - let zrot = segment[3]; - let zrot = if is_facing_left { zrot.neg() } else { zrot }; - - let size = segment[4] * SIZE_MULT; - - show_segment(module_accessor, z, y, x, zrot, size); - } -} - -pub fn print_string(module_accessor: &mut app::BattleObjectModuleAccessor, to_write: &str) { - // Delete any previous strings - unsafe { - app::lua_bind::EffectModule::kill_kind( - module_accessor, - Hash40::new("sys_raygun_bullet"), - false, - true, - ); - } - - let mut line_num = 0; - let mut horiz_offset = 0.0; - let mut char_num = 0; - - let facing_left = unsafe { app::lua_bind::PostureModule::lr(module_accessor) as i32 }; - let facing_direction = facing_left as f32; - - if to_write.len() <= 8 && !to_write.contains('\n') { - line_num = 1; - } - for curr_char in to_write.chars() { - if curr_char == '\n' { - horiz_offset = 0.0; - char_num = 0; - line_num += 1; - continue; - } - - print_char( - module_accessor, - curr_char.to_uppercase().collect::<Vec<_>>()[0], - line_num, - horiz_offset, - facing_left, - ); - - char_num += 1; - // short characters - if curr_char == 'D' || curr_char == '1' { - horiz_offset += facing_direction * (RAYGUN_LENGTH / 2.0 + 3.0); - } else { - horiz_offset += facing_direction * (RAYGUN_LENGTH + 3.0); - } - - if char_num > 8 { - horiz_offset = 0.0; - char_num = 0; - line_num += 1; - } - } -} +use std::ops::Neg; + +use smash::app; +use smash::phx::{Hash40, Vector3f}; + +pub const RAYGUN_LENGTH: f32 = 8.0; +pub const RAYGUN_HEIGHT: f32 = 6.0; +pub const RAYGUN_HORIZ_OFFSET: f32 = 2.0; + +/* + segment data list : {Z, Y, X, ZRot, Size} + segment labels : + _ + |_| from top to top left, clockwise: a->f + g mid + \|/ from top mid to top left, clockwise: h->m + --two half g's: n, o + |_| /|\ +*/ + +pub static SEGMENT_DICT: [[f32; 5]; 15] = [ + [0.0, RAYGUN_HEIGHT * 2.0, 0.0, 0.0, 0.25], // a + [0.0, RAYGUN_HEIGHT, RAYGUN_LENGTH, 90.0, 0.25], // b + [0.0, 0.0, RAYGUN_LENGTH, 90.0, 0.25], // c + [0.0, 0.0, 0.0, 0.0, 0.25], // d + [0.0, 0.0, 0.0, 90.0, 0.25], // e + [0.0, RAYGUN_HEIGHT, 0.0, 90.0, 0.25], // f + [0.0, RAYGUN_HEIGHT, 0.0, 0.0, 0.25], // g mid + [0.0, RAYGUN_HEIGHT, RAYGUN_LENGTH / 2.0, 90.0, 0.25], // h + [0.0, RAYGUN_HEIGHT, RAYGUN_LENGTH / 2.0, 52.0, 0.2], // i + [0.0, RAYGUN_HEIGHT, RAYGUN_LENGTH / 2.0, -52.0, 0.2], // j + [0.0, 0.0, RAYGUN_LENGTH / 2.0, 90.0, 0.25], // k + [ + 0.0, + RAYGUN_HEIGHT / 2.0, + RAYGUN_LENGTH * 3.0 / 16.0, + 52.0, + 0.2, + ], // l + [ + 0.0, + RAYGUN_HEIGHT * 3.0 / 2.0, + RAYGUN_LENGTH * 3.0 / 16.0, + -52.0, + 0.2, + ], // m + [0.0, RAYGUN_HEIGHT, 0.0, 0.0, 0.15], // n + [0.0, RAYGUN_HEIGHT, RAYGUN_LENGTH / 2.0, 0.0, 0.15], // o +]; + +/* + Segments making up each character, each index corresponding to: + 'A' through 'Z', '0' through '9', ' ', '-', '+', '#' (where '#' is all segments) +*/ +pub static ALPHABET: [&str; 40] = [ + "abcefg", + "adefijn", + "adef", + "eflm", + "adefn", + "aefn", + "acdefo", + "bcefg", + "adhk", + "bcd", + "efnij", + "def", + "bcefim", + "bcefjm", + "abcdef", + "abefg", + "abcdefj", + "aefijn", + "acdfg", + "ahk", + "bcdef", + "efil", + "bcefjl", + "ijlm", + "ikm", + "adil", + "abcdef", + "ef", + "abdeg", + "abcdg", + "bcfg", + "acdfg", + "acdefg", + "abc", + "abcdefg", + "abcdfg", + "", + "g", + "ghk", + "abcdefhijklmno", +]; + +// Each index is a segment's corresponding flipped segment, for when facing left +pub static SEGMENT_REV: [char; 15] = [ + 'a', 'f', 'e', 'd', 'c', 'b', 'g', 'h', 'm', 'l', 'k', 'j', 'i', 'o', 'n', +]; + +fn show_segment( + module_accessor: &mut app::BattleObjectModuleAccessor, + z: f32, + y: f32, + x: f32, + zrot: f32, + size: f32, +) { + let pos = Vector3f { x, y, z }; + let rot = Vector3f { + x: 0.0, + y: 90.0, + z: zrot, + }; + let random = Vector3f { + x: 0.0, + y: 0.0, + z: 0.0, + }; + + unsafe { + app::lua_bind::EffectModule::req_on_joint( + module_accessor, + Hash40::new("sys_raygun_bullet"), + Hash40::new("top"), + &pos, + &rot, + size, + &random, + &random, + false, + 0, + 0, + 0, + ); + } +} + +fn alphabet_index(to_print: char) -> i32 { + match to_print { + 'A'..='Z' => to_print as i32 - 'A' as i32, + '0'..='9' => to_print as i32 - '0' as i32 + 'Z' as i32 - 'A' as i32 + 1, + ' ' => 36, + '-' => 37, + '+' => 38, + '#' => 39, + _ => -1, + } +} + +fn print_char( + module_accessor: &mut app::BattleObjectModuleAccessor, + to_print: char, + line_num: i32, + horiz_offset: f32, + facing_left: i32, +) { + let is_facing_left = facing_left == -1; + let x_direction = facing_left as f32; + + let alph_index = alphabet_index(to_print); + if !(0..40).contains(&alph_index) { + return; + } + let segment_str = ALPHABET[alph_index as usize]; + + let line_offset = 40.0 - ((line_num as f32) * 16.0); + + for segment_char in segment_str.chars() { + let mut index = segment_char as i32 - 'a' as i32; + + let segment: [f32; 5]; + if is_facing_left { + index = SEGMENT_REV[index as usize] as i32 - 'a' as i32; + } + segment = SEGMENT_DICT[index as usize]; + + const SIZE_MULT: f32 = 0.5; + + let x = ((segment[2] + horiz_offset) * SIZE_MULT) + (x_direction * 5.0); + let y = ((segment[1] + line_offset) * SIZE_MULT) + 5.0; + let z = segment[0] * SIZE_MULT; + + let zrot = segment[3]; + let zrot = if is_facing_left { zrot.neg() } else { zrot }; + + let size = segment[4] * SIZE_MULT; + + show_segment(module_accessor, z, y, x, zrot, size); + } +} + +pub fn print_string(module_accessor: &mut app::BattleObjectModuleAccessor, to_write: &str) { + // Delete any previous strings + unsafe { + app::lua_bind::EffectModule::kill_kind( + module_accessor, + Hash40::new("sys_raygun_bullet"), + false, + true, + ); + } + + let mut line_num = 0; + let mut horiz_offset = 0.0; + let mut char_num = 0; + + let facing_left = unsafe { app::lua_bind::PostureModule::lr(module_accessor) as i32 }; + let facing_direction = facing_left as f32; + + if to_write.len() <= 8 && !to_write.contains('\n') { + line_num = 1; + } + for curr_char in to_write.chars() { + if curr_char == '\n' { + horiz_offset = 0.0; + char_num = 0; + line_num += 1; + continue; + } + + print_char( + module_accessor, + curr_char.to_uppercase().collect::<Vec<_>>()[0], + line_num, + horiz_offset, + facing_left, + ); + + char_num += 1; + // short characters + if curr_char == 'D' || curr_char == '1' { + horiz_offset += facing_direction * (RAYGUN_LENGTH / 2.0 + 3.0); + } else { + horiz_offset += facing_direction * (RAYGUN_LENGTH + 3.0); + } + + if char_num > 8 { + horiz_offset = 0.0; + char_num = 0; + line_num += 1; + } + } +} diff --git a/src/common/release.rs b/src/common/release.rs index 47a9e01..f19543a 100644 --- a/src/common/release.rs +++ b/src/common/release.rs @@ -1,36 +1,36 @@ -use skyline_web::DialogOk; -use std::fs; - -pub const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION"); -const VERSION_FILE_PATH: &str = "sd:/TrainingModpack/version.txt"; - -fn is_current_version(fpath: &str) -> bool { - // Create a blank version file if it doesn't exists - if fs::metadata(fpath).is_err() { - let _ = fs::File::create(fpath).expect("Could not create version file!"); - } - - fs::read_to_string(fpath) - .map(|content| content == CURRENT_VERSION) - .unwrap_or(false) -} - -fn record_current_version(fpath: &str) { - // Write the current version to the version file - fs::write(fpath, CURRENT_VERSION).expect("Could not record current version!") -} - -pub fn version_check() { - // Display dialog box on launch if changing versions - if !is_current_version(VERSION_FILE_PATH) { - DialogOk::ok( - format!( - "Thank you for installing version {} of the Training Modpack.\n\n\ - This version includes a change to the menu button combination, which is now SPECIAL+UPTAUNT.\n\ - Please refer to the Github page and the Discord server for a full list of recent changes.", - CURRENT_VERSION - ) - ); - record_current_version(VERSION_FILE_PATH); - } -} +use skyline_web::DialogOk; +use std::fs; + +pub const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION"); +const VERSION_FILE_PATH: &str = "sd:/TrainingModpack/version.txt"; + +fn is_current_version(fpath: &str) -> bool { + // Create a blank version file if it doesn't exists + if fs::metadata(fpath).is_err() { + let _ = fs::File::create(fpath).expect("Could not create version file!"); + } + + fs::read_to_string(fpath) + .map(|content| content == CURRENT_VERSION) + .unwrap_or(false) +} + +fn record_current_version(fpath: &str) { + // Write the current version to the version file + fs::write(fpath, CURRENT_VERSION).expect("Could not record current version!") +} + +pub fn version_check() { + // Display dialog box on launch if changing versions + if !is_current_version(VERSION_FILE_PATH) { + DialogOk::ok( + format!( + "Thank you for installing version {} of the Training Modpack.\n\n\ + This version includes a change to the menu button combination, which is now SPECIAL+UPTAUNT.\n\ + Please refer to the Github page and the Discord server for a full list of recent changes.", + CURRENT_VERSION + ) + ); + record_current_version(VERSION_FILE_PATH); + } +} diff --git a/src/lib.rs b/src/lib.rs index 33f77c1..c366659 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,141 +1,135 @@ -#![feature(proc_macro_hygiene)] -#![feature(with_options)] -#![feature(const_mut_refs)] -#![feature(exclusive_range_pattern)] -#![feature(once_cell)] -#![allow( - clippy::borrow_interior_mutable_const, - clippy::not_unsafe_ptr_arg_deref, - clippy::missing_safety_doc, - clippy::wrong_self_convention -)] - -pub mod common; -mod hazard_manager; -mod hitbox_visualizer; -mod training; - -#[cfg(test)] -mod test; - -#[macro_use] -extern crate bitflags; - -#[macro_use] -extern crate num_derive; - -use crate::common::*; -use crate::events::{Event, EVENT_QUEUE}; -use crate::menu::get_menu_from_url; - -use skyline::libc::mkdir; -use skyline::nro::{self, NroInfo}; -use std::fs; - -use owo_colors::OwoColorize; - -fn nro_main(nro: &NroInfo<'_>) { - if nro.module.isLoaded { - return; - } - - if nro.name == "common" { - skyline::install_hooks!( - training::shield::handle_sub_guard_cont, - training::directional_influence::handle_correct_damage_vector_common, - training::sdi::process_hit_stop_delay, - training::tech::handle_change_status, - ); - } -} - -macro_rules! c_str { - ($l:tt) => { - [$l.as_bytes(), "\u{0}".as_bytes()].concat().as_ptr(); - }; -} - -#[skyline::main(name = "training_modpack")] -pub fn main() { - macro_rules! log { - ($($arg:tt)*) => { - print!("{}", "[Training Modpack] ".green()); - println!($($arg)*); - }; - } - - log!("Initialized."); - unsafe { - EVENT_QUEUE.push(Event::smash_open()); - } - - hitbox_visualizer::hitbox_visualization(); - hazard_manager::hazard_manager(); - training::training_mods(); - nro::add_hook(nro_main).unwrap(); - - unsafe { - mkdir(c_str!("sd:/TrainingModpack/"), 777); - } - - let ovl_path = "sd:/switch/.overlays/ovlTrainingModpack.ovl"; - if fs::metadata(ovl_path).is_ok() { - log!("Removing ovlTrainingModpack.ovl..."); - fs::remove_file(ovl_path).unwrap(); - } - - log!("Performing version check..."); - release::version_check(); - - let menu_conf_path = "sd:/TrainingModpack/training_modpack_menu.conf"; - log!("Checking for previous menu in training_modpack_menu.conf..."); - if fs::metadata(menu_conf_path).is_ok() { - let menu_conf = fs::read(menu_conf_path).unwrap(); - if menu_conf.starts_with(b"http://localhost") { - log!("Previous menu found, loading from training_modpack_menu.conf"); - unsafe { - MENU = get_menu_from_url(MENU, std::str::from_utf8(&menu_conf).unwrap()); - } - } else { - log!("Previous menu found but is invalid."); - } - } else { - log!("No previous menu file found."); - } - - let menu_defaults_conf_path = "sd:/TrainingModpack/training_modpack_menu_defaults.conf"; - log!("Checking for previous menu defaults in training_modpack_menu_defaults.conf..."); - if fs::metadata(menu_defaults_conf_path).is_ok() { - let menu_defaults_conf = fs::read(menu_defaults_conf_path).unwrap(); - if menu_defaults_conf.starts_with(b"http://localhost") { - log!("Menu defaults found, loading from training_modpack_menu_defaults.conf"); - unsafe { - DEFAULT_MENU = get_menu_from_url( - DEFAULT_MENU, - std::str::from_utf8(&menu_defaults_conf).unwrap(), - ); - crate::menu::write_menu(); - } - } else { - log!("Previous menu defaults found but are invalid."); - } - } else { - log!("No previous menu defaults found."); - } - - std::thread::spawn(|| loop { - std::thread::sleep(std::time::Duration::from_secs(5)); - unsafe { - while let Some(event) = EVENT_QUEUE.pop() { - let host = "https://my-project-1511972643240-default-rtdb.firebaseio.com"; - let path = format!( - "/event/{}/device/{}/{}.json", - event.event_name, event.device_id, event.event_time - ); - - let url = format!("{}{}", host, path); - minreq::post(url).with_json(&event).unwrap().send().ok(); - } - } - }); -} +#![feature(proc_macro_hygiene)] +#![feature(with_options)] +#![feature(const_mut_refs)] +#![feature(exclusive_range_pattern)] +#![feature(once_cell)] +#![allow( + clippy::borrow_interior_mutable_const, + clippy::not_unsafe_ptr_arg_deref, + clippy::missing_safety_doc, + clippy::wrong_self_convention +)] + +pub mod common; +mod hazard_manager; +mod hitbox_visualizer; +mod training; + +#[cfg(test)] +mod test; + +use crate::common::*; +use crate::events::{Event, EVENT_QUEUE}; +use crate::menu::get_menu_from_url; + +use skyline::libc::mkdir; +use skyline::nro::{self, NroInfo}; +use std::fs; + +use owo_colors::OwoColorize; + +fn nro_main(nro: &NroInfo<'_>) { + if nro.module.isLoaded { + return; + } + + if nro.name == "common" { + skyline::install_hooks!( + training::shield::handle_sub_guard_cont, + training::directional_influence::handle_correct_damage_vector_common, + training::sdi::process_hit_stop_delay, + training::tech::handle_change_status, + ); + } +} + +macro_rules! c_str { + ($l:tt) => { + [$l.as_bytes(), "\u{0}".as_bytes()].concat().as_ptr(); + }; +} + +#[skyline::main(name = "training_modpack")] +pub fn main() { + macro_rules! log { + ($($arg:tt)*) => { + print!("{}", "[Training Modpack] ".green()); + println!($($arg)*); + }; + } + + log!("Initialized."); + unsafe { + EVENT_QUEUE.push(Event::smash_open()); + } + + hitbox_visualizer::hitbox_visualization(); + hazard_manager::hazard_manager(); + training::training_mods(); + nro::add_hook(nro_main).unwrap(); + + unsafe { + mkdir(c_str!("sd:/TrainingModpack/"), 777); + } + + let ovl_path = "sd:/switch/.overlays/ovlTrainingModpack.ovl"; + if fs::metadata(ovl_path).is_ok() { + log!("Removing ovlTrainingModpack.ovl..."); + fs::remove_file(ovl_path).unwrap(); + } + + log!("Performing version check..."); + release::version_check(); + + let menu_conf_path = "sd:/TrainingModpack/training_modpack_menu.conf"; + log!("Checking for previous menu in training_modpack_menu.conf..."); + if fs::metadata(menu_conf_path).is_ok() { + let menu_conf = fs::read(menu_conf_path).unwrap(); + if menu_conf.starts_with(b"http://localhost") { + log!("Previous menu found, loading from training_modpack_menu.conf"); + unsafe { + MENU = get_menu_from_url(MENU, std::str::from_utf8(&menu_conf).unwrap()); + } + } else { + log!("Previous menu found but is invalid."); + } + } else { + log!("No previous menu file found."); + } + + let menu_defaults_conf_path = "sd:/TrainingModpack/training_modpack_menu_defaults.conf"; + log!("Checking for previous menu defaults in training_modpack_menu_defaults.conf..."); + if fs::metadata(menu_defaults_conf_path).is_ok() { + let menu_defaults_conf = fs::read(menu_defaults_conf_path).unwrap(); + if menu_defaults_conf.starts_with(b"http://localhost") { + log!("Menu defaults found, loading from training_modpack_menu_defaults.conf"); + unsafe { + DEFAULT_MENU = get_menu_from_url( + DEFAULT_MENU, + std::str::from_utf8(&menu_defaults_conf).unwrap(), + ); + crate::menu::write_menu(); + } + } else { + log!("Previous menu defaults found but are invalid."); + } + } else { + log!("No previous menu defaults found."); + } + + std::thread::spawn(|| loop { + std::thread::sleep(std::time::Duration::from_secs(5)); + unsafe { + while let Some(event) = EVENT_QUEUE.pop() { + let host = "https://my-project-1511972643240-default-rtdb.firebaseio.com"; + let path = format!( + "/event/{}/device/{}/{}.json", + event.event_name, event.device_id, event.event_time + ); + + let url = format!("{}{}", host, path); + minreq::post(url).with_json(&event).unwrap().send().ok(); + } + } + }); +} diff --git a/src/templates/buff_state.svg b/src/templates/buff_state.svg index ce075af..71effba 100644 --- a/src/templates/buff_state.svg +++ b/src/templates/buff_state.svg @@ -1,82 +1,82 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg - width="80.0px" - height="80.0px" - viewBox="0 0 80.0 80.0" - version="1.1" - id="SVGRoot" - sodipodi:docname="buff_state.svg" - inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns="http://www.w3.org/2000/svg" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:dc="http://purl.org/dc/elements/1.1/"> - <defs - id="defs5503" /> - <sodipodi:namedview - id="base" - pagecolor="#000000" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="8" - inkscape:cx="52.625" - inkscape:cy="33.6875" - inkscape:document-units="px" - inkscape:current-layer="layer1" - inkscape:document-rotation="0" - showgrid="true" - inkscape:window-width="1431" - inkscape:window-height="1041" - inkscape:window-x="256" - inkscape:window-y="0" - inkscape:window-maximized="0" - inkscape:pagecheckerboard="1"> - <inkscape:grid - type="xygrid" - id="grid6073" - spacingx="0.1" - spacingy="0.1" /> - </sodipodi:namedview> - <metadata - id="metadata5506"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1"> - <g - id="g243" - style="fill:#ffffea;fill-opacity:1" - transform="matrix(0.54490954,0,0,-0.44245854,-24.388508,217.32339)"> - <g - id="g245" - style="fill:#ffffea;fill-opacity:1"> - <g - id="g251" - style="fill:#ffffea;fill-opacity:1"> - <g - id="g253" - style="fill:#ffffea;fill-opacity:1"> - <path - d="m 58.348,376.03 c 3.332,-50.936 45.698,-55.695 45.698,-55.695 v 0 c -36.178,20.944 -24.437,58.551 -1.666,79.495 v 0 c 13.625,12.533 9.71,21.5 9.313,22.314 v 0 c 7.57,-14.95 1.868,-19.957 -2.411,-37.546 v 0 c -4.284,-17.614 6.188,-36.178 17.613,-33.44 v 0 c 11.424,2.737 5.95,17.969 5.95,17.969 v 0 c 26.419,-26.896 0.952,-44.746 0.952,-44.746 v 0 c 0,0 39.034,10.948 42.604,47.365 v 0 c 3.571,36.415 -25.705,51.411 -25.705,51.411 v 0 c 9.758,-8.093 9.283,-23.327 -1.904,-24.279 v 0 c -11.187,-0.951 -18.089,3.333 -10.71,35.464 v 0 c 7.378,32.132 -34.036,50.457 -34.036,50.457 v 0 C 124.039,438.387 55.015,426.963 58.348,376.03 m 53.314,46.174 c 0,0 0,0 0,0 v 0 c 0,0 0,0 0,0" - style="fill:#ffffea;stroke:none;fill-opacity:1" - id="path267" /> - </g> - </g> - </g> - </g> - </g> -</svg> +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + width="80.0px" + height="80.0px" + viewBox="0 0 80.0 80.0" + version="1.1" + id="SVGRoot" + sodipodi:docname="buff_state.svg" + inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> + <defs + id="defs5503" /> + <sodipodi:namedview + id="base" + pagecolor="#000000" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:zoom="8" + inkscape:cx="52.625" + inkscape:cy="33.6875" + inkscape:document-units="px" + inkscape:current-layer="layer1" + inkscape:document-rotation="0" + showgrid="true" + inkscape:window-width="1431" + inkscape:window-height="1041" + inkscape:window-x="256" + inkscape:window-y="0" + inkscape:window-maximized="0" + inkscape:pagecheckerboard="1"> + <inkscape:grid + type="xygrid" + id="grid6073" + spacingx="0.1" + spacingy="0.1" /> + </sodipodi:namedview> + <metadata + id="metadata5506"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <g + id="g243" + style="fill:#ffffea;fill-opacity:1" + transform="matrix(0.54490954,0,0,-0.44245854,-24.388508,217.32339)"> + <g + id="g245" + style="fill:#ffffea;fill-opacity:1"> + <g + id="g251" + style="fill:#ffffea;fill-opacity:1"> + <g + id="g253" + style="fill:#ffffea;fill-opacity:1"> + <path + d="m 58.348,376.03 c 3.332,-50.936 45.698,-55.695 45.698,-55.695 v 0 c -36.178,20.944 -24.437,58.551 -1.666,79.495 v 0 c 13.625,12.533 9.71,21.5 9.313,22.314 v 0 c 7.57,-14.95 1.868,-19.957 -2.411,-37.546 v 0 c -4.284,-17.614 6.188,-36.178 17.613,-33.44 v 0 c 11.424,2.737 5.95,17.969 5.95,17.969 v 0 c 26.419,-26.896 0.952,-44.746 0.952,-44.746 v 0 c 0,0 39.034,10.948 42.604,47.365 v 0 c 3.571,36.415 -25.705,51.411 -25.705,51.411 v 0 c 9.758,-8.093 9.283,-23.327 -1.904,-24.279 v 0 c -11.187,-0.951 -18.089,3.333 -10.71,35.464 v 0 c 7.378,32.132 -34.036,50.457 -34.036,50.457 v 0 C 124.039,438.387 55.015,426.963 58.348,376.03 m 53.314,46.174 c 0,0 0,0 0,0 v 0 c 0,0 0,0 0,0" + style="fill:#ffffea;stroke:none;fill-opacity:1" + id="path267" /> + </g> + </g> + </g> + </g> + </g> +</svg> diff --git a/src/templates/menu.html b/src/templates/menu.html index 46aae67..09e0240 100644 --- a/src/templates/menu.html +++ b/src/templates/menu.html @@ -1,487 +1,487 @@ -<!DOCTYPE html> -<html lang="en"> - - <head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> - <title>Document</title> - <link rel="stylesheet" href="./help/css/common.css" /> - <link rel="stylesheet" href="./help/css/qa.css" /> - <link rel="stylesheet" href="./help/css/top.css" /> - <link id="font-stylesheet" rel="stylesheet" href="./help/css/font.css"> - <link rel="stylesheet" href="./help/css/keyword.css"> - <link href="./nouislider.min.css" rel="stylesheet"> - <style> - @-moz-document url-prefix() { - @font-face { - font-family: '-webkit-standard'; - src: url('nintendo_udsg-r_std_003.ttf'); - } - } - - body { - background: none; - } - - /* Overwrite padding from keyword stuff. */ - .l-main-content { - padding: 0px 0px 0px; - } - - /* Somehow isn't getting passed through from default css. */ - .ret-icon-shadow { - position: absolute; - z-index: -1; - top: 3px; - left: 2px; - display: inline-block; - width: 58px; - height: 58px; - opacity: 0; - transition: opacity 0.2s ease; - } - - /* Column size */ - .l-qa { - flex-basis: 33%; - } - - /* Full width for opened lists */ - .is-opened .question-outer { - width: 100%; - } - - /* Overwrite margin on the last child to avoid overlap with footer */ - .l-qa:last-child .qa { - margin-bottom: 75px; - } - - .l-qa:last-child .qa.is-opened { - margin-bottom: 0px; - } - - /* Fade icons slightly */ - img.question-icon:not(.toggle) { - opacity: 0.75; - } - - /* Override excessive question width on focus */ - .is-focused .question-message span:nth-child(1) { - width: auto; - } - - /* Handle alignment of items in the header */ - .l-header { - display: flex; - } - - /* Set menu description color */ - .header-desc { - color: white; - } - - /* Center Icons */ - .question::before { - width: 70px; - } - - /* Footer */ - .footer { - position: fixed; - z-index: 10; - } - - /* Save Defaults Container */ - .defaults-checkbox-container { - position: fixed; - right: 50px; - margin-top: 10px; - display: flex; - justify-content: center; - flex-direction: column; - } - - /* Checkbox element (hidden) */ - #saveDefaults { - position: absolute; - left: -100vw; - } - - .checkbox-display { - margin: 10px 70px; - } - - /* Displayed Checkbox (unchecked) */ - .checkbox-display::after { - content: "\E14C"; - color: white; - } - - /* Displayed Checkbox (checked) */ - #saveDefaults:checked ~ .checkbox-display::after { - content: "\E14B"; - } - </style> - </head> - - <body> - <script src="./help/js/jquery-3.3.1.min.js"></script> - <script src="./help/common/js/wsnd.min.js"></script> - <script src="./help/common/js/keyhelp.js"></script> - <script src="./help/js/common.js"></script> - <script src="./help/js/qa.js"></script> - <script src="./nouislider.min.js"></script> - - <div class="l-header"> - <div class="l-header-title"> - <div class="header-title f-u-bold"><span data-msgcom="true" data-msgid="textbox_id-10020">Ultimate - Training - Modpack Menu</span></div> - </div> - <div class="header" style="flex-grow: 1;"> - <a id="ret-button" tabindex="-1" class="header-decoration" href="javascript:goBackHook();" nx-se-disabled=""> - <div class="ret-icon-wrapper"> - <img class="ret-icon-shadow is-appear" ref="./help/img/icon/m_retnormal.svg" src="./help/img/icon/m_retnormal.svg"> - <img class="ret-icon is-appear" ref="./help/img/icon/m_retnormal.svg" src="./help/img/icon/m_retnormal.svg"> - </div> - </a> - </div> - <div class="header f-u-bold" style="flex-direction: column; justify-content: center; align-items: end;"> - <p class="header-desc">Reset Current Menu: </p> - <p class="header-desc">Reset All Menus: </p> - </div> - </div> - <br> - <br> - <br> - <br> - - <div class="l-grid"> - - <!-- - Script the part below via templating. Overall structure is perhaps - [ - l-qa qa [id=qa-{{menuName}} tabindex="{{index}}"] { - // make question for {{menuName}} - // make answer with l-grid : l-item list for options - }, - ... - ] - - - Remember to set make max keyword size greater than 23! - --> - {{#sub_menus}} - <div class="l-qa"> - {{^onoffselector}} - <a id="qa-{{id}}" class="qa" tabindex="{{index}}" href="javascript:void(0);" onfocus="focusQA(this);setHelpText({{help_text}})" onblur="defocusQA(this)" onclick="openAnswer(this)" nx-se-disabled=""> - <div class="question-outer"> - <div class="question-border"> - <div id="question-{{id}}" class="question scuffle-thema"> - <img class="question-icon" ref="./{{id}}.svg" src="./{{id}}.svg" /> - <p class="question-message f-u-bold"> - <span data-msgid="textbox_id-7">{{title}}</span> - </p> - </div> - </div> - </div> - <div id="answer-border-{{id}}" class="answer-border-outer is-hidden"> - <div class="l-main"> - <ul class="l-grid" id="{{id}}"> - {{#toggles}} - <li class="l-item" val="{{value}}"> - <div class="keyword-button-outer"> - <a tabindex="{{index}}" class="flex-button keyword-button scuffle-thema" href="javascript:void(0)" onclick="clickToggle(this);" nx-se-disabled=""> - <div class="button-icon-wrapper"> - <img class="button-icon toggle {{checked}} {{#is_single_option}}is-single-option{{/is_single_option}}" ref="./check.svg" src="./check.svg" default="{{default}}"> - </div> - <div class="button-msg-wrapper"> - <div class="keyword-message f-u-bold"> - {{title}} - </div> - </div> - </a> - </div> - </li> - {{/toggles}} - {{#sliders}} - <li class="l-item" val="{{value}}"> - <div class="keyword-button-outer"> - <a tabindex="{{index}}" class="flex-button keyword-button scuffle-thema" href="javascript:void(0)" onclick="clickToggle(this);" nx-se-disabled=""> - <div class="button-icon-wrapper"> - <img class="button-icon toggle {{checked}}" ref="./check.svg" src="./check.svg"> - </div> - <div class="button-msg-wrapper"> - <div class="keyword-message f-u-bold"> - <div name='range_slider' oninput="this.nextElementSibling.value = this.value"> - </div> - <output>{{value}}</output> - </div> - </div> - </a> - </div> - </li> - {{/sliders}} - {{#range_sliders}} - <li class="l-item" val="{{value}}"> - <div class="keyword-button-outer"> - <a tabindex="{{index}}" class="flex-button keyword-button scuffle-thema" href="javascript:void(0)" onclick="clickToggle(this);" nx-se-disabled=""> - <div class="button-icon-wrapper"> - <img class="button-icon toggle {{checked}}" ref="./check.svg" src="./check.svg"> - </div> - <div class="button-msg-wrapper"> - <div class="keyword-message f-u-bold"> - <div name="{{id}}"> - </div> - </div> - </div> - </a> - </div> - </li> - {{/range_sliders}} - </ul> - </div> - </div> - </a> - {{/onoffselector}} - {{#onoffselector}} - <a id="qa-{{id}}" class="qa" tabindex="{{index}}" href="javascript:void(0);" onfocus="focusQA(this);setHelpText({{help_text}})" onblur="defocusQA(this)" onclick="clickToggle(this)" nx-se-disabled=""> - <div class="question-outer"> - <div class="question-border"> - <div id="question-{{id}}" class="question scuffle-thema"> - <div id="{{id}}" class="onoff"> - <img class="question-icon" style="z-index: 1;" ref="./{{id}}.svg" src="./{{id}}.svg" /> - <div><img class="question-icon toggle {{checked}}" style="z-index: 2;" ref="./check.svg" src="./check.svg" default="{{default}}"/></div> - <p class="keyword-message f-u-bold"> - {{title}} - </p> - </div> - </div> - </div> - </div> - </a> - {{/onoffselector}} - </div> - {{/sub_menus}} - </div> - {{#sub_menus}} - {{#sliders}} - <script> - // todo: loop through - noUiSlider.create(document.getElementsByName('range_slider')[0], { - start: {{ value }}, - range: { - 'min': {{ min }}, - 'max': {{ max }} - } - }); - </script> - {{/sliders}} - {{/sub_menus}} - <footer id="footer" class="footer l-footer f-u-bold"> - <p id="help-text" class="header-desc"></p> - <div class="defaults-checkbox-container"> - <label class="header-desc" for="saveDefaults">Save defaults: </label> - <input type="checkbox" id="saveDefaults"> - <div class="checkbox-display"></div> - </div> - </footer> - <script> - if (isNx) { - window.nx.footer.setAssign('B', '', goBackHook, { - se: '' - }) - window.nx.footer.setAssign('X', '', resetSubmenu, { - se: '' - }) - window.nx.footer.setAssign('L', '', resetAllSubmenus, { - se: '' - }) - window.nx.footer.setAssign('R', '', toggleSaveDefaults, { - se: '' - }) - } - function goBackHook() { - // If any submenus are open, close them - // Otherwise if all submenus are closed, exit the menu and return to the game - if ($(".qa.is-opened").length == 0) { - // If all submenus are closed, exit and return through localhost - $('.is-focused').addClass('is-pause-anim') - $('#ret-button').addClass('is-focus') - - disabledOtherLink() - - playSound('cancel') - - var url = "http://localhost/" - - var settings = []; - - // Collect settings for toggles - $("ul.l-grid").each(function () { - var section = this.id; - var val = ""; - - var children = this.children; - for (var i = 0; i < children.length; i++) { - var child = children[i]; - if ($(child).find(".is-appear").length) { - val += child.getAttribute("val") + ","; - }; - } - - settings.push({ - name: section, - value: val - }); - }); - - // Collect settings for OnOffs - $("div.onoff").each(function () { - var section = this.id; - var val = ""; - if ($(this).find(".is-appear").length) { - val = "1" - } else { - val = "0" - } - settings.push({ - name: section, - value: val - }); - }); - - url += "?" + decodeURIComponent($.param(settings)); - if ($("#saveDefaults").prop("checked")) { - url += "&save_defaults=1"; - } - // console.log(url); - location.href = url; - - fadeOutPage(function () { - window.history.back() - }) - } else { - // Close any open submenus - $(".qa.is-opened").each(function () { openAnswer(this); }); - } - } - - function clickToggle(e) { - var toggleOptions = $(e).find(".toggle"); - if ($(e).find(".is-single-option").length) { // Single-option submenu - // Deselect all submenu options - $(e).closest(".l-qa").find(".toggle").removeClass("is-appear"); - $(e).closest(".l-qa").find(".toggle").addClass("is-hidden"); - // Then set the current one as the active setting - toggleOptions.addClass("is-appear"); - toggleOptions.removeClass("is-hidden"); - } else { // Multi-option submenu - toggleOptions.toggleClass("is-appear"); - toggleOptions.toggleClass("is-hidden"); - } - } - - function getParams(url) { - var regex = /[?&]([^=#]+)=([^&#]*)/g, - params = {}, - match; - while(match = regex.exec(url)) { - params[match[1]] = match[2]; - } - return params; - } - - function setSettings() { - // Get settings from the URL GET parameters - const settings = getParams(document.URL); - - // Set Toggles - $("ul.l-grid").each(function () { - var section = this.id; - var section_setting = decodeURIComponent(settings[section]); - - var children = $(this).children("li"); - for (var i = 0; i < children.length; i++) { - var child = children[i]; - var e = $(child).find("img.toggle"); - if (section_setting.split(",").includes(child.getAttribute("val"))) { - e.addClass("is-appear"); - e.removeClass("is-hidden"); - } else { - e.removeClass("is-appear"); - e.addClass("is-hidden"); - }; - } - }); - - // Set OnOffs - $("div.onoff").each(function () { - var section = this.id; - var section_setting = decodeURIComponent(settings[section]); - var e = $(this).find("img.toggle"); - if (section_setting == "1") { - e.addClass("is-appear"); - e.removeClass("is-hidden"); - } else { - e.removeClass("is-appear"); - e.addClass("is-hidden"); - }; - }); - } - - function resetSubmenu() { - // Resets any open or focused submenus to the default values - $("[default*='is-appear']").each(function () { - if (isSubmenuFocused(this)) { - $(this).addClass("is-appear"); - $(this).removeClass("is-hidden"); - } - }); - $("[default*='is-hidden']").each(function() { - if (isSubmenuFocused(this)) { - $(this).removeClass("is-appear"); - $(this).addClass("is-hidden"); - } - }); - } - - function isSubmenuFocused(elem) { - // Return true if the element is in a submenu which is either focused or opened - return ( - $(elem).closest(".l-qa").children(".is-opened, .is-focused").length - || $(elem).closest(".is-focused").length - ) - } - - function resetAllSubmenus() { - // Resets all submenus to the default values - if (confirm("Are you sure that you want to reset all menu settings to the default?")) { - $("[default*='is-appear']").addClass("is-appear"); - $("[default*='is-appear']").removeClass("is-hidden"); - - $("[default*='is-hidden']").removeClass("is-appear"); - $("[default*='is-hidden']").addClass("is-hidden"); - } - } - - function setHelpText(text) { - // Modify the help text in the footer - $("#help-text").text(text); - } - - function toggleSaveDefaults() { - // Change the status of the Save Defaults checkbox - var saveDefaultsCheckbox = $("#saveDefaults"); - saveDefaultsCheckbox.prop( - "checked", - !saveDefaultsCheckbox.prop("checked") - ); - } - </script> - <script> - window.onload = setSettings; - </script> - </body> - -</html> +<!DOCTYPE html> +<html lang="en"> + + <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> + <title>Document</title> + <link rel="stylesheet" href="./help/css/common.css" /> + <link rel="stylesheet" href="./help/css/qa.css" /> + <link rel="stylesheet" href="./help/css/top.css" /> + <link id="font-stylesheet" rel="stylesheet" href="./help/css/font.css"> + <link rel="stylesheet" href="./help/css/keyword.css"> + <link href="./nouislider.min.css" rel="stylesheet"> + <style> + @-moz-document url-prefix() { + @font-face { + font-family: '-webkit-standard'; + src: url('nintendo_udsg-r_std_003.ttf'); + } + } + + body { + background: none; + } + + /* Overwrite padding from keyword stuff. */ + .l-main-content { + padding: 0px 0px 0px; + } + + /* Somehow isn't getting passed through from default css. */ + .ret-icon-shadow { + position: absolute; + z-index: -1; + top: 3px; + left: 2px; + display: inline-block; + width: 58px; + height: 58px; + opacity: 0; + transition: opacity 0.2s ease; + } + + /* Column size */ + .l-qa { + flex-basis: 33%; + } + + /* Full width for opened lists */ + .is-opened .question-outer { + width: 100%; + } + + /* Overwrite margin on the last child to avoid overlap with footer */ + .l-qa:last-child .qa { + margin-bottom: 75px; + } + + .l-qa:last-child .qa.is-opened { + margin-bottom: 0px; + } + + /* Fade icons slightly */ + img.question-icon:not(.toggle) { + opacity: 0.75; + } + + /* Override excessive question width on focus */ + .is-focused .question-message span:nth-child(1) { + width: auto; + } + + /* Handle alignment of items in the header */ + .l-header { + display: flex; + } + + /* Set menu description color */ + .header-desc { + color: white; + } + + /* Center Icons */ + .question::before { + width: 70px; + } + + /* Footer */ + .footer { + position: fixed; + z-index: 10; + } + + /* Save Defaults Container */ + .defaults-checkbox-container { + position: fixed; + right: 50px; + margin-top: 10px; + display: flex; + justify-content: center; + flex-direction: column; + } + + /* Checkbox element (hidden) */ + #saveDefaults { + position: absolute; + left: -100vw; + } + + .checkbox-display { + margin: 10px 70px; + } + + /* Displayed Checkbox (unchecked) */ + .checkbox-display::after { + content: "\E14C"; + color: white; + } + + /* Displayed Checkbox (checked) */ + #saveDefaults:checked ~ .checkbox-display::after { + content: "\E14B"; + } + </style> + </head> + + <body> + <script src="./help/js/jquery-3.3.1.min.js"></script> + <script src="./help/common/js/wsnd.min.js"></script> + <script src="./help/common/js/keyhelp.js"></script> + <script src="./help/js/common.js"></script> + <script src="./help/js/qa.js"></script> + <script src="./nouislider.min.js"></script> + + <div class="l-header"> + <div class="l-header-title"> + <div class="header-title f-u-bold"><span data-msgcom="true" data-msgid="textbox_id-10020">Ultimate + Training + Modpack Menu</span></div> + </div> + <div class="header" style="flex-grow: 1;"> + <a id="ret-button" tabindex="-1" class="header-decoration" href="javascript:goBackHook();" nx-se-disabled=""> + <div class="ret-icon-wrapper"> + <img class="ret-icon-shadow is-appear" ref="./help/img/icon/m_retnormal.svg" src="./help/img/icon/m_retnormal.svg"> + <img class="ret-icon is-appear" ref="./help/img/icon/m_retnormal.svg" src="./help/img/icon/m_retnormal.svg"> + </div> + </a> + </div> + <div class="header f-u-bold" style="flex-direction: column; justify-content: center; align-items: end;"> + <p class="header-desc">Reset Current Menu: </p> + <p class="header-desc">Reset All Menus: </p> + </div> + </div> + <br> + <br> + <br> + <br> + + <div class="l-grid"> + + <!-- + Script the part below via templating. Overall structure is perhaps + [ + l-qa qa [id=qa-{{menuName}} tabindex="{{index}}"] { + // make question for {{menuName}} + // make answer with l-grid : l-item list for options + }, + ... + ] + + + Remember to set make max keyword size greater than 23! + --> + {{#sub_menus}} + <div class="l-qa"> + {{^onoffselector}} + <a id="qa-{{id}}" class="qa" tabindex="{{index}}" href="javascript:void(0);" onfocus="focusQA(this);setHelpText({{help_text}})" onblur="defocusQA(this)" onclick="openAnswer(this)" nx-se-disabled=""> + <div class="question-outer"> + <div class="question-border"> + <div id="question-{{id}}" class="question scuffle-thema"> + <img class="question-icon" ref="./{{id}}.svg" src="./{{id}}.svg" /> + <p class="question-message f-u-bold"> + <span data-msgid="textbox_id-7">{{title}}</span> + </p> + </div> + </div> + </div> + <div id="answer-border-{{id}}" class="answer-border-outer is-hidden"> + <div class="l-main"> + <ul class="l-grid" id="{{id}}"> + {{#toggles}} + <li class="l-item" val="{{value}}"> + <div class="keyword-button-outer"> + <a tabindex="{{index}}" class="flex-button keyword-button scuffle-thema" href="javascript:void(0)" onclick="clickToggle(this);" nx-se-disabled=""> + <div class="button-icon-wrapper"> + <img class="button-icon toggle {{checked}} {{#is_single_option}}is-single-option{{/is_single_option}}" ref="./check.svg" src="./check.svg" default="{{default}}"> + </div> + <div class="button-msg-wrapper"> + <div class="keyword-message f-u-bold"> + {{title}} + </div> + </div> + </a> + </div> + </li> + {{/toggles}} + {{#sliders}} + <li class="l-item" val="{{value}}"> + <div class="keyword-button-outer"> + <a tabindex="{{index}}" class="flex-button keyword-button scuffle-thema" href="javascript:void(0)" onclick="clickToggle(this);" nx-se-disabled=""> + <div class="button-icon-wrapper"> + <img class="button-icon toggle {{checked}}" ref="./check.svg" src="./check.svg"> + </div> + <div class="button-msg-wrapper"> + <div class="keyword-message f-u-bold"> + <div name='range_slider' oninput="this.nextElementSibling.value = this.value"> + </div> + <output>{{value}}</output> + </div> + </div> + </a> + </div> + </li> + {{/sliders}} + {{#range_sliders}} + <li class="l-item" val="{{value}}"> + <div class="keyword-button-outer"> + <a tabindex="{{index}}" class="flex-button keyword-button scuffle-thema" href="javascript:void(0)" onclick="clickToggle(this);" nx-se-disabled=""> + <div class="button-icon-wrapper"> + <img class="button-icon toggle {{checked}}" ref="./check.svg" src="./check.svg"> + </div> + <div class="button-msg-wrapper"> + <div class="keyword-message f-u-bold"> + <div name="{{id}}"> + </div> + </div> + </div> + </a> + </div> + </li> + {{/range_sliders}} + </ul> + </div> + </div> + </a> + {{/onoffselector}} + {{#onoffselector}} + <a id="qa-{{id}}" class="qa" tabindex="{{index}}" href="javascript:void(0);" onfocus="focusQA(this);setHelpText({{help_text}})" onblur="defocusQA(this)" onclick="clickToggle(this)" nx-se-disabled=""> + <div class="question-outer"> + <div class="question-border"> + <div id="question-{{id}}" class="question scuffle-thema"> + <div id="{{id}}" class="onoff"> + <img class="question-icon" style="z-index: 1;" ref="./{{id}}.svg" src="./{{id}}.svg" /> + <div><img class="question-icon toggle {{checked}}" style="z-index: 2;" ref="./check.svg" src="./check.svg" default="{{default}}"/></div> + <p class="keyword-message f-u-bold"> + {{title}} + </p> + </div> + </div> + </div> + </div> + </a> + {{/onoffselector}} + </div> + {{/sub_menus}} + </div> + {{#sub_menus}} + {{#sliders}} + <script> + // todo: loop through + noUiSlider.create(document.getElementsByName('range_slider')[0], { + start: {{ value }}, + range: { + 'min': {{ min }}, + 'max': {{ max }} + } + }); + </script> + {{/sliders}} + {{/sub_menus}} + <footer id="footer" class="footer l-footer f-u-bold"> + <p id="help-text" class="header-desc"></p> + <div class="defaults-checkbox-container"> + <label class="header-desc" for="saveDefaults">Save defaults: </label> + <input type="checkbox" id="saveDefaults"> + <div class="checkbox-display"></div> + </div> + </footer> + <script> + if (isNx) { + window.nx.footer.setAssign('B', '', goBackHook, { + se: '' + }) + window.nx.footer.setAssign('X', '', resetSubmenu, { + se: '' + }) + window.nx.footer.setAssign('L', '', resetAllSubmenus, { + se: '' + }) + window.nx.footer.setAssign('R', '', toggleSaveDefaults, { + se: '' + }) + } + function goBackHook() { + // If any submenus are open, close them + // Otherwise if all submenus are closed, exit the menu and return to the game + if ($(".qa.is-opened").length == 0) { + // If all submenus are closed, exit and return through localhost + $('.is-focused').addClass('is-pause-anim') + $('#ret-button').addClass('is-focus') + + disabledOtherLink() + + playSound('cancel') + + var url = "http://localhost/" + + var settings = []; + + // Collect settings for toggles + $("ul.l-grid").each(function () { + var section = this.id; + var val = ""; + + var children = this.children; + for (var i = 0; i < children.length; i++) { + var child = children[i]; + if ($(child).find(".is-appear").length) { + val += child.getAttribute("val") + ","; + }; + } + + settings.push({ + name: section, + value: val + }); + }); + + // Collect settings for OnOffs + $("div.onoff").each(function () { + var section = this.id; + var val = ""; + if ($(this).find(".is-appear").length) { + val = "1" + } else { + val = "0" + } + settings.push({ + name: section, + value: val + }); + }); + + url += "?" + decodeURIComponent($.param(settings)); + if ($("#saveDefaults").prop("checked")) { + url += "&save_defaults=1"; + } + // console.log(url); + location.href = url; + + fadeOutPage(function () { + window.history.back() + }) + } else { + // Close any open submenus + $(".qa.is-opened").each(function () { openAnswer(this); }); + } + } + + function clickToggle(e) { + var toggleOptions = $(e).find(".toggle"); + if ($(e).find(".is-single-option").length) { // Single-option submenu + // Deselect all submenu options + $(e).closest(".l-qa").find(".toggle").removeClass("is-appear"); + $(e).closest(".l-qa").find(".toggle").addClass("is-hidden"); + // Then set the current one as the active setting + toggleOptions.addClass("is-appear"); + toggleOptions.removeClass("is-hidden"); + } else { // Multi-option submenu + toggleOptions.toggleClass("is-appear"); + toggleOptions.toggleClass("is-hidden"); + } + } + + function getParams(url) { + var regex = /[?&]([^=#]+)=([^&#]*)/g, + params = {}, + match; + while(match = regex.exec(url)) { + params[match[1]] = match[2]; + } + return params; + } + + function setSettings() { + // Get settings from the URL GET parameters + const settings = getParams(document.URL); + + // Set Toggles + $("ul.l-grid").each(function () { + var section = this.id; + var section_setting = decodeURIComponent(settings[section]); + + var children = $(this).children("li"); + for (var i = 0; i < children.length; i++) { + var child = children[i]; + var e = $(child).find("img.toggle"); + if (section_setting.split(",").includes(child.getAttribute("val"))) { + e.addClass("is-appear"); + e.removeClass("is-hidden"); + } else { + e.removeClass("is-appear"); + e.addClass("is-hidden"); + }; + } + }); + + // Set OnOffs + $("div.onoff").each(function () { + var section = this.id; + var section_setting = decodeURIComponent(settings[section]); + var e = $(this).find("img.toggle"); + if (section_setting == "1") { + e.addClass("is-appear"); + e.removeClass("is-hidden"); + } else { + e.removeClass("is-appear"); + e.addClass("is-hidden"); + }; + }); + } + + function resetSubmenu() { + // Resets any open or focused submenus to the default values + $("[default*='is-appear']").each(function () { + if (isSubmenuFocused(this)) { + $(this).addClass("is-appear"); + $(this).removeClass("is-hidden"); + } + }); + $("[default*='is-hidden']").each(function() { + if (isSubmenuFocused(this)) { + $(this).removeClass("is-appear"); + $(this).addClass("is-hidden"); + } + }); + } + + function isSubmenuFocused(elem) { + // Return true if the element is in a submenu which is either focused or opened + return ( + $(elem).closest(".l-qa").children(".is-opened, .is-focused").length + || $(elem).closest(".is-focused").length + ) + } + + function resetAllSubmenus() { + // Resets all submenus to the default values + if (confirm("Are you sure that you want to reset all menu settings to the default?")) { + $("[default*='is-appear']").addClass("is-appear"); + $("[default*='is-appear']").removeClass("is-hidden"); + + $("[default*='is-hidden']").removeClass("is-appear"); + $("[default*='is-hidden']").addClass("is-hidden"); + } + } + + function setHelpText(text) { + // Modify the help text in the footer + $("#help-text").text(text); + } + + function toggleSaveDefaults() { + // Change the status of the Save Defaults checkbox + var saveDefaultsCheckbox = $("#saveDefaults"); + saveDefaultsCheckbox.prop( + "checked", + !saveDefaultsCheckbox.prop("checked") + ); + } + </script> + <script> + window.onload = setSettings; + </script> + </body> + +</html> diff --git a/src/templates/pummel_delay.svg b/src/templates/pummel_delay.svg index f012002..a8e38c7 100644 --- a/src/templates/pummel_delay.svg +++ b/src/templates/pummel_delay.svg @@ -1,116 +1,116 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg - width="80.0px" - height="80.0px" - viewBox="0 0 80.0 80.0" - version="1.1" - id="SVGRoot" - sodipodi:docname="pummel_delay.svg" - inkscape:version="1.1 (c68e22c387, 2021-05-23)" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns="http://www.w3.org/2000/svg" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:dc="http://purl.org/dc/elements/1.1/"> - <defs - id="defs2629" /> - <sodipodi:namedview - id="base" - pagecolor="#000000" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="5.6568543" - inkscape:cx="-2.032932" - inkscape:cy="40.923805" - inkscape:document-units="px" - inkscape:current-layer="layer1" - inkscape:document-rotation="0" - showgrid="true" - inkscape:window-width="1920" - inkscape:window-height="1137" - inkscape:window-x="-8" - inkscape:window-y="-8" - inkscape:window-maximized="1" - inkscape:pagecheckerboard="0"> - <inkscape:grid - type="xygrid" - id="grid3199" /> - </sodipodi:namedview> - <metadata - id="metadata2632"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:groupmode="layer" - id="layer2" - inkscape:label="Layer 2" - sodipodi:insensitive="true" - style="display:none"> - <image - width="71.856041" - height="75.363312" - preserveAspectRatio="none" - xlink:href="data:image/png;base64,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 KQOEAAAAAElFTkSuQmCC " - id="image132" - x="7.143959" - y="5" /> - </g> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1"> - <g - id="g18" - transform="translate(-15,-30)"> - <circle - style="fill:none;stroke:#ffffff;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - id="path3261" - cx="30.075506" - cy="44.96954" - r="10" /> - <path - style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="M 30,45 V 35" - id="path3263" /> - <path - style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 30,45 5,-5" - id="path3265" /> - </g> - <path - style="fill:none;stroke:#ffffff;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 44,19 c -3,4 -5,7 -5,11 0,3 9,17 17,17" - id="path47" - sodipodi:nodetypes="ccc" /> - <path - style="fill:none;stroke:#ffffff;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" - d="m 59,50 c -2.821595,-3.86007 -3,-2 -3,-7 0,-3 4,-7 7,-7 9.167808,0.161683 10,9 10,13 C 72,57 55,63 45,63 38,63 22,43 22,39 22,28 37,10 49,10 c 13,0 27,14 27,26 0,5 -1,5 -3,7" - id="path2904" - sodipodi:nodetypes="ccccccccc" /> - <path - style="fill:none;stroke:#ffffff;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" - d="M 23,43 12,54 c -3.0180618,3.403516 -2.4491226,5.976976 -1,8 l 12,12 c 3,2 8,1 9,0 L 43,63" - id="path590" - sodipodi:nodetypes="cccccc" /> - <path - style="fill:none;stroke:#ffffff;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" - d="m 22,56 8,8" - id="path2442" /> - <path - style="fill:none;stroke:#ffffff;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" - d="M 24,67 18,61" - id="path2510" /> - </g> -</svg> +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + width="80.0px" + height="80.0px" + viewBox="0 0 80.0 80.0" + version="1.1" + id="SVGRoot" + sodipodi:docname="pummel_delay.svg" + inkscape:version="1.1 (c68e22c387, 2021-05-23)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> + <defs + id="defs2629" /> + <sodipodi:namedview + id="base" + pagecolor="#000000" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:zoom="5.6568543" + inkscape:cx="-2.032932" + inkscape:cy="40.923805" + inkscape:document-units="px" + inkscape:current-layer="layer1" + inkscape:document-rotation="0" + showgrid="true" + inkscape:window-width="1920" + inkscape:window-height="1137" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" + inkscape:pagecheckerboard="0"> + <inkscape:grid + type="xygrid" + id="grid3199" /> + </sodipodi:namedview> + <metadata + id="metadata2632"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="Layer 2" + sodipodi:insensitive="true" + style="display:none"> + <image + width="71.856041" + height="75.363312" + preserveAspectRatio="none" + xlink:href="data:image/png;base64,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 KQOEAAAAAElFTkSuQmCC " + id="image132" + x="7.143959" + y="5" /> + </g> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <g + id="g18" + transform="translate(-15,-30)"> + <circle + style="fill:none;stroke:#ffffff;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3261" + cx="30.075506" + cy="44.96954" + r="10" /> + <path + style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 30,45 V 35" + id="path3263" /> + <path + style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 30,45 5,-5" + id="path3265" /> + </g> + <path + style="fill:none;stroke:#ffffff;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 44,19 c -3,4 -5,7 -5,11 0,3 9,17 17,17" + id="path47" + sodipodi:nodetypes="ccc" /> + <path + style="fill:none;stroke:#ffffff;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="m 59,50 c -2.821595,-3.86007 -3,-2 -3,-7 0,-3 4,-7 7,-7 9.167808,0.161683 10,9 10,13 C 72,57 55,63 45,63 38,63 22,43 22,39 22,28 37,10 49,10 c 13,0 27,14 27,26 0,5 -1,5 -3,7" + id="path2904" + sodipodi:nodetypes="ccccccccc" /> + <path + style="fill:none;stroke:#ffffff;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="M 23,43 12,54 c -3.0180618,3.403516 -2.4491226,5.976976 -1,8 l 12,12 c 3,2 8,1 9,0 L 43,63" + id="path590" + sodipodi:nodetypes="cccccc" /> + <path + style="fill:none;stroke:#ffffff;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="m 22,56 8,8" + id="path2442" /> + <path + style="fill:none;stroke:#ffffff;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="M 24,67 18,61" + id="path2510" /> + </g> +</svg> diff --git a/src/templates/throw_delay.svg b/src/templates/throw_delay.svg index 9cdc69e..927e7b2 100644 --- a/src/templates/throw_delay.svg +++ b/src/templates/throw_delay.svg @@ -1,127 +1,127 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg - width="80.0px" - height="80.0px" - viewBox="0 0 80.0 80.0" - version="1.1" - id="SVGRoot" - sodipodi:docname="throw_delay.svg" - inkscape:version="1.1 (c68e22c387, 2021-05-23)" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns="http://www.w3.org/2000/svg" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:dc="http://purl.org/dc/elements/1.1/"> - <defs - id="defs2629" /> - <sodipodi:namedview - id="base" - pagecolor="#000000" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="5.6" - inkscape:cx="36.160714" - inkscape:cy="31.160714" - inkscape:document-units="px" - inkscape:current-layer="layer1" - inkscape:document-rotation="0" - showgrid="true" - inkscape:window-width="1920" - inkscape:window-height="1137" - inkscape:window-x="-8" - inkscape:window-y="-8" - inkscape:window-maximized="1" - inkscape:pagecheckerboard="0"> - <inkscape:grid - type="xygrid" - id="grid3199" /> - </sodipodi:namedview> - <metadata - id="metadata2632"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1"> - <circle - style="fill:none;stroke:#ffffff;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - id="path3261" - cx="45.075508" - cy="44.96954" - r="10" /> - <path - style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="M 45,45 V 35" - id="path3263" /> - <path - style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 45,45 5,-5" - id="path3265" /> - </g> - <g - inkscape:groupmode="layer" - id="layer2" - inkscape:label="Layer 2" - style="display:inline"> - <image - width="512" - height="512" - preserveAspectRatio="none" - style="image-rendering:optimizeSpeed" - xlink:href="data:image/png;base64,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 AfsIk1iEi8i3AAAAAElFTkSuQmCC " - id="image2221" - x="-240.21637" - y="-250.94923" /> - <image - width="512" - height="512" - preserveAspectRatio="none" - style="opacity:0.884146;fill:#800000;image-rendering:optimizeSpeed" - xlink:href="data:image/png;base64,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 AfsIk1iEi8i3AAAAAElFTkSuQmCC " - id="image2233" - x="-172.15733" - y="-373.1777" /> - <g - id="g2453" - transform="matrix(0.1476827,0,0,0.1476827,28.063629,58.273139)" - style="fill:#ffffff;fill-opacity:1"> - <path - style="fill:#ffffff;fill-opacity:1" - d="m 177.83434,-320.9277 -22.49167,-22.75 22.75,22.49167 c 21.13932,20.89928 23.20102,23.00833 22.49167,23.00833 -0.14209,0 -10.37959,-10.2375 -22.75,-22.75 z M 13.1117,-326.87069 c 0.97297,-0.25354 2.32297,-0.23687 3,0.0371 0.67703,0.27392 -0.11903,0.48137 -1.76903,0.46099 -1.65,-0.0204 -2.20394,-0.2445 -1.23097,-0.49804 z" - id="path2465" /> - <path - style="fill:#ffffff;fill-opacity:1" - d="m 265.34267,58.8223 c 13.19141,-13.2 24.20937,-24 24.48437,-24 0.275,0 -10.29296,10.8 -23.48437,24 -13.19141,13.2 -24.20937,24 -24.48437,24 -0.275,0 10.29296,-10.8 23.48437,-24 z m -21.00426,-313.25 -43.99574,-44.25 44.25,43.99574 c 24.3375,24.19765 44.25,44.11015 44.25,44.25 0,0.70358 -3.61144,-2.86659 -44.50426,-43.99574 z m -151.54957,-18.5 -3.44617,-3.75 3.75,3.44617 c 2.0625,1.89539 3.75,3.58289 3.75,3.75 0,0.76362 -0.84622,0.0443 -4.05383,-3.44617 z" - id="path2463" /> - <path - style="fill:#ffffff;fill-opacity:1" - d="m 9.33329,-223.4277 -19.99062,-20.25 20.25,19.99062 c 11.1375,10.99485 20.25,20.10735 20.25,20.25 0,0.7108 -1.93525,-1.17549 -20.50938,-19.99062 z" - id="path2461" /> - <path - style="fill:#ffffff;fill-opacity:1" - d="m -85.775566,-3.6777 c 0.002,-6.6 0.16382,-9.16987 0.35965,-5.71081 0.19583,3.45905 0.194213,8.85905 -0.0036,12 -0.197803,3.14094 -0.358027,0.31081 -0.35605,-6.28919 z m -0.885514,-49.75 -49.99625,-50.25 50.25,49.99625 c 46.70792,46.47205 50.6991,50.50375 49.99625,50.50375 -0.13956,0 -22.75206,-22.6125 -50.25,-50.25 z m 19.9975,-113 -29.99375,-30.25 30.25,29.99375 c 28.11229,27.87415 30.70017,30.50625 29.99375,30.50625 -0.14094,0 -13.75344,-13.6125 -30.25,-30.25 z m 179.25625,-193.41228 c 0.6875,-0.27741 1.8125,-0.27741 2.5,0 0.6875,0.27741 0.125,0.50439 -1.25,0.50439 -1.375,0 -1.9375,-0.22698 -1.25,-0.50439 z" - id="path2459" /> - <path - style="fill:#ffffff;fill-opacity:1" - d="m 40.66541,125.07268 c 18.32751,-0.15264 48.02751,-0.15255 66,1.9e-4 17.97249,0.15275 2.97726,0.27763 -33.32274,0.27752 -36.3,-1.1e-4 -51.00477,-0.12508 -32.67726,-0.27771 z" - id="path2457" /> - <path - style="fill:#ffffff;fill-opacity:1" - d="m -13.2356,123.27051 c -38.61609,-6.99285 -70.305205,-31.54668 -87.46468,-67.77069 -8.90855,-18.80614 -11.43396,-35.14607 -11.4485,-74.0744 l -0.009,-22.89689 -23.75,-24.15125 c -14.51304,-14.75823 -25.01854,-26.298 -27.01166,-29.67096 -14.48601,-24.51465 -10.8692,-54.30964 8.97813,-73.9611 7.1312,-7.06083 19.47416,-14.12467 27.04457,-15.47753 4.6412,-0.8294 5.25632,-1.72617 3.26678,-4.76258 -3.06652,-4.6801 -6.54508,-15.1984 -7.72483,-23.35792 -3.89332,-26.9276 13.91946,-54.89072 40.63063,-63.78335 11.792941,-3.92608 27.7618,-3.68524 39.19131,0.59109 3.03219,1.13448 5.71391,1.86187 5.95937,1.6164 0.24546,-0.24546 1.1183,-3.10191 1.93964,-6.34766 3.8861,-15.35695 16.02707,-31.25487 29.16211,-38.18614 20.65084,-10.89731 45.83715,-9.14388 64.56294,4.49477 3.40146,2.4774 4.87884,3.08021 5.20664,2.12443 0.25171,-0.73394 1.37191,-4.0039 2.48935,-7.26658 5.64699,-16.48813 19.83504,-30.77956 36.33224,-36.59697 4.37705,-1.54348 9.48547,-2.34052 17.22278,-2.6872 12.9647,-0.5809 20.17191,0.92444 31,6.47484 6.36832,3.26435 13.21359,9.77654 75.85594,72.16481 74.25477,73.9536 82.12607,82.20537 90.98496,95.3829 20.49125,30.48058 29.8237,61.05096 29.8237,97.69377 0,37.54442 -10.39645,70.43549 -31.92483,101 -8.78297,12.46946 -71.72291,75.7536 -83.781,84.23917 -18.98786,13.36223 -39.01575,21.48796 -62.4227,25.32618 -12.29951,2.01685 -162.88942,1.91553 -174.11434,-0.11714 z M 167.84267,95.66544 c 13.00883,-3.58796 26.12766,-9.43085 36.66147,-16.32835 7.11327,-4.65775 15.48569,-12.44634 42.89208,-39.90112 36.27435,-36.33839 41.61946,-42.81883 51.2318,-62.11367 4.3762,-8.78432 10.52931,-27.2059 12.24201,-36.6509 7.19984,-39.70473 -1.75087,-80.60585 -24.79605,-113.30787 -6.86507,-9.74181 -148.42805,-152.33641 -154.76776,-155.89561 -13.52398,-7.59254 -30.53569,-5.42062 -41.15103,5.25383 -10.05024,10.10622 -12.54017,24.84956 -6.48524,38.40043 1.0322,2.31007 4.92543,7.47878 8.65161,11.48604 5.87698,6.32028 6.8438,7.89736 7.2949,11.89948 1.14661,10.17276 -7.75999,16.92979 -17.45869,13.2451 -1.54831,-0.58823 -13.1651,-11.31812 -25.8151,-23.84421 -25.19323,-24.94645 -28.01185,-26.95633 -39.04496,-27.84196 -15.10311,-1.21234 -29.26024,7.67126 -34.54398,21.67632 -1.69243,4.48594 -2.02141,7.14714 -1.67871,13.57935 0.68198,12.80006 2.48805,15.44584 28.32878,41.5 12.27363,12.375 22.81736,23.82981 23.43052,25.45513 2.49373,6.61024 -1.68946,14.45063 -8.79774,16.48926 -7.20346,2.06592 -8.46385,1.08726 -43.69391,-33.92736 -19.91433,-19.79256 -34.59872,-33.59856 -37.03172,-34.81658 -9.27783,-4.64471 -21.724692,-4.6611 -30.78177,-0.0405 -5.065503,2.58423 -12.005185,9.58424 -14.65931,14.78675 -4.98422,9.76989 -4.70653,22.57716 0.69499,32.05332 1.440534,2.52719 16.969332,18.99452 35.42143,37.56222 l 32.85638,33.0622 v 4.78349 c 0,5.81973 -2.30635,10.03503 -6.60719,12.07591 -3.74999,1.77949 -7.1903,1.92385 -11.03273,0.46296 -1.52356,-0.57925 -11.79036,-9.95835 -22.815096,-20.84244 -23.10878,-22.81396 -24.399475,-23.60436 -38.544984,-23.60436 -7.10469,0 -9.42713,0.43467 -14.14796,2.64796 -17.40512,8.16013 -24.17927,27.98266 -15.63289,45.74494 1.63049,3.38871 16.30771,18.77052 55.498786,58.16305 L -33.15733,0.4342 v 5.15925 c 0,4.32805 -0.49257,5.72026 -3.05734,8.64138 -3.49284,3.97813 -9.46969,5.48633 -14.35736,3.62294 -1.60308,-0.61116 -10.32378,-8.40666 -19.37933,-17.32334 l -16.464643,-16.21213 0.602934,17 c 0.620571,17.49727 2.04533,26.57739 5.706649,36.36897 9.52461,25.47196 30.59438,46.63873 55.36009,55.61493 15.16012,5.49471 13.55553,5.41277 101.589,5.18775 l 81.5,-0.20832 z" - id="path2455" /> - </g> - </g> -</svg> +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + width="80.0px" + height="80.0px" + viewBox="0 0 80.0 80.0" + version="1.1" + id="SVGRoot" + sodipodi:docname="throw_delay.svg" + inkscape:version="1.1 (c68e22c387, 2021-05-23)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> + <defs + id="defs2629" /> + <sodipodi:namedview + id="base" + pagecolor="#000000" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:zoom="5.6" + inkscape:cx="36.160714" + inkscape:cy="31.160714" + inkscape:document-units="px" + inkscape:current-layer="layer1" + inkscape:document-rotation="0" + showgrid="true" + inkscape:window-width="1920" + inkscape:window-height="1137" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" + inkscape:pagecheckerboard="0"> + <inkscape:grid + type="xygrid" + id="grid3199" /> + </sodipodi:namedview> + <metadata + id="metadata2632"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <circle + style="fill:none;stroke:#ffffff;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3261" + cx="45.075508" + cy="44.96954" + r="10" /> + <path + style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 45,45 V 35" + id="path3263" /> + <path + style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 45,45 5,-5" + id="path3265" /> + </g> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="Layer 2" + style="display:inline"> + <image + width="512" + height="512" + preserveAspectRatio="none" + style="image-rendering:optimizeSpeed" + xlink:href="data:image/png;base64,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 AfsIk1iEi8i3AAAAAElFTkSuQmCC " + id="image2221" + x="-240.21637" + y="-250.94923" /> + <image + width="512" + height="512" + preserveAspectRatio="none" + style="opacity:0.884146;fill:#800000;image-rendering:optimizeSpeed" + xlink:href="data:image/png;base64,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 AfsIk1iEi8i3AAAAAElFTkSuQmCC " + id="image2233" + x="-172.15733" + y="-373.1777" /> + <g + id="g2453" + transform="matrix(0.1476827,0,0,0.1476827,28.063629,58.273139)" + style="fill:#ffffff;fill-opacity:1"> + <path + style="fill:#ffffff;fill-opacity:1" + d="m 177.83434,-320.9277 -22.49167,-22.75 22.75,22.49167 c 21.13932,20.89928 23.20102,23.00833 22.49167,23.00833 -0.14209,0 -10.37959,-10.2375 -22.75,-22.75 z M 13.1117,-326.87069 c 0.97297,-0.25354 2.32297,-0.23687 3,0.0371 0.67703,0.27392 -0.11903,0.48137 -1.76903,0.46099 -1.65,-0.0204 -2.20394,-0.2445 -1.23097,-0.49804 z" + id="path2465" /> + <path + style="fill:#ffffff;fill-opacity:1" + d="m 265.34267,58.8223 c 13.19141,-13.2 24.20937,-24 24.48437,-24 0.275,0 -10.29296,10.8 -23.48437,24 -13.19141,13.2 -24.20937,24 -24.48437,24 -0.275,0 10.29296,-10.8 23.48437,-24 z m -21.00426,-313.25 -43.99574,-44.25 44.25,43.99574 c 24.3375,24.19765 44.25,44.11015 44.25,44.25 0,0.70358 -3.61144,-2.86659 -44.50426,-43.99574 z m -151.54957,-18.5 -3.44617,-3.75 3.75,3.44617 c 2.0625,1.89539 3.75,3.58289 3.75,3.75 0,0.76362 -0.84622,0.0443 -4.05383,-3.44617 z" + id="path2463" /> + <path + style="fill:#ffffff;fill-opacity:1" + d="m 9.33329,-223.4277 -19.99062,-20.25 20.25,19.99062 c 11.1375,10.99485 20.25,20.10735 20.25,20.25 0,0.7108 -1.93525,-1.17549 -20.50938,-19.99062 z" + id="path2461" /> + <path + style="fill:#ffffff;fill-opacity:1" + d="m -85.775566,-3.6777 c 0.002,-6.6 0.16382,-9.16987 0.35965,-5.71081 0.19583,3.45905 0.194213,8.85905 -0.0036,12 -0.197803,3.14094 -0.358027,0.31081 -0.35605,-6.28919 z m -0.885514,-49.75 -49.99625,-50.25 50.25,49.99625 c 46.70792,46.47205 50.6991,50.50375 49.99625,50.50375 -0.13956,0 -22.75206,-22.6125 -50.25,-50.25 z m 19.9975,-113 -29.99375,-30.25 30.25,29.99375 c 28.11229,27.87415 30.70017,30.50625 29.99375,30.50625 -0.14094,0 -13.75344,-13.6125 -30.25,-30.25 z m 179.25625,-193.41228 c 0.6875,-0.27741 1.8125,-0.27741 2.5,0 0.6875,0.27741 0.125,0.50439 -1.25,0.50439 -1.375,0 -1.9375,-0.22698 -1.25,-0.50439 z" + id="path2459" /> + <path + style="fill:#ffffff;fill-opacity:1" + d="m 40.66541,125.07268 c 18.32751,-0.15264 48.02751,-0.15255 66,1.9e-4 17.97249,0.15275 2.97726,0.27763 -33.32274,0.27752 -36.3,-1.1e-4 -51.00477,-0.12508 -32.67726,-0.27771 z" + id="path2457" /> + <path + style="fill:#ffffff;fill-opacity:1" + d="m -13.2356,123.27051 c -38.61609,-6.99285 -70.305205,-31.54668 -87.46468,-67.77069 -8.90855,-18.80614 -11.43396,-35.14607 -11.4485,-74.0744 l -0.009,-22.89689 -23.75,-24.15125 c -14.51304,-14.75823 -25.01854,-26.298 -27.01166,-29.67096 -14.48601,-24.51465 -10.8692,-54.30964 8.97813,-73.9611 7.1312,-7.06083 19.47416,-14.12467 27.04457,-15.47753 4.6412,-0.8294 5.25632,-1.72617 3.26678,-4.76258 -3.06652,-4.6801 -6.54508,-15.1984 -7.72483,-23.35792 -3.89332,-26.9276 13.91946,-54.89072 40.63063,-63.78335 11.792941,-3.92608 27.7618,-3.68524 39.19131,0.59109 3.03219,1.13448 5.71391,1.86187 5.95937,1.6164 0.24546,-0.24546 1.1183,-3.10191 1.93964,-6.34766 3.8861,-15.35695 16.02707,-31.25487 29.16211,-38.18614 20.65084,-10.89731 45.83715,-9.14388 64.56294,4.49477 3.40146,2.4774 4.87884,3.08021 5.20664,2.12443 0.25171,-0.73394 1.37191,-4.0039 2.48935,-7.26658 5.64699,-16.48813 19.83504,-30.77956 36.33224,-36.59697 4.37705,-1.54348 9.48547,-2.34052 17.22278,-2.6872 12.9647,-0.5809 20.17191,0.92444 31,6.47484 6.36832,3.26435 13.21359,9.77654 75.85594,72.16481 74.25477,73.9536 82.12607,82.20537 90.98496,95.3829 20.49125,30.48058 29.8237,61.05096 29.8237,97.69377 0,37.54442 -10.39645,70.43549 -31.92483,101 -8.78297,12.46946 -71.72291,75.7536 -83.781,84.23917 -18.98786,13.36223 -39.01575,21.48796 -62.4227,25.32618 -12.29951,2.01685 -162.88942,1.91553 -174.11434,-0.11714 z M 167.84267,95.66544 c 13.00883,-3.58796 26.12766,-9.43085 36.66147,-16.32835 7.11327,-4.65775 15.48569,-12.44634 42.89208,-39.90112 36.27435,-36.33839 41.61946,-42.81883 51.2318,-62.11367 4.3762,-8.78432 10.52931,-27.2059 12.24201,-36.6509 7.19984,-39.70473 -1.75087,-80.60585 -24.79605,-113.30787 -6.86507,-9.74181 -148.42805,-152.33641 -154.76776,-155.89561 -13.52398,-7.59254 -30.53569,-5.42062 -41.15103,5.25383 -10.05024,10.10622 -12.54017,24.84956 -6.48524,38.40043 1.0322,2.31007 4.92543,7.47878 8.65161,11.48604 5.87698,6.32028 6.8438,7.89736 7.2949,11.89948 1.14661,10.17276 -7.75999,16.92979 -17.45869,13.2451 -1.54831,-0.58823 -13.1651,-11.31812 -25.8151,-23.84421 -25.19323,-24.94645 -28.01185,-26.95633 -39.04496,-27.84196 -15.10311,-1.21234 -29.26024,7.67126 -34.54398,21.67632 -1.69243,4.48594 -2.02141,7.14714 -1.67871,13.57935 0.68198,12.80006 2.48805,15.44584 28.32878,41.5 12.27363,12.375 22.81736,23.82981 23.43052,25.45513 2.49373,6.61024 -1.68946,14.45063 -8.79774,16.48926 -7.20346,2.06592 -8.46385,1.08726 -43.69391,-33.92736 -19.91433,-19.79256 -34.59872,-33.59856 -37.03172,-34.81658 -9.27783,-4.64471 -21.724692,-4.6611 -30.78177,-0.0405 -5.065503,2.58423 -12.005185,9.58424 -14.65931,14.78675 -4.98422,9.76989 -4.70653,22.57716 0.69499,32.05332 1.440534,2.52719 16.969332,18.99452 35.42143,37.56222 l 32.85638,33.0622 v 4.78349 c 0,5.81973 -2.30635,10.03503 -6.60719,12.07591 -3.74999,1.77949 -7.1903,1.92385 -11.03273,0.46296 -1.52356,-0.57925 -11.79036,-9.95835 -22.815096,-20.84244 -23.10878,-22.81396 -24.399475,-23.60436 -38.544984,-23.60436 -7.10469,0 -9.42713,0.43467 -14.14796,2.64796 -17.40512,8.16013 -24.17927,27.98266 -15.63289,45.74494 1.63049,3.38871 16.30771,18.77052 55.498786,58.16305 L -33.15733,0.4342 v 5.15925 c 0,4.32805 -0.49257,5.72026 -3.05734,8.64138 -3.49284,3.97813 -9.46969,5.48633 -14.35736,3.62294 -1.60308,-0.61116 -10.32378,-8.40666 -19.37933,-17.32334 l -16.464643,-16.21213 0.602934,17 c 0.620571,17.49727 2.04533,26.57739 5.706649,36.36897 9.52461,25.47196 30.59438,46.63873 55.36009,55.61493 15.16012,5.49471 13.55553,5.41277 101.589,5.18775 l 81.5,-0.20832 z" + id="path2455" /> + </g> + </g> +</svg> diff --git a/src/templates/throw_state.svg b/src/templates/throw_state.svg index 99f815f..e6ea8cc 100644 --- a/src/templates/throw_state.svg +++ b/src/templates/throw_state.svg @@ -1,201 +1,201 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg - width="80.0px" - height="80.0px" - viewBox="0 0 80.0 80.0" - version="1.1" - id="SVGRoot" - sodipodi:docname="throw_state.svg" - inkscape:version="1.1 (c68e22c387, 2021-05-23)" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns="http://www.w3.org/2000/svg" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:dc="http://purl.org/dc/elements/1.1/"> - <defs - id="defs2629" /> - <sodipodi:namedview - id="base" - pagecolor="#000000" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="5.6" - inkscape:cx="35.625" - inkscape:cy="30.267857" - inkscape:document-units="px" - inkscape:current-layer="layer1" - inkscape:document-rotation="0" - showgrid="true" - inkscape:window-width="1920" - inkscape:window-height="1137" - inkscape:window-x="-8" - inkscape:window-y="-8" - inkscape:window-maximized="1" - inkscape:pagecheckerboard="0"> - <inkscape:grid - type="xygrid" - id="grid3199" /> - </sodipodi:namedview> - <metadata - id="metadata2632"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1"> - <path - style="fill:none;stroke:#ffffff;stroke-width:3.16228;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="M 45,58 V 33" - id="path3263" /> - <path - style="fill:none;stroke:#ffffff;stroke-width:3.09839;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="M 33,45 H 57" - id="path3263-9" /> - <g - id="g23" - transform="translate(4)"> - <g - id="g153"> - <path - style="fill:none;stroke:#ffffff;stroke-width:2.828;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 57,40 3,4.795832" - id="path3263-9-9" - inkscape:transform-center-x="1.5" - inkscape:transform-center-y="-2.397916" - transform="translate(-5)" /> - <path - style="fill:none;stroke:#ffffff;stroke-width:2.828;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 52,50 3,-4.795832" - id="path3263-9-9-4" - inkscape:transform-center-x="1.5" - inkscape:transform-center-y="2.397916" /> - </g> - </g> - <g - id="g23-3" - transform="matrix(-1,0,0,1,86,0.204168)"> - <g - id="g153-6"> - <path - style="fill:none;stroke:#ffffff;stroke-width:2.828;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 57,40 3,4.795832" - id="path3263-9-9-8" - inkscape:transform-center-x="1.5" - inkscape:transform-center-y="-2.397916" - transform="translate(-5)" /> - <path - style="fill:none;stroke:#ffffff;stroke-width:2.828;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 52,50 3,-4.795832" - id="path3263-9-9-4-6" - inkscape:transform-center-x="1.5" - inkscape:transform-center-y="2.397916" /> - </g> - </g> - <g - id="g23-2" - transform="rotate(-90,43.102084,42.897916)"> - <g - id="g153-8"> - <path - style="fill:none;stroke:#ffffff;stroke-width:2.828;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 57,40 3,4.795832" - id="path3263-9-9-1" - inkscape:transform-center-x="1.5" - inkscape:transform-center-y="-2.397916" - transform="translate(-5)" /> - <path - style="fill:none;stroke:#ffffff;stroke-width:2.828;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 52,50 3,-4.795832" - id="path3263-9-9-4-9" - inkscape:transform-center-x="1.5" - inkscape:transform-center-y="2.397916" /> - </g> - </g> - <g - id="g23-8" - transform="rotate(90,42.397916,47.397916)"> - <g - id="g153-4"> - <path - style="fill:none;stroke:#ffffff;stroke-width:2.828;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 57,40 3,4.795832" - id="path3263-9-9-0" - inkscape:transform-center-x="1.5" - inkscape:transform-center-y="-2.397916" - transform="translate(-5)" /> - <path - style="fill:none;stroke:#ffffff;stroke-width:2.828;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 52,50 3,-4.795832" - id="path3263-9-9-4-8" - inkscape:transform-center-x="1.5" - inkscape:transform-center-y="2.397916" /> - </g> - </g> - </g> - <g - inkscape:groupmode="layer" - id="layer2" - inkscape:label="Layer 2" - style="display:inline"> - <image - width="512" - height="512" - preserveAspectRatio="none" - style="image-rendering:optimizeSpeed" - xlink:href="data:image/png;base64,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 AfsIk1iEi8i3AAAAAElFTkSuQmCC " - id="image2221" - x="-240.21637" - y="-250.94923" /> - <image - width="512" - height="512" - preserveAspectRatio="none" - style="opacity:0.884146;fill:#800000;image-rendering:optimizeSpeed" - xlink:href="data:image/png;base64,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 AfsIk1iEi8i3AAAAAElFTkSuQmCC " - id="image2233" - x="-172.15733" - y="-373.1777" /> - <g - id="g2453" - transform="matrix(0.1476827,0,0,0.1476827,28.063629,58.273139)" - style="fill:#ffffff;fill-opacity:1"> - <path - style="fill:#ffffff;fill-opacity:1" - d="m 177.83434,-320.9277 -22.49167,-22.75 22.75,22.49167 c 21.13932,20.89928 23.20102,23.00833 22.49167,23.00833 -0.14209,0 -10.37959,-10.2375 -22.75,-22.75 z M 13.1117,-326.87069 c 0.97297,-0.25354 2.32297,-0.23687 3,0.0371 0.67703,0.27392 -0.11903,0.48137 -1.76903,0.46099 -1.65,-0.0204 -2.20394,-0.2445 -1.23097,-0.49804 z" - id="path2465" /> - <path - style="fill:#ffffff;fill-opacity:1" - d="m 265.34267,58.8223 c 13.19141,-13.2 24.20937,-24 24.48437,-24 0.275,0 -10.29296,10.8 -23.48437,24 -13.19141,13.2 -24.20937,24 -24.48437,24 -0.275,0 10.29296,-10.8 23.48437,-24 z m -21.00426,-313.25 -43.99574,-44.25 44.25,43.99574 c 24.3375,24.19765 44.25,44.11015 44.25,44.25 0,0.70358 -3.61144,-2.86659 -44.50426,-43.99574 z m -151.54957,-18.5 -3.44617,-3.75 3.75,3.44617 c 2.0625,1.89539 3.75,3.58289 3.75,3.75 0,0.76362 -0.84622,0.0443 -4.05383,-3.44617 z" - id="path2463" /> - <path - style="fill:#ffffff;fill-opacity:1" - d="m 9.33329,-223.4277 -19.99062,-20.25 20.25,19.99062 c 11.1375,10.99485 20.25,20.10735 20.25,20.25 0,0.7108 -1.93525,-1.17549 -20.50938,-19.99062 z" - id="path2461" /> - <path - style="fill:#ffffff;fill-opacity:1" - d="m -85.775566,-3.6777 c 0.002,-6.6 0.16382,-9.16987 0.35965,-5.71081 0.19583,3.45905 0.194213,8.85905 -0.0036,12 -0.197803,3.14094 -0.358027,0.31081 -0.35605,-6.28919 z m -0.885514,-49.75 -49.99625,-50.25 50.25,49.99625 c 46.70792,46.47205 50.6991,50.50375 49.99625,50.50375 -0.13956,0 -22.75206,-22.6125 -50.25,-50.25 z m 19.9975,-113 -29.99375,-30.25 30.25,29.99375 c 28.11229,27.87415 30.70017,30.50625 29.99375,30.50625 -0.14094,0 -13.75344,-13.6125 -30.25,-30.25 z m 179.25625,-193.41228 c 0.6875,-0.27741 1.8125,-0.27741 2.5,0 0.6875,0.27741 0.125,0.50439 -1.25,0.50439 -1.375,0 -1.9375,-0.22698 -1.25,-0.50439 z" - id="path2459" /> - <path - style="fill:#ffffff;fill-opacity:1" - d="m 40.66541,125.07268 c 18.32751,-0.15264 48.02751,-0.15255 66,1.9e-4 17.97249,0.15275 2.97726,0.27763 -33.32274,0.27752 -36.3,-1.1e-4 -51.00477,-0.12508 -32.67726,-0.27771 z" - id="path2457" /> - <path - style="fill:#ffffff;fill-opacity:1" - d="m -13.2356,123.27051 c -38.61609,-6.99285 -70.305205,-31.54668 -87.46468,-67.77069 -8.90855,-18.80614 -11.43396,-35.14607 -11.4485,-74.0744 l -0.009,-22.89689 -23.75,-24.15125 c -14.51304,-14.75823 -25.01854,-26.298 -27.01166,-29.67096 -14.48601,-24.51465 -10.8692,-54.30964 8.97813,-73.9611 7.1312,-7.06083 19.47416,-14.12467 27.04457,-15.47753 4.6412,-0.8294 5.25632,-1.72617 3.26678,-4.76258 -3.06652,-4.6801 -6.54508,-15.1984 -7.72483,-23.35792 -3.89332,-26.9276 13.91946,-54.89072 40.63063,-63.78335 11.792941,-3.92608 27.7618,-3.68524 39.19131,0.59109 3.03219,1.13448 5.71391,1.86187 5.95937,1.6164 0.24546,-0.24546 1.1183,-3.10191 1.93964,-6.34766 3.8861,-15.35695 16.02707,-31.25487 29.16211,-38.18614 20.65084,-10.89731 45.83715,-9.14388 64.56294,4.49477 3.40146,2.4774 4.87884,3.08021 5.20664,2.12443 0.25171,-0.73394 1.37191,-4.0039 2.48935,-7.26658 5.64699,-16.48813 19.83504,-30.77956 36.33224,-36.59697 4.37705,-1.54348 9.48547,-2.34052 17.22278,-2.6872 12.9647,-0.5809 20.17191,0.92444 31,6.47484 6.36832,3.26435 13.21359,9.77654 75.85594,72.16481 74.25477,73.9536 82.12607,82.20537 90.98496,95.3829 20.49125,30.48058 29.8237,61.05096 29.8237,97.69377 0,37.54442 -10.39645,70.43549 -31.92483,101 -8.78297,12.46946 -71.72291,75.7536 -83.781,84.23917 -18.98786,13.36223 -39.01575,21.48796 -62.4227,25.32618 -12.29951,2.01685 -162.88942,1.91553 -174.11434,-0.11714 z M 167.84267,95.66544 c 13.00883,-3.58796 26.12766,-9.43085 36.66147,-16.32835 7.11327,-4.65775 15.48569,-12.44634 42.89208,-39.90112 36.27435,-36.33839 41.61946,-42.81883 51.2318,-62.11367 4.3762,-8.78432 10.52931,-27.2059 12.24201,-36.6509 7.19984,-39.70473 -1.75087,-80.60585 -24.79605,-113.30787 -6.86507,-9.74181 -148.42805,-152.33641 -154.76776,-155.89561 -13.52398,-7.59254 -30.53569,-5.42062 -41.15103,5.25383 -10.05024,10.10622 -12.54017,24.84956 -6.48524,38.40043 1.0322,2.31007 4.92543,7.47878 8.65161,11.48604 5.87698,6.32028 6.8438,7.89736 7.2949,11.89948 1.14661,10.17276 -7.75999,16.92979 -17.45869,13.2451 -1.54831,-0.58823 -13.1651,-11.31812 -25.8151,-23.84421 -25.19323,-24.94645 -28.01185,-26.95633 -39.04496,-27.84196 -15.10311,-1.21234 -29.26024,7.67126 -34.54398,21.67632 -1.69243,4.48594 -2.02141,7.14714 -1.67871,13.57935 0.68198,12.80006 2.48805,15.44584 28.32878,41.5 12.27363,12.375 22.81736,23.82981 23.43052,25.45513 2.49373,6.61024 -1.68946,14.45063 -8.79774,16.48926 -7.20346,2.06592 -8.46385,1.08726 -43.69391,-33.92736 -19.91433,-19.79256 -34.59872,-33.59856 -37.03172,-34.81658 -9.27783,-4.64471 -21.724692,-4.6611 -30.78177,-0.0405 -5.065503,2.58423 -12.005185,9.58424 -14.65931,14.78675 -4.98422,9.76989 -4.70653,22.57716 0.69499,32.05332 1.440534,2.52719 16.969332,18.99452 35.42143,37.56222 l 32.85638,33.0622 v 4.78349 c 0,5.81973 -2.30635,10.03503 -6.60719,12.07591 -3.74999,1.77949 -7.1903,1.92385 -11.03273,0.46296 -1.52356,-0.57925 -11.79036,-9.95835 -22.815096,-20.84244 -23.10878,-22.81396 -24.399475,-23.60436 -38.544984,-23.60436 -7.10469,0 -9.42713,0.43467 -14.14796,2.64796 -17.40512,8.16013 -24.17927,27.98266 -15.63289,45.74494 1.63049,3.38871 16.30771,18.77052 55.498786,58.16305 L -33.15733,0.4342 v 5.15925 c 0,4.32805 -0.49257,5.72026 -3.05734,8.64138 -3.49284,3.97813 -9.46969,5.48633 -14.35736,3.62294 -1.60308,-0.61116 -10.32378,-8.40666 -19.37933,-17.32334 l -16.464643,-16.21213 0.602934,17 c 0.620571,17.49727 2.04533,26.57739 5.706649,36.36897 9.52461,25.47196 30.59438,46.63873 55.36009,55.61493 15.16012,5.49471 13.55553,5.41277 101.589,5.18775 l 81.5,-0.20832 z" - id="path2455" /> - </g> - </g> -</svg> +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + width="80.0px" + height="80.0px" + viewBox="0 0 80.0 80.0" + version="1.1" + id="SVGRoot" + sodipodi:docname="throw_state.svg" + inkscape:version="1.1 (c68e22c387, 2021-05-23)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> + <defs + id="defs2629" /> + <sodipodi:namedview + id="base" + pagecolor="#000000" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:zoom="5.6" + inkscape:cx="35.625" + inkscape:cy="30.267857" + inkscape:document-units="px" + inkscape:current-layer="layer1" + inkscape:document-rotation="0" + showgrid="true" + inkscape:window-width="1920" + inkscape:window-height="1137" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" + inkscape:pagecheckerboard="0"> + <inkscape:grid + type="xygrid" + id="grid3199" /> + </sodipodi:namedview> + <metadata + id="metadata2632"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <path + style="fill:none;stroke:#ffffff;stroke-width:3.16228;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 45,58 V 33" + id="path3263" /> + <path + style="fill:none;stroke:#ffffff;stroke-width:3.09839;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 33,45 H 57" + id="path3263-9" /> + <g + id="g23" + transform="translate(4)"> + <g + id="g153"> + <path + style="fill:none;stroke:#ffffff;stroke-width:2.828;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 57,40 3,4.795832" + id="path3263-9-9" + inkscape:transform-center-x="1.5" + inkscape:transform-center-y="-2.397916" + transform="translate(-5)" /> + <path + style="fill:none;stroke:#ffffff;stroke-width:2.828;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 52,50 3,-4.795832" + id="path3263-9-9-4" + inkscape:transform-center-x="1.5" + inkscape:transform-center-y="2.397916" /> + </g> + </g> + <g + id="g23-3" + transform="matrix(-1,0,0,1,86,0.204168)"> + <g + id="g153-6"> + <path + style="fill:none;stroke:#ffffff;stroke-width:2.828;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 57,40 3,4.795832" + id="path3263-9-9-8" + inkscape:transform-center-x="1.5" + inkscape:transform-center-y="-2.397916" + transform="translate(-5)" /> + <path + style="fill:none;stroke:#ffffff;stroke-width:2.828;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 52,50 3,-4.795832" + id="path3263-9-9-4-6" + inkscape:transform-center-x="1.5" + inkscape:transform-center-y="2.397916" /> + </g> + </g> + <g + id="g23-2" + transform="rotate(-90,43.102084,42.897916)"> + <g + id="g153-8"> + <path + style="fill:none;stroke:#ffffff;stroke-width:2.828;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 57,40 3,4.795832" + id="path3263-9-9-1" + inkscape:transform-center-x="1.5" + inkscape:transform-center-y="-2.397916" + transform="translate(-5)" /> + <path + style="fill:none;stroke:#ffffff;stroke-width:2.828;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 52,50 3,-4.795832" + id="path3263-9-9-4-9" + inkscape:transform-center-x="1.5" + inkscape:transform-center-y="2.397916" /> + </g> + </g> + <g + id="g23-8" + transform="rotate(90,42.397916,47.397916)"> + <g + id="g153-4"> + <path + style="fill:none;stroke:#ffffff;stroke-width:2.828;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 57,40 3,4.795832" + id="path3263-9-9-0" + inkscape:transform-center-x="1.5" + inkscape:transform-center-y="-2.397916" + transform="translate(-5)" /> + <path + style="fill:none;stroke:#ffffff;stroke-width:2.828;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 52,50 3,-4.795832" + id="path3263-9-9-4-8" + inkscape:transform-center-x="1.5" + inkscape:transform-center-y="2.397916" /> + </g> + </g> + </g> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="Layer 2" + style="display:inline"> + <image + width="512" + height="512" + preserveAspectRatio="none" + style="image-rendering:optimizeSpeed" + xlink:href="data:image/png;base64,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 AfsIk1iEi8i3AAAAAElFTkSuQmCC " + id="image2221" + x="-240.21637" + y="-250.94923" /> + <image + width="512" + height="512" + preserveAspectRatio="none" + style="opacity:0.884146;fill:#800000;image-rendering:optimizeSpeed" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABHNCSVQICAgIfAhkiAAAIABJREFU eJzt3Xe4XVWd//F3LoGEkFBiQpEWpCkIUkTFAiIqVqyMjijqjD90xooMoA+O6Oio2AvFNipNQRQB xcFGiQ0EacIoRQEFIfSQAAkhub8/Vq653Nxyzj1nf9fae79fz7OeADNmr+/e++zv5+yzC0iSJEmS JEmSJEmSJEmSJEmSJEmSJEmSJEmSJEmSJEmSJEmSJEmSpJ5MyT0BSY0zDdgK2Gbln+sBs4CZw/5c C1gMLFr559A/3w7csHLcET1xqU0MAJJ6sSGwF/AsYEdS098cGOjD330/KQhcD1wMzAeuAJb34e+W Ws8AIKkbjwFeQGr6ewGPD17+/cCvSWHgF8AlwcuXJKk11gFeB/wQeBgYLGj8GfgosENl1UuS1CJT SN/0vw08QP5G38m4AjgCmFvB+pAkqdGmAq8H/kD+hj7Z8SBwDOkCREmSNI4ZwDuBm8jfwPs1HgFO AXbu32qSJKkZBoC3km65y92wqxzfxzMCkiQB8HTg9+RvzlHjIeC/gLX7sfIkSaqbjYETgBXkb8o5 xs3AAT2vRUmSauQgYCH5m3AJ41xSGJIkqbFmASeRv+mWNhYA+/WwXiVJKtbupMfp5m62pY4VwKeA NSe7giVJKs0hwFLyN9k6jN/hnQKSpJobAI4nf1Ot27gd2HUS61uSpOymAaeTv5nWdSwE9ul6rUuS lNEs4DzyN9G6jyXAq7pc95IkZbEhcBn5m2dTxnLSUxIlSSrWTOBy8jfNJo73dLEdJEkKswZwDvkb ZZOHIUCSVJzjyN8g2zAMAZKkYhxK/sbYpmEIkCRl90rShWq5m2LbhiFAkpTNU4EHyd8M2zoMAaq9 NXJPQFLX5pHu9V8/8zza7AWkBwZdlHsi0mQZAKT6OQN4Yu5JyBAgSYrzZvKf/nb4c4AaYEruCUjq 2FzgT8Ds3BPp0A2k1xAvIL1kZ/ifD5KeXLjRsLHxyrEbsE6G+fbivcDnck9CktRMJ5P/2+54YzFw NvDvwON6qHMa8FzgM8A1BdTV6Tikh5olSRrVfuRvcGM1/S8A+wJrVVT7FqRn8l9UQL2GAElSmBnA X8jf3IaPB4FPkX6WiPQi4NIe524IkCTVwifJ39SGxkPA50m/1ef0Msp++ZEhQJLUk+2AZeRvaIPA CcBjqy23K1OAVwG3kX/dGAJUO94FoCaZCcwBNhgx1iY10YdXjmXAUtLV6H8lNZAVGebbieOAf8s8 h2WkW92OyzyPsWxKejbCU3JPZBTeHSBJfTQTeCZwMOl09E+BW5j8N7WHgRuBC4FvkK5ifzLVXdTW qQ2AB8j7LXYB8KyqC+2DacA3yf+t3zMBktRH04F9gI8AvyY17IgD9xLgYuBLwP6kMwmRDu9h7v0Y lwKbV15lf72Lcn4yMQRI0iTMBA4CfkK68Cz3AXyQ9G38jJXzqvphPFNJP0/kqvVU4gNPv+wD3EP+ /cUQIEkdGiDdT34i6f7y3Afs8cYy4H+Bl6ycd7+9JmNt55MCSJ3thiFAkoo3C/gP8n7j7WX8GTiU 9Jt9v/w2Uy03ki6kbAJDgCQVag7wX5R5kJ7MeAD4Mumq9F48NdP8FwM79zj30hgCJKkgG5Ku3s99 hXtV40HgaCZ/RuDEDHNeAbxykvMtnSFAkjIbAN4O3Ev+g2/EuId0Jf/0LtbRFODODHP9UBdzrCND gCRl8lTg9+Q/4OYYNwPP73A97Zphfn+gmgsZS2MIkKRA6wFfI51izn2gzT2+Bqw7wfo6LMO8XjbB nJrEECBJAfYgXSGf++Ba0vgr8IJx1tlPg+dz8ThzaapSQ8B7qyxakqIcQtwT++o4jgPWHLHOphP/ 0KPn0k6GAEnqs9nA2eQ/kNZhzAfmDlt3+wYv//zVtl67GAIkqU8eD9xE/gNoncZNrLr3/hPBy376 aluwfQwBktSjpwF3kf/AWcexGHgFsXdJ/Gn0zdhKhgBJmqQX09yH+kSNFcTeKfGZUbdkexkCJKlL b6bM1686xh/7jLYxW84QIEkdejP5D46O7sdCVr8DQYkhQJImsDfe5lfX8d1RtqdWMQRI0hi2xgv+ 6jzeuPom1QiGAEkaYX3gj+Q/EDomP7ZfbatqNIYASVppKvGPqnX0f8wauWE1JkOAJAHHkv/A5+ht LF5tq2oihgBJrfZO8h/wHL2PG0ZuWHXEECCplfYDHiH/wc7R+/glmixDgKRWWR+4g/wHOUd/hrcA 9sYQoMaYmnsCKt5HePQb60p2J3ApcNso425gA2CTYWPjlX/uCmyaYb453JV7AjV3GekVyj8n7U+l GHq082ezzkJSY+xM+af+LyeFlKcBAz3UuivwAeAiYHkBdVU1vtPDOtIqngmQ1GgXkv+ANtr4DXAw 1X1rn0t6WM7PCqi13+OC/q2m1is1BBxaZdGSmu+fyX8gGzn+AOxfZdGj2Id0ViB37f0a1/Z39bTe 7hgCJDXIOsAt5D+IDY2/AG+gt1P8vXo5cA3510WvY2G/V4wMAZKa4+PkP3gNAvcB7wDWqrbcjg2Q fhq4nfzrppcxo98rRoYASfW3LbCU/AeuP1HuM+s3Ay4h/zqa7Ni6/6tEGAIk1dxZ5D9g/QhYr+pC ezQdOIn862oy44AK1ocSQ4CkWtqC/LfAfYy8v/V3672Uf6vkyHFCJWtCQwwBkmrnP8l3cHoA+Kfq S6zE8yjzgD/WuIN6haw6MgRIqo0ppKvtcxyUlgMvrr7ESu1EvR6Z/LRqVoOGMQRIqoV9yHdAOiKg vgh1CgEfqWgd6NEMAZKKl+uCtlMiigtUlxBweVUrQKsxBEgq1rrAg8QfgC4hXU3fNHUJATtWtQK0 GkOApCIdTPyB5zaa/Qa+OoSAMyqrXqMxBEgqTo5n3b80pLK86hAC9qiseo3GECCpGE8g/mDzy5DK ylB6CPhJdaVrDIYASUX4d+IPNE8PqawcpYeAvasrXWMwBEjK7lvEHmDOCqmqPCWHgF9VWLfGZgiQ lFXkK26X0+4rz0sOAQdXWLfGVmoI+I8qi1YZpuSeQItsBWwJzB025oz497mkJnn3iHHXiH//M3At 6YPai1mkV+5GPRb2W8Cbg5ZVqp2AX5C2dUmWAfvSruszSrE78DNgg9wTGeEw4NO5JyHVzQCwC/BO 4DTg7/Q/od9NemvekaSn+K0ziXlGP/1vz0nMsYlKPROwgPRCKMXbHbiX/PuAZwKkLk0nXUh1JHAu sJD4D+ojwO+BY4DXkc4uTOSIwPktwBfQDFdqCLgcmFFh3RqbIUCqkR2AL1Dmh3Yp6ezDvoz9U8/3 A+fzjU5XaouUGgJOq7JojcsQIBVsGnAgMJ/8H8pOxw3A+4CNRtTyt8A5vLyLddwmpYYAXxaUjyFA Ksy2pAti7iL/B3Gy42Hge8B+pMfwRi33ISZ3jUJblBoCPlRhzRqfIUAqwJ6kq7ZXkP/D188ReXA5 p+u13j6GAI1kCJAymQN8neY1/hzjfV2u+7YyBGgkQ4AUaArw/0i32eX+kDVlHNTVFmg3Q4BGMgRI AXYlz5vxmj6e181GkCFAq3kyhgCpEusBXyLdS5/7A9XE0ebH/05WqSHgqCqL1rgMAVKfPQ+4nfwf oiaP2R1vDQ1Xagh4U4U1a3yGAKlP3kp6BnruD0+Tx5KOt4ZGU2IIWIKPdc7JECD1YAD4LPk/MG0Y N3W2STSOEkPANfho55wMAdIkzATOJv8HpS1jQWebRRMoMQS8sdKKNRFDgNSFzUgvO8n9AWnTWAFM 7WTjaEKlhYBLqi1XHTAESB3YnWpey+uYeGzWwfZRZ0oKAUsw3JWg1BBwWJVFS516KfAA+T8QbR17 TLyJ1IWSQsBOFdeqzhgCpFE8hfQymtwfhDaP/SfcSupWKSHgDVUXqo4ZAqRhNgduI/8HoO3jbRNt KE1KCSHgM5VXqW4YAtSxJt/Gsw7pav+Nc09EbJV7Ag31B2Bf4M6Mc/AhT2W5lPRws/tyT2SET2II UJApwA/In3odaVw1/uZSj3KeCXhPQH3qnmcC1FofJ/+O7nj08CxAtXKFgL0iitOkGALUOgeRfwd3 rD7ePd5GU19Eh4AVwLohlWmyDAFqjWeQ7k3OvXM7Vh+/GGe7qX8iQ8D1QTWpN4YANd488l8R7Rh7 LAPWH2vjqa+iQsDnowpSzwwBarSfkH9nnsxYCtwMXES6cPE0YD5wHbCogPn1c7x+zK2nfqs6BCwG NgyrRv1gCFAjvZj8O3En4xbgK8DLgR3p7BaqmcA2wD7AR6n3uwyuptm3npamyhDwgcA61D+GADXK VOBP5N+BRxvLgd+SDpa79LHmzYCDSc85qNsjjg/q43rQxKoIAedgkKszQ4Aa413k33FHjoeB44FN K6x7yNqke7HvDKyvl3ETsFYVK0Jj2on+vQjrKmC92OmrAoYA1d5s4G7y77RDYwXwbWDrKosewyzg KOD+Luaba3hLYLxNgd/R23Y7i7SfqRkMAaq1L5B/Zx0a5wBPqrbcjswBPkfZt0PegY0kh+nAx+j+ Z6M7SaFtSvyUVTFDgGppe9KtZbl31HuAF1Zc62RsBVxG/vUz1ji6utI1gY2B/wbOY+yD/23Aj4Ej MKw1nSFAtfMj8u+g15Cu0C/V2sB3yL+eRhsrgFdVV7q6sBVpWxwKvAhfoNVGhgDVxvPJv2OeSX2+ GR1BuiMh9zobOR6gv3dHSJq8UkPA4VUWrfq5gHw74wrgw9Tv99AXkl4RmvvDPHLcjA+UkUqxB4YA FWwLUhPOsRMuB15TfYmV2Y50G17uD/PI8Su8NVAqhSFAxXo/+XbA9wXUV7V5lBkCvkn9zqpITWUI UJGuIc+O9+2I4oLMo8wQcByGAKkUhgAVZTfy7HCXkq6ob5J5GAIkjc8QoGJ8lvgd7XbSs/ebaB6G AEnjMwQouzVIDyeJ3MGWAU+PKC6jeRgCJI3PEKCs9iN+5/pqSGX5zaPMEHAshgCpFIYAZXMysTvV QzT31P9o5mEIkDQ+Q4DCzQAWE7tDfSaksrLMo9wQIKkMhgCF2pPYHel+0lv12mgehgBJ4zMEKMxb id2Jjoopq1jzMARIGp8hQCGOIW7nuZf6vOSnSvMwBEga3x6U+Y4RQ0CDXEjcjnNKUE11MI8yQ8Ax 1ZUsqUuGAFXqHuJ2mtcF1VQX8zAESBqfIUCV2Iy4neURYHZMWbUyD0OApPEZAtR3LyRuR/llUE11 NA+4mfwfZkOAVK5SQ8ARVRat6hyBO0kptsIQIGl8hgD1TeQTAJ8YVFOdlRoCvlRl0ZK6YghQX1xJ zI6xnPTCIU3MECBpIoYA9Syq0SyIKqghDAGSJmIIUE/+SswOcVVUQQ1iCJA0EUOAJu0WYnaGn0YV 1DCGAEkTMQRoUm4lZkc4MaqgBjIESJqIIUBdu42YneBTUQU1VKkh4ItVFi2pK4YAdeV2YnaAD0YV 1GCGAEkTMQSoY3cQs/F9mEx/GAIkTeQpGALUgbuI2fCnRxXUAoYASRMxBGhCdxOz0X0PQH8ZAiRN xBCgcd1LzAa/LqqgFik1BHyhyqIldcUQoDFdR8zGvj+qoJYxBEiaiCFAozqTuI29TlBNbfM4DAGS xmcI0Go+TtyGfkFQTW30OOIe62wIkOrJEKBHOYi4jeytgNUyBEiaSKkh4H1VFq3R7UHcBr4xqKY2 KzUEfL7KoiV1xRAgAGYSu4F3jCmr1QwBkiZSagh4d5VFa3WRzeLwoJrazhAgaSIlhoAVwD9XWbQe 7SfEbdz5QTXJECBpYiWGgKXAc6ssWqt8nrgNuxzYKaYsYQiQNLESQ8AiYPcqi1byVmI37NkxZWkl Q4CkiZQYAhYA21RZtOAJxG/YPUMq05BSQ8DnqixaUldKDAF/BjausmjBlcRu1PNjytIwhgBJEykx BFwOrFtl0W33fuI36vNDKtNwpYaAj1ZZtKSulBgCzgOmVVl0m21F/Aa9FJgSUZwepdQQ8Poqi5bU lRJDwHeBgSqLbrOLid+g/xVSmUYqMQQsAbavsmhJXSkxBPhI+YocQvzGXAG8OqI4rWZrygsBp1Va saRulRgCPlBpxS21Kek+/eiN+QDwpID6tLrSQsAK4ImVViypWyWGgLdUWnFLXUCejXkTMLfy6jSa rYG/kf8DPTR8FrhUntJCwCPA/pVW3EJvI98GvRBYs/oSNYqSQsA3K65V0uSUFgIWA4+vtOKWmQM8 TL4NegqwRuVVajSlhIDLqy5U0qSVFgKuAqZXWnHLfJG8G/RkDAG5lBACluKZIKlkpYWAr1RbbrvM If/GNQTkU0II2KHyKiX14qnk7xPDx2uqLbddjiD/BjUE5JM7BGxQfYmSelRSCFhIOm6pD6YDN5N/ oxoC8skVAm6OKE5SX5QUAi4F1qq23PZ4Pfk3qCEgrxwh4KyQyiT1y7OAh8jfKwaBL1Rca2tMISWq 3BvUEJBXdAj4cExZkvroZaR783P3ikHg5RXX2hr7kH9jGgLyiwwBLwmqSVJ/vYX8fWIQuAfYsuJa W+OH5N+ghoD8IkLAH3H7SnV2JPn7xCDwW2BqxbW2wg7keUeAIaA8VYeAV8WVIqkiuZ8lMzQ+WXWh TTQF2Al4B3A6sID8G9IQUI6qQsC3I4uQVJkB4FTy94kVwAsrrrX2BoBdSC9hOQO4i/wbzhBQtm3o bwi4BFg7tAJJVVoL+Bn5+8SdwGMrrrWWdgSOB+4l/0YyBNTPlsAV9L4N5+ObIKUmmkUZd5JdQDqz 3XprAK8AziP/RjEE1N86TP5U3wpSAPW5/1JzbQhcT/4+8ZaqCy3ZHOB9lPF0P0NA8+wF/IrOt9e5 wG5ZZiop2lbAbeTtEXcCs6sutDRPJL1fvZSnNBkCmm1T4KXAUcCZwF+BG4DvAu8HXgBslG12knJ5 Eul5/Tl7xJcrr7IQM4HPUM6TmQwBktRuB5C3PywHnlx5lZm9nPTNK3czNgRIkoY7jrz94WIaekHg FsDZ5G/AJQxDgCSVZzr9uXuol9GoCwKnAocBi8nfeEsahgBJKs92wCLy9YbGXBC4K3AV+ZttqcMQ IEnlOZC8vaH2FwS+DHiA/E229GEIkKTyfJ18faHWFwS+h7Je0FP6MARIUlnWBq4mX1+o3QWBawDH kL+h1nEYAiSpLDuQ90x2bS4InAmcQ/5GWudhCJCksryZfD2hFhcEbgpcTv4G2oRhCJCkspxEvp5Q 9AWBOwO3kL9xNmkYAiSpHDOBP5GnHxR7QeA2wALyN8wmDkOAJJVjF/I9vv7CgPq6shHwZ/I3yiYP Q4AklePz5OsHzwioryOzgMvI3yDbMAwBklSG9YDbydMLzgmob0JrAb8gf2Ns0zAESFIZDiJfL3hS QH3jOoH8DbGNwxAgSflNAX5Fnj7wnYD6xvSmUSbkMARIUpvkuiDwEdLF9z3rtpE8HjiT9BOA8tgZ 2Bo4i7QzSJLi3Q7MBZ4SvNwB0iOKfxi50OnAleT/BuzwTIAklWB94A7ij/9LSQ/f60k3DeSLwEt6 XaD6xjMBkpTXEuBu0ptvIw317p/28pd0+pah/YBze1lQoIXAz4HrSA8oWkA6VTP0z4uBDUnPMNh4 2J8bk+6x3JV6vX3pFOCNpCdFSZJiTQF+AzwteLkPAFuSAkhl1iTf4w87HVcCnwD2Aqb2WO/GpAsd vwvcV0Bt/hwgSWXbjfQlLPrY/6GqCzs8Q1GdjJuAtwObVVZ5ChN7kW57zPX4R0OAJJXveOKP+3eT 3lFQiccCizIUNd74K/A20pmJSNuRTrfnSHmGAEkq22zgLuKP+4dWVdDJGYoZa9wKvAOYVlWxHXoC cBqwgvzrxBAgSeX4T/L0xr73xT0yFDLaWA4cRboNsSQ7AVeQf/0YAiSpDBsA9xN/zD+434WcnqGI keM+4EX9LqyPZgCnkn89GQIkqQyfJP54fz19vHtta/L/1v1H0u/udXAE+deXIUCS8tuE9HyA6ON9 314VfFyGyQ8fZwPr9quYIC8E7iV/0zcESFJeXyb+WH98PyY+F3gww+SHxqeo14N4htsWuIH8Td8Q IEn5PI74W8fvog93x30oeNLDx6m9Tr4Am5F+j8nd9A0BkpTPKcQf5/fvZcJTSA/YydGgLiG94agJ DAGS1G47EX+7+Gm9TPgZwZMdGreSHjrUJIYASWq3HxJ7fH+IHq6fOzZ4soOk6w32mOyEC2cIkKT2 ejrxx/c3TWaiU4E7M0z2wMlMtkYMAZLUXhcSe2z/+WQm+cLgSQ6uXDFtYAiQpHbaj9jj+nIm8ZP6 V4MnOQjs2e0ka8wQIEntdDmxx/WuXxB0bfAEz+x2gg1gCJCk9nknscf0y7qZ3MbBk1sO7NjNBBvE ECBJ7bIhsIzYY/oOnU7un4In9q1OJ9ZQhgBJapcfE3s8/+9OJ3ZM4KRWAPM6nViDGQIkqT0OJPZY fhMdPlb/qsBJXdLhymoDQ4AktcM6wGJij+XPmmhSU4n9beKDXaywNjAESFI7nEzscfxLE01o6+AJ 7drN2mqJkkNAXd/MKEmleQGxx/BrSprQ37paVe1Sagj4aJVFS1KLTAUWEHsM33CsyQwA2/S3vnGd E7isurkF2Ae4IfdERjgSOCD3JCSpAR4h/rX3e431f4gOAD8KXFYdlRoCjiFdwCJJ6s0pwct79nj/ x7OJOxURGTbqrMSfA46stGJJao/riDt2Xz3eRH4ZOBG/RXautBBwc7XlSlJrHEXcsXsFMGesiUS9 pOD+ya2nVistBIy5E0mSOrYNscfuV402iQFgVgXFjebvQctpktKuCfAWTknq3Q3AHwKX9+zR/uMA MDNoArcFLadpSgoBBgBJ6o/zA5e192j/MfIMgAFg8koJAQYASeqPCwKX9UTgMSP/4wAwLWgC9wUt p6lKCAHrZly2JDXJhaTf5yNMYZTnAQwADwRNYMynEaljuUPAZZmWK0lNcw/pRXxRnj3yPwyQ3k4U YZOg5TRdzhBwaYZlSlJTXRC4rGeP9h+vJeY2hBsrKam9ctwiuFlIZZLUDi8n7vi9Apg9cgKXBi18 SW/rSaOIDAELgmqSpLbYAFhOXAh4+fCFR/4EMI1UrPon8ueAMwOWIUltci+x1wHsPfxfBoA7Axf+ 2MBltUVECHgY+O8K/35JaqsLApe1y/B/GSD2YrLdA5fVJlWHgM8Bf63o75akNrsgcFnbjfwP/0rc 7w/frawsQTXXBPwMWCOyCElqkejrAB719N+9Axe8EFiz17WlcW0G/B/92V5X4nUbklS1y4jrw/94 ousA6RtjlHUZ5WlE6qtbgKcB5/T49/wAeAbpIhVJUnUuCFzWP34GGCA9o//BwIW/JHBZbXU/sD/w LuCOLv+3twH/Rnp9ZNQdIpLUZr8LXNa2I//DL4k7/ZD7hTZtsw7wHtL1FzeQHgYxfHusIJ0FOg14 JzAjzzQlqbV2I64Hnzhy4R8NXPgg6RS18hj6GeZfV/7pC34kKa9ZxPXfi0Yu/PmBCx8EzutpVUmS 1Cx/J6b/3jNywTOBZUELHxr79bSqJElqjguI679zIF0ECOlir8urrW01nyC9o1iSpLa7LnBZ28Gq AABwfuDCIT2S8LXBy5QkqURZA0COp/R9FB8MJElSZADYFh4dAH4fPAGAxwEfD16mJEmlCT8DMNJR xF4IODTe2P/6JEmqjbWAR4jpuVeONoFtgxY+ciwB9pzcOpMkqRFuIKbnPsgYF+H/LmgCI8ftwOaT W2eSJNXej4nruZsNvwZgyFerqWtCGwFn4aNoJUntFHkdwNzRAsBJpG/jOewKnEr6LUSSpDaJDAAz RwsAS4EvBk5ipJcCp2MIkCS1y42By5o1WgAAOJ68r4LdH0OAJKldFgYua9QzAAD3AV8LnMhoDAGS pDaJ/OI95hkAgM8CD0XNZAyGAElSW0QGgDHPAADcAhwdNZNxGAIkSW0QegZgov+H6cBfyPNcgJHj LAwBkqTmmkFcT/3EeGcAID2h7719K603ngmQJDXZg8CKoGVNeAZgyLnkPwPgmQBJUtPdT0wvPaHT CW1HSia5m78hQJLUZH8npo+eMdFPAEOuAw7pva6+8ecASVITRV0IOLPb/8F3yf/t3zMBkqSmuoyY /vnbbie2HuXcFWAIkCQ1zXxieucfOv0JYMhC4LXAskmX1n/+HCBJaoqonwA6vgtgpHeS/5u/ZwIk SU0T9VP7Xb1M8pNBkzQESJLa4hvE9MulvUxyCuk+wtxN3xAgSWqK/yGmVz7c60SnAj8OmqwhQJLU dKcR0yfv7sdk1wEuCpqwIUCS1GTnENMjb+7XhGcTd++iIUCS1FQXEtMfr+7npA0BkiT15vfE9MaL +j1xQ4AkSZN3LTF98WdVTN4QIEnS5ES9DOgHVRVgCJAkqXtRrwM+scoiDAGSJHVnOTG98Nhu3wXQ jXuA5wKXV7iMyfDdAZKkEs0AquzLwy2uekGGAEmSOjMzcFmLIpKGIUCSpIlFBoDKzwAMMQRIkjS+ xp0BGGIIkCRpbI08AzDEECBJ0ujWDVxW6BmAIYYASZJWt1XgssLPAAwxBEiS9GjbBS5rUeCyRuXD giRJSqJeBTwIbBFU07gMAZIkwXXE9LeHiHvg0IQMAZKkNlsTWEZMb7s6qKaOGQIkSW21HXF97Qwo 6BQAXhgoSWqvyAsAr4OyAgAYAiRJ7RQeAErlzwGSpDb5MnG97JlBNU2aIUCS1BbnEdfHNgyqqSeG AElSG9xCTP+6L6qgfjAESJKabB3ietclQTX1jSFAktRUuxDXt04ZWmhpdwGMxbsDJElNleUOgLoE ADAESJKa6cmByyr6FsCJ+HOAJKlJfkdcr9o9qKbKGAIkSU2wLvAIcX1q3ZiyqmUIkCTV3YuJ60+3 BdUUwhAgSaqzTxHXm+YH1RTGECBJqqtLiOtLXwqqKZQhQJJUN+sR+/v/q2PKimcIkCTVyUuI7Udz Y8rKwxAgSaqLTxPXh64JqikrQ4AkqQ4uJa4HHRtUU3aGAElSydYDlhODhV7jAAAQg0lEQVTXfw6I KasMhgBJUqleSmzv2SimrHIYAiRJJfoMcT3n/4JqKo4hQJJUmsi+dHxQTUUyBEiSSrElsIK4XvOa mLLKZQiQJJXg/cT2mY1jyirbbGAZ+Zu+IUCS2usa4vrLn4JqqoUl5G/4hgBJaqddiO0tXx5rIgP9 rkyTtj9wOoYASWqy1wcv74Lg5RWt1DMAngmQpGYbAG4ltqdsElJZTZQeAAwBktRM+xLbS64dbzL+ BFAmfw6QpOY5MHh55wcvr3h1OAPgmQBJapbpwEJie8hzQiqrkToFAEOAJDXDAcT2jluY4Cy/PwGU z58DJKn+oq/+/w7paYMapm5nADwTIEn1NhtYSmzP2CWkspqpawAwBEhSPb2V2F5xdUxZ9VPnAGAI kKT6uZjYPvH+mLLqp+4BwBAgSfXxbGL7wwpgi4jC6qgJAcAQIEn18BNie8P8mLLqqSkBwBAgSWXb nfi+cHBIZTXVpABgCJCkcp1ObD9YCmwQUllNNS0AGAIkqTzbA8uJ7QU/CKmsxpoYAAwBklSWbxDf B14VUlmNNTUAGAIkqQybAw8Te/y/D5jWzSR9FHCz+NhgScrvUGDN4GV+j3QNgMbR5DMAngmQpLzm AA8Qf9zfJ6K4umtDADAESFIeHyH+eH8TntHvSFsCgCFAkmLNAu4h/lj/rojimqBNAcAQIElxDiP+ GH8HMCOiuCZoWwAwBEhS9WYBtxN/fD8yorimaGMAMARIUrU+S/xxfSGwfkRxTdHWAGAIkKRq7Ags I/6Y/omI4pokKgCcDVwWtCxDgCTlcwHxx/KHgI0CamuUqADwLWA2hgBJarLXkec4fmxEcU0TGQDA ECBJTTUL+Dvxx+9lwLzqy2ue6AAAhgBJaqJPk+fYfWJEcU2UIwCAIUCSmmQH8lz4t2LlsjUJuQIA GAIkqSnOI8/x+gcRxTVVzgAAKQRcHjQHQ4Ak9d9ryXesfkpAfY2VOwCAIUCS6momcCt5jtE/D6iv 0UoIAGAIkKQ6+hT5js/7BtTXaKUEADAESFKdPBF4mDzH5YsD6mu8kgIAwGMwBEhS6dYGribP8XgF 8MzqS2y+0gIAlB0C1uiiDklqqq+T71h8QkB9rVBiAIAUAq4Imls34wtd1iFJTZPrcb+DwH34zP++ KTUAQLkh4M2TqEWSmmBb4H7yHX/fVX2J7VFyAIAyQ8DdwHqTrEeS6moaeR/ediUV/gw7UNVfrEm7 m3Srx5W5JzLMbOCw3JOQpGCfBnbNtOxB4O3A8kzLb6TSzwAMKe1MwC091iNJdfJK8h5zvfCvAnUJ AFBeCJjbh5okqXTzgHvJd6wNufDPnwDKNvRzwFW5J7LSLrknIEkVWxM4FVg/4xw+CCyoeiEGgPLd DTyHMkKAAUBS030MeGrG5V8FHBuxIANAPZRyJsAAIKnJXgQcmnH5gwRe+NfGAPBw0HLW7PPfdxf5 Q0C/a5KkUmwHnAhMyTiHk4BfRS2sjQFgUdByZlXwd+YOAZdnWq4kVemxwE9JF17nshA4PHKBBoDq zKzo780ZAi7LsExJqtL6wLnAlpnnEXLhX9tdQsxtHJdUXMcc0sOCIm9N2bDimiQp0nRgPvlvsb4Y X7wW4jxiNugfA2qZQzoTEFHPrQH1SFKUNYAzyd/87wO2qrhWrXQWMRs16sl5USHg80H1SFKEr5G/ +Q8Cr666UK1yEjEbdWFUQVQfAhbh6X9JzfFR8jf+QYLu99cqxxGzYaNf4FBlCAi9MlWSKvQO8jf+ QdJdVdMqrlUjHE3cBp4RVNOQKkLA98l7X6wk9ctrSF/Ocjf/RaTnDijYB4jbyJsE1TTcXPp3p8Nv qO52RkmK9FxgKfmb/yBwYMW1agzvJm4jPyOoppHWBk7pcI5jjW/i6SlJzbA7cD/5G/8g8D8V16px /AtxG/pfgmoayxuAG+luzjcAr80xWUmqwE7AHeRv/IPANcT/NKxh9iduYx8dVNN41gLeBHwVuBRY wqPnuIT0k8FXSIFhapZZSlL/ldT8HwB2rLZcTeTxxG3wM4Nq6sZUYGfgVSv/tOFLaqKSmv8g8K/V lqtOrAksI2aDRzwNUJL0aKU1/1OqLVfduI6Yjf4wfsOWpEilNf/r8G6qovyQuI2/bVBNktR2pTX/ JcAulVbcgza+Dhjg2sBlbR+4LElqq52AX5CehVKCQdKdYFfknshYDADVe3zgsiSpjUpr/gCHAt/O PQmt7lnEnQL6flBNktRGpZ32H6SMW8A1hg2J2xHupr1nWiSpSiU2/29VWbD6417idojdgmqSpLYo sfn/CO/8qoWLiNspDguqSZLaoMTm/xt8zG9tnEDcjvG/QTVJUtOV2PyvAWZXWbT66xDido7FpCcQ SpImr8Tm/zdg8yqLVv89idid5JkxZUlSI5XY/O8GdqiyaFVjCnAXcTvKB2PKkqTGKbH5PwDsWWXR qtb3iNtZ5gfVJElNUmLzXwa8uMqiVb23E7fDLAc2jSlLkhqhxOY/CLypwpoV5AnE7jRHxJQlSbVX avM/vMqiFes24nacq4NqkqQ6K7X5f6jCmpXBt4ndgXwqoCSNzeavMG8hdif6fExZklQ7Nn+F2prY HWkBPitakkay+SuLm4ndobx9RJJWsfkrm28Qu1OdFlOWJBXP5q+snkfsjrUE2CikMkkql81f2Q0A txC7g30ypDJJKpPNX8U4mtidbBG+PlJSO9n8VZQdid/ZPhxSmSSVw+avIv2e2B3uXmDdkMokKT+b v4r1buJ3vPeFVCZJedn8VbS5pNc8Ru58dwAzIoqTpExs/qqFHxK/E74npDJJimfzV228mvgd8VZg nYjiJCmQzV+1Mo10cV70DulzASQ1ic1ftXQc8Tvlw6RbESWp7mz+qq2tgUeI3zkvCKhNkqpk81ft nUyenfQNEcVJUgVs/mqEHYAVxO+otwPrB9QnSf1k81ejfI88O+wxEcVJUp/Y/NU4u5Jnp10O7B5Q nyT1yuavxvoReXbeq4C1A+qTpMmy+avR9iTfTvy1gPokaTJs/mqFX5BvZ35dQH2S1A2bv1pjH/Lt 0IuA7aovUZI6YvNX6/yafDv2FcD06kuUpHHZ/NVKzyTPcwGGxperL1GSxmTzV6t9i7w7+msrr1CS VvcU4C7yN3ubv7KZC9xDvp39fmCXyquUpFVeACwmf7O3+Su7fyPvTn878LjKq5QkOJD0ptLczd7m ryIMAJeQd+e/Adio6kIltdoh5L3uyeavIj2Z9LjenB+Cy4B1qy5UUisdTf5Gb/NXsY4j/4fhPGBa 1YVKao2p5L/Y2eav4m1AGbfEfI/0s4Qk9WIG+d59YvNX7byR/B+MQeD4qguV1Gizgd+Q/1hm81dt TAHmk/8DMgh8HVij2nIlNdCuwPXkP4bZ/FU7WwMLyf9BGQTOwkcGS+rcO4Al5D922fxVWweQ/8My NOYD61dbrqSaWx/4PvmPVzZ/NcKx5P/QDI2rgMdWW66kmnoqcCP5j1M2fzXGNNK9+bk/PEPjRnyN sKRVpgD/QZlP9rP5q/a2oZzrAQZJtynuUWnFkupgDnAO+Y9JNn812j+R/4M0fDwEvK3SiiWVbC/g FvIfi2z+aoUSnhI4cpyKjw6W2mQ94AvAI+Q//tj81RrTgMvJ/8EaOa4n3fMrqbmmkB5StoD8xxyb v1ppW+B+8n/ARo4lwL9XWLekfHYBfk3+44zNX633EmAZ+T9oo43T8ScBqSnWB46h/NP9Q+OoalaD VJY3UeY7tQeBvwAvrKxySVWbAvwLZbyYrJOxAjikkjUhFeow8n/wxhvfBzavrHpJVdgN+C35jx+d joeB11WyJqTCfZr8H8DxxmLgcGDNqlaApL7YGfgOsJz8x41uji/7VbEypDqYApxA/g/iROMaYO+K 1oGkydsT+CHl/qQ41rgTH0gmMRX4Efk/kJ2Mk4CNq1kNkrrwPOB88h8TJjNuwkeSS/8wg/rcpvMg 6UEim1WyJiSNZQrwCuAS8h8HJjt8KZk0ig2Aq8n/Ae10LAW+AjyuipUh6R+mAgcB/0f+z30vw9eS S+PYlHQbXu4PajdjGXAi8IQK1ofUZjsDR1P+M/s7GT8Apvd39UjNswlwJfk/sN2O5aQHCe3e/1Ui tcYWwPuAP5D/M92v8VVgjX6uJKnJ1gMuJP8Hd7LjKtI7xjfp94qRGmg2cDDpFHndruYfb6wAPtzH 9SS1xnTgTPJ/iHsZjwDnAgeSLnSUlEwHDiB9xpeS/7Pa73EnPlFU6skawNfJ/2Hux7gf+CawDzDQ z5Uk1cCawNOBI4GfAQ+Q/zNZ1biQdD2TpD74GPk/1P0c9wBnAG/HiwfVTFNJD+l5P/BTmt3wh8Zy 4CP4e7/Ud++hWb8PDh9/B04mvcBky36tMCnQTOBppAv4ziU95jb35ypy3A7s2/NaVHZTck9AYzqQ dBq96c/m/wvwe+BPwLUrx3WknxGkXNYCtga2JT3JbmhsS7sfbvNz4PXAgtwTUe8MAGV7HvBtYE7u iWRwG6sCwbXAX0nftBatHMP/eWmmOaoeBoB1SN/cZ638c+RYl/Sgq6GGvyWe3h5uOfAh0k+UK/JO Rf1iACjfpqQ3fz0r90QK9girQoEHJw1Zm9TcvSulN7eSXuM7P/dE1F8GgHpYg3SP7fvxqnpJcX4M vBG4K/dE1H8GgHp5HukCug1zT0RSo91Huo3xeNKFf2ogA0D9bAKcQrrHXpL6aRD4FnAE6QE/ajAv cqmfxcBJK/95LwxxkvrjCuDVwDGkV4Gr4Wwe9fYc0tmAjXNPRFJt3Qf8J+l0//LMc1EgzwDU242k swGbATtlnoukehkkvd77ZcD5+Ft/63gGoDmeAxwHbJ97IpKKdyXp8dy/zj0R5eMZgOa4kfQu7iWk F5E0/QmCkrq3EDgc+H/AzZnnosw8A9BM84AvAi/NPA9JZVhGOt1/JD7GVysZAJptf1IQ8KU7Ujs9 CHwN+Azwt8xzUWEMAM03A/gAcCjpBSeSmu8e0u18XwTuzjwXFcoA0B7bk271eS1e+yE11a3AZ0nX Ay3OPBcVzgDQPluT3ilwEF4oKDXFdcAnSbcFP5x5LqoJA0B7bU66GvgtwPTMc5E0OZcBHwfOwDdh qksGAG1Muj7gbaRXp0oq23LgZ8DngJ9mnotqzACgIY8B3g28E1g/81wkre5S0qO/TwVuzzwXNYAB QCOtC7yZ9A7wXTPPRWq7v5Ca/inAtZnnooYxAGg8TyRdLHgg8NjMc5Ha4i7gu8DJwG8zz0UNZgBQ JwaA55LCwCtIzxaQ1D8PAWeRvun/hPTkPqlSBgB1axbpneEHAXvjPiRN1q3ABaSGfyawKOts1Doe vNWLLYBXkt5EuBewXt7pSEUbavhD44aMc5EMAOqbNYDdgH1IgeCZwDpZZyTl9XdWNfvzseGrMAYA VWVN4CmkMLAPsCc+cEjNdgswn1VN//qck5EmYgBQlOnAU0l3Fmw/bGyB+6Hq415SY79u2Lh+5fA3 fNWKB17ltjawLasCweNX/rkd6ZkEUrQHWdXUhzf560i36EmNYABQyeaSnko4a9hYd8S/jxy+6VAA g6Rb6xatHIu7+OeHMsxXkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJdfP/ AfsIk1iEi8i3AAAAAElFTkSuQmCC " + id="image2233" + x="-172.15733" + y="-373.1777" /> + <g + id="g2453" + transform="matrix(0.1476827,0,0,0.1476827,28.063629,58.273139)" + style="fill:#ffffff;fill-opacity:1"> + <path + style="fill:#ffffff;fill-opacity:1" + d="m 177.83434,-320.9277 -22.49167,-22.75 22.75,22.49167 c 21.13932,20.89928 23.20102,23.00833 22.49167,23.00833 -0.14209,0 -10.37959,-10.2375 -22.75,-22.75 z M 13.1117,-326.87069 c 0.97297,-0.25354 2.32297,-0.23687 3,0.0371 0.67703,0.27392 -0.11903,0.48137 -1.76903,0.46099 -1.65,-0.0204 -2.20394,-0.2445 -1.23097,-0.49804 z" + id="path2465" /> + <path + style="fill:#ffffff;fill-opacity:1" + d="m 265.34267,58.8223 c 13.19141,-13.2 24.20937,-24 24.48437,-24 0.275,0 -10.29296,10.8 -23.48437,24 -13.19141,13.2 -24.20937,24 -24.48437,24 -0.275,0 10.29296,-10.8 23.48437,-24 z m -21.00426,-313.25 -43.99574,-44.25 44.25,43.99574 c 24.3375,24.19765 44.25,44.11015 44.25,44.25 0,0.70358 -3.61144,-2.86659 -44.50426,-43.99574 z m -151.54957,-18.5 -3.44617,-3.75 3.75,3.44617 c 2.0625,1.89539 3.75,3.58289 3.75,3.75 0,0.76362 -0.84622,0.0443 -4.05383,-3.44617 z" + id="path2463" /> + <path + style="fill:#ffffff;fill-opacity:1" + d="m 9.33329,-223.4277 -19.99062,-20.25 20.25,19.99062 c 11.1375,10.99485 20.25,20.10735 20.25,20.25 0,0.7108 -1.93525,-1.17549 -20.50938,-19.99062 z" + id="path2461" /> + <path + style="fill:#ffffff;fill-opacity:1" + d="m -85.775566,-3.6777 c 0.002,-6.6 0.16382,-9.16987 0.35965,-5.71081 0.19583,3.45905 0.194213,8.85905 -0.0036,12 -0.197803,3.14094 -0.358027,0.31081 -0.35605,-6.28919 z m -0.885514,-49.75 -49.99625,-50.25 50.25,49.99625 c 46.70792,46.47205 50.6991,50.50375 49.99625,50.50375 -0.13956,0 -22.75206,-22.6125 -50.25,-50.25 z m 19.9975,-113 -29.99375,-30.25 30.25,29.99375 c 28.11229,27.87415 30.70017,30.50625 29.99375,30.50625 -0.14094,0 -13.75344,-13.6125 -30.25,-30.25 z m 179.25625,-193.41228 c 0.6875,-0.27741 1.8125,-0.27741 2.5,0 0.6875,0.27741 0.125,0.50439 -1.25,0.50439 -1.375,0 -1.9375,-0.22698 -1.25,-0.50439 z" + id="path2459" /> + <path + style="fill:#ffffff;fill-opacity:1" + d="m 40.66541,125.07268 c 18.32751,-0.15264 48.02751,-0.15255 66,1.9e-4 17.97249,0.15275 2.97726,0.27763 -33.32274,0.27752 -36.3,-1.1e-4 -51.00477,-0.12508 -32.67726,-0.27771 z" + id="path2457" /> + <path + style="fill:#ffffff;fill-opacity:1" + d="m -13.2356,123.27051 c -38.61609,-6.99285 -70.305205,-31.54668 -87.46468,-67.77069 -8.90855,-18.80614 -11.43396,-35.14607 -11.4485,-74.0744 l -0.009,-22.89689 -23.75,-24.15125 c -14.51304,-14.75823 -25.01854,-26.298 -27.01166,-29.67096 -14.48601,-24.51465 -10.8692,-54.30964 8.97813,-73.9611 7.1312,-7.06083 19.47416,-14.12467 27.04457,-15.47753 4.6412,-0.8294 5.25632,-1.72617 3.26678,-4.76258 -3.06652,-4.6801 -6.54508,-15.1984 -7.72483,-23.35792 -3.89332,-26.9276 13.91946,-54.89072 40.63063,-63.78335 11.792941,-3.92608 27.7618,-3.68524 39.19131,0.59109 3.03219,1.13448 5.71391,1.86187 5.95937,1.6164 0.24546,-0.24546 1.1183,-3.10191 1.93964,-6.34766 3.8861,-15.35695 16.02707,-31.25487 29.16211,-38.18614 20.65084,-10.89731 45.83715,-9.14388 64.56294,4.49477 3.40146,2.4774 4.87884,3.08021 5.20664,2.12443 0.25171,-0.73394 1.37191,-4.0039 2.48935,-7.26658 5.64699,-16.48813 19.83504,-30.77956 36.33224,-36.59697 4.37705,-1.54348 9.48547,-2.34052 17.22278,-2.6872 12.9647,-0.5809 20.17191,0.92444 31,6.47484 6.36832,3.26435 13.21359,9.77654 75.85594,72.16481 74.25477,73.9536 82.12607,82.20537 90.98496,95.3829 20.49125,30.48058 29.8237,61.05096 29.8237,97.69377 0,37.54442 -10.39645,70.43549 -31.92483,101 -8.78297,12.46946 -71.72291,75.7536 -83.781,84.23917 -18.98786,13.36223 -39.01575,21.48796 -62.4227,25.32618 -12.29951,2.01685 -162.88942,1.91553 -174.11434,-0.11714 z M 167.84267,95.66544 c 13.00883,-3.58796 26.12766,-9.43085 36.66147,-16.32835 7.11327,-4.65775 15.48569,-12.44634 42.89208,-39.90112 36.27435,-36.33839 41.61946,-42.81883 51.2318,-62.11367 4.3762,-8.78432 10.52931,-27.2059 12.24201,-36.6509 7.19984,-39.70473 -1.75087,-80.60585 -24.79605,-113.30787 -6.86507,-9.74181 -148.42805,-152.33641 -154.76776,-155.89561 -13.52398,-7.59254 -30.53569,-5.42062 -41.15103,5.25383 -10.05024,10.10622 -12.54017,24.84956 -6.48524,38.40043 1.0322,2.31007 4.92543,7.47878 8.65161,11.48604 5.87698,6.32028 6.8438,7.89736 7.2949,11.89948 1.14661,10.17276 -7.75999,16.92979 -17.45869,13.2451 -1.54831,-0.58823 -13.1651,-11.31812 -25.8151,-23.84421 -25.19323,-24.94645 -28.01185,-26.95633 -39.04496,-27.84196 -15.10311,-1.21234 -29.26024,7.67126 -34.54398,21.67632 -1.69243,4.48594 -2.02141,7.14714 -1.67871,13.57935 0.68198,12.80006 2.48805,15.44584 28.32878,41.5 12.27363,12.375 22.81736,23.82981 23.43052,25.45513 2.49373,6.61024 -1.68946,14.45063 -8.79774,16.48926 -7.20346,2.06592 -8.46385,1.08726 -43.69391,-33.92736 -19.91433,-19.79256 -34.59872,-33.59856 -37.03172,-34.81658 -9.27783,-4.64471 -21.724692,-4.6611 -30.78177,-0.0405 -5.065503,2.58423 -12.005185,9.58424 -14.65931,14.78675 -4.98422,9.76989 -4.70653,22.57716 0.69499,32.05332 1.440534,2.52719 16.969332,18.99452 35.42143,37.56222 l 32.85638,33.0622 v 4.78349 c 0,5.81973 -2.30635,10.03503 -6.60719,12.07591 -3.74999,1.77949 -7.1903,1.92385 -11.03273,0.46296 -1.52356,-0.57925 -11.79036,-9.95835 -22.815096,-20.84244 -23.10878,-22.81396 -24.399475,-23.60436 -38.544984,-23.60436 -7.10469,0 -9.42713,0.43467 -14.14796,2.64796 -17.40512,8.16013 -24.17927,27.98266 -15.63289,45.74494 1.63049,3.38871 16.30771,18.77052 55.498786,58.16305 L -33.15733,0.4342 v 5.15925 c 0,4.32805 -0.49257,5.72026 -3.05734,8.64138 -3.49284,3.97813 -9.46969,5.48633 -14.35736,3.62294 -1.60308,-0.61116 -10.32378,-8.40666 -19.37933,-17.32334 l -16.464643,-16.21213 0.602934,17 c 0.620571,17.49727 2.04533,26.57739 5.706649,36.36897 9.52461,25.47196 30.59438,46.63873 55.36009,55.61493 15.16012,5.49471 13.55553,5.41277 101.589,5.18775 l 81.5,-0.20832 z" + id="path2455" /> + </g> + </g> +</svg> diff --git a/src/training/buff.rs b/src/training/buff.rs index bd2cbdd..bc1cb8d 100644 --- a/src/training/buff.rs +++ b/src/training/buff.rs @@ -1,246 +1,246 @@ -use crate::common::consts::*; -use crate::common::*; -use crate::is_operation_cpu; -use crate::training::frame_counter; -use crate::training::handle_add_limit; -use smash::app::{self, lua_bind::*}; -use smash::lib::lua_const::*; - -static mut BUFF_DELAY_COUNTER: usize = 0; - -static mut BUFF_REMAINING_PLAYER: i32 = 0; -static mut BUFF_REMAINING_CPU: i32 = 0; - -static mut IS_BUFFING_PLAYER: bool = false; -static mut IS_BUFFING_CPU: bool = false; - -pub fn init() { - unsafe { - BUFF_DELAY_COUNTER = frame_counter::register_counter(); - } -} - -pub unsafe fn restart_buff(module_accessor: &mut app::BattleObjectModuleAccessor) { - if is_operation_cpu(module_accessor) { - IS_BUFFING_CPU = false; - return; - } - IS_BUFFING_PLAYER = false; -} - -pub unsafe fn start_buff(module_accessor: &mut app::BattleObjectModuleAccessor) { - if is_operation_cpu(module_accessor) { - IS_BUFFING_CPU = true; - return; - } - IS_BUFFING_PLAYER = true; -} - -pub unsafe fn is_buffing(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { - if is_operation_cpu(module_accessor) { - return IS_BUFFING_CPU; - } - return IS_BUFFING_PLAYER; -} - -pub unsafe fn set_buff_rem(module_accessor: &mut app::BattleObjectModuleAccessor, new_value: i32) { - if is_operation_cpu(module_accessor) { - BUFF_REMAINING_CPU = new_value; - return; - } - BUFF_REMAINING_PLAYER = new_value; -} - -pub unsafe fn get_buff_rem(module_accessor: &mut app::BattleObjectModuleAccessor) -> i32 { - if is_operation_cpu(module_accessor) { - return BUFF_REMAINING_CPU; - } - return BUFF_REMAINING_PLAYER; -} - -fn get_spell_vec() -> Vec<BuffOption> { - unsafe { - let menu_buff = MENU.buff_state.to_vec(); - let menu_iter = menu_buff.iter(); - let mut spell_buff: Vec<BuffOption> = Vec::new(); - for buff in menu_iter { - if buff.into_int().unwrap_or(1) != 1 { - // all non-spells into_int as 1 - spell_buff.push(*buff); - } - } - return spell_buff; - } -} - -pub unsafe fn handle_buffs( - module_accessor: &mut app::BattleObjectModuleAccessor, - fighter_kind: i32, - status: i32, - percent: f32, -) -> bool { - SoundModule::stop_all_sound(module_accessor); // silences buff sfx other than KO Punch - ControlModule::stop_rumble(module_accessor, false); - MotionAnimcmdModule::set_sleep(module_accessor, false); - - let menu_vec = MENU.buff_state.to_vec(); - - if fighter_kind == *FIGHTER_KIND_BRAVE { - return buff_hero(module_accessor, status); - } else if fighter_kind == *FIGHTER_KIND_JACK && menu_vec.contains(&BuffOption::ARSENE) { - return buff_joker(module_accessor); - } else if fighter_kind == *FIGHTER_KIND_WIIFIT && menu_vec.contains(&BuffOption::BREATHING) { - return buff_wiifit(module_accessor, status); - } else if fighter_kind == *FIGHTER_KIND_CLOUD && menu_vec.contains(&BuffOption::LIMIT) { - return buff_cloud(module_accessor); - } else if fighter_kind == *FIGHTER_KIND_LITTLEMAC && menu_vec.contains(&BuffOption::KO) { - return buff_mac(module_accessor); - } else if fighter_kind == *FIGHTER_KIND_EDGE && menu_vec.contains(&BuffOption::WING) { - return buff_sepiroth(module_accessor, percent); - } - - return true; -} - -unsafe fn buff_hero(module_accessor: &mut app::BattleObjectModuleAccessor, status: i32) -> bool { - let buff_vec = get_spell_vec(); - if !is_buffing(module_accessor) { - // Initial set up for spells - start_buff(module_accessor); - set_buff_rem(module_accessor, buff_vec.len() as i32); - // Since it's the first step of buffing, we need to set up how many buffs there are - } - if get_buff_rem(module_accessor) <= 0 { - // If there are no buffs selected/left, we're done - return true; - } - buff_hero_single(module_accessor, status, buff_vec); - return false; -} - -unsafe fn buff_hero_single( - module_accessor: &mut app::BattleObjectModuleAccessor, - status: i32, - buff_vec: Vec<BuffOption>, -) { - let prev_status_kind = StatusModule::prev_status_kind(module_accessor, 0); - if prev_status_kind == FIGHTER_BRAVE_STATUS_KIND_SPECIAL_LW_START { - // If we just applied a buff successfully, subtract from buffs remaining - let new_rem_value = get_buff_rem(module_accessor) - 1; - set_buff_rem(module_accessor, new_rem_value); - } - let spell_index = get_buff_rem(module_accessor) - 1; - // Used to get spell from our vector - let spell_option = buff_vec.get(spell_index as usize); - if spell_option.is_none() { - // There are no spells selected, or something went wrong with making the vector - return; - } - let real_spell_value = spell_option.unwrap().into_int().unwrap(); - if status != FIGHTER_BRAVE_STATUS_KIND_SPECIAL_LW_START { - WorkModule::set_int( - module_accessor, - real_spell_value, - *FIGHTER_BRAVE_INSTANCE_WORK_ID_INT_SPECIAL_LW_DECIDE_COMMAND, - ); - StatusModule::change_status_force( - module_accessor, - *FIGHTER_BRAVE_STATUS_KIND_SPECIAL_LW_START, - true, - // True to prevent Shielding over the spells - ); - } - if status == FIGHTER_BRAVE_STATUS_KIND_SPECIAL_LW_START { - MotionModule::set_rate(module_accessor, 50.0); - } -} - -unsafe fn buff_cloud(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { - if !is_buffing(module_accessor) { - // Only need to add to the limit gauge once - start_buff(module_accessor); - handle_add_limit(100.0, module_accessor, 0); - } - if frame_counter::should_delay(2 as u32, BUFF_DELAY_COUNTER) { - // Need to wait 2 frames to make sure we stop the limit SFX, since it's a bit delayed - return false; - } - return true; -} - -unsafe fn buff_joker(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { - if !is_buffing(module_accessor) { - // Only need to add to the rebel gauge once - start_buff(module_accessor); - let entry_id = app::FighterEntryID(FighterId::CPU as i32); - // Strangely, this doesn't actually matter and works for both fighters - app::FighterSpecializer_Jack::add_rebel_gauge(module_accessor, entry_id, 120.0); - } - if frame_counter::should_delay(2 as u32, BUFF_DELAY_COUNTER) { - // Need to wait 2 frames to make sure we stop the voice call, since it's a bit delayed - return false; - } - return true; -} - -unsafe fn buff_mac(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { - WorkModule::set_float( - module_accessor, - 100.0, - *FIGHTER_LITTLEMAC_INSTANCE_WORK_ID_FLOAT_KO_GAGE, - ); - // Trying to stop KO Punch from playing seems to make it play multiple times in rapid succession - // Look at 0x7100c44b60 for the func that handles this - // Need to figure out how to update the KO meter if this is fixed - return true; -} - -unsafe fn buff_sepiroth( - module_accessor: &mut app::BattleObjectModuleAccessor, - percent: f32, -) -> bool { - start_buff(module_accessor); - if WorkModule::get_int( - module_accessor, - *FIGHTER_EDGE_INSTANCE_WORK_ID_INT_ONE_WINGED_WING_STATE, - ) == 1 - { - // Once we're in wing, heal to correct damage - DamageModule::heal( - module_accessor, - -1.0 * DamageModule::damage(module_accessor, 0), - 0, - ); - DamageModule::add_damage(module_accessor, percent, 0); - return true; - } else { - // if we're not in wing, add damage - DamageModule::add_damage(module_accessor, 1000.0, 0); - } - return false; -} - -unsafe fn buff_wiifit(module_accessor: &mut app::BattleObjectModuleAccessor, status: i32) -> bool { - if is_buffing(module_accessor) { - if frame_counter::should_delay(2 as u32, BUFF_DELAY_COUNTER) { - // Need to wait 2 frames to make sure we stop breathing SFX - return false; - } - return true; - } - let prev_status_kind = StatusModule::prev_status_kind(module_accessor, 0); - if prev_status_kind == FIGHTER_WIIFIT_STATUS_KIND_SPECIAL_LW_SUCCESS { - start_buff(module_accessor); - return false; - } - if status != FIGHTER_WIIFIT_STATUS_KIND_SPECIAL_LW_SUCCESS { - StatusModule::change_status_force( - module_accessor, - *FIGHTER_WIIFIT_STATUS_KIND_SPECIAL_LW_SUCCESS, - false, - ); - } else { - MotionModule::set_rate(module_accessor, 40.0); - } - return false; -} +use crate::common::consts::*; +use crate::common::*; +use crate::is_operation_cpu; +use crate::training::frame_counter; +use crate::training::handle_add_limit; +use smash::app::{self, lua_bind::*}; +use smash::lib::lua_const::*; + +static mut BUFF_DELAY_COUNTER: usize = 0; + +static mut BUFF_REMAINING_PLAYER: i32 = 0; +static mut BUFF_REMAINING_CPU: i32 = 0; + +static mut IS_BUFFING_PLAYER: bool = false; +static mut IS_BUFFING_CPU: bool = false; + +pub fn init() { + unsafe { + BUFF_DELAY_COUNTER = frame_counter::register_counter(); + } +} + +pub unsafe fn restart_buff(module_accessor: &mut app::BattleObjectModuleAccessor) { + if is_operation_cpu(module_accessor) { + IS_BUFFING_CPU = false; + return; + } + IS_BUFFING_PLAYER = false; +} + +pub unsafe fn start_buff(module_accessor: &mut app::BattleObjectModuleAccessor) { + if is_operation_cpu(module_accessor) { + IS_BUFFING_CPU = true; + return; + } + IS_BUFFING_PLAYER = true; +} + +pub unsafe fn is_buffing(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { + if is_operation_cpu(module_accessor) { + return IS_BUFFING_CPU; + } + return IS_BUFFING_PLAYER; +} + +pub unsafe fn set_buff_rem(module_accessor: &mut app::BattleObjectModuleAccessor, new_value: i32) { + if is_operation_cpu(module_accessor) { + BUFF_REMAINING_CPU = new_value; + return; + } + BUFF_REMAINING_PLAYER = new_value; +} + +pub unsafe fn get_buff_rem(module_accessor: &mut app::BattleObjectModuleAccessor) -> i32 { + if is_operation_cpu(module_accessor) { + return BUFF_REMAINING_CPU; + } + return BUFF_REMAINING_PLAYER; +} + +fn get_spell_vec() -> Vec<BuffOption> { + unsafe { + let menu_buff = MENU.buff_state.to_vec(); + let menu_iter = menu_buff.iter(); + let mut spell_buff: Vec<BuffOption> = Vec::new(); + for buff in menu_iter { + if buff.into_int().unwrap_or(1) != 1 { + // all non-spells into_int as 1 + spell_buff.push(*buff); + } + } + return spell_buff; + } +} + +pub unsafe fn handle_buffs( + module_accessor: &mut app::BattleObjectModuleAccessor, + fighter_kind: i32, + status: i32, + percent: f32, +) -> bool { + SoundModule::stop_all_sound(module_accessor); // silences buff sfx other than KO Punch + ControlModule::stop_rumble(module_accessor, false); + MotionAnimcmdModule::set_sleep(module_accessor, false); + + let menu_vec = MENU.buff_state.to_vec(); + + if fighter_kind == *FIGHTER_KIND_BRAVE { + return buff_hero(module_accessor, status); + } else if fighter_kind == *FIGHTER_KIND_JACK && menu_vec.contains(&BuffOption::ARSENE) { + return buff_joker(module_accessor); + } else if fighter_kind == *FIGHTER_KIND_WIIFIT && menu_vec.contains(&BuffOption::BREATHING) { + return buff_wiifit(module_accessor, status); + } else if fighter_kind == *FIGHTER_KIND_CLOUD && menu_vec.contains(&BuffOption::LIMIT) { + return buff_cloud(module_accessor); + } else if fighter_kind == *FIGHTER_KIND_LITTLEMAC && menu_vec.contains(&BuffOption::KO) { + return buff_mac(module_accessor); + } else if fighter_kind == *FIGHTER_KIND_EDGE && menu_vec.contains(&BuffOption::WING) { + return buff_sepiroth(module_accessor, percent); + } + + return true; +} + +unsafe fn buff_hero(module_accessor: &mut app::BattleObjectModuleAccessor, status: i32) -> bool { + let buff_vec = get_spell_vec(); + if !is_buffing(module_accessor) { + // Initial set up for spells + start_buff(module_accessor); + set_buff_rem(module_accessor, buff_vec.len() as i32); + // Since it's the first step of buffing, we need to set up how many buffs there are + } + if get_buff_rem(module_accessor) <= 0 { + // If there are no buffs selected/left, we're done + return true; + } + buff_hero_single(module_accessor, status, buff_vec); + return false; +} + +unsafe fn buff_hero_single( + module_accessor: &mut app::BattleObjectModuleAccessor, + status: i32, + buff_vec: Vec<BuffOption>, +) { + let prev_status_kind = StatusModule::prev_status_kind(module_accessor, 0); + if prev_status_kind == FIGHTER_BRAVE_STATUS_KIND_SPECIAL_LW_START { + // If we just applied a buff successfully, subtract from buffs remaining + let new_rem_value = get_buff_rem(module_accessor) - 1; + set_buff_rem(module_accessor, new_rem_value); + } + let spell_index = get_buff_rem(module_accessor) - 1; + // Used to get spell from our vector + let spell_option = buff_vec.get(spell_index as usize); + if spell_option.is_none() { + // There are no spells selected, or something went wrong with making the vector + return; + } + let real_spell_value = spell_option.unwrap().into_int().unwrap(); + if status != FIGHTER_BRAVE_STATUS_KIND_SPECIAL_LW_START { + WorkModule::set_int( + module_accessor, + real_spell_value, + *FIGHTER_BRAVE_INSTANCE_WORK_ID_INT_SPECIAL_LW_DECIDE_COMMAND, + ); + StatusModule::change_status_force( + module_accessor, + *FIGHTER_BRAVE_STATUS_KIND_SPECIAL_LW_START, + true, + // True to prevent Shielding over the spells + ); + } + if status == FIGHTER_BRAVE_STATUS_KIND_SPECIAL_LW_START { + MotionModule::set_rate(module_accessor, 50.0); + } +} + +unsafe fn buff_cloud(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { + if !is_buffing(module_accessor) { + // Only need to add to the limit gauge once + start_buff(module_accessor); + handle_add_limit(100.0, module_accessor, 0); + } + if frame_counter::should_delay(2 as u32, BUFF_DELAY_COUNTER) { + // Need to wait 2 frames to make sure we stop the limit SFX, since it's a bit delayed + return false; + } + return true; +} + +unsafe fn buff_joker(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { + if !is_buffing(module_accessor) { + // Only need to add to the rebel gauge once + start_buff(module_accessor); + let entry_id = app::FighterEntryID(FighterId::CPU as i32); + // Strangely, this doesn't actually matter and works for both fighters + app::FighterSpecializer_Jack::add_rebel_gauge(module_accessor, entry_id, 120.0); + } + if frame_counter::should_delay(2 as u32, BUFF_DELAY_COUNTER) { + // Need to wait 2 frames to make sure we stop the voice call, since it's a bit delayed + return false; + } + return true; +} + +unsafe fn buff_mac(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { + WorkModule::set_float( + module_accessor, + 100.0, + *FIGHTER_LITTLEMAC_INSTANCE_WORK_ID_FLOAT_KO_GAGE, + ); + // Trying to stop KO Punch from playing seems to make it play multiple times in rapid succession + // Look at 0x7100c44b60 for the func that handles this + // Need to figure out how to update the KO meter if this is fixed + return true; +} + +unsafe fn buff_sepiroth( + module_accessor: &mut app::BattleObjectModuleAccessor, + percent: f32, +) -> bool { + start_buff(module_accessor); + if WorkModule::get_int( + module_accessor, + *FIGHTER_EDGE_INSTANCE_WORK_ID_INT_ONE_WINGED_WING_STATE, + ) == 1 + { + // Once we're in wing, heal to correct damage + DamageModule::heal( + module_accessor, + -1.0 * DamageModule::damage(module_accessor, 0), + 0, + ); + DamageModule::add_damage(module_accessor, percent, 0); + return true; + } else { + // if we're not in wing, add damage + DamageModule::add_damage(module_accessor, 1000.0, 0); + } + return false; +} + +unsafe fn buff_wiifit(module_accessor: &mut app::BattleObjectModuleAccessor, status: i32) -> bool { + if is_buffing(module_accessor) { + if frame_counter::should_delay(2 as u32, BUFF_DELAY_COUNTER) { + // Need to wait 2 frames to make sure we stop breathing SFX + return false; + } + return true; + } + let prev_status_kind = StatusModule::prev_status_kind(module_accessor, 0); + if prev_status_kind == FIGHTER_WIIFIT_STATUS_KIND_SPECIAL_LW_SUCCESS { + start_buff(module_accessor); + return false; + } + if status != FIGHTER_WIIFIT_STATUS_KIND_SPECIAL_LW_SUCCESS { + StatusModule::change_status_force( + module_accessor, + *FIGHTER_WIIFIT_STATUS_KIND_SPECIAL_LW_SUCCESS, + false, + ); + } else { + MotionModule::set_rate(module_accessor, 40.0); + } + return false; +} diff --git a/src/training/mod.rs b/src/training/mod.rs index d46ee4f..858b8e7 100644 --- a/src/training/mod.rs +++ b/src/training/mod.rs @@ -1,535 +1,535 @@ -use crate::common::{is_training_mode, menu, FIGHTER_MANAGER_ADDR, STAGE_MANAGER_ADDR}; -use crate::hitbox_visualizer; -use skyline::hooks::{getRegionAddress, InlineCtx, Region}; -use skyline::nn::hid::*; -use skyline::nn::ro::LookupSymbol; -use smash::app::{self, enSEType, lua_bind::*}; -use smash::lib::lua_const::*; -use smash::params::*; -use smash::phx::{Hash40, Vector3f}; - -pub mod buff; -pub mod combo; -pub mod directional_influence; -pub mod frame_counter; -pub mod ledge; -pub mod sdi; -pub mod shield; -pub mod tech; -pub mod throw; - -mod air_dodge_direction; -mod attack_angle; -mod character_specific; -mod fast_fall; -mod full_hop; -mod input_delay; -mod input_record; -mod mash; -mod reset; -mod save_states; -mod shield_tilt; - -#[skyline::hook(replace = WorkModule::get_param_float)] -pub unsafe fn handle_get_param_float( - module_accessor: &mut app::BattleObjectModuleAccessor, - param_type: u64, - param_hash: u64, -) -> f32 { - let ori = original!()(module_accessor, param_type, param_hash); - if !is_training_mode() { - return ori; - } - - shield::get_param_float(module_accessor, param_type, param_hash).unwrap_or(ori) -} - -#[skyline::hook(replace = WorkModule::get_param_int)] -pub unsafe fn handle_get_param_int( - module_accessor: &mut app::BattleObjectModuleAccessor, - param_type: u64, - param_hash: u64, -) -> i32 { - let ori = original!()(module_accessor, param_type, param_hash); - - if !is_training_mode() { - return ori; - } - - save_states::get_param_int(module_accessor, param_type, param_hash).unwrap_or(ori) -} - -#[skyline::hook(replace = ControlModule::get_attack_air_kind)] -pub unsafe fn handle_get_attack_air_kind( - module_accessor: &mut app::BattleObjectModuleAccessor, -) -> i32 { - let ori = original!()(module_accessor); - if !is_training_mode() { - return ori; - } - - mash::get_attack_air_kind(module_accessor).unwrap_or(ori) -} - -#[skyline::hook(replace = ControlModule::get_command_flag_cat)] -pub unsafe fn handle_get_command_flag_cat( - module_accessor: &mut app::BattleObjectModuleAccessor, - category: i32, -) -> i32 { - let mut flag = original!()(module_accessor, category); - - if category == FIGHTER_PAD_COMMAND_CATEGORY1 { - shield::param_installer(); - } - - if !is_training_mode() { - return flag; - } - - flag |= mash::get_command_flag_cat(module_accessor, category); - // Get throw directions - flag |= throw::get_command_flag_throw_direction(module_accessor); - - once_per_frame_per_fighter(module_accessor, category); - - flag -} - -fn once_per_frame_per_fighter( - module_accessor: &mut app::BattleObjectModuleAccessor, - category: i32, -) { - if category != FIGHTER_PAD_COMMAND_CATEGORY1 { - return; - } - - unsafe { - if crate::common::menu::menu_condition(module_accessor) { - crate::common::menu::spawn_menu(); - } - - input_record::get_command_flag_cat(module_accessor); - combo::get_command_flag_cat(module_accessor); - hitbox_visualizer::get_command_flag_cat(module_accessor); - save_states::save_states(module_accessor); - tech::get_command_flag_cat(module_accessor); - } - - fast_fall::get_command_flag_cat(module_accessor); - frame_counter::get_command_flag_cat(module_accessor); - ledge::get_command_flag_cat(module_accessor); - shield::get_command_flag_cat(module_accessor); - directional_influence::get_command_flag_cat(module_accessor); - reset::check_reset(module_accessor); -} - -/** - * This is called to get the stick position when - * shielding (shield tilt) - * 1 is fully right, -1 is fully left - */ -#[skyline::hook(replace = ControlModule::get_stick_x_no_clamp)] -pub unsafe fn get_stick_x_no_clamp(module_accessor: &mut app::BattleObjectModuleAccessor) -> f32 { - let ori = original!()(module_accessor); - if !is_training_mode() { - return ori; - } - - shield_tilt::mod_get_stick_x(module_accessor).unwrap_or(ori) -} - -/** - * This is called to get the stick position when - * shielding (shield tilt) - * 1 is fully up, -1 is fully down - */ -#[skyline::hook(replace = ControlModule::get_stick_y_no_clamp)] -pub unsafe fn get_stick_y_no_clamp(module_accessor: &mut app::BattleObjectModuleAccessor) -> f32 { - let ori = original!()(module_accessor); - if !is_training_mode() { - return ori; - } - - shield_tilt::mod_get_stick_y(module_accessor).unwrap_or(ori) -} - -/** - * Called when: - * Walking in the facing direction - * Air Dodging - */ -#[skyline::hook(replace = ControlModule::get_stick_x)] -pub unsafe fn get_stick_x(module_accessor: &mut app::BattleObjectModuleAccessor) -> f32 { - let ori = original!()(module_accessor); - if !is_training_mode() { - return ori; - } - - air_dodge_direction::mod_get_stick_x(module_accessor).unwrap_or(ori) -} - -/** - * Called when: - * angled ftilt/fsmash - */ -#[skyline::hook(replace = ControlModule::get_stick_dir)] -pub unsafe fn get_stick_dir(module_accessor: &mut app::BattleObjectModuleAccessor) -> f32 { - let ori = original!()(module_accessor); - if !is_training_mode() { - return ori; - } - - attack_angle::mod_get_stick_dir(module_accessor).unwrap_or(ori) -} - -/** - * - */ -#[skyline::hook(replace = ControlModule::get_stick_y)] -pub unsafe fn get_stick_y(module_accessor: &mut app::BattleObjectModuleAccessor) -> f32 { - let ori = original!()(module_accessor); - if !is_training_mode() { - return ori; - } - - air_dodge_direction::mod_get_stick_y(module_accessor).unwrap_or(ori) -} - -#[skyline::hook(replace = ControlModule::check_button_on)] -pub unsafe fn handle_check_button_on( - module_accessor: &mut app::BattleObjectModuleAccessor, - button: i32, -) -> bool { - let ori = original!()(module_accessor, button); - if !is_training_mode() { - return ori; - } - - shield::check_button_on(module_accessor, button) - .unwrap_or_else(|| full_hop::check_button_on(module_accessor, button).unwrap_or(ori)) -} - -#[skyline::hook(replace = ControlModule::check_button_off)] -pub unsafe fn handle_check_button_off( - module_accessor: &mut app::BattleObjectModuleAccessor, - button: i32, -) -> bool { - let ori = original!()(module_accessor, button); - if !is_training_mode() { - return ori; - } - - shield::check_button_off(module_accessor, button) - .unwrap_or_else(|| full_hop::check_button_off(module_accessor, button).unwrap_or(ori)) -} - -#[skyline::hook(replace = MotionModule::change_motion)] -pub unsafe fn handle_change_motion( - module_accessor: &mut app::BattleObjectModuleAccessor, - motion_kind: u64, - unk1: f32, - unk2: f32, - unk3: bool, - unk4: f32, - unk5: bool, - unk6: bool, -) -> u64 { - let mod_motion_kind = if is_training_mode() { - tech::change_motion(module_accessor, motion_kind).unwrap_or(motion_kind) - } else { - motion_kind - }; - - original!()( - module_accessor, - mod_motion_kind, - unk1, - unk2, - unk3, - unk4, - unk5, - unk6, - ) -} - -#[skyline::hook(replace = WorkModule::is_enable_transition_term)] -pub unsafe fn handle_is_enable_transition_term( - module_accessor: *mut app::BattleObjectModuleAccessor, - transition_term: i32, -) -> bool { - let ori = original!()(module_accessor, transition_term); - - if !is_training_mode() { - return ori; - } - - combo::is_enable_transition_term(module_accessor, transition_term, ori); - match ledge::is_enable_transition_term(module_accessor, transition_term) { - Some(r) => r, - None => ori, - } -} - -extern "C" { - #[link_name = "\u{1}_ZN3app15sv_fighter_util15set_dead_rumbleEP9lua_State"] - pub fn set_dead_rumble(lua_state: u64) -> u64; -} - -#[skyline::hook(replace = set_dead_rumble)] -pub unsafe fn handle_set_dead_rumble(lua_state: u64) -> u64 { - if is_training_mode() { - return 0; - } - - original!()(lua_state) -} - -#[skyline::hook(replace = CameraModule::req_quake)] -pub unsafe fn handle_req_quake( - module_accessor: &mut app::BattleObjectModuleAccessor, - my_int: i32, -) -> u64 { - if !is_training_mode() { - return original!()(module_accessor, my_int); - } - if save_states::is_killing() { - return original!()(module_accessor, *CAMERA_QUAKE_KIND_NONE); - } - original!()(module_accessor, my_int) -} - -pub static mut COMMON_PARAMS: *mut CommonParams = 0 as *mut _; - -fn params_main(params_info: &ParamsInfo<'_>) { - if let Ok(common) = params_info.get::<CommonParams>() { - unsafe { - COMMON_PARAMS = common as *mut _; - } - } -} - -static CLOUD_ADD_LIMIT_OFFSET: usize = 0x008dc140; // this function is used to add limit to Cloud's limit gauge. Hooking it here so we can call it in buff.rs -#[skyline::hook(offset = CLOUD_ADD_LIMIT_OFFSET)] -pub unsafe fn handle_add_limit( - add_limit: f32, - module_accessor: &mut app::BattleObjectModuleAccessor, - is_special_lw: u64, -) { - original!()(add_limit, module_accessor, is_special_lw) -} - -#[skyline::hook(replace = EffectModule::req_screen)] // hooked to prevent the screen from darkening when loading a save state with One-Winged Angel -pub unsafe fn handle_req_screen( - module_accessor: &mut app::BattleObjectModuleAccessor, - my_hash: Hash40, - bool_1: bool, - bool_2: bool, - bool_3: bool, -) -> u64 { - if !is_training_mode() { - return original!()(module_accessor, my_hash, bool_1, bool_2, bool_3); - } - let new_hash = my_hash.hash; - if new_hash == 72422354958 && buff::is_buffing(module_accessor) { - // Wing bg hash - let replace_hash = Hash40::new("bg"); - return original!()(module_accessor, replace_hash, bool_1, bool_2, bool_3); - } - original!()(module_accessor, my_hash, bool_1, bool_2, bool_3) -} - -#[skyline::hook(replace = app::FighterSpecializer_Jack::check_doyle_summon_dispatch)] // returns status of summon dispatch if triggered, -1 as u64 otherwise -pub unsafe fn handle_check_doyle_summon_dispatch( - module_accessor: &mut app::BattleObjectModuleAccessor, - bool_1: bool, - bool_2: bool, -) -> u64 { - let ori = original!()(module_accessor, bool_1, bool_2); - if !is_training_mode() { - return ori; - } - if ori == *FIGHTER_JACK_STATUS_KIND_SUMMON as u64 { - if buff::is_buffing(module_accessor) { - return 4294967295; - } - } - return ori; -} - -// Set Stale Moves to On -static STALE_OFFSET: usize = 0x013e88a4; -// One instruction after stale moves toggle register is set to 0 -#[skyline::hook(offset=STALE_OFFSET, inline)] -unsafe fn stale_handle(ctx: &mut InlineCtx) { - let x22 = ctx.registers[22].x.as_mut(); - let training_structure_address = (*x22 + 0xb60) as *mut u8; - *training_structure_address = 1; -} - -// Set Stale Moves to On in the menu text -static STALE_MENU_OFFSET: usize = 0x013e88a0; -// One instruction after menu text register is set to off -#[skyline::hook(offset=STALE_MENU_OFFSET, inline)] -unsafe fn stale_menu_handle(ctx: &mut InlineCtx) { - // Set the text pointer to where "mel_training_on" is located - let on_text_ptr = ((getRegionAddress(Region::Text) as u64) + (0x42b215e as u64)) as u64; - let x1 = ctx.registers[1].x.as_mut(); - *x1 = on_text_ptr; -} - -#[skyline::hook(replace = SoundModule::play_se)] // hooked to prevent death sfx from playing when loading save states -pub unsafe fn handle_se( - module_accessor: &mut app::BattleObjectModuleAccessor, - my_hash: Hash40, - bool1: bool, - bool2: bool, - bool3: bool, - bool4: bool, - se_type: enSEType, -) -> u64 { - // Make effects silent while we're killing fighters. Stops death explosion and fighter misfoot. - if save_states::is_killing() { - let silent_hash = Hash40::new("se_silent"); - return original!()( - module_accessor, - silent_hash, - bool1, - bool2, - bool3, - bool4, - se_type, - ); - } - original!()( - module_accessor, - my_hash, - bool1, - bool2, - bool3, - bool4, - se_type, - ) -} - -#[skyline::hook(replace = EffectModule::req)] // hooked to prevent death gfx from playing when loading save states -pub unsafe fn handle_effect( - module_accessor: &mut app::BattleObjectModuleAccessor, - eff_hash: Hash40, - pos: *const Vector3f, - rot: *const Vector3f, - size: f32, - arg6: u32, - arg7: i32, - arg8: bool, - arg9: i32, -) -> u64 { - if save_states::is_killing() { - // Making the size 0 prevents these effects from being displayed. Fixs throw explosions, ICs squall, etc. - return original!()( - module_accessor, - eff_hash, - pos, - rot, - 0.0, - arg6, - arg7, - arg8, - arg9, - ); - } - original!()( - module_accessor, - eff_hash, - pos, - rot, - size, - arg6, - arg7, - arg8, - arg9, - ) -} - -#[allow(improper_ctypes)] -extern "C" { - fn add_nn_hid_hook(callback: fn(*mut NpadHandheldState, *const u32)); -} - -pub fn training_mods() { - println!("[Training Modpack] Applying training mods."); - - // Input Recording/Delay - unsafe { - if (add_nn_hid_hook as *const ()).is_null() { - panic!("The NN-HID hook plugin could not be found and is required to add NRO hooks. Make sure libnn_hid_hook.nro is installed."); - } - add_nn_hid_hook(input_delay::handle_get_npad_state); - } - - unsafe { - LookupSymbol( - &mut FIGHTER_MANAGER_ADDR, - "_ZN3lib9SingletonIN3app14FighterManagerEE9instance_E\u{0}" - .as_bytes() - .as_ptr(), - ); - - LookupSymbol( - &mut STAGE_MANAGER_ADDR, - "_ZN3lib9SingletonIN3app12StageManagerEE9instance_E\u{0}" - .as_bytes() - .as_ptr(), - ); - - smash::params::add_hook(params_main).unwrap(); - } - - skyline::install_hooks!( - // Mash airdodge/jump - handle_get_command_flag_cat, - // Hold/Infinite shield - handle_check_button_on, - handle_check_button_off, - handle_get_param_float, - // Save states - handle_get_param_int, - handle_set_dead_rumble, - handle_req_quake, - // Mash attack - handle_get_attack_air_kind, - // Attack angle - get_stick_dir, - // Tech options - handle_change_motion, - // Directional AirDodge, - get_stick_x, - get_stick_y, - // Shield Tilt - get_stick_x_no_clamp, - get_stick_y_no_clamp, - // Combo - handle_is_enable_transition_term, - // SDI - crate::training::sdi::check_hit_stop_delay_command, - // Buffs - handle_add_limit, - handle_check_doyle_summon_dispatch, - handle_req_screen, - // Stale Moves - stale_handle, - stale_menu_handle, - // Death SFX - handle_se, - // Death GFX - handle_effect, - ); - - combo::init(); - shield::init(); - fast_fall::init(); - mash::init(); - ledge::init(); - throw::init(); - menu::init(); - buff::init(); -} +use crate::common::{is_training_mode, menu, FIGHTER_MANAGER_ADDR, STAGE_MANAGER_ADDR}; +use crate::hitbox_visualizer; +use skyline::hooks::{getRegionAddress, InlineCtx, Region}; +use skyline::nn::hid::*; +use skyline::nn::ro::LookupSymbol; +use smash::app::{self, enSEType, lua_bind::*}; +use smash::lib::lua_const::*; +use smash::params::*; +use smash::phx::{Hash40, Vector3f}; + +pub mod buff; +pub mod combo; +pub mod directional_influence; +pub mod frame_counter; +pub mod ledge; +pub mod sdi; +pub mod shield; +pub mod tech; +pub mod throw; + +mod air_dodge_direction; +mod attack_angle; +mod character_specific; +mod fast_fall; +mod full_hop; +mod input_delay; +mod input_record; +mod mash; +mod reset; +mod save_states; +mod shield_tilt; + +#[skyline::hook(replace = WorkModule::get_param_float)] +pub unsafe fn handle_get_param_float( + module_accessor: &mut app::BattleObjectModuleAccessor, + param_type: u64, + param_hash: u64, +) -> f32 { + let ori = original!()(module_accessor, param_type, param_hash); + if !is_training_mode() { + return ori; + } + + shield::get_param_float(module_accessor, param_type, param_hash).unwrap_or(ori) +} + +#[skyline::hook(replace = WorkModule::get_param_int)] +pub unsafe fn handle_get_param_int( + module_accessor: &mut app::BattleObjectModuleAccessor, + param_type: u64, + param_hash: u64, +) -> i32 { + let ori = original!()(module_accessor, param_type, param_hash); + + if !is_training_mode() { + return ori; + } + + save_states::get_param_int(module_accessor, param_type, param_hash).unwrap_or(ori) +} + +#[skyline::hook(replace = ControlModule::get_attack_air_kind)] +pub unsafe fn handle_get_attack_air_kind( + module_accessor: &mut app::BattleObjectModuleAccessor, +) -> i32 { + let ori = original!()(module_accessor); + if !is_training_mode() { + return ori; + } + + mash::get_attack_air_kind(module_accessor).unwrap_or(ori) +} + +#[skyline::hook(replace = ControlModule::get_command_flag_cat)] +pub unsafe fn handle_get_command_flag_cat( + module_accessor: &mut app::BattleObjectModuleAccessor, + category: i32, +) -> i32 { + let mut flag = original!()(module_accessor, category); + + if category == FIGHTER_PAD_COMMAND_CATEGORY1 { + shield::param_installer(); + } + + if !is_training_mode() { + return flag; + } + + flag |= mash::get_command_flag_cat(module_accessor, category); + // Get throw directions + flag |= throw::get_command_flag_throw_direction(module_accessor); + + once_per_frame_per_fighter(module_accessor, category); + + flag +} + +fn once_per_frame_per_fighter( + module_accessor: &mut app::BattleObjectModuleAccessor, + category: i32, +) { + if category != FIGHTER_PAD_COMMAND_CATEGORY1 { + return; + } + + unsafe { + if crate::common::menu::menu_condition(module_accessor) { + crate::common::menu::spawn_menu(); + } + + input_record::get_command_flag_cat(module_accessor); + combo::get_command_flag_cat(module_accessor); + hitbox_visualizer::get_command_flag_cat(module_accessor); + save_states::save_states(module_accessor); + tech::get_command_flag_cat(module_accessor); + } + + fast_fall::get_command_flag_cat(module_accessor); + frame_counter::get_command_flag_cat(module_accessor); + ledge::get_command_flag_cat(module_accessor); + shield::get_command_flag_cat(module_accessor); + directional_influence::get_command_flag_cat(module_accessor); + reset::check_reset(module_accessor); +} + +/** + * This is called to get the stick position when + * shielding (shield tilt) + * 1 is fully right, -1 is fully left + */ +#[skyline::hook(replace = ControlModule::get_stick_x_no_clamp)] +pub unsafe fn get_stick_x_no_clamp(module_accessor: &mut app::BattleObjectModuleAccessor) -> f32 { + let ori = original!()(module_accessor); + if !is_training_mode() { + return ori; + } + + shield_tilt::mod_get_stick_x(module_accessor).unwrap_or(ori) +} + +/** + * This is called to get the stick position when + * shielding (shield tilt) + * 1 is fully up, -1 is fully down + */ +#[skyline::hook(replace = ControlModule::get_stick_y_no_clamp)] +pub unsafe fn get_stick_y_no_clamp(module_accessor: &mut app::BattleObjectModuleAccessor) -> f32 { + let ori = original!()(module_accessor); + if !is_training_mode() { + return ori; + } + + shield_tilt::mod_get_stick_y(module_accessor).unwrap_or(ori) +} + +/** + * Called when: + * Walking in the facing direction + * Air Dodging + */ +#[skyline::hook(replace = ControlModule::get_stick_x)] +pub unsafe fn get_stick_x(module_accessor: &mut app::BattleObjectModuleAccessor) -> f32 { + let ori = original!()(module_accessor); + if !is_training_mode() { + return ori; + } + + air_dodge_direction::mod_get_stick_x(module_accessor).unwrap_or(ori) +} + +/** + * Called when: + * angled ftilt/fsmash + */ +#[skyline::hook(replace = ControlModule::get_stick_dir)] +pub unsafe fn get_stick_dir(module_accessor: &mut app::BattleObjectModuleAccessor) -> f32 { + let ori = original!()(module_accessor); + if !is_training_mode() { + return ori; + } + + attack_angle::mod_get_stick_dir(module_accessor).unwrap_or(ori) +} + +/** + * + */ +#[skyline::hook(replace = ControlModule::get_stick_y)] +pub unsafe fn get_stick_y(module_accessor: &mut app::BattleObjectModuleAccessor) -> f32 { + let ori = original!()(module_accessor); + if !is_training_mode() { + return ori; + } + + air_dodge_direction::mod_get_stick_y(module_accessor).unwrap_or(ori) +} + +#[skyline::hook(replace = ControlModule::check_button_on)] +pub unsafe fn handle_check_button_on( + module_accessor: &mut app::BattleObjectModuleAccessor, + button: i32, +) -> bool { + let ori = original!()(module_accessor, button); + if !is_training_mode() { + return ori; + } + + shield::check_button_on(module_accessor, button) + .unwrap_or_else(|| full_hop::check_button_on(module_accessor, button).unwrap_or(ori)) +} + +#[skyline::hook(replace = ControlModule::check_button_off)] +pub unsafe fn handle_check_button_off( + module_accessor: &mut app::BattleObjectModuleAccessor, + button: i32, +) -> bool { + let ori = original!()(module_accessor, button); + if !is_training_mode() { + return ori; + } + + shield::check_button_off(module_accessor, button) + .unwrap_or_else(|| full_hop::check_button_off(module_accessor, button).unwrap_or(ori)) +} + +#[skyline::hook(replace = MotionModule::change_motion)] +pub unsafe fn handle_change_motion( + module_accessor: &mut app::BattleObjectModuleAccessor, + motion_kind: u64, + unk1: f32, + unk2: f32, + unk3: bool, + unk4: f32, + unk5: bool, + unk6: bool, +) -> u64 { + let mod_motion_kind = if is_training_mode() { + tech::change_motion(module_accessor, motion_kind).unwrap_or(motion_kind) + } else { + motion_kind + }; + + original!()( + module_accessor, + mod_motion_kind, + unk1, + unk2, + unk3, + unk4, + unk5, + unk6, + ) +} + +#[skyline::hook(replace = WorkModule::is_enable_transition_term)] +pub unsafe fn handle_is_enable_transition_term( + module_accessor: *mut app::BattleObjectModuleAccessor, + transition_term: i32, +) -> bool { + let ori = original!()(module_accessor, transition_term); + + if !is_training_mode() { + return ori; + } + + combo::is_enable_transition_term(module_accessor, transition_term, ori); + match ledge::is_enable_transition_term(module_accessor, transition_term) { + Some(r) => r, + None => ori, + } +} + +extern "C" { + #[link_name = "\u{1}_ZN3app15sv_fighter_util15set_dead_rumbleEP9lua_State"] + pub fn set_dead_rumble(lua_state: u64) -> u64; +} + +#[skyline::hook(replace = set_dead_rumble)] +pub unsafe fn handle_set_dead_rumble(lua_state: u64) -> u64 { + if is_training_mode() { + return 0; + } + + original!()(lua_state) +} + +#[skyline::hook(replace = CameraModule::req_quake)] +pub unsafe fn handle_req_quake( + module_accessor: &mut app::BattleObjectModuleAccessor, + my_int: i32, +) -> u64 { + if !is_training_mode() { + return original!()(module_accessor, my_int); + } + if save_states::is_killing() { + return original!()(module_accessor, *CAMERA_QUAKE_KIND_NONE); + } + original!()(module_accessor, my_int) +} + +pub static mut COMMON_PARAMS: *mut CommonParams = 0 as *mut _; + +fn params_main(params_info: &ParamsInfo<'_>) { + if let Ok(common) = params_info.get::<CommonParams>() { + unsafe { + COMMON_PARAMS = common as *mut _; + } + } +} + +static CLOUD_ADD_LIMIT_OFFSET: usize = 0x008dc140; // this function is used to add limit to Cloud's limit gauge. Hooking it here so we can call it in buff.rs +#[skyline::hook(offset = CLOUD_ADD_LIMIT_OFFSET)] +pub unsafe fn handle_add_limit( + add_limit: f32, + module_accessor: &mut app::BattleObjectModuleAccessor, + is_special_lw: u64, +) { + original!()(add_limit, module_accessor, is_special_lw) +} + +#[skyline::hook(replace = EffectModule::req_screen)] // hooked to prevent the screen from darkening when loading a save state with One-Winged Angel +pub unsafe fn handle_req_screen( + module_accessor: &mut app::BattleObjectModuleAccessor, + my_hash: Hash40, + bool_1: bool, + bool_2: bool, + bool_3: bool, +) -> u64 { + if !is_training_mode() { + return original!()(module_accessor, my_hash, bool_1, bool_2, bool_3); + } + let new_hash = my_hash.hash; + if new_hash == 72422354958 && buff::is_buffing(module_accessor) { + // Wing bg hash + let replace_hash = Hash40::new("bg"); + return original!()(module_accessor, replace_hash, bool_1, bool_2, bool_3); + } + original!()(module_accessor, my_hash, bool_1, bool_2, bool_3) +} + +#[skyline::hook(replace = app::FighterSpecializer_Jack::check_doyle_summon_dispatch)] // returns status of summon dispatch if triggered, -1 as u64 otherwise +pub unsafe fn handle_check_doyle_summon_dispatch( + module_accessor: &mut app::BattleObjectModuleAccessor, + bool_1: bool, + bool_2: bool, +) -> u64 { + let ori = original!()(module_accessor, bool_1, bool_2); + if !is_training_mode() { + return ori; + } + if ori == *FIGHTER_JACK_STATUS_KIND_SUMMON as u64 { + if buff::is_buffing(module_accessor) { + return 4294967295; + } + } + return ori; +} + +// Set Stale Moves to On +static STALE_OFFSET: usize = 0x013e88a4; +// One instruction after stale moves toggle register is set to 0 +#[skyline::hook(offset=STALE_OFFSET, inline)] +unsafe fn stale_handle(ctx: &mut InlineCtx) { + let x22 = ctx.registers[22].x.as_mut(); + let training_structure_address = (*x22 + 0xb60) as *mut u8; + *training_structure_address = 1; +} + +// Set Stale Moves to On in the menu text +static STALE_MENU_OFFSET: usize = 0x013e88a0; +// One instruction after menu text register is set to off +#[skyline::hook(offset=STALE_MENU_OFFSET, inline)] +unsafe fn stale_menu_handle(ctx: &mut InlineCtx) { + // Set the text pointer to where "mel_training_on" is located + let on_text_ptr = ((getRegionAddress(Region::Text) as u64) + (0x42b215e as u64)) as u64; + let x1 = ctx.registers[1].x.as_mut(); + *x1 = on_text_ptr; +} + +#[skyline::hook(replace = SoundModule::play_se)] // hooked to prevent death sfx from playing when loading save states +pub unsafe fn handle_se( + module_accessor: &mut app::BattleObjectModuleAccessor, + my_hash: Hash40, + bool1: bool, + bool2: bool, + bool3: bool, + bool4: bool, + se_type: enSEType, +) -> u64 { + // Make effects silent while we're killing fighters. Stops death explosion and fighter misfoot. + if save_states::is_killing() { + let silent_hash = Hash40::new("se_silent"); + return original!()( + module_accessor, + silent_hash, + bool1, + bool2, + bool3, + bool4, + se_type, + ); + } + original!()( + module_accessor, + my_hash, + bool1, + bool2, + bool3, + bool4, + se_type, + ) +} + +#[skyline::hook(replace = EffectModule::req)] // hooked to prevent death gfx from playing when loading save states +pub unsafe fn handle_effect( + module_accessor: &mut app::BattleObjectModuleAccessor, + eff_hash: Hash40, + pos: *const Vector3f, + rot: *const Vector3f, + size: f32, + arg6: u32, + arg7: i32, + arg8: bool, + arg9: i32, +) -> u64 { + if save_states::is_killing() { + // Making the size 0 prevents these effects from being displayed. Fixs throw explosions, ICs squall, etc. + return original!()( + module_accessor, + eff_hash, + pos, + rot, + 0.0, + arg6, + arg7, + arg8, + arg9, + ); + } + original!()( + module_accessor, + eff_hash, + pos, + rot, + size, + arg6, + arg7, + arg8, + arg9, + ) +} + +#[allow(improper_ctypes)] +extern "C" { + fn add_nn_hid_hook(callback: fn(*mut NpadHandheldState, *const u32)); +} + +pub fn training_mods() { + println!("[Training Modpack] Applying training mods."); + + // Input Recording/Delay + unsafe { + if (add_nn_hid_hook as *const ()).is_null() { + panic!("The NN-HID hook plugin could not be found and is required to add NRO hooks. Make sure libnn_hid_hook.nro is installed."); + } + add_nn_hid_hook(input_delay::handle_get_npad_state); + } + + unsafe { + LookupSymbol( + &mut FIGHTER_MANAGER_ADDR, + "_ZN3lib9SingletonIN3app14FighterManagerEE9instance_E\u{0}" + .as_bytes() + .as_ptr(), + ); + + LookupSymbol( + &mut STAGE_MANAGER_ADDR, + "_ZN3lib9SingletonIN3app12StageManagerEE9instance_E\u{0}" + .as_bytes() + .as_ptr(), + ); + + smash::params::add_hook(params_main).unwrap(); + } + + skyline::install_hooks!( + // Mash airdodge/jump + handle_get_command_flag_cat, + // Hold/Infinite shield + handle_check_button_on, + handle_check_button_off, + handle_get_param_float, + // Save states + handle_get_param_int, + handle_set_dead_rumble, + handle_req_quake, + // Mash attack + handle_get_attack_air_kind, + // Attack angle + get_stick_dir, + // Tech options + handle_change_motion, + // Directional AirDodge, + get_stick_x, + get_stick_y, + // Shield Tilt + get_stick_x_no_clamp, + get_stick_y_no_clamp, + // Combo + handle_is_enable_transition_term, + // SDI + crate::training::sdi::check_hit_stop_delay_command, + // Buffs + handle_add_limit, + handle_check_doyle_summon_dispatch, + handle_req_screen, + // Stale Moves + stale_handle, + stale_menu_handle, + // Death SFX + handle_se, + // Death GFX + handle_effect, + ); + + combo::init(); + shield::init(); + fast_fall::init(); + mash::init(); + ledge::init(); + throw::init(); + menu::init(); + buff::init(); +} diff --git a/src/training/reset.rs b/src/training/reset.rs index b13b594..e0a1c89 100644 --- a/src/training/reset.rs +++ b/src/training/reset.rs @@ -1,49 +1,49 @@ -use crate::common::*; -use crate::training::frame_counter; -use crate::training::ledge; -use crate::training::mash; -use crate::training::sdi; -use crate::training::shield_tilt; -use crate::training::throw; -use smash::app::{self, lua_bind::*}; -use smash::lib::lua_const::*; - -pub fn check_reset(module_accessor: &mut app::BattleObjectModuleAccessor) { - if !is_operation_cpu(module_accessor) { - return; - } - - if !should_reset(module_accessor) { - return; - } - - on_reset(); -} - -fn should_reset(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { - if !is_idle(module_accessor) { - return false; - } - - let prev_status; - - unsafe { - prev_status = StatusModule::prev_status_kind(module_accessor, 0); - } - - // Only reset automatically on training mode reset - if prev_status != *FIGHTER_STATUS_KIND_NONE { - return false; - } - - true -} - -pub fn on_reset() { - mash::full_reset(); - sdi::roll_direction(); - frame_counter::reset_all(); - ledge::reset_ledge_delay(); - throw::reset_throw_delay(); - shield_tilt::roll_direction(); -} +use crate::common::*; +use crate::training::frame_counter; +use crate::training::ledge; +use crate::training::mash; +use crate::training::sdi; +use crate::training::shield_tilt; +use crate::training::throw; +use smash::app::{self, lua_bind::*}; +use smash::lib::lua_const::*; + +pub fn check_reset(module_accessor: &mut app::BattleObjectModuleAccessor) { + if !is_operation_cpu(module_accessor) { + return; + } + + if !should_reset(module_accessor) { + return; + } + + on_reset(); +} + +fn should_reset(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { + if !is_idle(module_accessor) { + return false; + } + + let prev_status; + + unsafe { + prev_status = StatusModule::prev_status_kind(module_accessor, 0); + } + + // Only reset automatically on training mode reset + if prev_status != *FIGHTER_STATUS_KIND_NONE { + return false; + } + + true +} + +pub fn on_reset() { + mash::full_reset(); + sdi::roll_direction(); + frame_counter::reset_all(); + ledge::reset_ledge_delay(); + throw::reset_throw_delay(); + shield_tilt::roll_direction(); +} diff --git a/src/training/save_states.rs b/src/training/save_states.rs index 1a976f1..c56a897 100644 --- a/src/training/save_states.rs +++ b/src/training/save_states.rs @@ -1,8 +1,9 @@ +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::is_dead; use crate::common::MENU; -use crate::common::{get_random_int, is_dead}; use crate::training::buff; use crate::training::reset; use smash::app::{self, lua_bind::*}; diff --git a/src/training/tech.rs b/src/training/tech.rs index d491cec..2ff20ac 100644 --- a/src/training/tech.rs +++ b/src/training/tech.rs @@ -1,272 +1,272 @@ -use crate::common::consts::*; -use crate::common::*; -use crate::training::mash; -use smash::app::sv_system; -use smash::app::{self, lua_bind::*}; -use smash::hash40; -use smash::lib::lua_const::*; -use smash::lib::L2CValue; -use smash::lua2cpp::L2CFighterBase; - -static mut TECH_ROLL_DIRECTION: Direction = Direction::empty(); -static mut MISS_TECH_ROLL_DIRECTION: Direction = Direction::empty(); - -#[skyline::hook(replace = smash::lua2cpp::L2CFighterBase_change_status)] -pub unsafe fn handle_change_status( - fighter: &mut L2CFighterBase, - status_kind: L2CValue, - unk: L2CValue, -) -> L2CValue { - let mut status_kind = status_kind; - let mut unk = unk; - - if is_training_mode() { - mod_handle_change_status(fighter, &mut status_kind, &mut unk); - } - - original!()(fighter, status_kind, unk) -} - -unsafe fn mod_handle_change_status( - fighter: &mut L2CFighterBase, - status_kind: &mut L2CValue, - unk: &mut L2CValue, -) { - let module_accessor = sv_system::battle_object_module_accessor(fighter.lua_state_agent); - if !is_operation_cpu(module_accessor) { - return; - } - - let status_kind_int = status_kind - .try_get_int() - .unwrap_or(*FIGHTER_STATUS_KIND_WAIT as u64) as i32; - - let state: TechFlags = MENU.tech_state.get_random(); - - if handle_grnd_tech(module_accessor, status_kind, unk, status_kind_int, state) { - return; - } - - if handle_wall_tech(module_accessor, status_kind, unk, status_kind_int, state) { - return; - } - - handle_ceil_tech(module_accessor, status_kind, unk, status_kind_int, state); -} -fn handle_grnd_tech( - module_accessor: &mut app::BattleObjectModuleAccessor, - status_kind: &mut L2CValue, - unk: &mut L2CValue, - status_kind_int: i32, - state: TechFlags, -) -> bool { - if status_kind_int != *FIGHTER_STATUS_KIND_DOWN - && status_kind_int != *FIGHTER_STATUS_KIND_DAMAGE_FLY_REFLECT_D - { - return false; - } - - unsafe { - let can_tech = WorkModule::is_enable_transition_term( - module_accessor, - *FIGHTER_STATUS_TRANSITION_TERM_ID_PASSIVE, - ); - - if !can_tech { - return false; - } - } - - match state { - TechFlags::IN_PLACE => { - *status_kind = FIGHTER_STATUS_KIND_PASSIVE.as_lua_int(); - *unk = LUA_TRUE; - unsafe { - mash::perform_defensive_option(); - } - } - TechFlags::ROLL_F => { - *status_kind = FIGHTER_STATUS_KIND_PASSIVE_FB.as_lua_int(); - *unk = LUA_TRUE; - unsafe { - TECH_ROLL_DIRECTION = Direction::IN; // = In - mash::perform_defensive_option(); - } - } - TechFlags::ROLL_B => { - *status_kind = FIGHTER_STATUS_KIND_PASSIVE_FB.as_lua_int(); - *unk = LUA_TRUE; - unsafe { - TECH_ROLL_DIRECTION = Direction::OUT; // = Away - mash::perform_defensive_option(); - } - } - _ => (), - } - - true -} - -fn handle_wall_tech( - module_accessor: &mut app::BattleObjectModuleAccessor, - status_kind: &mut L2CValue, - unk: &mut L2CValue, - status_kind_int: i32, - state: TechFlags, -) -> bool { - if status_kind_int != *FIGHTER_STATUS_KIND_STOP_WALL - && status_kind_int != *FIGHTER_STATUS_KIND_DAMAGE_FLY_REFLECT_LR - { - return false; - } - - if state == TechFlags::NO_TECH { - return false; - } - - unsafe { - let can_tech = WorkModule::is_enable_transition_term( - module_accessor, - *FIGHTER_STATUS_TRANSITION_TERM_ID_PASSIVE_WALL, - ); - - if !can_tech { - return false; - } - } - - match state { - TechFlags::IN_PLACE => { - *status_kind = FIGHTER_STATUS_KIND_PASSIVE_WALL.as_lua_int(); - *unk = LUA_TRUE; - } - TechFlags::ROLL_F => { - *status_kind = FIGHTER_STATUS_KIND_PASSIVE_WALL_JUMP.as_lua_int(); - *unk = LUA_TRUE; - } - _ => (), - } - - true -} - -fn handle_ceil_tech( - module_accessor: &mut app::BattleObjectModuleAccessor, - status_kind: &mut L2CValue, - unk: &mut L2CValue, - status_kind_int: i32, - state: TechFlags, -) -> bool { - if status_kind_int != *FIGHTER_STATUS_KIND_STOP_CEIL - && status_kind_int != *FIGHTER_STATUS_KIND_DAMAGE_FLY_REFLECT_U - { - return false; - } - - if state == TechFlags::NO_TECH { - return false; - } - - unsafe { - let can_tech = WorkModule::is_enable_transition_term( - module_accessor, - *FIGHTER_STATUS_TRANSITION_TERM_ID_PASSIVE_CEIL, - ); - - if !can_tech { - return false; - } - } - - *status_kind = FIGHTER_STATUS_KIND_PASSIVE_CEIL.as_lua_int(); - *unk = LUA_TRUE; - true -} - -pub unsafe fn get_command_flag_cat(module_accessor: &mut app::BattleObjectModuleAccessor) { - if !is_operation_cpu(module_accessor) { - return; - } - - if MENU.tech_state == TechFlags::empty() { - return; - } - - let status = StatusModule::status_kind(module_accessor) as i32; - - if [ - *FIGHTER_STATUS_KIND_DOWN_WAIT, // Mistech - *FIGHTER_STATUS_KIND_DOWN_WAIT_CONTINUE, // Mistech - *FIGHTER_STATUS_KIND_LAY_DOWN, // Snake down throw - ] - .contains(&status) - { - let status: i32 = match MENU.miss_tech_state.get_random() { - MissTechFlags::GETUP => *FIGHTER_STATUS_KIND_DOWN_STAND, - MissTechFlags::ATTACK => *FIGHTER_STATUS_KIND_DOWN_STAND_ATTACK, - MissTechFlags::ROLL_F => { - MISS_TECH_ROLL_DIRECTION = Direction::IN; // = In - *FIGHTER_STATUS_KIND_DOWN_STAND_FB - } - MissTechFlags::ROLL_B => { - MISS_TECH_ROLL_DIRECTION = Direction::OUT; // = Away - *FIGHTER_STATUS_KIND_DOWN_STAND_FB - } - _ => return, - }; - StatusModule::change_status_request_from_script(module_accessor, status, false); - - mash::perform_defensive_option(); - } else if [ - // Handle slips (like Diddy banana) - *FIGHTER_STATUS_KIND_SLIP_WAIT, - ] - .contains(&status) - { - let status: i32 = match MENU.miss_tech_state.get_random() { - MissTechFlags::GETUP => *FIGHTER_STATUS_KIND_SLIP_STAND, - MissTechFlags::ATTACK => *FIGHTER_STATUS_KIND_SLIP_STAND_ATTACK, - MissTechFlags::ROLL_F => *FIGHTER_STATUS_KIND_SLIP_STAND_F, - MissTechFlags::ROLL_B => *FIGHTER_STATUS_KIND_SLIP_STAND_B, - _ => return, - }; - StatusModule::change_status_request_from_script(module_accessor, status, false); - - mash::perform_defensive_option(); - }; -} - -pub unsafe fn change_motion( - module_accessor: &mut app::BattleObjectModuleAccessor, - motion_kind: u64, -) -> Option<u64> { - if !is_operation_cpu(module_accessor) { - return None; - } - - if MENU.tech_state == TechFlags::empty() { - return None; - } - - if [hash40("passive_stand_f"), hash40("passive_stand_b")].contains(&motion_kind) { - if TECH_ROLL_DIRECTION == Direction::IN { - return Some(hash40("passive_stand_f")); - } else { - return Some(hash40("passive_stand_b")); - } - } else if [hash40("down_forward_u"), hash40("down_back_u")].contains(&motion_kind) { - if MISS_TECH_ROLL_DIRECTION == Direction::IN { - return Some(hash40("down_forward_u")); - } else { - return Some(hash40("down_back_u")); - } - } else if [hash40("down_forward_d"), hash40("down_back_d")].contains(&motion_kind) { - if MISS_TECH_ROLL_DIRECTION == Direction::IN { - return Some(hash40("down_forward_d")); - } else { - return Some(hash40("down_back_d")); - } - } - - None -} +use crate::common::consts::*; +use crate::common::*; +use crate::training::mash; +use smash::app::sv_system; +use smash::app::{self, lua_bind::*}; +use smash::hash40; +use smash::lib::lua_const::*; +use smash::lib::L2CValue; +use smash::lua2cpp::L2CFighterBase; + +static mut TECH_ROLL_DIRECTION: Direction = Direction::empty(); +static mut MISS_TECH_ROLL_DIRECTION: Direction = Direction::empty(); + +#[skyline::hook(replace = smash::lua2cpp::L2CFighterBase_change_status)] +pub unsafe fn handle_change_status( + fighter: &mut L2CFighterBase, + status_kind: L2CValue, + unk: L2CValue, +) -> L2CValue { + let mut status_kind = status_kind; + let mut unk = unk; + + if is_training_mode() { + mod_handle_change_status(fighter, &mut status_kind, &mut unk); + } + + original!()(fighter, status_kind, unk) +} + +unsafe fn mod_handle_change_status( + fighter: &mut L2CFighterBase, + status_kind: &mut L2CValue, + unk: &mut L2CValue, +) { + let module_accessor = sv_system::battle_object_module_accessor(fighter.lua_state_agent); + if !is_operation_cpu(module_accessor) { + return; + } + + let status_kind_int = status_kind + .try_get_int() + .unwrap_or(*FIGHTER_STATUS_KIND_WAIT as u64) as i32; + + let state: TechFlags = MENU.tech_state.get_random(); + + if handle_grnd_tech(module_accessor, status_kind, unk, status_kind_int, state) { + return; + } + + if handle_wall_tech(module_accessor, status_kind, unk, status_kind_int, state) { + return; + } + + handle_ceil_tech(module_accessor, status_kind, unk, status_kind_int, state); +} +fn handle_grnd_tech( + module_accessor: &mut app::BattleObjectModuleAccessor, + status_kind: &mut L2CValue, + unk: &mut L2CValue, + status_kind_int: i32, + state: TechFlags, +) -> bool { + if status_kind_int != *FIGHTER_STATUS_KIND_DOWN + && status_kind_int != *FIGHTER_STATUS_KIND_DAMAGE_FLY_REFLECT_D + { + return false; + } + + unsafe { + let can_tech = WorkModule::is_enable_transition_term( + module_accessor, + *FIGHTER_STATUS_TRANSITION_TERM_ID_PASSIVE, + ); + + if !can_tech { + return false; + } + } + + match state { + TechFlags::IN_PLACE => { + *status_kind = FIGHTER_STATUS_KIND_PASSIVE.as_lua_int(); + *unk = LUA_TRUE; + unsafe { + mash::perform_defensive_option(); + } + } + TechFlags::ROLL_F => { + *status_kind = FIGHTER_STATUS_KIND_PASSIVE_FB.as_lua_int(); + *unk = LUA_TRUE; + unsafe { + TECH_ROLL_DIRECTION = Direction::IN; // = In + mash::perform_defensive_option(); + } + } + TechFlags::ROLL_B => { + *status_kind = FIGHTER_STATUS_KIND_PASSIVE_FB.as_lua_int(); + *unk = LUA_TRUE; + unsafe { + TECH_ROLL_DIRECTION = Direction::OUT; // = Away + mash::perform_defensive_option(); + } + } + _ => (), + } + + true +} + +fn handle_wall_tech( + module_accessor: &mut app::BattleObjectModuleAccessor, + status_kind: &mut L2CValue, + unk: &mut L2CValue, + status_kind_int: i32, + state: TechFlags, +) -> bool { + if status_kind_int != *FIGHTER_STATUS_KIND_STOP_WALL + && status_kind_int != *FIGHTER_STATUS_KIND_DAMAGE_FLY_REFLECT_LR + { + return false; + } + + if state == TechFlags::NO_TECH { + return false; + } + + unsafe { + let can_tech = WorkModule::is_enable_transition_term( + module_accessor, + *FIGHTER_STATUS_TRANSITION_TERM_ID_PASSIVE_WALL, + ); + + if !can_tech { + return false; + } + } + + match state { + TechFlags::IN_PLACE => { + *status_kind = FIGHTER_STATUS_KIND_PASSIVE_WALL.as_lua_int(); + *unk = LUA_TRUE; + } + TechFlags::ROLL_F => { + *status_kind = FIGHTER_STATUS_KIND_PASSIVE_WALL_JUMP.as_lua_int(); + *unk = LUA_TRUE; + } + _ => (), + } + + true +} + +fn handle_ceil_tech( + module_accessor: &mut app::BattleObjectModuleAccessor, + status_kind: &mut L2CValue, + unk: &mut L2CValue, + status_kind_int: i32, + state: TechFlags, +) -> bool { + if status_kind_int != *FIGHTER_STATUS_KIND_STOP_CEIL + && status_kind_int != *FIGHTER_STATUS_KIND_DAMAGE_FLY_REFLECT_U + { + return false; + } + + if state == TechFlags::NO_TECH { + return false; + } + + unsafe { + let can_tech = WorkModule::is_enable_transition_term( + module_accessor, + *FIGHTER_STATUS_TRANSITION_TERM_ID_PASSIVE_CEIL, + ); + + if !can_tech { + return false; + } + } + + *status_kind = FIGHTER_STATUS_KIND_PASSIVE_CEIL.as_lua_int(); + *unk = LUA_TRUE; + true +} + +pub unsafe fn get_command_flag_cat(module_accessor: &mut app::BattleObjectModuleAccessor) { + if !is_operation_cpu(module_accessor) { + return; + } + + if MENU.tech_state == TechFlags::empty() { + return; + } + + let status = StatusModule::status_kind(module_accessor) as i32; + + if [ + *FIGHTER_STATUS_KIND_DOWN_WAIT, // Mistech + *FIGHTER_STATUS_KIND_DOWN_WAIT_CONTINUE, // Mistech + *FIGHTER_STATUS_KIND_LAY_DOWN, // Snake down throw + ] + .contains(&status) + { + let status: i32 = match MENU.miss_tech_state.get_random() { + MissTechFlags::GETUP => *FIGHTER_STATUS_KIND_DOWN_STAND, + MissTechFlags::ATTACK => *FIGHTER_STATUS_KIND_DOWN_STAND_ATTACK, + MissTechFlags::ROLL_F => { + MISS_TECH_ROLL_DIRECTION = Direction::IN; // = In + *FIGHTER_STATUS_KIND_DOWN_STAND_FB + } + MissTechFlags::ROLL_B => { + MISS_TECH_ROLL_DIRECTION = Direction::OUT; // = Away + *FIGHTER_STATUS_KIND_DOWN_STAND_FB + } + _ => return, + }; + StatusModule::change_status_request_from_script(module_accessor, status, false); + + mash::perform_defensive_option(); + } else if [ + // Handle slips (like Diddy banana) + *FIGHTER_STATUS_KIND_SLIP_WAIT, + ] + .contains(&status) + { + let status: i32 = match MENU.miss_tech_state.get_random() { + MissTechFlags::GETUP => *FIGHTER_STATUS_KIND_SLIP_STAND, + MissTechFlags::ATTACK => *FIGHTER_STATUS_KIND_SLIP_STAND_ATTACK, + MissTechFlags::ROLL_F => *FIGHTER_STATUS_KIND_SLIP_STAND_F, + MissTechFlags::ROLL_B => *FIGHTER_STATUS_KIND_SLIP_STAND_B, + _ => return, + }; + StatusModule::change_status_request_from_script(module_accessor, status, false); + + mash::perform_defensive_option(); + }; +} + +pub unsafe fn change_motion( + module_accessor: &mut app::BattleObjectModuleAccessor, + motion_kind: u64, +) -> Option<u64> { + if !is_operation_cpu(module_accessor) { + return None; + } + + if MENU.tech_state == TechFlags::empty() { + return None; + } + + if [hash40("passive_stand_f"), hash40("passive_stand_b")].contains(&motion_kind) { + if TECH_ROLL_DIRECTION == Direction::IN { + return Some(hash40("passive_stand_f")); + } else { + return Some(hash40("passive_stand_b")); + } + } else if [hash40("down_forward_u"), hash40("down_back_u")].contains(&motion_kind) { + if MISS_TECH_ROLL_DIRECTION == Direction::IN { + return Some(hash40("down_forward_u")); + } else { + return Some(hash40("down_back_u")); + } + } else if [hash40("down_forward_d"), hash40("down_back_d")].contains(&motion_kind) { + if MISS_TECH_ROLL_DIRECTION == Direction::IN { + return Some(hash40("down_forward_d")); + } else { + return Some(hash40("down_back_d")); + } + } + + None +} diff --git a/src/training/throw.rs b/src/training/throw.rs index a70d7ab..6b57d84 100644 --- a/src/training/throw.rs +++ b/src/training/throw.rs @@ -1,154 +1,154 @@ -use crate::common::consts::*; -use crate::common::*; -use crate::training::frame_counter; -use crate::training::mash; -use smash::app::{self, lua_bind::*}; -use smash::lib::lua_const::*; - -const NOT_SET: u32 = 9001; -static mut THROW_DELAY: u32 = NOT_SET; -static mut THROW_DELAY_COUNTER: usize = 0; -static mut THROW_CASE: ThrowOption = ThrowOption::empty(); - -static mut PUMMEL_DELAY: u32 = NOT_SET; -static mut PUMMEL_DELAY_COUNTER: usize = 0; - -pub fn init() { - unsafe { - THROW_DELAY_COUNTER = frame_counter::register_counter(); - PUMMEL_DELAY_COUNTER = frame_counter::register_counter(); - } -} - -// Rolling Throw Delays and Pummel Delays separately - -pub fn reset_throw_delay() { - unsafe { - if THROW_DELAY != NOT_SET { - THROW_DELAY = NOT_SET; - frame_counter::full_reset(THROW_DELAY_COUNTER); - } - } -} - -pub fn reset_pummel_delay() { - unsafe { - if PUMMEL_DELAY != NOT_SET { - PUMMEL_DELAY = NOT_SET; - frame_counter::full_reset(PUMMEL_DELAY_COUNTER); - } - } -} - -pub fn reset_throw_case() { - unsafe { - if THROW_CASE != ThrowOption::empty() { - // Don't roll another throw option if one is already selected - THROW_CASE = ThrowOption::empty(); - } - } -} - -fn roll_throw_delay() { - unsafe { - if THROW_DELAY != NOT_SET { - // Don't roll another throw delay if one is already selected - return; - } - - THROW_DELAY = MENU.throw_delay.get_random().into_meddelay(); - } -} - -fn roll_pummel_delay() { - unsafe { - if PUMMEL_DELAY != NOT_SET { - // Don't roll another pummel delay if one is already selected - return; - } - - PUMMEL_DELAY = MENU.pummel_delay.get_random().into_meddelay(); - } -} - -fn roll_throw_case() { - unsafe { - // Don't re-roll if there is already a throw option selected - if THROW_CASE != ThrowOption::empty() { - return; - } - - THROW_CASE = MENU.throw_state.get_random(); - } -} - -pub unsafe fn get_command_flag_throw_direction( - module_accessor: &mut app::BattleObjectModuleAccessor, -) -> i32 { - if !is_operation_cpu(module_accessor) { - return 0; - } - - if StatusModule::status_kind(module_accessor) as i32 != *FIGHTER_STATUS_KIND_CATCH_WAIT - && StatusModule::status_kind(module_accessor) as i32 != *FIGHTER_STATUS_KIND_CATCH_PULL - && StatusModule::status_kind(module_accessor) as i32 != *FIGHTER_STATUS_KIND_CATCH_ATTACK - { - // No longer holding character, so re-roll the throw case and reset the delay counter for next time - reset_throw_case(); - reset_throw_delay(); - - reset_pummel_delay(); - return 0; - } - - if !WorkModule::is_enable_transition_term( - // If you can't throw right now, don't bother - module_accessor, - *FIGHTER_STATUS_TRANSITION_TERM_ID_CONT_THROW_HI, - ) { - return 0; - } - - roll_throw_delay(); - roll_throw_case(); - - roll_pummel_delay(); - - if THROW_CASE == ThrowOption::NONE { - // Do nothing, but don't reroll the throw case. - return 0; - } - - if frame_counter::should_delay(THROW_DELAY, THROW_DELAY_COUNTER) { - // Not yet time to perform the throw action - if frame_counter::should_delay(PUMMEL_DELAY, PUMMEL_DELAY_COUNTER) { - // And not yet time to pummel either, so don't do anything - return 0; - } - - // If no pummel delay is selected (default), then don't pummel - if MENU.pummel_delay == MedDelay::empty() { - return 0; - } - - // (this conditional would need to be changed to speed up pummelling) - if StatusModule::status_kind(module_accessor) as i32 == *FIGHTER_STATUS_KIND_CATCH_WAIT { - let status = *FIGHTER_STATUS_KIND_CATCH_ATTACK; //.unwrap_or(0); - StatusModule::change_status_request_from_script(module_accessor, status, true); - } - - return 0; - } - - // If you can uthrow, then throw (since all throws should be possible at the same times) - if WorkModule::is_enable_transition_term( - module_accessor, - *FIGHTER_STATUS_TRANSITION_TERM_ID_CONT_THROW_HI, - ) { - let cmd = THROW_CASE.into_cmd().unwrap_or(0); - mash::buffer_menu_mash(); - return cmd; - } - - return 0; -} +use crate::common::consts::*; +use crate::common::*; +use crate::training::frame_counter; +use crate::training::mash; +use smash::app::{self, lua_bind::*}; +use smash::lib::lua_const::*; + +const NOT_SET: u32 = 9001; +static mut THROW_DELAY: u32 = NOT_SET; +static mut THROW_DELAY_COUNTER: usize = 0; +static mut THROW_CASE: ThrowOption = ThrowOption::empty(); + +static mut PUMMEL_DELAY: u32 = NOT_SET; +static mut PUMMEL_DELAY_COUNTER: usize = 0; + +pub fn init() { + unsafe { + THROW_DELAY_COUNTER = frame_counter::register_counter(); + PUMMEL_DELAY_COUNTER = frame_counter::register_counter(); + } +} + +// Rolling Throw Delays and Pummel Delays separately + +pub fn reset_throw_delay() { + unsafe { + if THROW_DELAY != NOT_SET { + THROW_DELAY = NOT_SET; + frame_counter::full_reset(THROW_DELAY_COUNTER); + } + } +} + +pub fn reset_pummel_delay() { + unsafe { + if PUMMEL_DELAY != NOT_SET { + PUMMEL_DELAY = NOT_SET; + frame_counter::full_reset(PUMMEL_DELAY_COUNTER); + } + } +} + +pub fn reset_throw_case() { + unsafe { + if THROW_CASE != ThrowOption::empty() { + // Don't roll another throw option if one is already selected + THROW_CASE = ThrowOption::empty(); + } + } +} + +fn roll_throw_delay() { + unsafe { + if THROW_DELAY != NOT_SET { + // Don't roll another throw delay if one is already selected + return; + } + + THROW_DELAY = MENU.throw_delay.get_random().into_meddelay(); + } +} + +fn roll_pummel_delay() { + unsafe { + if PUMMEL_DELAY != NOT_SET { + // Don't roll another pummel delay if one is already selected + return; + } + + PUMMEL_DELAY = MENU.pummel_delay.get_random().into_meddelay(); + } +} + +fn roll_throw_case() { + unsafe { + // Don't re-roll if there is already a throw option selected + if THROW_CASE != ThrowOption::empty() { + return; + } + + THROW_CASE = MENU.throw_state.get_random(); + } +} + +pub unsafe fn get_command_flag_throw_direction( + module_accessor: &mut app::BattleObjectModuleAccessor, +) -> i32 { + if !is_operation_cpu(module_accessor) { + return 0; + } + + if StatusModule::status_kind(module_accessor) as i32 != *FIGHTER_STATUS_KIND_CATCH_WAIT + && StatusModule::status_kind(module_accessor) as i32 != *FIGHTER_STATUS_KIND_CATCH_PULL + && StatusModule::status_kind(module_accessor) as i32 != *FIGHTER_STATUS_KIND_CATCH_ATTACK + { + // No longer holding character, so re-roll the throw case and reset the delay counter for next time + reset_throw_case(); + reset_throw_delay(); + + reset_pummel_delay(); + return 0; + } + + if !WorkModule::is_enable_transition_term( + // If you can't throw right now, don't bother + module_accessor, + *FIGHTER_STATUS_TRANSITION_TERM_ID_CONT_THROW_HI, + ) { + return 0; + } + + roll_throw_delay(); + roll_throw_case(); + + roll_pummel_delay(); + + if THROW_CASE == ThrowOption::NONE { + // Do nothing, but don't reroll the throw case. + return 0; + } + + if frame_counter::should_delay(THROW_DELAY, THROW_DELAY_COUNTER) { + // Not yet time to perform the throw action + if frame_counter::should_delay(PUMMEL_DELAY, PUMMEL_DELAY_COUNTER) { + // And not yet time to pummel either, so don't do anything + return 0; + } + + // If no pummel delay is selected (default), then don't pummel + if MENU.pummel_delay == MedDelay::empty() { + return 0; + } + + // (this conditional would need to be changed to speed up pummelling) + if StatusModule::status_kind(module_accessor) as i32 == *FIGHTER_STATUS_KIND_CATCH_WAIT { + let status = *FIGHTER_STATUS_KIND_CATCH_ATTACK; //.unwrap_or(0); + StatusModule::change_status_request_from_script(module_accessor, status, true); + } + + return 0; + } + + // If you can uthrow, then throw (since all throws should be possible at the same times) + if WorkModule::is_enable_transition_term( + module_accessor, + *FIGHTER_STATUS_TRANSITION_TERM_ID_CONT_THROW_HI, + ) { + let cmd = THROW_CASE.into_cmd().unwrap_or(0); + mash::buffer_menu_mash(); + return cmd; + } + + return 0; +} diff --git a/training_mod_consts/Cargo.toml b/training_mod_consts/Cargo.toml new file mode 100644 index 0000000..ece0179 --- /dev/null +++ b/training_mod_consts/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "training_mod_consts" +version = "0.1.0" +edition = "2018" + +[dependencies] +bitflags = "1.2.1" +strum = "0.21.0" +strum_macros = "0.21.0" +num = "0.4.0" +num-derive = "0.3" +num-traits = "0.2" +skyline_smash = { git = "https://github.com/ultimate-research/skyline-smash.git" } \ No newline at end of file diff --git a/training_mod_consts/src/lib.rs b/training_mod_consts/src/lib.rs new file mode 100644 index 0000000..df124ec --- /dev/null +++ b/training_mod_consts/src/lib.rs @@ -0,0 +1,960 @@ +#[macro_use] +extern crate bitflags; + +#[macro_use] +extern crate num_derive; + +use core::f64::consts::PI; +use smash::lib::lua_const::*; +use strum_macros::EnumIter; + +// 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); + } + } + } + + pub 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() + } + + pub fn to_toggle_vals() -> Vec<usize> { + let all_options = <$e>::all().to_vec(); + all_options.iter().map(|i| i.bits() as usize).collect() + } + pub fn to_url_param(&self) -> String { + self.to_vec() + .into_iter() + .map(|field| field.bits().to_string()) + .collect::<Vec<_>>() + .join(",") + } + } + } +} + +pub fn get_random_int(max: i32) -> i32 { + unsafe { smash::app::sv_math::rand(smash::hash40("fighter"), max) } +} + +pub fn random_option<T>(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<f64> { + 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} + +// 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<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, + }) + } + + 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, + }) + } +} + +extra_bitflag_impls! {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} + +// 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} + +/// Shield States +#[repr(i32)] +#[derive(Debug, Clone, Copy, PartialEq, FromPrimitive, EnumIter)] +pub enum Shield { + None = 0, + Infinite = 1, + Hold = 2, + Constant = 3, +} + +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", + }) + } + + pub fn to_url_param(&self) -> String { + (*self as i32).to_string() + } +} + +// Save State Mirroring +#[repr(i32)] +#[derive(Debug, Clone, Copy, PartialEq, FromPrimitive, EnumIter)] +pub enum SaveStateMirroring { + None = 0, + Alternate = 1, + Random = 2, +} + +impl SaveStateMirroring { + pub fn as_str(self) -> Option<&'static str> { + Some(match self { + SaveStateMirroring::None => "None", + SaveStateMirroring::Alternate => "Alternate", + SaveStateMirroring::Random => "Random", + }) + } + + fn to_url_param(&self) -> String { + (*self as i32).to_string() + } +} + +// Defensive States +bitflags! { + pub struct Defensive : u32 { + const SPOT_DODGE = 0x1; + const ROLL_F = 0x2; + const ROLL_B = 0x4; + const JAB = 0x8; + const SHIELD = 0x10; + } +} + +impl Defensive { + fn as_str(self) -> Option<&'static str> { + Some(match self { + Defensive::SPOT_DODGE => "Spotdodge", + Defensive::ROLL_F => "Roll Forwards", + Defensive::ROLL_B => "Roll Backwards", + Defensive::JAB => "Jab", + Defensive::SHIELD => "Shield", + _ => return None, + }) + } +} + +extra_bitflag_impls! {Defensive} + +#[repr(i32)] +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum OnOff { + Off = 0, + On = 1, +} + +impl OnOff { + pub fn from_val(val: u32) -> Option<Self> { + 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", + }) + } + + pub fn to_url_param(&self) -> String { + (*self as i32).to_string() + } +} + +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<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, + }) + } + + 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} + +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} + +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<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, + }) + } + + 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} + +// 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<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, + }) + } + + 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 => "One-Winged Angel", + _ => return None, + }) + } +} + +extra_bitflag_impls! {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} + +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} + +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} + +bitflags! { + pub struct BoolFlag : u32 { + const TRUE = 0x1; + const FALSE = 0x2; + } +} + +extra_bitflag_impls! {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)] +pub enum SdiStrength { + Normal = 0, + Medium = 1, + High = 2, +} + +impl SdiStrength { + pub fn into_u32(self) -> u32 { + match self { + SdiStrength::Normal => 8, + SdiStrength::Medium => 6, + SdiStrength::High => 4, + } + } + + pub fn as_str(self) -> Option<&'static str> { + Some(match self { + SdiStrength::Normal => "Normal", + SdiStrength::Medium => "Medium", + SdiStrength::High => "High", + }) + } + + pub fn to_url_param(&self) -> String { + (*self as u32).to_string() + } +} + +// For input delay +trait ToUrlParam { + fn to_url_param(&self) -> String; +} + +impl ToUrlParam for i32 { + fn to_url_param(&self) -> String { + self.to_string() + } +} + +// Macro to build the url parameter string +macro_rules! url_params { + ( + #[derive($($trait_name:ident, )*)] + pub struct $e:ident { + $(pub $field_name:ident: $field_type:ty,)* + } + ) => { + #[derive($($trait_name, )*)] + pub struct $e { + $(pub $field_name: $field_type,)* + } + impl $e { + pub fn to_url_params(&self) -> String { + let mut s = "?".to_string(); + $( + s.push_str(stringify!($field_name)); + s.push_str(&"="); + s.push_str(&self.$field_name.to_url_param()); + s.push_str(&"&"); + )* + s.pop(); + s + } + } + } +} + +#[repr(C)] +url_params! { + #[derive(Clone, Copy, )] + pub struct TrainingModpackMenu { + pub hitbox_vis: OnOff, + pub stage_hazards: OnOff, + pub di_state: Direction, + pub sdi_state: Direction, + pub sdi_strength: SdiStrength, + pub air_dodge_dir: Direction, + pub mash_state: Action, + pub follow_up: Action, + pub attack_angle: AttackAngle, + pub ledge_state: LedgeOption, + pub ledge_delay: LongDelay, + pub tech_state: TechFlags, + pub miss_tech_state: MissTechFlags, + pub shield_state: Shield, + pub defensive_state: Defensive, + pub oos_offset: Delay, + pub reaction_time: Delay, + pub shield_tilt: Direction, + pub mash_in_neutral: OnOff, + pub fast_fall: BoolFlag, + pub fast_fall_delay: Delay, + pub falling_aerials: BoolFlag, + pub aerial_delay: Delay, + pub full_hop: BoolFlag, + pub input_delay: i32, + pub save_damage: OnOff, + pub save_state_mirroring: SaveStateMirroring, + pub frame_advantage: OnOff, + pub save_state_enable: OnOff, + pub throw_state: ThrowOption, + pub throw_delay: MedDelay, + pub pummel_delay: MedDelay, + pub buff_state: BuffOption, + } +} + +macro_rules! set_by_str { + ($obj:ident, $s:ident, $($field:ident = $rhs:expr,)*) => { + $( + if $s == stringify!($field) { + $obj.$field = $rhs.unwrap(); + } + )* + } +} + +impl TrainingModpackMenu { + pub fn set(&mut self, s: &str, val: u32) { + set_by_str!( + self, + s, + aerial_delay = Delay::from_bits(val), + air_dodge_dir = Direction::from_bits(val), + attack_angle = AttackAngle::from_bits(val), + defensive_state = Defensive::from_bits(val), + di_state = Direction::from_bits(val), + falling_aerials = BoolFlag::from_bits(val), + fast_fall_delay = Delay::from_bits(val), + fast_fall = BoolFlag::from_bits(val), + follow_up = Action::from_bits(val), + full_hop = BoolFlag::from_bits(val), + hitbox_vis = OnOff::from_val(val), + input_delay = Some(val as i32), + ledge_delay = LongDelay::from_bits(val), + ledge_state = LedgeOption::from_bits(val), + mash_in_neutral = OnOff::from_val(val), + mash_state = Action::from_bits(val), + miss_tech_state = MissTechFlags::from_bits(val), + oos_offset = Delay::from_bits(val), + reaction_time = Delay::from_bits(val), + sdi_state = Direction::from_bits(val), + sdi_strength = num::FromPrimitive::from_u32(val), + shield_state = num::FromPrimitive::from_u32(val), + shield_tilt = Direction::from_bits(val), + stage_hazards = OnOff::from_val(val), + tech_state = TechFlags::from_bits(val), + save_damage = OnOff::from_val(val), + frame_advantage = OnOff::from_val(val), + save_state_mirroring = num::FromPrimitive::from_u32(val), + save_state_enable = OnOff::from_val(val), + throw_state = ThrowOption::from_bits(val), + throw_delay = MedDelay::from_bits(val), + pummel_delay = MedDelay::from_bits(val), + buff_state = BuffOption::from_bits(val), + ); + } +} + +// Fighter Ids +#[repr(i32)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum FighterId { + Player = 0, + CPU = 1, +} diff --git a/training_mod_metrics/Cargo.toml b/training_mod_metrics/Cargo.toml new file mode 100644 index 0000000..4753903 --- /dev/null +++ b/training_mod_metrics/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "training_mod_metrics" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +datafusion = "5.0.0" +tokio = "1.11.0" +plotters = "0.3.1" +chrono = "0.4.19" \ No newline at end of file diff --git a/training_mod_metrics/src/main.rs b/training_mod_metrics/src/main.rs new file mode 100644 index 0000000..14db349 --- /dev/null +++ b/training_mod_metrics/src/main.rs @@ -0,0 +1,144 @@ +use datafusion::prelude::*; +use datafusion::arrow::record_batch::RecordBatch; +use datafusion::datasource::json::NdJsonFile; +use datafusion::physical_plan::json::NdJsonReadOptions; +use datafusion::arrow::datatypes::{Schema, Field, DataType}; + +use std::sync::Arc; + +// export.json is relative to /event/ +// cat export.json | jq -c '.SMASH_OPEN.device[][][]' > smash_open.json +#[derive(Debug)] +struct Event { + device_id: String, + event_name: String, + event_time: i64, + menu_settings: String, + mod_version: String, + session_id: String, + smash_version: String, + user_id: String +} + +use chrono::{DateTime, NaiveDateTime, Utc}; +fn timestamp_secs_to_datetime(ts: i64) -> DateTime<Utc> { + DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(ts, 0), Utc) +} + +use plotters::prelude::*; +const OUT_FILE_NAME: &'static str = "boxplot.svg"; +fn draw_chart(results: Vec<RecordBatch>) -> Result<(), Box<dyn std::error::Error>> { + let num_devices_idx = results[0].schema().column_with_name("num_devices").unwrap().0; + let num_sessions_idx = results[0].schema().column_with_name("num_sessions").unwrap().0; + let timestamps_idx = results[0].schema().column_with_name("date").unwrap().0; + + let num_devices = results[0].column(num_devices_idx).as_any() + .downcast_ref::<datafusion::arrow::array::UInt64Array>() + .expect("Failed to downcast").values(); + let num_sessions = results[0].column(num_sessions_idx).as_any() + .downcast_ref::<datafusion::arrow::array::UInt64Array>() + .expect("Failed to downcast").values(); + let timestamp_millis = results[0].column(timestamps_idx).as_any() + .downcast_ref::<datafusion::arrow::array::TimestampMillisecondArray>() + .expect("Failed to downcast").values(); + + let device_data_points = num_devices.iter() + .enumerate().map(|(i, x)| (timestamp_secs_to_datetime(timestamp_millis[i] / 1000), *x)); + let session_data_points = num_sessions.iter() + .enumerate().map(|(i, x)| (timestamp_secs_to_datetime(timestamp_millis[i] / 1000), *x)); + + let root = SVGBackend::new(OUT_FILE_NAME, (1024, 768)).into_drawing_area(); + root.fill(&WHITE)?; + let mut chart = ChartBuilder::on(&root) + .caption("Users and Sessions by Date", ("sans-serif", 50).into_font()) + .margin(5) + .x_label_area_size(30) + .y_label_area_size(30) + .build_cartesian_2d( + (timestamp_secs_to_datetime(timestamp_millis[0] / 1000))..(timestamp_secs_to_datetime(*timestamp_millis.last().unwrap() / 1000)), + 0..*num_sessions.iter().max().unwrap())?; + + chart.configure_mesh().draw()?; + + chart + .draw_series(LineSeries::new( + device_data_points, + &RED, + ))? + .label("Unique Devices") + .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &RED)); + chart + .draw_series(LineSeries::new( + session_data_points, + &BLUE, + ))? + .label("Unique Sessions") + .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &BLUE)); + + chart + .configure_series_labels() + .background_style(&WHITE.mix(0.8)) + .border_style(&BLACK) + .draw()?; + + Ok(()) +} + +#[tokio::main] +async fn main() -> datafusion::error::Result<()> { + // let smash_open_table = NdJsonFile::try_new( + // "smash_open.json", + // NdJsonReadOptions{ + // schema: None, + // schema_infer_max_records: 1, + // file_extension: ".json", + // } + // ).unwrap(); + + let menu_open_table = NdJsonFile::try_new( + "menu_open.json", + NdJsonReadOptions{ + schema: Some(Arc::new(Schema::new(vec![ + Field::new("device_id", DataType::Utf8, false), + Field::new("event_name", DataType::Utf8, false), + Field::new("event_time", DataType::Int64, false), + Field::new("menu_settings", DataType::Utf8, false), + Field::new("session_id", DataType::Utf8, false), + Field::new("smash_version", DataType::Utf8, false), + Field::new("mod_version", DataType::Utf8, false), + Field::new("user_id", DataType::Utf8, false), + ]))), + schema_infer_max_records: 0, + file_extension: ".json", + } + ).unwrap(); + + // // declare a new context. In spark API, this corresponds to a new spark SQLsession + let mut ctx = ExecutionContext::new(); + + // ctx.register_table("smash_open", Arc::new(smash_open_table))?; + ctx.register_table("menu_open", Arc::new(menu_open_table))?; + + // create a plan to run a SQL query + let df = ctx.sql( + "SELECT + COUNT(DISTINCT device_id) num_devices, + COUNT(DISTINCT session_id) num_sessions, + COUNT(*) num_events, + TO_TIMESTAMP_MILLIS(DATE_TRUNC('day', CAST(event_time * 1000000 AS timestamp))) AS date FROM menu_open + WHERE + -- after 09/01/2021 + event_time > 1630454400000 + -- before today + AND CAST(event_time * 1000000 AS timestamp) < NOW() + GROUP BY date ORDER BY date" + )?; + + let results: Vec<RecordBatch> = df.collect().await?; + // use datafusion::arrow::util::pretty::pretty_format_batches; + // println!("{}", pretty_format_batches(&results)?); + + draw_chart(results).unwrap(); + + Ok(()) +} \ No newline at end of file