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

Add Status Only option for input logger (#653)

* Initial attempt, working but menu crashing

* Assign raw enum values to fix crash

* Merge main into add-status-display

---------

Co-authored-by: asimon-1 <40246417+asimon-1@users.noreply.github.com>
This commit is contained in:
GradualSyrup 2023-12-06 02:27:26 -06:00 committed by GitHub
parent 65f87df1e0
commit 1475fb2509
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 378 additions and 366 deletions

View file

@ -1,366 +1,377 @@
use itertools::Itertools; use itertools::Itertools;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::collections::VecDeque; use std::collections::VecDeque;
use crate::common::{input::*, menu::QUICK_MENU_ACTIVE, try_get_module_accessor}; use crate::common::{input::*, menu::QUICK_MENU_ACTIVE, try_get_module_accessor};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use parking_lot::Mutex; use parking_lot::Mutex;
use skyline::nn::ui2d::ResColor; use skyline::nn::ui2d::ResColor;
use smash::app::{lua_bind::*, utility}; use smash::app::{lua_bind::*, utility};
use training_mod_consts::{FighterId, InputDisplay, MENU}; use training_mod_consts::{FighterId, InputDisplay, MENU};
use super::{frame_counter, input_record::STICK_CLAMP_MULTIPLIER}; use super::{frame_counter, input_record::STICK_CLAMP_MULTIPLIER};
const GREEN: ResColor = ResColor { const GREEN: ResColor = ResColor {
r: 22, r: 22,
g: 156, g: 156,
b: 0, b: 0,
a: 0, a: 0,
}; };
const RED: ResColor = ResColor { const RED: ResColor = ResColor {
r: 153, r: 153,
g: 10, g: 10,
b: 10, b: 10,
a: 0, a: 0,
}; };
const CYAN: ResColor = ResColor { const CYAN: ResColor = ResColor {
r: 0, r: 0,
g: 255, g: 255,
b: 255, b: 255,
a: 0, a: 0,
}; };
const BLUE: ResColor = ResColor { const BLUE: ResColor = ResColor {
r: 0, r: 0,
g: 40, g: 40,
b: 108, b: 108,
a: 0, a: 0,
}; };
const PURPLE: ResColor = ResColor { const PURPLE: ResColor = ResColor {
r: 100, r: 100,
g: 66, g: 66,
b: 202, b: 202,
a: 0, a: 0,
}; };
pub const YELLOW: ResColor = ResColor { pub const YELLOW: ResColor = ResColor {
r: 230, r: 230,
g: 180, g: 180,
b: 14, b: 14,
a: 0, a: 0,
}; };
pub const WHITE: ResColor = ResColor { pub const WHITE: ResColor = ResColor {
r: 255, r: 255,
g: 255, g: 255,
b: 255, b: 255,
a: 0, a: 0,
}; };
pub static PER_LOG_FRAME_COUNTER: Lazy<usize> = pub static PER_LOG_FRAME_COUNTER: Lazy<usize> =
Lazy::new(|| frame_counter::register_counter(frame_counter::FrameCounterType::InGameNoReset)); Lazy::new(|| frame_counter::register_counter(frame_counter::FrameCounterType::InGameNoReset));
pub static OVERALL_FRAME_COUNTER: Lazy<usize> = pub static OVERALL_FRAME_COUNTER: Lazy<usize> =
Lazy::new(|| frame_counter::register_counter(frame_counter::FrameCounterType::InGameNoReset)); Lazy::new(|| frame_counter::register_counter(frame_counter::FrameCounterType::InGameNoReset));
pub const NUM_LOGS: usize = 15; pub const NUM_LOGS: usize = 15;
pub static mut DRAW_LOG_BASE_IDX: Lazy<Mutex<usize>> = Lazy::new(|| Mutex::new(0)); pub static mut DRAW_LOG_BASE_IDX: Lazy<Mutex<usize>> = Lazy::new(|| Mutex::new(0));
#[derive(PartialEq, Eq, Debug, Copy, Clone)] #[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum DirectionStrength { pub enum DirectionStrength {
None, None,
Weak, Weak,
// Strong, // Strong,
} }
#[derive(Copy, Clone, Default)] #[derive(Copy, Clone, Default)]
pub struct InputLog { pub struct InputLog {
pub ttl: u32, pub ttl: u32,
pub frames: u32, pub frames: u32,
pub overall_frame: u32, pub overall_frame: u32,
pub raw_inputs: Controller, pub raw_inputs: Controller,
pub smash_inputs: MappedInputs, pub smash_inputs: MappedInputs,
pub status: i32, pub status: i32,
pub fighter_kind: i32, pub fighter_kind: i32,
} }
impl PartialEq for InputLog { impl PartialEq for InputLog {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.frames == other.frames && !self.is_different(other) self.frames == other.frames && !self.is_different(other)
} }
} }
impl Eq for InputLog {} impl Eq for InputLog {}
const WALK_THRESHOLD_X: i8 = 20; const WALK_THRESHOLD_X: i8 = 20;
const _DASH_THRESHOLD_X: i8 = 102; const _DASH_THRESHOLD_X: i8 = 102;
const DEADZONE_THRESHOLD_Y: i8 = 30; const DEADZONE_THRESHOLD_Y: i8 = 30;
const _TAP_JUMP_THRESHOLD_Y: i8 = 90; const _TAP_JUMP_THRESHOLD_Y: i8 = 90;
fn bin_stick_values(x: i8, y: i8) -> (DirectionStrength, f32) { fn bin_stick_values(x: i8, y: i8) -> (DirectionStrength, f32) {
( (
// TODO // TODO
DirectionStrength::Weak, DirectionStrength::Weak,
match (x, y) { match (x, y) {
// X only // X only
(x, y) if y.abs() < DEADZONE_THRESHOLD_Y => match x { (x, y) if y.abs() < DEADZONE_THRESHOLD_Y => match x {
x if x > WALK_THRESHOLD_X => 0.0, x if x > WALK_THRESHOLD_X => 0.0,
x if x < -WALK_THRESHOLD_X => 180.0, x if x < -WALK_THRESHOLD_X => 180.0,
_ => return (DirectionStrength::None, 0.0), _ => return (DirectionStrength::None, 0.0),
}, },
// Y only // Y only
(x, y) if x.abs() < WALK_THRESHOLD_X => match y { (x, y) if x.abs() < WALK_THRESHOLD_X => match y {
y if y > DEADZONE_THRESHOLD_Y => 90.0, y if y > DEADZONE_THRESHOLD_Y => 90.0,
y if y < -DEADZONE_THRESHOLD_Y => 270.0, y if y < -DEADZONE_THRESHOLD_Y => 270.0,
_ => return (DirectionStrength::None, 0.0), _ => return (DirectionStrength::None, 0.0),
}, },
// Positive Y // Positive Y
(x, y) if y > DEADZONE_THRESHOLD_Y => match x { (x, y) if y > DEADZONE_THRESHOLD_Y => match x {
x if x > WALK_THRESHOLD_X => 45.0, x if x > WALK_THRESHOLD_X => 45.0,
x if x < -WALK_THRESHOLD_X => 135.0, x if x < -WALK_THRESHOLD_X => 135.0,
_ => return (DirectionStrength::Weak, 90.0), _ => return (DirectionStrength::Weak, 90.0),
}, },
// Negative Y // Negative Y
(x, y) if y < DEADZONE_THRESHOLD_Y => match x { (x, y) if y < DEADZONE_THRESHOLD_Y => match x {
x if x > WALK_THRESHOLD_X => 315.0, x if x > WALK_THRESHOLD_X => 315.0,
x if x < -WALK_THRESHOLD_X => 225.0, x if x < -WALK_THRESHOLD_X => 225.0,
_ => return (DirectionStrength::Weak, 270.0), _ => return (DirectionStrength::Weak, 270.0),
}, },
_ => return (DirectionStrength::None, 0.0), _ => return (DirectionStrength::None, 0.0),
}, },
) )
} }
impl InputLog { impl InputLog {
pub fn is_different(&self, other: &InputLog) -> bool { pub fn is_different(&self, other: &InputLog) -> bool {
unsafe { unsafe {
match MENU.input_display { match MENU.input_display {
InputDisplay::SMASH => self.is_smash_different(other), InputDisplay::SMASH => self.is_smash_different(other),
InputDisplay::RAW => self.is_raw_different(other), InputDisplay::RAW => self.is_raw_different(other),
InputDisplay::NONE => false, InputDisplay::STATUS => self.is_status_different(other),
_ => panic!("Invalid value in is_different: {}", MENU.input_display), InputDisplay::NONE => false,
} _ => panic!("Invalid value in is_different: {}", MENU.input_display),
} }
} }
}
pub fn binned_lstick(&self) -> (DirectionStrength, f32) {
unsafe { pub fn binned_lstick(&self) -> (DirectionStrength, f32) {
match MENU.input_display { unsafe {
InputDisplay::SMASH => self.smash_binned_lstick(), match MENU.input_display {
InputDisplay::RAW => self.raw_binned_lstick(), InputDisplay::SMASH => self.smash_binned_lstick(),
InputDisplay::NONE => panic!("Invalid input display to log"), InputDisplay::RAW => self.raw_binned_lstick(),
_ => panic!("Invalid value in binned_lstick: {}", MENU.input_display), InputDisplay::STATUS => (DirectionStrength::None, 0.0),
} InputDisplay::NONE => panic!("Invalid input display to log"),
} _ => panic!("Invalid value in binned_lstick: {}", MENU.input_display),
} }
}
pub fn binned_rstick(&self) -> (DirectionStrength, f32) { }
unsafe {
match MENU.input_display { pub fn binned_rstick(&self) -> (DirectionStrength, f32) {
InputDisplay::SMASH => self.smash_binned_rstick(), unsafe {
InputDisplay::RAW => self.raw_binned_rstick(), match MENU.input_display {
InputDisplay::NONE => panic!("Invalid input display to log"), InputDisplay::SMASH => self.smash_binned_rstick(),
_ => panic!("Invalid value in binned_rstick: {}", MENU.input_display), InputDisplay::RAW => self.raw_binned_rstick(),
} InputDisplay::STATUS => (DirectionStrength::None, 0.0),
} InputDisplay::NONE => panic!("Invalid input display to log"),
} _ => panic!("Invalid value in binned_rstick: {}", MENU.input_display),
}
pub fn button_icons(&self) -> VecDeque<(&str, ResColor)> { }
unsafe { }
match MENU.input_display {
InputDisplay::SMASH => self.smash_button_icons(), pub fn button_icons(&self) -> VecDeque<(&str, ResColor)> {
InputDisplay::RAW => self.raw_button_icons(), unsafe {
InputDisplay::NONE => panic!("Invalid input display to log"), match MENU.input_display {
_ => unreachable!(), InputDisplay::SMASH => self.smash_button_icons(),
} InputDisplay::RAW => self.raw_button_icons(),
} InputDisplay::STATUS => VecDeque::new(),
} InputDisplay::NONE => panic!("Invalid input display to log"),
_ => unreachable!(),
fn smash_button_icons(&self) -> VecDeque<(&str, ResColor)> { }
self.smash_inputs }
.buttons }
.to_vec()
.iter() fn smash_button_icons(&self) -> VecDeque<(&str, ResColor)> {
.filter_map(|button| { self.smash_inputs
Some(match *button { .buttons
Buttons::ATTACK | Buttons::ATTACK_RAW => ("a", GREEN), .to_vec()
Buttons::SPECIAL | Buttons::SPECIAL_RAW2 => ("b", RED), .iter()
Buttons::JUMP => ("x", CYAN), .filter_map(|button| {
Buttons::GUARD | Buttons::GUARD_HOLD => ("lb", BLUE), Some(match *button {
Buttons::CATCH => ("zr", PURPLE), Buttons::ATTACK | Buttons::ATTACK_RAW => ("a", GREEN),
Buttons::STOCK_SHARE => ("plus", WHITE), Buttons::SPECIAL | Buttons::SPECIAL_RAW2 => ("b", RED),
Buttons::APPEAL_HI => ("dpad_up", WHITE), Buttons::JUMP => ("x", CYAN),
Buttons::APPEAL_LW => ("dpad_down", WHITE), Buttons::GUARD | Buttons::GUARD_HOLD => ("lb", BLUE),
Buttons::APPEAL_SL => ("dpad_right", WHITE), Buttons::CATCH => ("zr", PURPLE),
Buttons::APPEAL_SR => ("dpad_left", WHITE), Buttons::STOCK_SHARE => ("plus", WHITE),
_ => return None, Buttons::APPEAL_HI => ("dpad_up", WHITE),
}) Buttons::APPEAL_LW => ("dpad_down", WHITE),
}) Buttons::APPEAL_SL => ("dpad_right", WHITE),
.unique_by(|(s, _)| *s) Buttons::APPEAL_SR => ("dpad_left", WHITE),
.collect::<VecDeque<(&str, ResColor)>>() _ => return None,
} })
})
fn raw_button_icons(&self) -> VecDeque<(&str, ResColor)> { .unique_by(|(s, _)| *s)
let buttons = self.raw_inputs.current_buttons; .collect::<VecDeque<(&str, ResColor)>>()
let mut icons = VecDeque::new(); }
if buttons.a() {
icons.push_front(("a", GREEN)); fn raw_button_icons(&self) -> VecDeque<(&str, ResColor)> {
} let buttons = self.raw_inputs.current_buttons;
if buttons.b() { let mut icons = VecDeque::new();
icons.push_front(("b", RED)); if buttons.a() {
} icons.push_front(("a", GREEN));
if buttons.x() { }
icons.push_front(("x", CYAN)); if buttons.b() {
} icons.push_front(("b", RED));
if buttons.y() { }
icons.push_front(("y", CYAN)); if buttons.x() {
} icons.push_front(("x", CYAN));
if buttons.l() || buttons.real_digital_l() { }
icons.push_front(("lb", BLUE)); if buttons.y() {
} icons.push_front(("y", CYAN));
if buttons.r() || buttons.real_digital_r() { }
icons.push_front(("rb", BLUE)); if buttons.l() || buttons.real_digital_l() {
} icons.push_front(("lb", BLUE));
if buttons.zl() { }
icons.push_front(("zl", PURPLE)); if buttons.r() || buttons.real_digital_r() {
} icons.push_front(("rb", BLUE));
if buttons.zr() { }
icons.push_front(("zr", PURPLE)); if buttons.zl() {
} icons.push_front(("zl", PURPLE));
if buttons.plus() { }
icons.push_front(("plus", WHITE)); if buttons.zr() {
} icons.push_front(("zr", PURPLE));
if buttons.minus() { }
icons.push_front(("minus", WHITE)); if buttons.plus() {
} icons.push_front(("plus", WHITE));
if buttons.dpad_up() { }
icons.push_front(("dpad_up", WHITE)); if buttons.minus() {
} icons.push_front(("minus", WHITE));
if buttons.dpad_down() { }
icons.push_front(("dpad_down", WHITE)); if buttons.dpad_up() {
} icons.push_front(("dpad_up", WHITE));
if buttons.dpad_left() { }
icons.push_front(("dpad_left", WHITE)); if buttons.dpad_down() {
} icons.push_front(("dpad_down", WHITE));
if buttons.dpad_right() { }
icons.push_front(("dpad_right", WHITE)); if buttons.dpad_left() {
} icons.push_front(("dpad_left", WHITE));
}
icons if buttons.dpad_right() {
} icons.push_front(("dpad_right", WHITE));
}
fn is_smash_different(&self, other: &InputLog) -> bool {
self.smash_inputs.buttons != other.smash_inputs.buttons icons
|| self.smash_binned_lstick() != other.smash_binned_lstick() }
|| self.smash_binned_rstick() != other.smash_binned_rstick()
|| (unsafe { MENU.input_display_status.as_bool() } && self.status != other.status) fn is_smash_different(&self, other: &InputLog) -> bool {
} self.smash_inputs.buttons != other.smash_inputs.buttons
|| self.smash_binned_lstick() != other.smash_binned_lstick()
fn smash_binned_lstick(&self) -> (DirectionStrength, f32) { || self.smash_binned_rstick() != other.smash_binned_rstick()
bin_stick_values(self.smash_inputs.lstick_x, self.smash_inputs.lstick_y) || (unsafe { MENU.input_display_status.as_bool() } && self.status != other.status)
} }
fn smash_binned_rstick(&self) -> (DirectionStrength, f32) { fn is_status_different(&self, other: &InputLog) -> bool {
bin_stick_values(self.smash_inputs.rstick_x, self.smash_inputs.rstick_y) unsafe {
} let input_display_status = MENU.input_display_status.as_bool();
input_display_status && (self.status != other.status)
fn is_raw_different(&self, other: &InputLog) -> bool { }
self.raw_inputs.current_buttons != other.raw_inputs.current_buttons }
|| self.raw_binned_lstick() != other.raw_binned_lstick()
|| self.raw_binned_rstick() != other.raw_binned_rstick() fn smash_binned_lstick(&self) -> (DirectionStrength, f32) {
|| (unsafe { MENU.input_display_status.as_bool() } && self.status != other.status) bin_stick_values(self.smash_inputs.lstick_x, self.smash_inputs.lstick_y)
} }
fn raw_binned_lstick(&self) -> (DirectionStrength, f32) { fn smash_binned_rstick(&self) -> (DirectionStrength, f32) {
let x = (self.raw_inputs.left_stick_x / STICK_CLAMP_MULTIPLIER) as i8; bin_stick_values(self.smash_inputs.rstick_x, self.smash_inputs.rstick_y)
let y = (self.raw_inputs.left_stick_y / STICK_CLAMP_MULTIPLIER) as i8; }
bin_stick_values(x, y)
} fn is_raw_different(&self, other: &InputLog) -> bool {
self.raw_inputs.current_buttons != other.raw_inputs.current_buttons
fn raw_binned_rstick(&self) -> (DirectionStrength, f32) { || self.raw_binned_lstick() != other.raw_binned_lstick()
let x = (self.raw_inputs.right_stick_x / STICK_CLAMP_MULTIPLIER) as i8; || self.raw_binned_rstick() != other.raw_binned_rstick()
let y = (self.raw_inputs.right_stick_y / STICK_CLAMP_MULTIPLIER) as i8; || (unsafe { MENU.input_display_status.as_bool() } && self.status != other.status)
bin_stick_values(x, y) }
}
} fn raw_binned_lstick(&self) -> (DirectionStrength, f32) {
let x = (self.raw_inputs.left_stick_x / STICK_CLAMP_MULTIPLIER) as i8;
fn insert_in_place<T>(array: &mut [T], value: T, index: usize) { let y = (self.raw_inputs.left_stick_y / STICK_CLAMP_MULTIPLIER) as i8;
array[index..].rotate_right(1); bin_stick_values(x, y)
array[index] = value; }
}
fn raw_binned_rstick(&self) -> (DirectionStrength, f32) {
fn insert_in_front<T>(array: &mut [T], value: T) { let x = (self.raw_inputs.right_stick_x / STICK_CLAMP_MULTIPLIER) as i8;
insert_in_place(array, value, 0); let y = (self.raw_inputs.right_stick_y / STICK_CLAMP_MULTIPLIER) as i8;
} bin_stick_values(x, y)
}
lazy_static! { }
pub static ref P1_INPUT_LOGS: Mutex<[InputLog; NUM_LOGS]> =
Mutex::new([InputLog::default(); NUM_LOGS]); fn insert_in_place<T>(array: &mut [T], value: T, index: usize) {
} array[index..].rotate_right(1);
array[index] = value;
pub fn handle_final_input_mapping( }
player_idx: i32,
controller_struct: &SomeControllerStruct, fn insert_in_front<T>(array: &mut [T], value: T) {
out: *mut MappedInputs, insert_in_place(array, value, 0);
) { }
unsafe {
if MENU.input_display == InputDisplay::NONE { lazy_static! {
return; pub static ref P1_INPUT_LOGS: Mutex<[InputLog; NUM_LOGS]> =
} Mutex::new([InputLog::default(); NUM_LOGS]);
}
if QUICK_MENU_ACTIVE {
return; pub fn handle_final_input_mapping(
} player_idx: i32,
controller_struct: &SomeControllerStruct,
if player_idx == 0 { out: *mut MappedInputs,
let module_accessor = try_get_module_accessor(FighterId::Player); ) {
if module_accessor.is_none() { unsafe {
return; if MENU.input_display == InputDisplay::NONE {
} return;
let module_accessor = module_accessor.unwrap(); }
let current_frame = frame_counter::get_frame_count(*PER_LOG_FRAME_COUNTER); if QUICK_MENU_ACTIVE {
let current_overall_frame = frame_counter::get_frame_count(*OVERALL_FRAME_COUNTER); return;
// We should always be counting }
frame_counter::start_counting(*PER_LOG_FRAME_COUNTER);
frame_counter::start_counting(*OVERALL_FRAME_COUNTER); if player_idx == 0 {
let module_accessor = try_get_module_accessor(FighterId::Player);
let potential_input_log = InputLog { if module_accessor.is_none() {
ttl: 600, return;
frames: 1, }
overall_frame: current_overall_frame, let module_accessor = module_accessor.unwrap();
raw_inputs: *controller_struct.controller,
smash_inputs: *out, let current_frame = frame_counter::get_frame_count(*PER_LOG_FRAME_COUNTER);
status: StatusModule::status_kind(module_accessor), let current_overall_frame = frame_counter::get_frame_count(*OVERALL_FRAME_COUNTER);
fighter_kind: utility::get_kind(&mut *module_accessor), // We should always be counting
}; frame_counter::start_counting(*PER_LOG_FRAME_COUNTER);
frame_counter::start_counting(*OVERALL_FRAME_COUNTER);
let input_logs = &mut *P1_INPUT_LOGS.lock();
let latest_input_log = input_logs.first_mut().unwrap(); let potential_input_log = InputLog {
let prev_overall_frames = latest_input_log.overall_frame; ttl: 600,
let prev_ttl = latest_input_log.ttl; frames: 1,
// Only update if we are on a new frame according to the latest log overall_frame: current_overall_frame,
let is_new_frame = prev_overall_frames != current_overall_frame; raw_inputs: *controller_struct.controller,
if is_new_frame && latest_input_log.is_different(&potential_input_log) { smash_inputs: *out,
frame_counter::reset_frame_count(*PER_LOG_FRAME_COUNTER); status: StatusModule::status_kind(module_accessor),
// We should count this frame already fighter_kind: utility::get_kind(&mut *module_accessor),
frame_counter::tick_idx(*PER_LOG_FRAME_COUNTER); };
insert_in_front(input_logs, potential_input_log);
let draw_log_base_idx = &mut *DRAW_LOG_BASE_IDX.data_ptr(); let input_logs = &mut *P1_INPUT_LOGS.lock();
*draw_log_base_idx = (*draw_log_base_idx + 1) % NUM_LOGS; let latest_input_log = input_logs.first_mut().unwrap();
} else if is_new_frame { let prev_overall_frames = latest_input_log.overall_frame;
*latest_input_log = potential_input_log; let prev_ttl = latest_input_log.ttl;
latest_input_log.frames = std::cmp::min(current_frame, 99); // Only update if we are on a new frame according to the latest log
latest_input_log.ttl = prev_ttl; let is_new_frame = prev_overall_frames != current_overall_frame;
} if is_new_frame && latest_input_log.is_different(&potential_input_log) {
frame_counter::reset_frame_count(*PER_LOG_FRAME_COUNTER);
// Decrease TTL // We should count this frame already
for input_log in input_logs.iter_mut() { frame_counter::tick_idx(*PER_LOG_FRAME_COUNTER);
if input_log.ttl > 0 && is_new_frame { insert_in_front(input_logs, potential_input_log);
input_log.ttl -= 1; let draw_log_base_idx = &mut *DRAW_LOG_BASE_IDX.data_ptr();
} *draw_log_base_idx = (*draw_log_base_idx + 1) % NUM_LOGS;
} } else if is_new_frame {
} *latest_input_log = potential_input_log;
} latest_input_log.frames = std::cmp::min(current_frame, 99);
} latest_input_log.ttl = prev_ttl;
}
// Decrease TTL
for input_log in input_logs.iter_mut() {
if input_log.ttl > 0 && is_new_frame {
input_log.ttl -= 1;
}
}
}
}
}

View file

@ -1041,5 +1041,6 @@ byteflags! {
pub NONE = "None", pub NONE = "None",
pub SMASH = "Smash Inputs", pub SMASH = "Smash Inputs",
pub RAW = "Raw Inputs", pub RAW = "Raw Inputs",
pub STATUS = "Status Only",
} }
} }