1
0
Fork 0
mirror of https://github.com/jugeeya/UltimateTrainingModpack.git synced 2025-03-14 02:16:10 +00:00

new method of hitbox visualization

This commit is contained in:
jugeeya 2020-05-06 17:50:31 -07:00
parent a5fc17904f
commit 7a57b8e247
4 changed files with 229 additions and 147 deletions

View file

@ -63,7 +63,7 @@ pub fn color_lerp(min_color: Vector3f, max_color: Vector3f, t: f32,
align)}
}
const ID_COLORS: &[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)
@ -77,19 +77,12 @@ const ID_COLORS: &[Vector3f] = &[
];
const MAX_EFFECTS_PER_HITBOX: i32 = 16; // max # of circles drawn for an extended hitbox
pub unsafe fn wrap(func: unsafe extern "C" fn(lua_state: u64), agent: &mut L2CAgent, vals: &mut Vec::<L2CValue>) {
agent.clear_lua_stack();
for val in vals {
agent.push_lua_stack(val);
}
func(agent.lua_state_agent);
agent.clear_lua_stack();
}
pub unsafe fn generate_hitbox_effects(l2c_agent: &mut L2CAgent, bone: L2CValue,
size: L2CValue, x: L2CValue, y: L2CValue,
z: L2CValue, x2: L2CValue, y2: L2CValue,
z2: L2CValue, color: Vector3f) {
pub unsafe fn generate_hitbox_effects(
module_accessor: &mut app::BattleObjectModuleAccessor,
bone: u64,
size: f32, x: f32, y: f32,
z: f32, x2: Option<f32>, y2: Option<f32>,
z2: Option<f32>, color: Vector3f) {
let red = L2CValue::new_num(color.x);
let green = L2CValue::new_num(color.y);
let blue = L2CValue::new_num(color.z);
@ -99,7 +92,7 @@ pub unsafe fn generate_hitbox_effects(l2c_agent: &mut L2CAgent, bone: L2CValue,
let shield_effect = L2CValue::new_int(hash40("sys_shield"));
let zero_rot = L2CValue::new_num(0.0);
let terminate = L2CValue::new_bool(true);
let effect_size = L2CValue::new_num(size.get_num() * size_mult);
let effect_size = L2CValue::new_num(size * size_mult);
let rate = L2CValue::new_num(8.0);
@ -107,17 +100,17 @@ pub unsafe fn generate_hitbox_effects(l2c_agent: &mut L2CAgent, bone: L2CValue,
let y_dist : f32;
let z_dist : f32;
let mut n_effects : i32;
if let L2CValueType::Void = x2.val_type { // && let lib::L2CValueType::Void = y2.val_type && let lib::L2CValueType::Void = z2.val_type { // extended hitbox
if x2 == None { // && let lib::L2CValueType::Void = y2.val_type && let lib::L2CValueType::Void = z2.val_type { // extended hitbox
x_dist = 0.0; y_dist = 0.0; z_dist = 0.0;
n_effects = 1;
}
else { // non-extended hitbox
x_dist = x2.get_num() - x.get_num();
y_dist = y2.get_num() - y.get_num();
z_dist = z2.get_num() - z.get_num();
x_dist = x2.unwrap() - x;
y_dist = y2.unwrap() - y;
z_dist = z2.unwrap() - 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.get_num() * 1.75)) + 1.0).ceil() as i32; // just enough effects to form a continuous line
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 {
@ -130,83 +123,80 @@ pub unsafe fn generate_hitbox_effects(l2c_agent: &mut L2CAgent, bone: L2CValue,
if n_effects > 1 {
t = (i as f32) / ((n_effects - 1) as f32);
}
let x_curr = L2CValue::new_num(x.get_num() + x_dist * t);
let y_curr = L2CValue::new_num(y.get_num() + y_dist * t);
let z_curr = L2CValue::new_num(z.get_num() + z_dist * t);
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};
// EffectModule::req_follow(module_accessor,
// Hash40{hash: hash40("sys_shield")}, Hash40{hash: 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);
EffectModule::req_on_joint(module_accessor,
Hash40{hash: hash40("sys_shield")}, Hash40{hash: 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);
wrap(sv_animcmd::EFFECT_FOLLOW_NO_SCALE,
l2c_agent,
&mut [
shield_effect, bone, x_curr,
y_curr, z_curr, zero_rot, zero_rot,
zero_rot, effect_size, terminate].to_vec());
// set to hitbox ID color
wrap(sv_animcmd::LAST_EFFECT_SET_COLOR, l2c_agent,
&mut [red, green, blue].to_vec());
EffectModule::set_rgb_partial_last(
module_accessor, color.x, color.y, color.z
);
// speed up animation by rate to remove pulsing effect
wrap(sv_animcmd::LAST_EFFECT_SET_RATE, l2c_agent,
&mut [rate].to_vec());
EffectModule::set_rate_last(module_accessor, 8.0);
}
}
#[allow(unused_unsafe)]
#[skyline::hook(replace = sv_animcmd::ATTACK)]
unsafe fn handle_attack(lua_state: u64) {
let mut l2c_agent = L2CAgent::new(lua_state);
// get all necessary hitbox params
let id = l2c_agent.pop_lua_stack(1); // int
let bone = 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
original!()(lua_state);
if menu.HITBOX_VIS && is_training_mode() { // generate hitbox effect(s)
let color_scale: f32;
if true { // color intensity scales with damage
color_scale = unlerp_bounded(1.0, 18.0, damage.get_num());
} else { // color intensity scales with total KB
// calculate the expected KB a character with 95 weight will receive
// at 80% pre-hit
let target_percent = 80.0;
let target_weight = 95.0;
let percent_component: f32;
if fkb.get_int() > 0 {
percent_component = (10.0 + fkb.get_int() as f32) * 0.1 * (1.0 + fkb.get_int() as f32 * 0.5);
} else {
percent_component = (target_percent + damage.get_num()) * 0.1 *
(1.0 + damage.get_num() * 0.5);
}
let weight_component: f32 = 200.0 / (target_weight + 100.0);
let kb: f32 = (percent_component * weight_component * 1.4 + 18.0) *
(kbg.get_int() as f32 * 0.01) + bkb.get_int() as f32;
color_scale = unlerp_bounded(50.0, 200.0, kb);
}
// non-linear scaling to magnify
// differences at lower values
let color_t: f32 = 0.8 + 0.2 * color_scale.powf(0.5);
let color = color_lerp(
Vector3f{x: 1.0, y: 1.0, z: 1.0},
ID_COLORS[(id.get_int() % 8) as usize],
color_t,
2.0
);
generate_hitbox_effects(&mut l2c_agent, bone, size, x, y, z, x2, y2, z2, color);
}
}
// if menu.HITBOX_VIS && is_training_mode() { // generate hitbox effect(s)
// let color_scale: f32;
// if true { // color intensity scales with damage
// color_scale = unlerp_bounded(1.0, 18.0, damage.get_num());
// } else { // color intensity scales with total KB
// // calculate the expected KB a character with 95 weight will receive
// // at 80% pre-hit
// let target_percent = 80.0;
// let target_weight = 95.0;
// let percent_component: f32;
// if fkb.get_int() > 0 {
// percent_component = (10.0 + fkb.get_int() as f32) * 0.1 * (1.0 + fkb.get_int() as f32 * 0.5);
// } else {
// percent_component = (target_percent + damage.get_num()) * 0.1 *
// (1.0 + damage.get_num() * 0.5);
// }
// let weight_component: f32 = 200.0 / (target_weight + 100.0);
// let kb: f32 = (percent_component * weight_component * 1.4 + 18.0) *
// (kbg.get_int() as f32 * 0.01) + bkb.get_int() as f32;
// color_scale = unlerp_bounded(50.0, 200.0, kb);
// }
// // non-linear scaling to magnify
// // differences at lower values
// let color_t: f32 = 0.8 + 0.2 * color_scale.powf(0.5);
// let color = color_lerp(
// Vector3f{x: 1.0, y: 1.0, z: 1.0},
// ID_COLORS[(id.get_int() % 8) as usize],
// color_t,
// 2.0
// );
// generate_hitbox_effects(
// sv_system::battle_object_module_accessor(lua_state),
// bone.get_int(),
// size.get_num(),
// x.get_num(), y.get_num(), z.get_num(),
// x2.try_get_num(), y2.try_get_num(), z2.try_get_num(),
// color);
// }
#[allow(unused_unsafe)]
#[skyline::hook(replace = sv_animcmd::CATCH)]
@ -227,7 +217,13 @@ unsafe fn handle_catch(lua_state: u64) {
original!()(lua_state);
if menu.HITBOX_VIS && is_training_mode() {
generate_hitbox_effects(&mut l2c_agent, joint, size, x, y, z, x2, y2, z2, ID_COLORS[(id.get_int() + 3 % 8) as usize]);
generate_hitbox_effects(
sv_system::battle_object_module_accessor(lua_state),
joint.get_int(),
size.get_num(),
x.get_num(), y.get_num(), z.get_num(),
x2.try_get_num(), y2.try_get_num(), z2.try_get_num(),
ID_COLORS[(id.get_int() + 3 % 8) as usize]);
}
}
@ -236,25 +232,13 @@ pub unsafe fn is_shielding(module_accessor: *mut app::BattleObjectModuleAccessor
(FIGHTER_STATUS_KIND_GUARD_ON..=FIGHTER_STATUS_KIND_GUARD_OFF).contains(&status_kind)
}
#[allow(unused_unsafe)]
#[skyline::hook(replace = AttackModule::clear_all)]
pub unsafe fn handle_clear_all(module_accessor: *mut app::BattleObjectModuleAccessor) {
if is_training_mode() {
// only if we're not shielding
if !is_shielding(module_accessor) {
EffectModule::kill_kind(module_accessor, Hash40{hash: hash40("sys_shield")}, false, true);
}
}
original!()(module_accessor);
}
#[allow(unused_unsafe)]
#[skyline::hook(replace = GrabModule::set_rebound)]
pub unsafe fn handle_set_rebound(module_accessor: *mut app::BattleObjectModuleAccessor, rebound: bool) {
if is_training_mode() && rebound == false {
// only if we're not shielding
if !is_shielding(module_accessor) {
EffectModule::set_visible_kind(module_accessor, Hash40{hash: hash40("sys_shield")}, false);
EffectModule::kill_kind(module_accessor, Hash40{hash: hash40("sys_shield")}, false, true);
}
}
@ -264,8 +248,6 @@ pub unsafe fn handle_set_rebound(module_accessor: *mut app::BattleObjectModuleAc
pub fn hitbox_visualization() {
println!("Applying hitbox visualization mods.");
skyline::install_hook!(handle_attack);
skyline::install_hook!(handle_catch);
skyline::install_hook!(handle_clear_all);
skyline::install_hook!(handle_set_rebound);
}

View file

@ -17,11 +17,11 @@ use smash::app::{self};
use smash::app::lua_bind::{*};
use smash::app::sv_animcmd::{self};
use smash::app::sv_system::{self};
use skyline::libc::{size_t, c_int, c_void, strlen};
use skyline::libc::{size_t, c_int, c_void, strlen, fopen, fwrite, fclose};
use smash::Result;
use skyline::nn;
use skyline::patching::patch_data_from_text;
use skyline::{from_c_str, c_str, hooks::A64HookFunction};
use skyline::{from_c_str, c_str, hooks::A64HookFunction, logging::hex_dump_ptr};
use std::fs;
use skyline::nro::{self, NroInfo};
@ -49,29 +49,6 @@ pub unsafe fn handle_AttackAirMain(fighter: *mut L2CAgent) {
original!()(fighter);
}
#[allow(unused_unsafe)]
#[skyline::hook(replace = game_BatSwing4Common1)]
pub unsafe fn handle_game_BatSwing4Common1(fighter: *mut L2CAgent) {
println!("[handle_game_BatSwing4Common1]");
original!()(fighter);
}
#[allow(unused_unsafe)]
#[skyline::hook(replace = game_BatSwing4Common2)]
pub unsafe fn handle_game_BatSwing4Common2(fighter: *mut L2CAgent) {
println!("[handle_game_BatSwing4Common2]");
sv_animcmd::frame((*fighter).lua_state_agent, 0x30 as f32);
if sv_animcmd::is_excute((*fighter).lua_state_agent) {
hitbox_visualizer::wrap(sv_animcmd::EFFECT_FOLLOW_NO_SCALE,
&mut (*fighter),
&mut [
L2CValue::new_int(hash40("sys_shield")), L2CValue::new_int(hash40("top")),
L2CValue::new_num(0.0), L2CValue::new_num(0.0), L2CValue::new_num(0.0), L2CValue::new_num(0.0), L2CValue::new_num(0.0), L2CValue::new_num(0.0),
L2CValue::new_num(1.0), L2CValue::new_bool(false)].to_vec());
}
original!()(fighter);
}
fn nro_main(nro: &NroInfo) {
match nro.name {
"common" =>
@ -94,8 +71,6 @@ fn nro_main(nro: &NroInfo) {
println!("Result: {}", res);
// skyline::install_hook!(handle_AttackAirMain);
// skyline::install_hook!(handle_game_BatSwing4Common1);
// skyline::install_hook!(handle_game_BatSwing4Common2);
println!("Hooked!");
}
},
@ -107,13 +82,109 @@ fn nro_main(nro: &NroInfo) {
#[skyline::main(name = "test")]
pub fn main() {
println!("Training modpack initialized.");
hitbox_visualizer::hitbox_visualization();
// hitbox_visualizer::hitbox_visualization();
training::training_mods();
nro::add_hook(nro_main).unwrap();
// println!("OpenMode_Write: {} {}", nn::fs::OpenMode_OpenMode_Write, nn::fs::OpenMode_OpenMode_Write as i32);
// let buffer = format!("{:x}", &common::menu as *const _ as u64);
// println!("Writing training_modpack.log with {}...\n", buffer);
let buffer = format!("{:x}", &common::menu as *const _ as u64);
println!("Writing training_modpack.log with {}...\n", buffer);
unsafe {
// let f = fopen(c_str("sd:/test.log"), c_str("w"));
let f = fopen("sd:/SaltySD/training_modpack.log\u{0}".as_bytes().as_ptr(), "w\u{0}".as_bytes().as_ptr());
println!("File pointer: {:#?}", f);
if !f.is_null() {
fwrite(c_str(&buffer) as *const c_void, 1, buffer.len(), f);
fclose(f);
}
}
// fs::File::create("sd:/test.log").unwrap();
}
// #![feature(proc_macro_hygiene)]
// use smash::hash40;
// use smash::lib::lua_const::{*};
// use smash::lib::{self, L2CAgent, L2CValue};
// use smash::app;
// use smash::app::{lua_bind::*, sv_animcmd, sv_system};
// use skyline::libc::{size_t, c_int, c_void, strlen};
// use smash::Result;
// use skyline::nro::{self, NroInfo};
// use skyline::logging::HexDump;
// extern "C" {
// #[link_name = "\u{1}_ZN7lua2cpp16L2CFighterCommon17status_Catch_MainEv"]
// pub fn status_Catch_Main(
// arg1: *mut L2CAgent
// ) -> u64;
// #[link_name = "\u{1}_ZN7lua2cpp16L2CFighterCommon28sub_wait_ground_check_commonEN3lib8L2CValueE"]
// pub fn sub_wait_ground_check_common(
// arg1: *mut L2CAgent,
// arg2: L2CValue
// ) -> L2CValue;
// #[link_name = "\u{1}_ZN7lua2cpp16L2CFighterCommon25sub_air_check_fall_commonEv"]
// pub fn sub_air_check_fall_common(
// arg1: *mut L2CAgent
// ) -> L2CValue;
// #[link_name = "\u{1}_ZN7lua2cpp14L2CFighterBase13change_statusEN3lib8L2CValueES2_"]
// pub fn change_status(
// arg1: *mut L2CAgent,
// arg2: L2CValue,
// arg3: L2CValue
// ) -> u64;
// }
// #[allow(unused_unsafe)]
// #[skyline::hook(replace = status_Catch_Main)]
// pub unsafe fn handle_status_Catch_Main(fighter: *mut L2CAgent) {
// let module_accessor = sv_system::battle_object_module_accessor((*fighter).lua_state_agent);
// // if CancelModule::is_enable_cancel(module_accessor) {
// // let ret = sub_wait_ground_check_common(fighter, L2CValue::new_bool(false));
// // println!("{}", HexDump(&ret));
// // // sub_air_check_fall_common(fighter);
// // }
// let situation_kind = StatusModule::situation_kind(module_accessor) as i32;
// if situation_kind == SITUATION_KIND_AIR {
// // change_status(fighter,L2CValue::new_int(*FIGHTER_STATUS_KIND_FALL as u64), L2CValue::new_bool(false));
// StatusModule::change_status_request(module_accessor, *FIGHTER_STATUS_KIND_FALL, false);
// return;
// }
// if WorkModule::is_enable_transition_term(module_accessor,*FIGHTER_STATUS_TRANSITION_TERM_ID_WAIT) {
// if MotionModule::is_end(module_accessor) {
// if situation_kind != SITUATION_KIND_GROUND {
// return;
// }
// // change_status(fighter,L2CValue::new_int(*FIGHTER_STATUS_KIND_WAIT as u64), L2CValue::new_bool(false));
// StatusModule::change_status_request(module_accessor, *FIGHTER_STATUS_KIND_WAIT, false);
// return;
// }
// }
// // original!()(fighter); // to call original
// }
// fn nro_main(nro: &NroInfo) {
// match nro.name {
// "common" =>
// {
// println!("Loaded common NRO!");
// skyline::install_hook!(handle_status_Catch_Main);
// println!("change_status: {:p}", change_status as *const());
// println!("sub_air_check_fall_common: {:p}", sub_air_check_fall_common as *const());
// println!("sub_wait_ground_check_common: {:p}", sub_wait_ground_check_common as *const());
// },
// _ => ()
// }
// }
// #[skyline::main(name = "test")]
// pub fn main() {
// println!("Hello from Skyline plugin!");
// nro::add_hook(nro_main).unwrap();
// }

View file

@ -68,8 +68,9 @@ pub unsafe fn should_perform_defensive_option(
)
&&
(
WorkModule::is_enable_transition_term(module_accessor, *FIGHTER_STATUS_TRANSITION_TERM_ID_CONT_GUARD_ON) ||
CancelModule::is_enable_cancel(module_accessor)
WorkModule::is_enable_transition_term(module_accessor, *FIGHTER_STATUS_TRANSITION_TERM_ID_CONT_GUARD_ON)
// ||
// CancelModule::is_enable_cancel(module_accessor)
)
}
@ -125,13 +126,13 @@ pub unsafe fn change_motion(
} else {
return Some(hash40("passive_stand_b"))
}
} else if [hash40("down_forward_u") | hash40("down_back_u")].contains(&motion_kind) {
} else if [hash40("down_forward_u"), hash40("down_back_u")].contains(&motion_kind) {
if app::sv_math::rand(hash40("fighter"), 2) != 0 {
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) {
} else if [hash40("down_forward_d"), hash40("down_back_d")].contains(&motion_kind) {
if app::sv_math::rand(hash40("fighter"), 2) != 0 {
return Some(hash40("down_forward_d"))
} else {

View file

@ -6,6 +6,7 @@ use skyline::{c_str, nn::ro::LookupSymbol, logging::hex_dump_ptr};
use crate::common::fighter_manager_addr;
use crate::common::*;
use crate::common::consts::*;
use crate::hitbox_visualizer;
mod DirectionalInfluence;
mod Shield;
@ -49,13 +50,40 @@ pub unsafe fn handle_get_command_flag_cat(
{
//save_states(module_accessor);
// Pause Effect AnimCMD if hitbox visualization is active
let status_kind = StatusModule::status_kind(module_accessor) as i32;
MotionAnimcmdModule::set_sleep_effect(module_accessor,
is_training_mode() &&
menu.HITBOX_VIS &&
!((*FIGHTER_STATUS_KIND_CATCH..=*FIGHTER_STATUS_KIND_TREAD_FALL).contains(&status_kind) ||
(*FIGHTER_STATUS_KIND_WAIT..=*FIGHTER_STATUS_KIND_REBOUND_JUMP).contains(&status_kind)));
// apply only once per frame
if category == 0 && is_training_mode() && menu.HITBOX_VIS {
// Pause Effect AnimCMD if hitbox visualization is active
let status_kind = StatusModule::status_kind(module_accessor) as i32;
MotionAnimcmdModule::set_sleep_effect(module_accessor,
!((*FIGHTER_STATUS_KIND_CATCH..=*FIGHTER_STATUS_KIND_TREAD_FALL).contains(&status_kind) ||
(*FIGHTER_STATUS_KIND_WAIT..=*FIGHTER_STATUS_KIND_REBOUND_JUMP).contains(&status_kind)));
if !(*FIGHTER_STATUS_KIND_CATCH..=*FIGHTER_STATUS_KIND_CATCH_TURN).contains(&status_kind) {
EffectModule::set_visible_kind(module_accessor, Hash40{hash: hash40("sys_shield")}, false);
EffectModule::kill_kind(module_accessor, Hash40{hash: hash40("sys_shield")}, false, true);
for i in 0..8 {
if AttackModule::is_attack(module_accessor, i, false) {
let attack_data = *AttackModule::attack_data(module_accessor, i, false);
let is_capsule = attack_data.x2 != 0.0 || attack_data.y2 != 0.0 || attack_data.z2 != 0.0;
let mut x2 = None;
let mut y2 = None;
let mut z2 = None;
if is_capsule {
x2 = Some(attack_data.x2);
y2 = Some(attack_data.y2);
z2 = Some(attack_data.z2);
}
hitbox_visualizer::generate_hitbox_effects(
module_accessor,
attack_data.node_, // joint
attack_data.size_,
attack_data.x, attack_data.y, attack_data.z,
x2, y2, z2,
hitbox_visualizer::ID_COLORS[(i % 8) as usize]);
}
}
}
}
let mut flag = original!()(module_accessor, category);