mirror of
https://github.com/jugeeya/UltimateTrainingModpack.git
synced 2024-11-20 00:46:34 +00:00
Shulk Monado Art Buffs, SFX and Visual Buff Fixes (#564)
* Monado Buffs, Shulk Dial on Input Recording, Silent Buffs * All Buffs Silent * Shulk Flash removed, working on KO UI update * Little Mac Meter Update * Remove a few debug functions * More cleanup * merge with main * Paren * Paren * rustfmt * Clippy Fixes * Fix further Shulk effect issues, improve buff functions
This commit is contained in:
parent
c2f5bd5bb1
commit
dcb1e831dc
7 changed files with 777 additions and 582 deletions
|
@ -9,7 +9,6 @@
|
|||
clippy::missing_safety_doc,
|
||||
clippy::wrong_self_convention,
|
||||
clippy::option_map_unit_fn,
|
||||
incorrect_fn_null_checks,
|
||||
clippy::transmute_num_to_bytes
|
||||
)]
|
||||
|
||||
|
|
|
@ -58,31 +58,18 @@ pub unsafe fn get_buff_rem(module_accessor: &mut app::BattleObjectModuleAccessor
|
|||
BUFF_REMAINING_PLAYER
|
||||
}
|
||||
|
||||
fn get_spell_vec() -> Vec<BuffOption> {
|
||||
unsafe {
|
||||
let menu_buff = MENU.buff_state.to_vec();
|
||||
let menu_iter = menu_buff.iter();
|
||||
let mut spell_buff: Vec<BuffOption> = Vec::new();
|
||||
for buff in menu_iter {
|
||||
if buff.into_int().unwrap_or(1) != 1 {
|
||||
// all non-spells into_int as 1
|
||||
spell_buff.push(*buff);
|
||||
}
|
||||
}
|
||||
spell_buff
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn handle_buffs(
|
||||
module_accessor: &mut app::BattleObjectModuleAccessor,
|
||||
fighter_kind: i32,
|
||||
status: i32,
|
||||
percent: f32,
|
||||
) -> bool {
|
||||
// Future Enhancement - Remove startup effects on buffs (Flash of Limit, Wii Fit's flash, Shulk's occasional Jump Art smoke, etc.)
|
||||
SoundModule::stop_all_sound(module_accessor); // silences buff sfx other than KO Punch
|
||||
ControlModule::stop_rumble(module_accessor, false);
|
||||
MotionAnimcmdModule::set_sleep(module_accessor, false);
|
||||
CameraModule::stop_quake(module_accessor, *CAMERA_QUAKE_KIND_M); // stops Psyche-Up quake
|
||||
CameraModule::stop_quake(module_accessor, *CAMERA_QUAKE_KIND_S); // stops Monado Art quake
|
||||
|
||||
let menu_vec = MENU.buff_state.to_vec();
|
||||
|
||||
|
@ -98,13 +85,14 @@ pub unsafe fn handle_buffs(
|
|||
return buff_mac(module_accessor);
|
||||
} else if fighter_kind == *FIGHTER_KIND_EDGE && menu_vec.contains(&BuffOption::WING) {
|
||||
return buff_sepiroth(module_accessor, percent);
|
||||
} else if fighter_kind == *FIGHTER_KIND_SHULK {
|
||||
return buff_shulk(module_accessor, status);
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
unsafe fn buff_hero(module_accessor: &mut app::BattleObjectModuleAccessor, status: i32) -> bool {
|
||||
let buff_vec = get_spell_vec();
|
||||
let buff_vec = MENU.buff_state.hero_buffs().to_vec();
|
||||
if !is_buffing(module_accessor) {
|
||||
// Initial set up for spells
|
||||
start_buff(module_accessor);
|
||||
|
@ -113,6 +101,10 @@ unsafe fn buff_hero(module_accessor: &mut app::BattleObjectModuleAccessor, statu
|
|||
}
|
||||
if get_buff_rem(module_accessor) <= 0 {
|
||||
// If there are no buffs selected/left, we're done
|
||||
if frame_counter::should_delay(3_u32, BUFF_DELAY_COUNTER) {
|
||||
// Need to wait 3 frames to make sure we stop the spell SFX, since it's a bit delayed
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
buff_hero_single(module_accessor, status, buff_vec);
|
||||
|
@ -185,14 +177,19 @@ unsafe fn buff_joker(module_accessor: &mut app::BattleObjectModuleAccessor) -> b
|
|||
}
|
||||
|
||||
unsafe fn buff_mac(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
WorkModule::set_float(
|
||||
module_accessor,
|
||||
100.0,
|
||||
*FIGHTER_LITTLEMAC_INSTANCE_WORK_ID_FLOAT_KO_GAGE,
|
||||
);
|
||||
// Trying to stop KO Punch from playing seems to make it play multiple times in rapid succession
|
||||
// Look at 0x7100c44b60 for the func that handles this
|
||||
// Need to figure out how to update the KO meter if this is fixed
|
||||
if !is_buffing(module_accessor) {
|
||||
// Only need to set KO gauge once
|
||||
start_buff(module_accessor);
|
||||
WorkModule::set_float(
|
||||
module_accessor,
|
||||
100.0,
|
||||
*FIGHTER_LITTLEMAC_INSTANCE_WORK_ID_FLOAT_KO_GAGE,
|
||||
);
|
||||
}
|
||||
if frame_counter::should_delay(2_u32, BUFF_DELAY_COUNTER) {
|
||||
// Need to wait 2 frames to make sure we stop the KO sound, since it's a bit delayed
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
|
@ -221,6 +218,47 @@ unsafe fn buff_sepiroth(
|
|||
false
|
||||
}
|
||||
|
||||
unsafe fn buff_shulk(module_accessor: &mut app::BattleObjectModuleAccessor, status: i32) -> bool {
|
||||
let current_art = MENU.buff_state.shulk_buffs().get_random();
|
||||
if current_art == BuffOption::empty() {
|
||||
// No Monado Arts selected in the buff menu, so we don't need to buff
|
||||
return true;
|
||||
}
|
||||
start_buff(module_accessor);
|
||||
let prev_status_kind = StatusModule::prev_status_kind(module_accessor, 0);
|
||||
if prev_status_kind == FIGHTER_SHULK_STATUS_KIND_SPECIAL_N_ACTION {
|
||||
if frame_counter::should_delay(3_u32, BUFF_DELAY_COUNTER) {
|
||||
// Need to continue to be buffing to make sure we stop "JUMP!" voice line
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if status != FIGHTER_SHULK_STATUS_KIND_SPECIAL_N_ACTION {
|
||||
WorkModule::set_int(
|
||||
module_accessor,
|
||||
current_art.into_int().unwrap(),
|
||||
*FIGHTER_SHULK_INSTANCE_WORK_ID_INT_SPECIAL_N_TYPE_SELECT,
|
||||
);
|
||||
WorkModule::set_int(
|
||||
module_accessor,
|
||||
29,
|
||||
*FIGHTER_SHULK_INSTANCE_WORK_ID_INT_SPECIAL_N_SELECT_TIMER,
|
||||
);
|
||||
WorkModule::on_flag(
|
||||
module_accessor,
|
||||
*FIGHTER_SHULK_INSTANCE_WORK_ID_FLAG_SPECIAL_N_SELECT,
|
||||
);
|
||||
StatusModule::change_status_force(
|
||||
module_accessor,
|
||||
*FIGHTER_SHULK_STATUS_KIND_SPECIAL_N_ACTION,
|
||||
true,
|
||||
);
|
||||
} else {
|
||||
MotionModule::set_rate(module_accessor, 40.0);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
unsafe fn buff_wiifit(
|
||||
module_accessor: &mut app::BattleObjectModuleAccessor,
|
||||
status: i32,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -79,12 +79,11 @@ pub fn buffer_action(action: Action) {
|
|||
&& !is_playback_queued()
|
||||
&& action != Action::PLAYBACK
|
||||
{
|
||||
println!("Stopping mash playback for menu option!");
|
||||
input_record::stop_playback();
|
||||
}
|
||||
// if we don't want to leave playback on mash actions, then don't perform the mash
|
||||
if input_record::is_playback() {
|
||||
return;
|
||||
//println!("Stopping mash playback for menu option!");
|
||||
// if we don't want to leave playback on mash actions, then don't perform the mash
|
||||
if input_record::is_playback() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
use skyline::hooks::{getRegionAddress, InlineCtx, Region};
|
||||
use skyline::nn::hid::*;
|
||||
use skyline::nn::ro::LookupSymbol;
|
||||
use smash::app::{self, enSEType, lua_bind::*};
|
||||
use smash::app::{self, enSEType, lua_bind::*, utility};
|
||||
use smash::lib::lua_const::*;
|
||||
use smash::params::*;
|
||||
use smash::phx::{Hash40, Vector3f};
|
||||
|
||||
use crate::common::{
|
||||
dev_config, is_training_mode, menu, FIGHTER_MANAGER_ADDR, ITEM_MANAGER_ADDR, STAGE_MANAGER_ADDR,
|
||||
consts::BuffOption, consts::FighterId, consts::MENU, dev_config, get_module_accessor,
|
||||
is_training_mode, menu, FIGHTER_MANAGER_ADDR, ITEM_MANAGER_ADDR, STAGE_MANAGER_ADDR,
|
||||
};
|
||||
use crate::hitbox_visualizer;
|
||||
use crate::logging::*;
|
||||
|
@ -119,6 +120,26 @@ fn once_per_frame_per_fighter(
|
|||
return;
|
||||
}
|
||||
|
||||
/*unsafe {
|
||||
let fighter_kind = app::utility::get_kind(module_accessor);
|
||||
if fighter_kind == *FIGHTER_KIND_LITTLEMAC {
|
||||
let status = StatusModule::status_kind(module_accessor);
|
||||
let gage_max_keep = WorkModule::get_int(module_accessor, *FIGHTER_LITTLEMAC_INSTANCE_WORK_ID_INT_KO_GAGE_MAX_KEEP_FRAME);
|
||||
let select_timer = WorkModule::get_int(module_accessor, *FIGHTER_LITTLEMAC_INSTANCE_WORK_ID_INT_SPECIAL_N2_HIT_FRAME);
|
||||
|
||||
let max_effect_flag = WorkModule::is_flag(module_accessor, *FIGHTER_LITTLEMAC_INSTANCE_WORK_ID_FLAG_REQUEST_KO_GAUGE_MAX_EFFECT);
|
||||
let mesh_flag = WorkModule::is_flag(module_accessor, *FIGHTER_LITTLEMAC_INSTANCE_WORK_ID_FLAG_DAMAGE_MESH);
|
||||
|
||||
let gage_max_keep = WorkModule::get_float(module_accessor, *FIGHTER_LITTLEMAC_INSTANCE_WORK_ID_INT_KO_GAGE_MAX_KEEP_FRAME);
|
||||
let select_timer = WorkModule::get_float(module_accessor, *FIGHTER_LITTLEMAC_INSTANCE_WORK_ID_INT_SPECIAL_N2_HIT_FRAME);
|
||||
|
||||
// may need to check out his status flags and not just work insts
|
||||
println!("Status: {:x}, Counter: {}, Select_Timer: {}, Decide_Interval_Frame: {}, SNType: {}, SNTypeSelect: {}, Sel_F: {}, Active_F: {}, SelButton: {}, Circ Menu: {}",
|
||||
status, counter, select_timer, decide_interval_frame, special_n_type, special_n_type_select, select_flag, active_flag, select_button_push_flag, circle_menu_flag
|
||||
);
|
||||
}
|
||||
}*/
|
||||
|
||||
unsafe {
|
||||
if menu::menu_condition(module_accessor) {
|
||||
menu::spawn_menu();
|
||||
|
@ -453,6 +474,54 @@ pub unsafe fn handle_se(
|
|||
)
|
||||
}
|
||||
|
||||
static PLAY_SE_OFFSET: usize = 0x04cf6a0;
|
||||
// fighters don't use the symbol and go straight through their vtable to this function
|
||||
#[skyline::hook(offset = PLAY_SE_OFFSET)]
|
||||
pub unsafe fn handle_fighter_play_se(
|
||||
module_accessor: &mut app::BattleObjectModuleAccessor,
|
||||
my_hash: Hash40,
|
||||
bool1: bool,
|
||||
bool2: bool,
|
||||
bool3: bool,
|
||||
bool4: bool,
|
||||
se_type: enSEType,
|
||||
) -> u64 {
|
||||
if !is_training_mode() {
|
||||
return original!()(
|
||||
module_accessor,
|
||||
my_hash,
|
||||
bool1,
|
||||
bool2,
|
||||
bool3,
|
||||
bool4,
|
||||
se_type,
|
||||
);
|
||||
}
|
||||
|
||||
// Supress Buff Sound Effects while buffing
|
||||
if buff::is_buffing(module_accessor) {
|
||||
let silent_hash = Hash40::new("se_silent");
|
||||
return original!()(
|
||||
module_accessor,
|
||||
silent_hash,
|
||||
bool1,
|
||||
bool2,
|
||||
bool3,
|
||||
bool4,
|
||||
se_type,
|
||||
);
|
||||
}
|
||||
original!()(
|
||||
module_accessor,
|
||||
my_hash,
|
||||
bool1,
|
||||
bool2,
|
||||
bool3,
|
||||
bool4,
|
||||
se_type,
|
||||
)
|
||||
}
|
||||
|
||||
#[skyline::hook(replace = EffectModule::req)] // hooked to prevent death gfx from playing when loading save states
|
||||
pub unsafe fn handle_effect(
|
||||
module_accessor: &mut app::BattleObjectModuleAccessor,
|
||||
|
@ -518,6 +587,34 @@ pub unsafe fn handle_star_ko(my_long_ptr: &mut u64) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
static REUSED_UI_OFFSET: usize = 0x068cd80;
|
||||
// A function reused by many functions to update UI. Called to update at least Little Mac's meter.
|
||||
#[skyline::hook(offset = REUSED_UI_OFFSET)]
|
||||
pub unsafe fn handle_reused_ui(
|
||||
fighter_data: *mut u32, // a pointer to length 4 data in the Fighter's FighterEntry in the FighterManager
|
||||
mut param_2: u32, // In Little Mac's case, the meter value as an integer
|
||||
) {
|
||||
if !is_training_mode() {
|
||||
original!()(fighter_data, param_2);
|
||||
}
|
||||
|
||||
if save_states::is_loading() {
|
||||
let player_module_accessor = &mut *get_module_accessor(FighterId::Player);
|
||||
let cpu_module_accessor = &mut *get_module_accessor(FighterId::CPU);
|
||||
let player_fighter_kind = utility::get_kind(player_module_accessor);
|
||||
let cpu_fighter_kind = utility::get_kind(cpu_module_accessor);
|
||||
// If Little Mac is in the game and we're buffing him, set the meter to 100
|
||||
if (player_fighter_kind == *FIGHTER_KIND_LITTLEMAC
|
||||
|| cpu_fighter_kind == *FIGHTER_KIND_LITTLEMAC)
|
||||
&& MENU.buff_state.to_vec().contains(&BuffOption::KO)
|
||||
{
|
||||
param_2 = 100;
|
||||
}
|
||||
}
|
||||
|
||||
original!()(fighter_data, param_2)
|
||||
}
|
||||
|
||||
#[allow(improper_ctypes)]
|
||||
extern "C" {
|
||||
fn add_nn_hid_hook(callback: fn(*mut NpadGcState, *const u32));
|
||||
|
@ -528,12 +625,13 @@ pub fn training_mods() {
|
|||
|
||||
// Input Mods
|
||||
unsafe {
|
||||
if (add_nn_hid_hook as *const ()).is_null() {
|
||||
if let Some(_f) = (add_nn_hid_hook as *const ()).as_ref() {
|
||||
add_nn_hid_hook(input_delay::handle_get_npad_state);
|
||||
add_nn_hid_hook(menu::handle_get_npad_state);
|
||||
add_nn_hid_hook(dev_config::handle_get_npad_state);
|
||||
} else {
|
||||
panic!("The NN-HID hook plugin could not be found and is required to add NRO hooks. Make sure libnn_hid_hook.nro is installed.");
|
||||
}
|
||||
add_nn_hid_hook(input_delay::handle_get_npad_state);
|
||||
add_nn_hid_hook(menu::handle_get_npad_state);
|
||||
add_nn_hid_hook(dev_config::handle_get_npad_state);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
|
@ -591,6 +689,7 @@ pub fn training_mods() {
|
|||
// Buffs
|
||||
handle_add_limit,
|
||||
handle_check_doyle_summon_dispatch,
|
||||
handle_reused_ui,
|
||||
handle_req_screen,
|
||||
// Stale Moves
|
||||
stale_handle,
|
||||
|
@ -603,6 +702,8 @@ pub fn training_mods() {
|
|||
handle_star_ko,
|
||||
// Clatter
|
||||
clatter::hook_start_clatter,
|
||||
// Buff SFX
|
||||
handle_fighter_play_se,
|
||||
);
|
||||
|
||||
combo::init();
|
||||
|
|
|
@ -224,6 +224,14 @@ pub unsafe fn get_param_int(
|
|||
*FIGHTER_INSTANCE_WORK_ID_INT_HIT_STOP_IGNORE_JOSTLE_FRAME,
|
||||
);
|
||||
}
|
||||
// Remove Shulk Monado Art Damage Effects
|
||||
WorkModule::set_int(
|
||||
module_accessor,
|
||||
0,
|
||||
*FIGHTER_INSTANCE_WORK_ID_INT_SHULK_MONAD_ARTS_DAMAGE_FLASH_FRAME,
|
||||
);
|
||||
EffectModule::remove_common(module_accessor, Hash40::new("monad_arts_damage_buster"));
|
||||
EffectModule::remove_common(module_accessor, Hash40::new("monad_arts_damage_smash"));
|
||||
return Some(1);
|
||||
}
|
||||
if param_hash == hash40("rebirth_move_frame") {
|
||||
|
@ -389,6 +397,7 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor)
|
|||
*FIGHTER_KIND_LITTLEMAC,
|
||||
*FIGHTER_KIND_EDGE,
|
||||
*FIGHTER_KIND_WIIFIT,
|
||||
*FIGHTER_KIND_SHULK,
|
||||
]
|
||||
.contains(&fighter_kind);
|
||||
|
||||
|
|
|
@ -588,6 +588,11 @@ bitflags! {
|
|||
const LIMIT = 0x40;
|
||||
const KO = 0x80;
|
||||
const WING = 0x100;
|
||||
const MONAD_JUMP = 0x200;
|
||||
const MONAD_SPEED = 0x400;
|
||||
const MONAD_SHIELD = 0x800;
|
||||
const MONAD_BUSTER = 0x1000;
|
||||
const MONAD_SMASH = 0x2000;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -605,6 +610,11 @@ impl BuffOption {
|
|||
BuffOption::LIMIT => 1,
|
||||
BuffOption::KO => 1,
|
||||
BuffOption::WING => 1,
|
||||
BuffOption::MONAD_JUMP => *FIGHTER_SHULK_MONAD_TYPE_JUMP,
|
||||
BuffOption::MONAD_SPEED => *FIGHTER_SHULK_MONAD_TYPE_SPEED,
|
||||
BuffOption::MONAD_SHIELD => *FIGHTER_SHULK_MONAD_TYPE_SHIELD,
|
||||
BuffOption::MONAD_BUSTER => *FIGHTER_SHULK_MONAD_TYPE_BUSTER,
|
||||
BuffOption::MONAD_SMASH => *FIGHTER_SHULK_MONAD_TYPE_SMASH,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
@ -624,9 +634,33 @@ impl BuffOption {
|
|||
BuffOption::LIMIT => "Limit Break",
|
||||
BuffOption::KO => "KO Punch",
|
||||
BuffOption::WING => "1-Winged Angel",
|
||||
BuffOption::MONAD_JUMP => "Jump",
|
||||
BuffOption::MONAD_SPEED => "Speed",
|
||||
BuffOption::MONAD_SHIELD => "Shield",
|
||||
BuffOption::MONAD_BUSTER => "Buster",
|
||||
BuffOption::MONAD_SMASH => "Smash",
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn hero_buffs(self) -> BuffOption {
|
||||
// Return a struct with only Hero's selected buffs
|
||||
let hero_buffs_bitflags = BuffOption::ACCELERATLE
|
||||
.union(BuffOption::OOMPH)
|
||||
.union(BuffOption::BOUNCE)
|
||||
.union(BuffOption::PSYCHE);
|
||||
self.intersection(hero_buffs_bitflags)
|
||||
}
|
||||
|
||||
pub fn shulk_buffs(self) -> BuffOption {
|
||||
// Return a struct with only Shulk's selected arts
|
||||
let shulk_buffs_bitflags = BuffOption::MONAD_JUMP
|
||||
.union(BuffOption::MONAD_SPEED)
|
||||
.union(BuffOption::MONAD_SHIELD)
|
||||
.union(BuffOption::MONAD_BUSTER)
|
||||
.union(BuffOption::MONAD_SMASH);
|
||||
self.intersection(shulk_buffs_bitflags)
|
||||
}
|
||||
}
|
||||
|
||||
extra_bitflag_impls! {BuffOption}
|
||||
|
|
Loading…
Reference in a new issue