mirror of
https://github.com/jugeeya/UltimateTrainingModpack.git
synced 2025-03-26 08:12:42 +00:00
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>
This commit is contained in:
parent
54f4e002fa
commit
884c00b069
24 changed files with 4958 additions and 4787 deletions
240
.github/workflows/rust.yml
vendored
240
.github/workflows/rust.yml
vendored
|
@ -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
|
101
Cargo.toml
101
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 = []
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
1246
src/common/menu.rs
1246
src/common/menu.rs
File diff suppressed because it is too large
Load diff
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
276
src/lib.rs
276
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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Before (image error) Size: 2.9 KiB After (image error) Size: 3 KiB |
|
@ -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>
|
||||
|
|
File diff suppressed because one or more lines are too long
Before (image error) Size: 27 KiB After (image error) Size: 27 KiB |
File diff suppressed because one or more lines are too long
Before (image error) Size: 42 KiB After (image error) Size: 42 KiB |
File diff suppressed because one or more lines are too long
Before (image error) Size: 45 KiB After (image error) Size: 45 KiB |
|
@ -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;
|
||||
}
|
||||
|
|
1070
src/training/mod.rs
1070
src/training/mod.rs
File diff suppressed because it is too large
Load diff
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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::*};
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
13
training_mod_consts/Cargo.toml
Normal file
13
training_mod_consts/Cargo.toml
Normal file
|
@ -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" }
|
960
training_mod_consts/src/lib.rs
Normal file
960
training_mod_consts/src/lib.rs
Normal file
|
@ -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,
|
||||
}
|
12
training_mod_metrics/Cargo.toml
Normal file
12
training_mod_metrics/Cargo.toml
Normal file
|
@ -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"
|
144
training_mod_metrics/src/main.rs
Normal file
144
training_mod_metrics/src/main.rs
Normal file
|
@ -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(())
|
||||
}
|
Loading…
Add table
Reference in a new issue