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

Blujay's visualizer

Use blujay's visualizer plugin for hitbox/hurtbox visualization (#236)

* working

* Update rust.yml

Fix GH Action for getting visualizer.nro

Fix to visualization, allow hitbox/hurtbox toggle
This commit is contained in:
jugeeya 2021-08-22 16:29:23 -07:00
parent ed135e88c3
commit f8dba57044
10 changed files with 89 additions and 360 deletions

View file

@ -86,11 +86,17 @@ 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
zip -r training_modpack_beta.zip atmosphere colors.json
- name: Update Release
uses: meeDamian/github-release@2.0
with:

View file

@ -79,6 +79,25 @@ pub fn random_option<T>(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
@ -711,7 +730,7 @@ macro_rules! url_params {
url_params! {
#[derive(Clone, Copy, )]
pub struct TrainingModpackMenu {
pub hitbox_vis: OnOff,
pub visualization: VisualizationFlags,
pub stage_hazards: OnOff,
pub di_state: Direction,
pub sdi_state: Direction,
@ -765,7 +784,7 @@ impl TrainingModpackMenu {
(fast_fall = BoolFlag::from_bits(val))
(follow_up = Action::from_bits(val))
(full_hop = BoolFlag::from_bits(val))
(hitbox_vis = OnOff::from_val(val))
(visualization = VisualizationFlags::from_bits(val))
(input_delay = Some(val as i32))
(ledge_delay = LongDelay::from_bits(val))
(ledge_state = LedgeOption::from_bits(val))

View file

@ -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_onoff_submenu!(overall_menu, "Hitbox Visualization", hitbox_vis, "Hitbox Visualization: Should hitboxes be displayed, hiding other visual effects");
add_bitflag_submenu!(overall_menu, "Visualization", visualization, VisualizationFlags, "Visualization: Should hitboxes and/or hurtboxes be displayed.");
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");

View file

@ -9,7 +9,7 @@ use smash::hash40;
use smash::lib::lua_const::*;
pub static DEFAULT_MENU: consts::TrainingModpackMenu = consts::TrainingModpackMenu {
hitbox_vis: OnOff::On,
visualization: VisualizationFlags::all(),
stage_hazards: OnOff::Off,
di_state: Direction::empty(),
sdi_state: Direction::empty(),

View file

@ -1,344 +0,0 @@
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<Vector3f>,
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<L2CValue> =
(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);
}

View file

@ -6,7 +6,6 @@
pub mod common;
mod hazard_manager;
mod hitbox_visualizer;
mod training;
#[cfg(test)]
@ -59,7 +58,6 @@ pub fn main() {
}
log!("Initialized.");
hitbox_visualizer::hitbox_visualization();
hazard_manager::hazard_manager();
training::training_mods();
nro::add_hook(nro_main).unwrap();

View file

@ -12,7 +12,7 @@
viewBox="0 0 80.0 80.0"
version="1.1"
id="SVGRoot"
sodipodi:docname="hitbox_vis.svg"
sodipodi:docname="visualization.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<defs
id="defs2223" />

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -1,8 +1,7 @@
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::*};
use smash::app::{self, lua_bind::*, sv_animcmd};
use smash::lib::lua_const::*;
use smash::params::*;
@ -12,6 +11,7 @@ pub mod sdi;
pub mod shield;
pub mod tech;
pub mod ledge;
pub mod visualizer;
mod air_dodge_direction;
mod attack_angle;
@ -78,6 +78,8 @@ pub unsafe fn handle_get_command_flag_cat(
shield::param_installer();
}
visualizer::get_command_flag_cat(module_accessor);
if !is_training_mode() {
return flag;
}
@ -104,7 +106,6 @@ 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);
}
@ -279,6 +280,15 @@ 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<'_>) {
@ -349,6 +359,8 @@ 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();

View file

@ -2,12 +2,9 @@ use crate::common::consts::*;
use crate::common::*;
use crate::training::frame_counter;
use crate::training::mash;
use smash::app;
use smash::app::lua_bind::*;
use smash::app::sv_system;
use smash::app::{self, lua_bind::*, sv_system};
use smash::hash40;
use smash::lib::lua_const::*;
use smash::lib::L2CValue;
use smash::lib::{L2CAgent, L2CValue, lua_const::*};
use smash::lua2cpp::L2CFighterCommon;
// How many hits to hold shield until picking an Out Of Shield option
@ -387,3 +384,22 @@ 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<L2CValue> =
(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);
}
}
}
}

View file

@ -0,0 +1,22 @@
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);
}