From 7eec4090866417442eee11bd1c8427b70f890576 Mon Sep 17 00:00:00 2001 From: asimon-1 <40246417+asimon-1@users.noreply.github.com> Date: Wed, 12 Jul 2023 11:36:32 -0700 Subject: [PATCH] Mash Overrides (#533) * Merge from CookieScythe branch * Rename block to shieldstun, add icons to layout.arc, add new options to ui_menu * Address clippy warnings and reformat * Add fn_null_check back to whitelist * Pray to clippy gods --- src/common/mod.rs | 14 ++- src/common/release.rs | 2 +- src/hitbox_visualizer/mod.rs | 6 +- src/lib.rs | 2 +- src/static/layout.arc | Bin 2007304 -> 2474768 bytes src/training/attack_angle.rs | 2 +- src/training/clatter.rs | 8 +- src/training/ledge.rs | 13 ++- src/training/mash.rs | 146 +++++++++++++++++++++++------ src/training/shield.rs | 31 +++++- src/training/tech.rs | 24 ++++- src/training/throw.rs | 2 +- src/training/ui/damage.rs | 2 +- training_mod_consts/src/lib.rs | 124 ++++++++++++++++++++++++ training_mod_consts/src/options.rs | 6 +- 15 files changed, 322 insertions(+), 60 deletions(-) diff --git a/src/common/mod.rs b/src/common/mod.rs index 499410e..769d924 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -116,12 +116,20 @@ pub fn is_in_shieldstun(module_accessor: &mut app::BattleObjectModuleAccessor) - 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 + // If we are taking shield damage or we are dropping shield from taking shield damage we are in hitstun + // check if we're in first frames of guard off; don't try to mash in parryable frames - is this a problem for jump/grab OoS? 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_in_tech(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { + let status_kind = StatusModule::status_kind(module_accessor); + (*FIGHTER_STATUS_KIND_DOWN_STAND..=*FIGHTER_STATUS_KIND_DOWN_STAND_ATTACK) + .contains(&status_kind) + || (*FIGHTER_STATUS_KIND_PASSIVE..=*FIGHTER_STATUS_KIND_PASSIVE_CEIL).contains(&status_kind) +} + pub unsafe fn is_ptrainer(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { [ *FIGHTER_KIND_PZENIGAME, @@ -216,7 +224,8 @@ pub unsafe fn is_in_tumble(module_accessor: &mut app::BattleObjectModuleAccessor pub unsafe fn is_in_landing(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool { let status_kind = StatusModule::status_kind(module_accessor); - (*FIGHTER_STATUS_KIND_LANDING..=*FIGHTER_STATUS_KIND_LANDING_LIGHT).contains(&status_kind) + (*FIGHTER_STATUS_KIND_LANDING..=*FIGHTER_STATUS_KIND_LANDING_DAMAGE_LIGHT) + .contains(&status_kind) } // Returns true if a match is currently active @@ -225,7 +234,6 @@ pub unsafe fn is_ready_go() -> bool { FighterManager::is_ready_go(fighter_manager) } -// Returns true if a match is currently active pub unsafe fn entry_count() -> i32 { let fighter_manager = *(FIGHTER_MANAGER_ADDR as *mut *mut app::FighterManager); FighterManager::entry_count(fighter_manager) diff --git a/src/common/release.rs b/src/common/release.rs index 0c63d4d..c6925e6 100644 --- a/src/common/release.rs +++ b/src/common/release.rs @@ -22,7 +22,7 @@ fn is_current_version(fpath: &str) -> VersionCheck { return VersionCheck::NoFile; } - if fs::read_to_string(fpath).unwrap_or("".to_string()) == CURRENT_VERSION { + if fs::read_to_string(fpath).unwrap_or_else(|_| "".to_string()) == CURRENT_VERSION { VersionCheck::Current } else { VersionCheck::Update diff --git a/src/hitbox_visualizer/mod.rs b/src/hitbox_visualizer/mod.rs index ca6e44f..eaa445a 100644 --- a/src/hitbox_visualizer/mod.rs +++ b/src/hitbox_visualizer/mod.rs @@ -75,11 +75,7 @@ pub unsafe fn generate_hitbox_effects( let dist_sq: f32 = x_dist * x_dist + y_dist * y_dist + z_dist * z_dist; let dist = dist_sq.sqrt(); n_effects = ((dist / (size * 1.75)) + 1.0).ceil() as i32; // just enough effects to form a continuous line - if n_effects < 2 { - n_effects = 2; - } else if n_effects > MAX_EFFECTS_PER_HITBOX { - n_effects = MAX_EFFECTS_PER_HITBOX; - } + n_effects = n_effects.clamp(2, MAX_EFFECTS_PER_HITBOX); } else { x_dist = 0.0; y_dist = 0.0; diff --git a/src/lib.rs b/src/lib.rs index 91cadbe..033c831 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,7 @@ clippy::missing_safety_doc, clippy::wrong_self_convention, clippy::option_map_unit_fn, - clippy::fn_null_check, + clippy::incorrect_fn_null_checks, clippy::transmute_num_to_bytes )] diff --git a/src/static/layout.arc b/src/static/layout.arc index 9827d8fff746550f3036d19e80fe9173120b892b..2500abdf00acb16051f7d775e034e50a043fe487 100644 GIT binary patch delta 33974 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®_6`su_)6tC(uD$C8lVaHtiS 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 z%0DO(dO#xdfTkC$t`zg!~pfJEp4O=I9J`2!N62P8rdXu1|R zF3K^bMCbvD&;yFD5XHDzkpoPU2t6PXdO*{saT6o^dvK8={{vVWOG=mCk)0}|2yHR1k54q)>#VtybIdO*`laJwP<*GYsPkO)1X=^|WW z$o{1gp$8;F4`{j(mmab|{~9v?*(5>_FmnH2g*yp3K&?dR0g2E9nl8Y7g6v-`5qdx( z^nj*=t(AX)M6?2lXa$<)nG}cMU$9~1-=bmUUzL&lKmS4uJM?d@kblF4Y5qYLM*f8r zM*hhZM*ayDM*ejZM*g)DM*a~JM*dY1M*f))M*hVQM*am4M*ckxM*Ok{m)|7#*D@IS zw=Wp^mn<0hcPSY8cP1G5S0flbH!9?xb6}c(oPp6cP9gtr0@M6M2#n%3h5RE0Oczg8 zc)DEo|NN5x?7{c@GxCM~jC=z>Bj2pg*jTBMFT7`(FR*9i3+YY5D)`2DruhnZM!wRW zk?&|{-vKjf_Yev4un(<&*Azw7jbOm1>%@)22nvt(|X5_1y8TsyIM!r^=k*`T+Q#lUSNgbJ9gQD@62W7J8&8KPFqI4qn45H zoMq%YWciQpgylcJ;+6mS##a903t0J&?^ESJz8;nT__kC2pV^P1uecZ6BrsCm+{tu{{{Jbsl5OI delta 13670 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 bool { +unsafe fn get_buffered_action( + module_accessor: &mut app::BattleObjectModuleAccessor, +) -> Option { + // TODO: refactor this so it is less repetitive. Maybe a macro is the right tool for this if save_states::is_loading() { - return false; + return None; } - let fighter_distance = get_fighter_distance(); - MENU.mash_triggers.contains(MashTrigger::ALWAYS) - || (MENU.mash_triggers.contains(MashTrigger::HIT) && is_in_hitstun(module_accessor)) - // BLOCK handled in shield.rs - || (MENU.mash_triggers.contains(MashTrigger::PARRY) && is_in_parry(module_accessor)) - || (MENU.mash_triggers.contains(MashTrigger::TUMBLE) && is_in_tumble(module_accessor)) - || (MENU.mash_triggers.contains(MashTrigger::LANDING) && is_in_landing(module_accessor)) - || (MENU.mash_triggers.contains(MashTrigger::TRUMP) && is_in_ledgetrump(module_accessor)) - || (MENU.mash_triggers.contains(MashTrigger::FOOTSTOOL) && is_in_footstool(module_accessor)) - // CLATTER handled in clatter.rs - // LEDGE handled in ledge.rs - // TECH handled in tech.rs - // MISTECH handled in tech.rs - || (MENU.mash_triggers.contains(MashTrigger::GROUNDED) && is_grounded(module_accessor)) + if is_in_tech(module_accessor) { + let action = MENU.tech_action_override.get_random(); + if action != Action::empty() { + Some(action) + } else if MENU.mash_triggers.contains(MashTrigger::TECH) { + Some(MENU.mash_state.get_random()) + } else { + None + } + } else if is_in_clatter(module_accessor) { + let action = MENU.clatter_override.get_random(); + if action != Action::empty() { + Some(action) + } else if MENU.mash_triggers.contains(MashTrigger::CLATTER) { + Some(MENU.mash_state.get_random()) + } else { + None + } + } else if is_in_tumble(module_accessor) { + // Note that the tumble check needs to come before hitstun, + // otherwise the hitstun check will always return first + let action = MENU.tumble_override.get_random(); + if action != Action::empty() { + Some(action) + } else if MENU.mash_triggers.contains(MashTrigger::TUMBLE) { + Some(MENU.mash_state.get_random()) + } else { + None + } + } else if is_in_hitstun(module_accessor) { + let action = MENU.hitstun_override.get_random(); + if action != Action::empty() { + Some(action) + } else if MENU.mash_triggers.contains(MashTrigger::HIT) { + Some(MENU.mash_state.get_random()) + } else { + None + } + } else if is_in_parry(module_accessor) { + let action = MENU.parry_override.get_random(); + if action != Action::empty() { + Some(action) + } else if MENU.mash_triggers.contains(MashTrigger::PARRY) { + Some(MENU.mash_state.get_random()) + } else { + None + } + } else if is_in_shieldstun(module_accessor) { + let action = MENU.shieldstun_override.get_random(); + if action != Action::empty() { + Some(action) + } else if MENU.mash_triggers.contains(MashTrigger::SHIELDSTUN) { + Some(MENU.mash_state.get_random()) + } else { + None + } + } else if is_in_footstool(module_accessor) { + let action = MENU.footstool_override.get_random(); + if action != Action::empty() { + Some(action) + } else if MENU.mash_triggers.contains(MashTrigger::FOOTSTOOL) { + Some(MENU.mash_state.get_random()) + } else { + None + } + } else if is_in_ledgetrump(module_accessor) { + let action = MENU.trump_override.get_random(); + if action != Action::empty() { + Some(action) + } else if MENU.mash_triggers.contains(MashTrigger::TRUMP) { + Some(MENU.mash_state.get_random()) + } else { + None + } + } else if is_in_landing(module_accessor) { + let action = MENU.landing_override.get_random(); + if action != Action::empty() { + Some(action) + } else if MENU.mash_triggers.contains(MashTrigger::LANDING) { + Some(MENU.mash_state.get_random()) + } else { + None + } + } else if (MENU.mash_triggers.contains(MashTrigger::GROUNDED) && is_grounded(module_accessor)) || (MENU.mash_triggers.contains(MashTrigger::AIRBORNE) && is_airborne(module_accessor)) - || (MENU.mash_triggers.contains(MashTrigger::DISTANCE_CLOSE) && fighter_distance < DISTANCE_CLOSE_THRESHOLD) - || (MENU.mash_triggers.contains(MashTrigger::DISTANCE_MID) && fighter_distance < DISTANCE_MID_THRESHOLD) - || (MENU.mash_triggers.contains(MashTrigger::DISTANCE_FAR) && fighter_distance < DISTANCE_FAR_THRESHOLD) + || (MENU.mash_triggers.contains(MashTrigger::DISTANCE_CLOSE) + && fighter_distance < DISTANCE_CLOSE_THRESHOLD) + || (MENU.mash_triggers.contains(MashTrigger::DISTANCE_MID) + && fighter_distance < DISTANCE_MID_THRESHOLD) + || (MENU.mash_triggers.contains(MashTrigger::DISTANCE_FAR) + && fighter_distance < DISTANCE_FAR_THRESHOLD) + || MENU.mash_triggers.contains(MashTrigger::ALWAYS) + { + Some(MENU.mash_state.get_random()) + } else { + // LEDGE handled in ledge.rs + None + } } -// Temp Translation -pub fn buffer_menu_mash() { +fn buffer_menu_mash(action: Action) { unsafe { - let action = MENU.mash_state.get_random(); buffer_action(action); full_hop::roll_full_hop(); fast_fall::roll_fast_fall(); @@ -178,6 +259,11 @@ pub fn buffer_menu_mash() { } } +pub fn external_buffer_menu_mash(action: Action) { + full_reset(); + buffer_menu_mash(action); +} + unsafe fn perform_action(module_accessor: &mut app::BattleObjectModuleAccessor) -> i32 { let action = get_current_buffer(); match action { diff --git a/src/training/shield.rs b/src/training/shield.rs index 99cbe55..607825e 100644 --- a/src/training/shield.rs +++ b/src/training/shield.rs @@ -238,8 +238,12 @@ unsafe fn mod_handle_sub_guard_cont(fighter: &mut L2CFighterCommon) { return; } - if MENU.mash_triggers.contains(MashTrigger::BLOCK) { - mash::buffer_menu_mash(); + if MENU.mash_triggers.contains(MashTrigger::SHIELDSTUN) { + if MENU.shieldstun_override == Action::empty() { + mash::external_buffer_menu_mash(MENU.mash_state.get_random()) + } else { + mash::external_buffer_menu_mash(MENU.shieldstun_override.get_random()) + } } let action = mash::get_current_buffer(); @@ -341,7 +345,30 @@ fn needs_oos_handling_drop_shield() -> bool { if action == Action::U_SMASH { return true; } + // Make sure we only flicker shield when Airdodge and Shield mash options are selected + if action == Action::AIR_DODGE { + let shield_state; + unsafe { + shield_state = &MENU.shield_state; + } + // If we're supposed to be holding shield, let airdodge make us drop shield + if [Shield::Hold, Shield::Infinite, Shield::Constant].contains(shield_state) { + suspend_shield(Action::AIR_DODGE); + } + return true; + } + if action == Action::SHIELD { + let shield_state; + unsafe { + shield_state = &MENU.shield_state; + } + // Don't drop shield on shield hit if we're supposed to be holding shield + if [Shield::Hold, Shield::Infinite, Shield::Constant].contains(shield_state) { + return false; + } + return true; + } false } diff --git a/src/training/tech.rs b/src/training/tech.rs index 3013788..9386e92 100644 --- a/src/training/tech.rs +++ b/src/training/tech.rs @@ -116,7 +116,11 @@ unsafe fn handle_grnd_tech( _ => false, }; if do_tech && MENU.mash_triggers.contains(MashTrigger::TECH) { - mash::buffer_menu_mash(); + if MENU.tech_action_override == Action::empty() { + mash::external_buffer_menu_mash(MENU.mash_state.get_random()) + } else { + mash::external_buffer_menu_mash(MENU.tech_action_override.get_random()) + } } true @@ -159,7 +163,11 @@ unsafe fn handle_wall_tech( _ => false, }; if do_tech && MENU.mash_triggers.contains(MashTrigger::TECH) { - mash::buffer_menu_mash(); + if MENU.tech_action_override == Action::empty() { + mash::external_buffer_menu_mash(MENU.mash_state.get_random()) + } else { + mash::external_buffer_menu_mash(MENU.tech_action_override.get_random()) + } } true } @@ -190,7 +198,11 @@ unsafe fn handle_ceil_tech( *status_kind = FIGHTER_STATUS_KIND_PASSIVE_CEIL.as_lua_int(); *unk = LUA_TRUE; if MENU.mash_triggers.contains(MashTrigger::TECH) { - mash::buffer_menu_mash(); + if MENU.tech_action_override == Action::empty() { + mash::external_buffer_menu_mash(MENU.mash_state.get_random()) + } else { + mash::external_buffer_menu_mash(MENU.tech_action_override.get_random()) + } } true } @@ -258,7 +270,11 @@ pub unsafe fn get_command_flag_cat(module_accessor: &mut BattleObjectModuleAcces if requested_status != 0 { StatusModule::change_status_request_from_script(module_accessor, requested_status, false); if MENU.mash_triggers.contains(MashTrigger::MISTECH) { - mash::buffer_menu_mash(); + if MENU.tech_action_override == Action::empty() { + mash::external_buffer_menu_mash(MENU.mash_state.get_random()) + } else { + mash::external_buffer_menu_mash(MENU.tech_action_override.get_random()) + } } } } diff --git a/src/training/throw.rs b/src/training/throw.rs index 41d8894..c5e09bb 100644 --- a/src/training/throw.rs +++ b/src/training/throw.rs @@ -147,7 +147,7 @@ pub unsafe fn get_command_flag_throw_direction( *FIGHTER_STATUS_TRANSITION_TERM_ID_CONT_THROW_HI, ) { let cmd = THROW_CASE.into_cmd().unwrap_or(0); - mash::buffer_menu_mash(); + mash::external_buffer_menu_mash(MENU.mash_state.get_random()); return cmd; } diff --git a/src/training/ui/damage.rs b/src/training/ui/damage.rs index 437f611..918649e 100644 --- a/src/training/ui/damage.rs +++ b/src/training/ui/damage.rs @@ -38,7 +38,7 @@ pub unsafe fn parse_anim_transform(anim_transform: &mut AnimTransform, layout_na as *const ResAnimationContent; let name = skyline::try_from_c_str((*res_animation_cont).name.as_ptr()) - .unwrap_or("UNKNOWN".to_string()); + .unwrap_or_else(|_| "UNKNOWN".to_string()); let anim_type = (*res_animation_cont).anim_content_type; // AnimContentType 1 == MATERIAL diff --git a/training_mod_consts/src/lib.rs b/training_mod_consts/src/lib.rs index 4faf74a..90935e8 100644 --- a/training_mod_consts/src/lib.rs +++ b/training_mod_consts/src/lib.rs @@ -59,6 +59,19 @@ pub struct TrainingModpackMenu { pub tech_state: TechFlags, pub throw_delay: MedDelay, pub throw_state: ThrowOption, + pub ledge_neutral_override: Action, + pub ledge_roll_override: Action, + pub ledge_jump_override: Action, + pub ledge_attack_override: Action, + pub tech_action_override: Action, + pub clatter_override: Action, + pub tumble_override: Action, + pub hitstun_override: Action, + pub parry_override: Action, + pub shieldstun_override: Action, + pub footstool_override: Action, + pub landing_override: Action, + pub trump_override: Action, } #[repr(C)] @@ -136,6 +149,19 @@ pub static DEFAULTS_MENU: TrainingModpackMenu = TrainingModpackMenu { tech_state: TechFlags::all(), throw_delay: MedDelay::empty(), throw_state: ThrowOption::NONE, + ledge_neutral_override: Action::empty(), + ledge_roll_override: Action::empty(), + ledge_jump_override: Action::empty(), + ledge_attack_override: Action::empty(), + tech_action_override: Action::empty(), + clatter_override: Action::empty(), + tumble_override: Action::empty(), + hitstun_override: Action::empty(), + parry_override: Action::empty(), + shieldstun_override: Action::empty(), + footstool_override: Action::empty(), + landing_override: Action::empty(), + trump_override: Action::empty(), }; pub static mut MENU: TrainingModpackMenu = DEFAULTS_MENU; @@ -385,6 +411,104 @@ pub unsafe fn ui_menu(menu: TrainingModpackMenu) -> UiMenu<'static> { ); overall_menu.tabs.push(mash_tab); + let mut override_tab = Tab { + tab_id: "override", + tab_title: "Override Settings", + tab_submenus: Vec::new(), + }; + override_tab.add_submenu_with_toggles::( + "Ledge Neutral Getup", + "ledge_neutral_override", + "Neutral Getup Override: Mash Actions to be performed after a Neutral Getup from ledge", + false, + &(menu.ledge_neutral_override.bits()), + ); + override_tab.add_submenu_with_toggles::( + "Ledge Roll", + "ledge_roll_override", + "Ledge Roll Override: Mash Actions to be performed after a Roll Getup from ledge", + false, + &(menu.ledge_roll_override.bits()), + ); + override_tab.add_submenu_with_toggles::( + "Ledge Jump", + "ledge_jump_override", + "Ledge Jump Override: Mash Actions to be performed after a Jump Getup from ledge", + false, + &(menu.ledge_jump_override.bits()), + ); + override_tab.add_submenu_with_toggles::( + "Ledge Attack", + "ledge_attack_override", + "Ledge Attack Override: Mash Actions to be performed after a Getup Attack from ledge", + false, + &(menu.ledge_attack_override.bits()), + ); + override_tab.add_submenu_with_toggles::( + "Tech Action", + "tech_action_override", + "Tech Action Override: Mash Actions to be performed after any tech action", + false, + &(menu.tech_action_override.bits()), + ); + override_tab.add_submenu_with_toggles::( + "Clatter", + "clatter_override", + "Clatter Override: Mash Actions to be performed after leaving a clatter situation (grab, bury, etc)", + false, + &(menu.clatter_override.bits()), + ); + override_tab.add_submenu_with_toggles::( + "Tumble", + "tumble_override", + "Tumble Override: Mash Actions to be performed after exiting a tumble state", + false, + &(menu.tumble_override.bits()), + ); + override_tab.add_submenu_with_toggles::( + "Hitstun", + "hitstun_override", + "Hitstun Override: Mash Actions to be performed after exiting a hitstun state", + false, + &(menu.hitstun_override.bits()), + ); + override_tab.add_submenu_with_toggles::( + "Parry", + "parry_override", + "Parry Override: Mash Actions to be performed after a parry", + false, + &(menu.parry_override.bits()), + ); + override_tab.add_submenu_with_toggles::( + "Shieldstun", + "shieldstun_override", + "Shieldstun Override: Mash Actions to be performed after exiting a shieldstun state", + false, + &(menu.shieldstun_override.bits()), + ); + override_tab.add_submenu_with_toggles::( + "Footstool", + "footstool_override", + "Footstool Override: Mash Actions to be performed after exiting a footstool state", + false, + &(menu.footstool_override.bits()), + ); + override_tab.add_submenu_with_toggles::( + "Landing", + "landing_override", + "Landing Override: Mash Actions to be performed after landing on the ground", + false, + &(menu.landing_override.bits()), + ); + override_tab.add_submenu_with_toggles::( + "Ledge Trump", + "trump_override", + "Ledge Trump Override: Mash Actions to be performed after leaving a ledgetrump state", + false, + &(menu.trump_override.bits()), + ); + overall_menu.tabs.push(override_tab); + let mut defensive_tab = Tab { tab_id: "defensive", tab_title: "Defensive Settings", diff --git a/training_mod_consts/src/options.rs b/training_mod_consts/src/options.rs index 58ce6e7..7cbee20 100644 --- a/training_mod_consts/src/options.rs +++ b/training_mod_consts/src/options.rs @@ -1014,7 +1014,7 @@ impl ToggleTrait for CharacterItem { bitflags! { pub struct MashTrigger : u32 { const HIT = 0b0000_0000_0000_0000_0001; - const BLOCK = 0b0000_0000_0000_0000_0010; + const SHIELDSTUN = 0b0000_0000_0000_0000_0010; const PARRY = 0b0000_0000_0000_0000_0100; const TUMBLE = 0b0000_0000_0000_0000_1000; const LANDING = 0b0000_0000_0000_0001_0000; @@ -1037,7 +1037,7 @@ impl MashTrigger { pub fn as_str(self) -> Option<&'static str> { Some(match self { MashTrigger::HIT => "Hitstun", - MashTrigger::BLOCK => "Shieldstun", + MashTrigger::SHIELDSTUN => "Shieldstun", MashTrigger::PARRY => "Parry", MashTrigger::TUMBLE => "Tumble", MashTrigger::LANDING => "Landing", @@ -1060,7 +1060,7 @@ impl MashTrigger { pub const fn default() -> MashTrigger { // Hit, block, clatter MashTrigger::HIT - .union(MashTrigger::BLOCK) + .union(MashTrigger::SHIELDSTUN) .union(MashTrigger::CLATTER) } }