From c82cd073f4dbb494d5087c88da4c824490a969c8 Mon Sep 17 00:00:00 2001 From: jugeeya Date: Tue, 24 Aug 2021 19:27:29 -0700 Subject: [PATCH] Revert "Blujay's visualizer" This reverts commit f8dba570447b21858c9d1f1826f9a4db072b15f9. --- .github/workflows/rust.yml | 8 +- src/common/consts.rs | 23 +- src/common/menu.rs | 2 +- src/common/mod.rs | 2 +- src/hitbox_visualizer/mod.rs | 344 ++++++++++++++++++ src/lib.rs | 2 + .../{visualization.svg => hitbox_vis.svg} | 2 +- src/training/mod.rs | 18 +- src/training/shield.rs | 26 +- src/training/visualizer.rs | 22 -- 10 files changed, 360 insertions(+), 89 deletions(-) create mode 100644 src/hitbox_visualizer/mod.rs rename src/templates/{visualization.svg => hitbox_vis.svg} (98%) delete mode 100644 src/training/visualizer.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 8bde63e..35c2da2 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -86,17 +86,11 @@ jobs: 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.1.1/libnro_hook.nro wget https://github.com/jugeeya/nn-hid-hook/releases/download/beta/libnn_hid_hook.nro - wget https://github.com/blu-dev/smash-visualizer/releases/download/0.1.0/Smash-Visualizer-0.1.0.zip 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 - unzip -o Smash-Visualizer-0.1.0.zip - ls - #cp Smash-Visualizer/${{env.SMASH_PLUGIN_DIR}}/visualizer.nro ${{env.SMASH_PLUGIN_DIR}}/visualizer.nro - #cp Smash-Visualizer/colors.json colors.json - ls -1 svg/*.svg | xargs -n 1 basename | xargs -L1 -I{} cp svg/{} ${{env.SMASH_WEB_DIR}}/{} - zip -r training_modpack_beta.zip atmosphere colors.json + zip -r training_modpack_beta.zip atmosphere - name: Update Release uses: meeDamian/github-release@2.0 with: diff --git a/src/common/consts.rs b/src/common/consts.rs index 03ac422..9c28e45 100644 --- a/src/common/consts.rs +++ b/src/common/consts.rs @@ -79,25 +79,6 @@ pub fn random_option(arg: &[T]) -> &T { &arg[get_random_int(arg.len() as i32) as usize] } -bitflags! { - pub struct VisualizationFlags : u32 { - const HITBOX_VIS = 0x1; - const HURTBOX_VIS = 0x2; - } -} - -impl VisualizationFlags { - fn into_string(self) -> String { - match self { - VisualizationFlags::HITBOX_VIS => "Hitbox Visualization", - VisualizationFlags::HURTBOX_VIS => "Hurtbox Visualization", - _ => "", - }.to_string() - } -} - -extra_bitflag_impls! {VisualizationFlags} - // DI /* 0, 0.785398, 1.570796, 2.356194, -3.14159, -2.356194, -1.570796, -0.785398 @@ -730,7 +711,7 @@ macro_rules! url_params { url_params! { #[derive(Clone, Copy, )] pub struct TrainingModpackMenu { - pub visualization: VisualizationFlags, + pub hitbox_vis: OnOff, pub stage_hazards: OnOff, pub di_state: Direction, pub sdi_state: Direction, @@ -784,7 +765,7 @@ impl TrainingModpackMenu { (fast_fall = BoolFlag::from_bits(val)) (follow_up = Action::from_bits(val)) (full_hop = BoolFlag::from_bits(val)) - (visualization = VisualizationFlags::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)) diff --git a/src/common/menu.rs b/src/common/menu.rs index 7dc7c4b..cb2b1f4 100644 --- a/src/common/menu.rs +++ b/src/common/menu.rs @@ -299,7 +299,7 @@ pub unsafe fn write_menu() { ); add_onoff_submenu!(overall_menu, "Save Damage", save_damage, "Save Damage: Should save states retain player/CPU damage"); - add_bitflag_submenu!(overall_menu, "Visualization", visualization, VisualizationFlags, "Visualization: Should hitboxes and/or hurtboxes be displayed."); + add_onoff_submenu!(overall_menu, "Hitbox Visualization", hitbox_vis, "Hitbox Visualization: Should hitboxes be displayed, hiding other visual effects"); add_onoff_submenu!(overall_menu, "Stage Hazards", stage_hazards, "Stage Hazards: Should stage hazards be present"); add_onoff_submenu!(overall_menu, "Frame Advantage", frame_advantage, "Frame Advantage: Display the time difference between when the player is actionable and the CPU is actionable"); add_onoff_submenu!(overall_menu, "Mash In Neutral", mash_in_neutral, "Mash In Neutral: Should Mash options be performed repeatedly or only when the CPU is hit"); diff --git a/src/common/mod.rs b/src/common/mod.rs index ea20141..be5878f 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -9,7 +9,7 @@ use smash::hash40; use smash::lib::lua_const::*; pub static DEFAULT_MENU: consts::TrainingModpackMenu = consts::TrainingModpackMenu { - visualization: VisualizationFlags::all(), + hitbox_vis: OnOff::On, stage_hazards: OnOff::Off, di_state: Direction::empty(), sdi_state: Direction::empty(), diff --git a/src/hitbox_visualizer/mod.rs b/src/hitbox_visualizer/mod.rs new file mode 100644 index 0000000..d0fcac6 --- /dev/null +++ b/src/hitbox_visualizer/mod.rs @@ -0,0 +1,344 @@ +use crate::common::{consts::*, *}; +use smash::app::{self, lua_bind::*, sv_animcmd, sv_system}; +use smash::lib::{lua_const::*, L2CAgent, L2CValue}; +use smash::phx::{Hash40, Vector3f}; + +pub const ID_COLORS: &[Vector3f] = &[ + // used to tint the hitbox effects -- make sure that at least one component + // is equal to 1.0 + Vector3f { + x: 1.0, + y: 0.0, + z: 0.0, + }, // #ff0000 (red) + Vector3f { + x: 1.0, + y: 0.4, + z: 0.0, + }, // #ff9900 (orange) + Vector3f { + x: 0.8, + y: 1.0, + z: 0.0, + }, // #ccff00 (yellow) + Vector3f { + x: 0.2, + y: 1.0, + z: 0.2, + }, // #00ff33 (green) + Vector3f { + x: 0.0, + y: 0.8, + z: 1.0, + }, // #00ccff (sky blue) + Vector3f { + x: 0.4, + y: 0.4, + z: 1.0, + }, // #6666ff (blue) + Vector3f { + x: 0.8, + y: 0.0, + z: 1.0, + }, // #cc00ff (purple) + Vector3f { + x: 1.0, + y: 0.2, + z: 0.8, + }, // #ff33cc (pink) +]; +const MAX_EFFECTS_PER_HITBOX: i32 = 16; // max # of circles drawn for an extended hitbox + +pub unsafe fn generate_hitbox_effects( + module_accessor: &mut app::BattleObjectModuleAccessor, + bone: u64, + size: f32, + center: Vector3f, + capsule_center: Option, + color: Vector3f, +) { + let size_mult = 19.0 / 200.0; + + let (x,y,z) = (center.x, center.y, center.z); + + let x_dist: f32; + let y_dist: f32; + let z_dist: f32; + let mut n_effects: i32; + if let Some(capsule_center) = capsule_center { + let (x2,y2,z2) = (capsule_center.x, capsule_center.y, capsule_center.z); + x_dist = x2 - x; + y_dist = y2 - y; + z_dist = z2 - z; + 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; + } + } else { + x_dist = 0.0; + y_dist = 0.0; + z_dist = 0.0; + n_effects = 1; + } + + for i in 0..n_effects { + let t = if n_effects > 1 { + (i as f32) / ((n_effects - 1) as f32) + } else { + 0.0 + }; + let x_curr = x + x_dist * t; + let y_curr = y + y_dist * t; + let z_curr = z + z_dist * t; + + let pos = Vector3f { + x: x_curr, + y: y_curr, + z: z_curr, + }; + let zeros = Vector3f { + x: 0.0, + y: 0.0, + z: 0.0, + }; + + if false { + // is_fighter(module_accessor) { + EffectModule::req_on_joint( + module_accessor, + Hash40::new("sys_shield"), + Hash40::new_raw(bone), + &pos, + &zeros, + size * size_mult, + &zeros, + &zeros, + true, + *EFFECT_SUB_ATTRIBUTE_NO_JOINT_SCALE as u32 + | *EFFECT_SUB_ATTRIBUTE_FOLLOW as u32 + | *EFFECT_SUB_ATTRIBUTE_CONCLUDE_STATUS as u32, + 0, + 0, + ); + } else { + EffectModule::req_follow( + module_accessor, + Hash40::new("sys_shield"), + Hash40::new_raw(bone), + &pos, + &zeros, + size * size_mult, + true, + *EFFECT_SUB_ATTRIBUTE_NO_JOINT_SCALE as u32 + | *EFFECT_SUB_ATTRIBUTE_FOLLOW as u32 + | *EFFECT_SUB_ATTRIBUTE_CONCLUDE_STATUS as u32, + 0, + 0, + 0, + 0, + true, + true, + ); + } + + // set to hitbox ID color + EffectModule::set_rgb_partial_last(module_accessor, color.x, color.y, color.z); + + // speed up animation by rate to remove pulsing effect + EffectModule::set_rate_last(module_accessor, 8.0); + } +} + +pub unsafe fn get_command_flag_cat(module_accessor: &mut app::BattleObjectModuleAccessor) { + // Resume Effect AnimCMD incase we don't display hitboxes + MotionAnimcmdModule::set_sleep_effect(module_accessor, false); + + if MENU.hitbox_vis == OnOff::Off { + return; + } + + let status_kind = StatusModule::status_kind(module_accessor) as i32; + if (*FIGHTER_STATUS_KIND_CATCH..=*FIGHTER_STATUS_KIND_CATCH_TURN).contains(&status_kind) { + return; + } + + if is_shielding(module_accessor) { + return; + } + + // Pause Effect AnimCMD if hitbox visualization is active + // Keep effects on for missed tech effect + MotionAnimcmdModule::set_sleep_effect(module_accessor, status_kind != FIGHTER_STATUS_KIND_DOWN); + + EffectModule::set_visible_kind(module_accessor, Hash40::new("sys_shield"), false); + EffectModule::kill_kind(module_accessor, Hash40::new("sys_shield"), false, true); + for i in 0..8 { + if !AttackModule::is_attack(module_accessor, i, false) { + continue; + } + + let attack_data = *AttackModule::attack_data(module_accessor, i, false); + let center = Vector3f{x: attack_data.x, y: attack_data.y, z: attack_data.z}; + let is_capsule = attack_data.x2 != 0.0 || attack_data.y2 != 0.0 || attack_data.z2 != 0.0; + let capsule_center = if is_capsule { + Some(Vector3f{x: attack_data.x2, y: attack_data.y2, z: attack_data.z2}) + } else { + None + }; + generate_hitbox_effects( + module_accessor, + attack_data.node, // joint + attack_data.size, + center, + capsule_center, + ID_COLORS[(i % 8) as usize], + ); + } +} + +// Necessary to ensure we visualize on the first frame of the hitbox +#[skyline::hook(replace = sv_animcmd::ATTACK)] +unsafe fn handle_attack(lua_state: u64) { + if is_training_mode() { + mod_handle_attack(lua_state); + } + + original!()(lua_state); +} + +unsafe fn mod_handle_attack(lua_state: u64) { + let mut l2c_agent = L2CAgent::new(lua_state); + + // necessary if param object fails + // hacky way of forcing no shield damage on all hitboxes + if MENU.shield_state == Shield::Infinite { + let mut hitbox_params: Vec = + (0..36).map(|i| l2c_agent.pop_lua_stack(i + 1)).collect(); + l2c_agent.clear_lua_stack(); + for (i, mut x) in hitbox_params.iter_mut().enumerate().take(36) { + if i == 20 { + l2c_agent.push_lua_stack(&mut L2CValue::new_num(-999.0)); + } else { + l2c_agent.push_lua_stack(&mut x); + } + } + } + + // Hitbox Visualization + if MENU.hitbox_vis == OnOff::On { + // get all necessary grabbox params + let id = l2c_agent.pop_lua_stack(1); // int + let joint = l2c_agent.pop_lua_stack(3); // hash40 + let _damage = l2c_agent.pop_lua_stack(4); // float + let _angle = l2c_agent.pop_lua_stack(5); // int + let _kbg = l2c_agent.pop_lua_stack(6); // int + let _fkb = l2c_agent.pop_lua_stack(7); // int + let _bkb = l2c_agent.pop_lua_stack(8); // int + let size = l2c_agent.pop_lua_stack(9); // float + let x = l2c_agent.pop_lua_stack(10); // float + let y = l2c_agent.pop_lua_stack(11); // float + let z = l2c_agent.pop_lua_stack(12); // float + let x2 = l2c_agent.pop_lua_stack(13); // float or void + let y2 = l2c_agent.pop_lua_stack(14); // float or void + let z2 = l2c_agent.pop_lua_stack(15); // float or void + + let center = Vector3f{x: x.get_num(), y: y.get_num(), z: z.get_num()}; + let capsule_center = + if let (Some(x2), Some(y2), Some(z2)) = (x2.try_get_num(), y2.try_get_num(), z2.try_get_num()) { + Some(Vector3f{x: x2, y: y2, z: z2}) + } else { + None + }; + + generate_hitbox_effects( + sv_system::battle_object_module_accessor(lua_state), + joint.get_int(), + size.get_num(), + center, + capsule_center, + ID_COLORS[(id.get_int() % 8) as usize], + ); + } +} + +#[skyline::hook(replace = sv_animcmd::CATCH)] +unsafe fn handle_catch(lua_state: u64) { + if is_training_mode() { + mod_handle_catch(lua_state); + } + + original!()(lua_state); +} + +unsafe fn mod_handle_catch(lua_state: u64) { + if MENU.hitbox_vis == OnOff::Off { + return; + } + + // get all necessary grabbox params + let mut l2c_agent = L2CAgent::new(lua_state); + let id = l2c_agent.pop_lua_stack(1); // int + let joint = l2c_agent.pop_lua_stack(2); // hash40 + let size = l2c_agent.pop_lua_stack(3); // float + let x = l2c_agent.pop_lua_stack(4); // float + let y = l2c_agent.pop_lua_stack(5); // float + let z = l2c_agent.pop_lua_stack(6); // float + let x2 = l2c_agent.pop_lua_stack(7); // float or void + let y2 = l2c_agent.pop_lua_stack(8); // float or void + let z2 = l2c_agent.pop_lua_stack(9); // float or void + + let center = Vector3f{x: x.get_num(), y: y.get_num(), z: z.get_num()}; + let capsule_center = + if let (Some(x2), Some(y2), Some(z2)) = (x2.try_get_num(), y2.try_get_num(), z2.try_get_num()) { + Some(Vector3f{x: x2, y: y2, z: z2}) + } else { + None + }; + + generate_hitbox_effects( + sv_system::battle_object_module_accessor(lua_state), + joint.get_int(), + size.get_num(), + center, + capsule_center, + ID_COLORS[(id.get_int() + 3 % 8) as usize], + ); +} + +#[skyline::hook(replace = GrabModule::set_rebound)] +pub unsafe fn handle_set_rebound( + module_accessor: *mut app::BattleObjectModuleAccessor, + rebound: bool, +) { + if is_training_mode() { + mod_handle_handle_set_rebound(module_accessor, rebound); + } + + original!()(module_accessor, rebound); +} + +unsafe fn mod_handle_handle_set_rebound( + module_accessor: *mut app::BattleObjectModuleAccessor, + rebound: bool, +) { + if rebound { + return; + } + + // only if we're not shielding + if is_shielding(module_accessor) { + return; + } + + EffectModule::set_visible_kind(module_accessor, Hash40::new("sys_shield"), false); + EffectModule::kill_kind(module_accessor, Hash40::new("sys_shield"), false, true); +} + +pub fn hitbox_visualization() { + println!("[Training Modpack] Applying hitbox visualization mods."); + skyline::install_hooks!(handle_attack, handle_catch, handle_set_rebound); +} diff --git a/src/lib.rs b/src/lib.rs index 632a8de..9aec475 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ pub mod common; mod hazard_manager; +mod hitbox_visualizer; mod training; #[cfg(test)] @@ -58,6 +59,7 @@ pub fn main() { } log!("Initialized."); + hitbox_visualizer::hitbox_visualization(); hazard_manager::hazard_manager(); training::training_mods(); nro::add_hook(nro_main).unwrap(); diff --git a/src/templates/visualization.svg b/src/templates/hitbox_vis.svg similarity index 98% rename from src/templates/visualization.svg rename to src/templates/hitbox_vis.svg index 6d922a7..a3d07d3 100644 --- a/src/templates/visualization.svg +++ b/src/templates/hitbox_vis.svg @@ -12,7 +12,7 @@ viewBox="0 0 80.0 80.0" version="1.1" id="SVGRoot" - sodipodi:docname="visualization.svg" + sodipodi:docname="hitbox_vis.svg" inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"> diff --git a/src/training/mod.rs b/src/training/mod.rs index b01c1c3..c727ba6 100644 --- a/src/training/mod.rs +++ b/src/training/mod.rs @@ -1,7 +1,8 @@ use crate::common::{is_training_mode, menu, FIGHTER_MANAGER_ADDR, STAGE_MANAGER_ADDR}; +use crate::hitbox_visualizer; use skyline::nn::ro::LookupSymbol; use skyline::nn::hid::*; -use smash::app::{self, lua_bind::*, sv_animcmd}; +use smash::app::{self, lua_bind::*}; use smash::lib::lua_const::*; use smash::params::*; @@ -11,7 +12,6 @@ pub mod sdi; pub mod shield; pub mod tech; pub mod ledge; -pub mod visualizer; mod air_dodge_direction; mod attack_angle; @@ -78,8 +78,6 @@ pub unsafe fn handle_get_command_flag_cat( shield::param_installer(); } - visualizer::get_command_flag_cat(module_accessor); - if !is_training_mode() { return flag; } @@ -106,6 +104,7 @@ fn once_per_frame_per_fighter( input_record::get_command_flag_cat(module_accessor); combo::get_command_flag_cat(module_accessor); + hitbox_visualizer::get_command_flag_cat(module_accessor); save_states::save_states(module_accessor); tech::get_command_flag_cat(module_accessor); } @@ -280,15 +279,6 @@ pub unsafe fn handle_set_dead_rumble(lua_state: u64) -> u64 { original!()(lua_state) } -#[skyline::hook(replace = sv_animcmd::ATTACK)] -unsafe fn handle_attack(lua_state: u64) { - if is_training_mode() { - shield::mod_handle_attack(lua_state); - } - - original!()(lua_state); -} - pub static mut COMMON_PARAMS: *mut CommonParams = 0 as *mut _; fn params_main(params_info: &ParamsInfo<'_>) { @@ -359,8 +349,6 @@ pub fn training_mods() { handle_is_enable_transition_term, // SDI crate::training::sdi::check_hit_stop_delay_command, - // Infinite shield damage - handle_attack ); combo::init(); diff --git a/src/training/shield.rs b/src/training/shield.rs index 72bf791..ad06b08 100644 --- a/src/training/shield.rs +++ b/src/training/shield.rs @@ -2,9 +2,12 @@ use crate::common::consts::*; use crate::common::*; use crate::training::frame_counter; use crate::training::mash; -use smash::app::{self, lua_bind::*, sv_system}; +use smash::app; +use smash::app::lua_bind::*; +use smash::app::sv_system; use smash::hash40; -use smash::lib::{L2CAgent, L2CValue, lua_const::*}; +use smash::lib::lua_const::*; +use smash::lib::L2CValue; use smash::lua2cpp::L2CFighterCommon; // How many hits to hold shield until picking an Out Of Shield option @@ -384,22 +387,3 @@ fn was_in_shieldstun(module_accessor: &mut app::BattleObjectModuleAccessor) -> b StatusModule::prev_status_kind(module_accessor, 0) == FIGHTER_STATUS_KIND_GUARD_DAMAGE } } - -pub unsafe fn mod_handle_attack(lua_state: u64) { - let mut l2c_agent = L2CAgent::new(lua_state); - - // necessary if param object fails - // hacky way of forcing no shield damage on all hitboxes - if MENU.shield_state == Shield::Infinite { - let mut hitbox_params: Vec = - (0..36).map(|i| l2c_agent.pop_lua_stack(i + 1)).collect(); - l2c_agent.clear_lua_stack(); - for (i, mut x) in hitbox_params.iter_mut().enumerate().take(36) { - if i == 20 { - l2c_agent.push_lua_stack(&mut L2CValue::new_num(-999.0)); - } else { - l2c_agent.push_lua_stack(&mut x); - } - } - } -} diff --git a/src/training/visualizer.rs b/src/training/visualizer.rs deleted file mode 100644 index 7c66316..0000000 --- a/src/training/visualizer.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::common::{consts::*, *}; -use smash::app; - -extern "C" { - fn enable_hitbox_vis(enable: bool); - fn enable_hurtbox_vis(enable: bool); - fn enable_special_vis(enable: bool); -} - -pub unsafe fn get_command_flag_cat(_module_accessor: &mut app::BattleObjectModuleAccessor) { - if (enable_hitbox_vis as *const ()).is_null() { - panic!("The visualizer plugin is not found. Please check your Skyline plugins directory for visualizer.nro."); - } - - let vis_options = MENU.visualization.to_vec(); - let hitbox_vis = is_training_mode() && vis_options.contains(&VisualizationFlags::HITBOX_VIS); - let hurtbox_vis = is_training_mode() && vis_options.contains(&VisualizationFlags::HURTBOX_VIS); - - enable_hitbox_vis(hitbox_vis); - enable_hurtbox_vis(hurtbox_vis); - enable_special_vis(hitbox_vis); -}