mirror of
https://github.com/jugeeya/UltimateTrainingModpack.git
synced 2024-11-24 02:44:17 +00:00
Functional PT Savestates and additional debug (#616)
* Add reusable print_fighter_info() * rustfmt * Clippy * starting charge * Working Pikmin Save States, motion not working, order can be messed up by loading during an aerial, if you save too quickly you lose pikmin in the state * often failing boid read * notes * Prevent Pikmin from being cleaned up by the regular cleanup process * migration progress * skyline-smash branch, more pikmin work * Using buff to spawn pikmin, character specific file for cleanliness * failed reordering attempt * more failed ordering * almost kind of works but out of order * failed all, need to try individual hold and status change * hard reorder crash * battleobject not boma * comment clarification * messing around with printing * printing, about to try to find where autonomy is being set * solution found, going to hook backshield to find where autonomy is being set to true * found where to hook, will start hooking * switched to charge, issues with inline hook * need to only have autonomy ignored on save state load * distance and status controls (doesn't work perfectly) * Christmas Miracle * Working w/ Cleanup * training mode check, improved owner check * Debug for hooking, Working Implementation (No Transform, No Charge) * Working for Aegis, PT having issues (LRA into state load works, but not regular state load) * Working ptrainer hats * debugging ptrainer * great debug * Pokemon Swaps to Saved State * invisible ball, need to silence trainer/ball and hook joint eff * cpu isn't being saved properly, lra + state load crash fixed * CPU working, unsure if flag is necessary though * Visual fixes working, can't load states again if player if trainer, cpu getting stuck? * working, trainer stuck if no move on state load, switch initialization issue * PT is saved * debug cleanup * rustfmt, still needs clippy and main merge * clippy fmt * one more fmt * prevent flying plate from existing if you load state during death * debug cleanup * rustfmt * cleanup
This commit is contained in:
parent
7e0617e377
commit
7fa0e933e9
7 changed files with 508 additions and 98 deletions
|
@ -5,6 +5,7 @@ use smash::lua2cpp::L2CFighterCommon;
|
|||
|
||||
pub use crate::common::consts::MENU;
|
||||
use crate::common::consts::*;
|
||||
use crate::training::character_specific::ptrainer;
|
||||
|
||||
pub mod button_config;
|
||||
pub mod consts;
|
||||
|
@ -155,15 +156,18 @@ pub unsafe fn is_ptrainer(module_accessor: &mut app::BattleObjectModuleAccessor)
|
|||
pub unsafe fn is_dead(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let status_kind = StatusModule::status_kind(module_accessor);
|
||||
let prev_status_kind = StatusModule::prev_status_kind(module_accessor, 0);
|
||||
// Pokemon trainer enters FIGHTER_STATUS_KIND_WAIT for one frame during their respawn animation
|
||||
// And the previous status is FIGHTER_STATUS_NONE
|
||||
let is_dead_status =
|
||||
[*FIGHTER_STATUS_KIND_DEAD, *FIGHTER_STATUS_KIND_STANDBY].contains(&status_kind);
|
||||
let mut ptrainer_switch_dead = false;
|
||||
// There's one frame during switching that we can't detect as alive early, where the Pokemon is in Wait with no previous status.
|
||||
// To prevent this matching the situation after a L + R + A reset, we check the status of the PTrainer to see if we're switching.
|
||||
if is_ptrainer(module_accessor) {
|
||||
[*FIGHTER_STATUS_KIND_DEAD, *FIGHTER_STATUS_KIND_STANDBY].contains(&status_kind)
|
||||
|| (status_kind == FIGHTER_STATUS_KIND_WAIT
|
||||
&& prev_status_kind == FIGHTER_STATUS_KIND_NONE)
|
||||
} else {
|
||||
[*FIGHTER_STATUS_KIND_DEAD, *FIGHTER_STATUS_KIND_STANDBY].contains(&status_kind)
|
||||
ptrainer_switch_dead = (status_kind == FIGHTER_STATUS_KIND_WAIT
|
||||
&& prev_status_kind == FIGHTER_STATUS_KIND_NONE)
|
||||
&& (StatusModule::status_kind(ptrainer::get_ptrainer_module_accessor(module_accessor))
|
||||
== *WEAPON_PTRAINER_PTRAINER_STATUS_KIND_RESTART_CHANGE);
|
||||
}
|
||||
is_dead_status || ptrainer_switch_dead
|
||||
}
|
||||
|
||||
pub unsafe fn is_in_clatter(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
|
|
|
@ -3,6 +3,7 @@ use smash::app::{self};
|
|||
mod bowser;
|
||||
pub mod items;
|
||||
pub mod pikmin;
|
||||
pub mod ptrainer;
|
||||
pub mod steve;
|
||||
|
||||
/**
|
||||
|
|
160
src/training/character_specific/ptrainer.rs
Normal file
160
src/training/character_specific/ptrainer.rs
Normal file
|
@ -0,0 +1,160 @@
|
|||
use crate::training::frame_counter;
|
||||
use crate::training::save_states;
|
||||
use once_cell::sync::Lazy;
|
||||
use skyline::hooks::InlineCtx;
|
||||
use smash::app::{self, lua_bind::*, smashball::is_training_mode};
|
||||
use smash::hash40;
|
||||
use smash::lib::lua_const::*;
|
||||
use smash::phx::Hash40;
|
||||
|
||||
static SWITCH_DELAY_COUNTER: Lazy<usize> =
|
||||
Lazy::new(|| frame_counter::register_counter(frame_counter::FrameCounterType::InGame));
|
||||
|
||||
pub unsafe fn is_switched(ptrainer_module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let status_kind = StatusModule::status_kind(ptrainer_module_accessor);
|
||||
let situ_kind = StatusModule::situation_kind(ptrainer_module_accessor);
|
||||
if status_kind == *WEAPON_PTRAINER_PTRAINER_STATUS_KIND_RUN {
|
||||
MotionModule::set_rate(ptrainer_module_accessor, 1.0);
|
||||
}
|
||||
if frame_counter::should_delay(5_u32, *SWITCH_DELAY_COUNTER) {
|
||||
// Need to wait to make sure we stop the flash effect
|
||||
return false;
|
||||
}
|
||||
// If you're trying to fix PT getting locked up, maybe try
|
||||
// to run FUN_71000106c0 in lua2cpp_ptrainer to get her wandering correct
|
||||
// Also worth trying is figuring out how to prevent PT from entering WEAPON_PTRAINER_PTRAINER_STATUS_KIND_RUN_STOP
|
||||
// instead of run below after that status change
|
||||
if situ_kind == SITUATION_KIND_AIR {
|
||||
StatusModule::set_situation_kind(
|
||||
ptrainer_module_accessor,
|
||||
app::SituationKind(*SITUATION_KIND_GROUND),
|
||||
true,
|
||||
);
|
||||
StatusModule::change_status_force(
|
||||
ptrainer_module_accessor,
|
||||
*WEAPON_PTRAINER_PTRAINER_STATUS_KIND_RUN,
|
||||
false,
|
||||
);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub unsafe fn change_motion(
|
||||
ptrainer_module_accessor: &mut app::BattleObjectModuleAccessor,
|
||||
motion_kind: u64,
|
||||
) {
|
||||
if app::utility::get_kind(ptrainer_module_accessor) == *WEAPON_KIND_PTRAINER_PTRAINER
|
||||
&& hash40("restart") == motion_kind
|
||||
&& save_states::is_loading()
|
||||
{
|
||||
MotionModule::set_rate(ptrainer_module_accessor, 1000.0);
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn get_ptrainer_mball_module_accessor(
|
||||
ptrainer_module_accessor: &mut app::BattleObjectModuleAccessor,
|
||||
) -> Option<&mut app::BattleObjectModuleAccessor> {
|
||||
if ArticleModule::is_exist(
|
||||
ptrainer_module_accessor,
|
||||
*WEAPON_PTRAINER_PTRAINER_GENERATE_ARTICLE_MBALL,
|
||||
) {
|
||||
let ptrainer_masterball: *mut app::Article = ArticleModule::get_article(
|
||||
ptrainer_module_accessor,
|
||||
*WEAPON_PTRAINER_PTRAINER_GENERATE_ARTICLE_MBALL,
|
||||
);
|
||||
let ptrainer_masterball_id = Article::get_battle_object_id(ptrainer_masterball);
|
||||
return Some(&mut *app::sv_battle_object::module_accessor(
|
||||
ptrainer_masterball_id as u32,
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub unsafe fn get_ptrainer_module_accessor(
|
||||
module_accessor: &mut app::BattleObjectModuleAccessor,
|
||||
) -> &mut app::BattleObjectModuleAccessor {
|
||||
let ptrainer_object_id =
|
||||
LinkModule::get_parent_object_id(module_accessor, *FIGHTER_POKEMON_LINK_NO_PTRAINER);
|
||||
&mut *app::sv_battle_object::module_accessor(ptrainer_object_id as u32)
|
||||
}
|
||||
|
||||
pub unsafe fn get_pokemon_module_accessor(
|
||||
ptrainer_module_accessor: *mut app::BattleObjectModuleAccessor,
|
||||
) -> *mut app::BattleObjectModuleAccessor {
|
||||
let pokemon_object_id = LinkModule::get_node_object_id(
|
||||
ptrainer_module_accessor,
|
||||
*WEAPON_PTRAINER_PTRAINER_LINK_NO_POKEMON,
|
||||
);
|
||||
&mut *app::sv_battle_object::module_accessor(pokemon_object_id as u32)
|
||||
}
|
||||
|
||||
pub unsafe fn handle_pokemon_effect(
|
||||
module_accessor: &mut app::BattleObjectModuleAccessor,
|
||||
hash: Hash40,
|
||||
size: f32,
|
||||
) -> f32 {
|
||||
let kind = app::utility::get_kind(module_accessor);
|
||||
if ![
|
||||
*FIGHTER_KIND_PZENIGAME,
|
||||
*FIGHTER_KIND_PFUSHIGISOU,
|
||||
*FIGHTER_KIND_PLIZARDON,
|
||||
*WEAPON_KIND_PTRAINER_PTRAINER,
|
||||
*WEAPON_KIND_PTRAINER_MBALL,
|
||||
-1,
|
||||
]
|
||||
.contains(&kind)
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
let is_ptrainer_switch_hash = [
|
||||
Hash40::new("sys_flying_plate"), // for Req
|
||||
Hash40::new("ptrainer_change_light"), // for ReqOnJoint
|
||||
]
|
||||
.contains(&hash)
|
||||
|| hash.hash == 0x10e3fac8d9;
|
||||
|
||||
// We never want the flying plate, and otherwise we allow outside of savestates
|
||||
if (is_ptrainer_switch_hash && save_states::is_loading())
|
||||
|| Hash40::new("sys_flying_plate") == hash
|
||||
{
|
||||
// Making the size 0 prevents these effects from being displayed. Fixes Pokemon Trainer Angel Platform Effect.
|
||||
return 0.0;
|
||||
}
|
||||
size
|
||||
}
|
||||
|
||||
pub unsafe fn handle_pokemon_sound_effect(hash: Hash40) -> Hash40 {
|
||||
let is_ptrainer_switch_sound_hash = [
|
||||
Hash40::new("se_ptrainer_change_appear"),
|
||||
Hash40::new("se_ptrainer_ball_open"),
|
||||
Hash40::new("se_ptrainer_ball_swing"),
|
||||
]
|
||||
.contains(&hash);
|
||||
if is_ptrainer_switch_sound_hash && save_states::is_loading() {
|
||||
return Hash40::new("se_silent");
|
||||
}
|
||||
hash
|
||||
}
|
||||
|
||||
// Choose which pokemon to switch to!
|
||||
static POKEMON_DECIDE_OFFSET: usize = 0x34cdc64;
|
||||
|
||||
#[skyline::hook(offset = POKEMON_DECIDE_OFFSET, inline)]
|
||||
unsafe fn handle_pokemon_decide(ctx: &mut InlineCtx) {
|
||||
if !is_training_mode() || !save_states::is_loading() {
|
||||
return;
|
||||
}
|
||||
let x20 = ctx.registers[20].x.as_mut();
|
||||
let fighter = *x20 as *mut u64 as *mut app::Fighter;
|
||||
let module_accessor = (*fighter).battle_object.module_accessor;
|
||||
let pokemon_value = save_states::get_state_pokemon(module_accessor);
|
||||
if pokemon_value <= 2 {
|
||||
let w8 = ctx.registers[8].w.as_mut();
|
||||
*w8 = pokemon_value;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
skyline::install_hooks!(handle_pokemon_decide,);
|
||||
}
|
174
src/training/debug.rs
Normal file
174
src/training/debug.rs
Normal file
|
@ -0,0 +1,174 @@
|
|||
#![allow(dead_code)] // For Debug
|
||||
#![allow(unused_imports)]
|
||||
#![cfg(debug_assertions)]
|
||||
use crate::common::is_operation_cpu;
|
||||
use smash::app::{self, lua_bind::*, smashball::is_training_mode, utility};
|
||||
use smash::lib::lua_const::*;
|
||||
|
||||
#[skyline::from_offset(0x1655400)]
|
||||
fn is_visible_backshield(module_accessor: *mut app::BattleObjectModuleAccessor) -> bool;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct WorkModule2 {
|
||||
vtable: u64,
|
||||
owner: &'static mut app::BattleObjectModuleAccessor,
|
||||
}
|
||||
|
||||
static ON_FLAG_OFFSET: usize = 0x4e4910;
|
||||
#[skyline::hook(offset = ON_FLAG_OFFSET)]
|
||||
pub unsafe fn handle_on_flag(work_module: &mut WorkModule2, address: i32) {
|
||||
if address == *WEAPON_PTRAINER_PTRAINER_INSTANCE_WORK_ID_FLAG_OUTFIELD_INVISIBLE
|
||||
&& app::utility::get_kind(work_module.owner) != *FIGHTER_KIND_SHEIK
|
||||
{
|
||||
is_visible_backshield(work_module.owner);
|
||||
}
|
||||
original!()(work_module, address);
|
||||
}
|
||||
|
||||
static SET_INT_OFFSET: usize = 0x4e4600;
|
||||
#[skyline::hook(offset = SET_INT_OFFSET)]
|
||||
pub unsafe fn handle_set_int(work_module: &mut WorkModule2, value: u32, address: i32) {
|
||||
if !is_training_mode() {
|
||||
original!()(work_module, value, address);
|
||||
}
|
||||
if address == *WEAPON_PTRAINER_MBALL_INSTANCE_WORK_ID_INT_PLATE_EFF_ID
|
||||
&& app::utility::get_kind(work_module.owner) == *WEAPON_KIND_PTRAINER_MBALL
|
||||
{
|
||||
is_visible_backshield(work_module.owner);
|
||||
}
|
||||
original!()(work_module, value, address);
|
||||
}
|
||||
|
||||
static SET_INT64_OFFSET: usize = 0x4e4680;
|
||||
#[skyline::hook(offset = SET_INT64_OFFSET)]
|
||||
pub unsafe fn handle_set_int_64(work_module: &mut WorkModule2, value: u64, address: i32) {
|
||||
if !is_training_mode() {
|
||||
original!()(work_module, value, address);
|
||||
}
|
||||
original!()(work_module, value, address);
|
||||
}
|
||||
|
||||
static SET_FLOAT_OFFSET: usize = 0x4e4420;
|
||||
#[skyline::hook(offset = SET_FLOAT_OFFSET)]
|
||||
pub unsafe fn handle_set_float(work_module: &mut WorkModule2, value: f32, address: i32) {
|
||||
if !is_training_mode() {
|
||||
original!()(work_module, value, address);
|
||||
}
|
||||
original!()(work_module, value, address);
|
||||
}
|
||||
|
||||
static IS_FLAG_OFFSET: usize = 0x4e48e0;
|
||||
#[skyline::hook(offset = IS_FLAG_OFFSET)]
|
||||
pub unsafe fn handle_is_flag(work_module: &mut WorkModule2, address: i32) -> bool {
|
||||
if !is_training_mode() {
|
||||
original!()(work_module, address);
|
||||
}
|
||||
if address == *WEAPON_PTRAINER_PTRAINER_INSTANCE_WORK_ID_FLAG_ENABLE_CHANGE_POKEMON //*FIGHTER_KIRBY_INSTANCE_WORK_ID_FLAG_COPY_ON_START
|
||||
&& app::utility::get_kind(work_module.owner) != *FIGHTER_KIND_SHEIK
|
||||
&& original!()(work_module, address)
|
||||
{
|
||||
is_visible_backshield(work_module.owner);
|
||||
}
|
||||
original!()(work_module, address)
|
||||
}
|
||||
|
||||
static GET_INT_OFFSET: usize = 0x4e45e0;
|
||||
#[skyline::hook(offset = GET_INT_OFFSET)]
|
||||
pub unsafe fn handle_get_int(work_module: &mut WorkModule2, address: i32) {
|
||||
if !is_training_mode() {
|
||||
original!()(work_module, address);
|
||||
}
|
||||
original!()(work_module, address);
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
skyline::install_hooks!(
|
||||
//handle_on_flag,
|
||||
//handle_set_int,
|
||||
// handle_set_int_64,
|
||||
// handle_set_float,
|
||||
// handle_get_int,
|
||||
//handle_is_flag,
|
||||
);
|
||||
}
|
||||
|
||||
// Example Call:
|
||||
|
||||
// print_fighter_info(
|
||||
// module_accessor,
|
||||
// "DebugTest",
|
||||
// true,
|
||||
// false,
|
||||
// true,
|
||||
// true,
|
||||
// vec![
|
||||
// ("FIGHTER_INSTANCE_WORK_ID_INT_CLIFF_COUNT", FIGHTER_INSTANCE_WORK_ID_INT_CLIFF_COUNT),
|
||||
// ],
|
||||
// Vec::new(),
|
||||
// vec![
|
||||
// ("FIGHTER_STATUS_CLIFF_FLAG_TO_FALL", FIGHTER_STATUS_CLIFF_FLAG_TO_FALL),
|
||||
// ],
|
||||
// );
|
||||
#[allow(clippy::too_many_arguments)] // This function has so many arguments so it's easy to quickly fill them in when debugging with the analyzer
|
||||
pub fn print_fighter_info(
|
||||
module_accessor: &mut app::BattleObjectModuleAccessor,
|
||||
title: &str,
|
||||
player_only: bool,
|
||||
cpu_only: bool,
|
||||
print_fighter_kind: bool,
|
||||
print_status: bool,
|
||||
work_int_pairs: Vec<(&str, i32)>,
|
||||
work_float_pairs: Vec<(&str, i32)>,
|
||||
work_flag_pairs: Vec<(&str, i32)>,
|
||||
) {
|
||||
unsafe {
|
||||
// Don't print for fighters we don't want to
|
||||
let is_cpu = is_operation_cpu(module_accessor);
|
||||
if (player_only && is_cpu) || (cpu_only && !is_cpu) {
|
||||
return;
|
||||
}
|
||||
// Print Title
|
||||
print!("{}: ", title);
|
||||
// Print Fighter Kind:
|
||||
if print_fighter_kind {
|
||||
print!("FIGHTER_KIND: {}, ", utility::get_kind(module_accessor));
|
||||
}
|
||||
// Print Status:
|
||||
if print_status {
|
||||
print!(
|
||||
"FIGHTER_STATUS: {}, ",
|
||||
StatusModule::status_kind(module_accessor)
|
||||
);
|
||||
}
|
||||
|
||||
// Print Work Ints:
|
||||
for work_int_pair in work_int_pairs {
|
||||
print!(
|
||||
"{}: {}, ",
|
||||
work_int_pair.0,
|
||||
WorkModule::get_int(module_accessor, work_int_pair.1)
|
||||
);
|
||||
}
|
||||
|
||||
// Print Work Floats:
|
||||
for work_float_pair in work_float_pairs {
|
||||
print!(
|
||||
"{}: {}, ",
|
||||
work_float_pair.0,
|
||||
WorkModule::get_float(module_accessor, work_float_pair.1)
|
||||
);
|
||||
}
|
||||
|
||||
// Print Work Flags:
|
||||
for work_flag_pair in work_flag_pairs {
|
||||
print!(
|
||||
"{}: {}, ",
|
||||
work_flag_pair.0,
|
||||
WorkModule::is_flag(module_accessor, work_flag_pair.1)
|
||||
);
|
||||
}
|
||||
|
||||
// End Line
|
||||
println!("|");
|
||||
}
|
||||
}
|
|
@ -366,6 +366,7 @@ pub unsafe fn playback_ledge(slot: Option<usize>) {
|
|||
pub unsafe fn stop_playback() {
|
||||
INPUT_RECORD = None;
|
||||
INPUT_RECORD_FRAME = 0;
|
||||
POSSESSION = Player;
|
||||
}
|
||||
|
||||
pub unsafe fn is_input_neutral(input_frame: usize) -> bool {
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::common::{
|
|||
use crate::hitbox_visualizer;
|
||||
use crate::input::*;
|
||||
use crate::logging::*;
|
||||
use crate::training::character_specific::{items, pikmin};
|
||||
use crate::training::character_specific::{items, pikmin, ptrainer};
|
||||
use skyline::hooks::{getRegionAddress, InlineCtx, Region};
|
||||
use skyline::nn::ro::LookupSymbol;
|
||||
use smash::app::{self, enSEType, lua_bind::*, utility};
|
||||
|
@ -31,7 +31,7 @@ pub mod ui;
|
|||
|
||||
mod air_dodge_direction;
|
||||
mod attack_angle;
|
||||
mod character_specific;
|
||||
pub mod character_specific;
|
||||
mod fast_fall;
|
||||
mod full_hop;
|
||||
pub mod input_delay;
|
||||
|
@ -42,6 +42,9 @@ mod reset;
|
|||
pub mod save_states;
|
||||
mod shield_tilt;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
mod debug;
|
||||
|
||||
#[skyline::hook(replace = WorkModule::get_param_float)]
|
||||
pub unsafe fn handle_get_param_float(
|
||||
module_accessor: &mut app::BattleObjectModuleAccessor,
|
||||
|
@ -279,7 +282,7 @@ pub unsafe fn handle_change_motion(
|
|||
motion_kind
|
||||
};
|
||||
|
||||
original!()(
|
||||
let ori = original!()(
|
||||
module_accessor,
|
||||
mod_motion_kind,
|
||||
unk1,
|
||||
|
@ -288,7 +291,12 @@ pub unsafe fn handle_change_motion(
|
|||
unk4,
|
||||
unk5,
|
||||
unk6,
|
||||
)
|
||||
);
|
||||
// After we've changed motion, speed up if necessary
|
||||
if is_training_mode() {
|
||||
ptrainer::change_motion(module_accessor, motion_kind);
|
||||
}
|
||||
ori
|
||||
}
|
||||
|
||||
#[skyline::hook(replace = WorkModule::is_enable_transition_term)]
|
||||
|
@ -494,7 +502,7 @@ static PLAY_SE_OFFSET: usize = 0x04cf6a0;
|
|||
#[skyline::hook(offset = PLAY_SE_OFFSET)]
|
||||
pub unsafe fn handle_fighter_play_se(
|
||||
sound_module: *mut FighterSoundModule, // pointer to fighter's SoundModule
|
||||
my_hash: Hash40,
|
||||
mut my_hash: Hash40,
|
||||
bool1: bool,
|
||||
bool2: bool,
|
||||
bool3: bool,
|
||||
|
@ -504,49 +512,30 @@ pub unsafe fn handle_fighter_play_se(
|
|||
if !is_training_mode() {
|
||||
return original!()(sound_module, my_hash, bool1, bool2, bool3, bool4, se_type);
|
||||
}
|
||||
|
||||
// Supress Buff Sound Effects while buffing
|
||||
if buff::is_buffing_any() {
|
||||
let silent_hash = Hash40::new("se_silent");
|
||||
return original!()(
|
||||
sound_module,
|
||||
silent_hash,
|
||||
bool1,
|
||||
bool2,
|
||||
bool3,
|
||||
bool4,
|
||||
se_type,
|
||||
);
|
||||
my_hash = Hash40::new("se_silent");
|
||||
}
|
||||
|
||||
// Supress Kirby Copy Ability SFX when loading Save State
|
||||
if my_hash.hash == 0x1453dd86e4 || my_hash.hash == 0x14bdd3e7c8 {
|
||||
let module_accessor = (*sound_module).owner;
|
||||
if StatusModule::status_kind(module_accessor) != FIGHTER_KIRBY_STATUS_KIND_SPECIAL_N_DRINK {
|
||||
let silent_hash = Hash40::new("se_silent");
|
||||
return original!()(
|
||||
sound_module,
|
||||
silent_hash,
|
||||
bool1,
|
||||
bool2,
|
||||
bool3,
|
||||
bool4,
|
||||
se_type,
|
||||
);
|
||||
my_hash = Hash40::new("se_silent");
|
||||
}
|
||||
}
|
||||
my_hash = ptrainer::handle_pokemon_sound_effect(my_hash);
|
||||
original!()(sound_module, my_hash, bool1, bool2, bool3, bool4, se_type)
|
||||
}
|
||||
|
||||
static FOLLOW_REQ_OFFSET: usize = 0x044f860;
|
||||
#[skyline::hook(offset = FOLLOW_REQ_OFFSET)] // hooked to prevent score gfx from playing when loading save states
|
||||
pub unsafe fn handle_effect_follow(
|
||||
module_accessor: &mut app::BattleObjectModuleAccessor,
|
||||
effect_module: *mut FighterEffectModule,
|
||||
eff_hash: Hash40,
|
||||
eff_hash2: Hash40,
|
||||
pos: *const Vector3f,
|
||||
rot: *const Vector3f,
|
||||
size: f32,
|
||||
mut size: f32,
|
||||
arg5: bool,
|
||||
arg6: u32,
|
||||
arg7: i32,
|
||||
|
@ -558,7 +547,7 @@ pub unsafe fn handle_effect_follow(
|
|||
) -> u64 {
|
||||
if !is_training_mode() {
|
||||
return original!()(
|
||||
module_accessor,
|
||||
effect_module,
|
||||
eff_hash,
|
||||
eff_hash2,
|
||||
pos,
|
||||
|
@ -576,25 +565,10 @@ pub unsafe fn handle_effect_follow(
|
|||
}
|
||||
// Prevent the score GFX from playing on the CPU when loading save state during hitstop
|
||||
if eff_hash == Hash40::new("sys_score_aura") && save_states::is_loading() {
|
||||
return original!()(
|
||||
module_accessor,
|
||||
eff_hash,
|
||||
eff_hash2,
|
||||
pos,
|
||||
rot,
|
||||
0.0,
|
||||
arg5,
|
||||
arg6,
|
||||
arg7,
|
||||
arg8,
|
||||
arg9,
|
||||
arg10,
|
||||
arg11,
|
||||
arg12,
|
||||
);
|
||||
size = 0.0
|
||||
}
|
||||
original!()(
|
||||
module_accessor,
|
||||
effect_module,
|
||||
eff_hash,
|
||||
eff_hash2,
|
||||
pos,
|
||||
|
@ -611,6 +585,100 @@ pub unsafe fn handle_effect_follow(
|
|||
)
|
||||
}
|
||||
|
||||
pub struct FighterEffectModule {
|
||||
_table: u64,
|
||||
owner: *mut app::BattleObjectModuleAccessor,
|
||||
}
|
||||
|
||||
static EFFECT_REQ_OFFSET: usize = 0x44de50;
|
||||
#[skyline::hook(offset = EFFECT_REQ_OFFSET)] // hooked to prevent death gfx from playing when loading save states
|
||||
pub unsafe fn handle_fighter_effect(
|
||||
effect_module: *mut FighterEffectModule, // pointer to effect module
|
||||
eff_hash: Hash40,
|
||||
pos: *const Vector3f,
|
||||
rot: *const Vector3f,
|
||||
mut size: f32,
|
||||
arg6: u32,
|
||||
arg7: i32,
|
||||
arg8: bool,
|
||||
arg9: i32,
|
||||
) -> u64 {
|
||||
if !is_training_mode() {
|
||||
return original!()(
|
||||
effect_module,
|
||||
eff_hash,
|
||||
pos,
|
||||
rot,
|
||||
size,
|
||||
arg6,
|
||||
arg7,
|
||||
arg8,
|
||||
arg9,
|
||||
);
|
||||
}
|
||||
size = ptrainer::handle_pokemon_effect(&mut *(*effect_module).owner, eff_hash, size);
|
||||
original!()(
|
||||
effect_module,
|
||||
eff_hash,
|
||||
pos,
|
||||
rot,
|
||||
size,
|
||||
arg6,
|
||||
arg7,
|
||||
arg8,
|
||||
arg9,
|
||||
)
|
||||
}
|
||||
|
||||
static JOINT_EFFECT_REQ_OFFSET: usize = 0x44e1e0;
|
||||
#[skyline::hook(offset = JOINT_EFFECT_REQ_OFFSET)] // hooked to prevent death gfx from playing when loading save states
|
||||
pub unsafe fn handle_fighter_joint_effect(
|
||||
effect_module: *mut FighterEffectModule, // pointer to effect module
|
||||
eff_hash: Hash40,
|
||||
joint_hash: Hash40,
|
||||
pos: *const Vector3f,
|
||||
rot: *const Vector3f,
|
||||
mut size: f32,
|
||||
pos2: *const Vector3f, //unk, maybe displacement and not pos/rot
|
||||
rot2: *const Vector3f, //unk, ^
|
||||
arg5: bool,
|
||||
arg6: u32,
|
||||
arg7: i32,
|
||||
arg9: i32,
|
||||
) -> u64 {
|
||||
if !is_training_mode() {
|
||||
return original!()(
|
||||
effect_module,
|
||||
eff_hash,
|
||||
joint_hash,
|
||||
pos,
|
||||
rot,
|
||||
size,
|
||||
pos2,
|
||||
rot2,
|
||||
arg5,
|
||||
arg6,
|
||||
arg7,
|
||||
arg9,
|
||||
);
|
||||
}
|
||||
size = ptrainer::handle_pokemon_effect(&mut *(*effect_module).owner, eff_hash, size);
|
||||
original!()(
|
||||
effect_module,
|
||||
eff_hash,
|
||||
joint_hash,
|
||||
pos,
|
||||
rot,
|
||||
size,
|
||||
pos2,
|
||||
rot2,
|
||||
arg5,
|
||||
arg6,
|
||||
arg7,
|
||||
arg9,
|
||||
)
|
||||
}
|
||||
|
||||
#[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,
|
||||
|
@ -773,15 +841,6 @@ unsafe fn handle_final_input_mapping(
|
|||
input_record::handle_final_input_mapping(player_idx, out);
|
||||
}
|
||||
|
||||
static BOMA_OFFSET: usize = 0x15cf1b0;
|
||||
|
||||
#[skyline::hook(offset = BOMA_OFFSET)]
|
||||
pub unsafe fn handle_get_module_accessor(
|
||||
battle_object_id: u32,
|
||||
) -> *mut app::BattleObjectModuleAccessor {
|
||||
original!()(battle_object_id)
|
||||
}
|
||||
|
||||
pub fn training_mods() {
|
||||
info!("Applying training mods.");
|
||||
|
||||
|
@ -863,11 +922,16 @@ pub fn training_mods() {
|
|||
handle_final_input_mapping,
|
||||
// Charge
|
||||
handle_article_get_int,
|
||||
handle_get_module_accessor,
|
||||
handle_fighter_effect,
|
||||
handle_fighter_joint_effect,
|
||||
);
|
||||
|
||||
items::init();
|
||||
input_record::init();
|
||||
ui::init();
|
||||
pikmin::init();
|
||||
ptrainer::init();
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
debug::init();
|
||||
}
|
||||
|
|
|
@ -3,11 +3,12 @@ use std::collections::HashMap;
|
|||
use log::info;
|
||||
use parking_lot::Mutex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smash::app::{self, lua_bind::*, Item};
|
||||
use smash::app::{self, lua_bind::*, ArticleOperationTarget, Item};
|
||||
use smash::cpp::l2c_value::LuaConst;
|
||||
use smash::hash40;
|
||||
use smash::lib::lua_const::*;
|
||||
use smash::phx::{Hash40, Vector3f};
|
||||
use std::ptr;
|
||||
use training_mod_consts::{CharacterItem, SaveDamage};
|
||||
|
||||
use SaveState::*;
|
||||
|
@ -22,11 +23,12 @@ use crate::common::consts::RecordTrigger;
|
|||
use crate::common::consts::SaveStateMirroring;
|
||||
//TODO: Cleanup above
|
||||
use crate::common::consts::SAVE_STATES_TOML_PATH;
|
||||
use crate::common::get_module_accessor;
|
||||
use crate::common::is_dead;
|
||||
use crate::common::MENU;
|
||||
use crate::is_operation_cpu;
|
||||
use crate::training::buff;
|
||||
use crate::training::character_specific::steve;
|
||||
use crate::training::character_specific::{ptrainer, steve};
|
||||
use crate::training::charge::{self, ChargeState};
|
||||
use crate::training::input_record;
|
||||
use crate::training::items::apply_item;
|
||||
|
@ -69,6 +71,7 @@ pub enum SaveState {
|
|||
PosMove,
|
||||
NanaPosMove,
|
||||
ApplyBuff,
|
||||
WaitForPokemonSwitch,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||
|
@ -174,6 +177,20 @@ unsafe fn save_state_cpu(slot: usize) -> &'static mut SavedState {
|
|||
&mut (*SAVE_STATE_SLOTS.data_ptr()).cpu[slot]
|
||||
}
|
||||
|
||||
pub unsafe fn get_state_pokemon(
|
||||
ptrainer_module_accessor: *mut app::BattleObjectModuleAccessor,
|
||||
) -> u32 {
|
||||
let selected_slot = get_slot();
|
||||
let pokemon_module_accessor = ptrainer::get_pokemon_module_accessor(ptrainer_module_accessor);
|
||||
let cpu_module_accessor = get_module_accessor(FighterId::CPU);
|
||||
let fighter_kind = if !ptr::eq(pokemon_module_accessor, cpu_module_accessor) {
|
||||
save_state_player(selected_slot).fighter_kind
|
||||
} else {
|
||||
save_state_cpu(selected_slot).fighter_kind
|
||||
};
|
||||
(fighter_kind - *FIGHTER_KIND_PZENIGAME) as u32
|
||||
}
|
||||
|
||||
// MIRROR_STATE == 1 -> Do not mirror
|
||||
// MIRROR_STATE == -1 -> Do Mirror
|
||||
static mut MIRROR_STATE: f32 = 1.0;
|
||||
|
@ -287,49 +304,31 @@ fn set_damage(module_accessor: &mut app::BattleObjectModuleAccessor, damage: f32
|
|||
}
|
||||
}
|
||||
|
||||
unsafe fn get_ptrainer_module_accessor(
|
||||
module_accessor: &mut app::BattleObjectModuleAccessor,
|
||||
) -> &mut app::BattleObjectModuleAccessor {
|
||||
let ptrainer_object_id =
|
||||
LinkModule::get_parent_object_id(module_accessor, *FIGHTER_POKEMON_LINK_NO_PTRAINER);
|
||||
&mut *app::sv_battle_object::module_accessor(ptrainer_object_id as u32)
|
||||
}
|
||||
|
||||
unsafe fn on_ptrainer_death(module_accessor: &mut app::BattleObjectModuleAccessor) {
|
||||
if !is_ptrainer(module_accessor) {
|
||||
return;
|
||||
}
|
||||
let ptrainer_module_accessor = ptrainer::get_ptrainer_module_accessor(module_accessor);
|
||||
WorkModule::off_flag(
|
||||
get_ptrainer_module_accessor(module_accessor),
|
||||
ptrainer_module_accessor,
|
||||
*WEAPON_PTRAINER_PTRAINER_INSTANCE_WORK_ID_FLAG_ENABLE_CHANGE_POKEMON,
|
||||
);
|
||||
let ptrainer_module_accessor = get_ptrainer_module_accessor(module_accessor);
|
||||
MotionModule::set_rate(ptrainer_module_accessor, 1000.0);
|
||||
if ArticleModule::is_exist(
|
||||
ptrainer_module_accessor,
|
||||
*WEAPON_PTRAINER_PTRAINER_GENERATE_ARTICLE_MBALL,
|
||||
) {
|
||||
let ptrainer_masterball: *mut app::Article = ArticleModule::get_article(
|
||||
ptrainer_module_accessor,
|
||||
*WEAPON_PTRAINER_PTRAINER_GENERATE_ARTICLE_MBALL,
|
||||
);
|
||||
let ptrainer_masterball_id = Article::get_battle_object_id(ptrainer_masterball);
|
||||
let ptrainer_masterball_module_accessor =
|
||||
&mut *app::sv_battle_object::module_accessor(ptrainer_masterball_id as u32);
|
||||
if let Some(ptrainer_masterball_module_accessor) =
|
||||
ptrainer::get_ptrainer_mball_module_accessor(ptrainer_module_accessor)
|
||||
{
|
||||
MotionModule::set_rate(ptrainer_masterball_module_accessor, 1000.0);
|
||||
ArticleModule::set_visibility_whole(
|
||||
ptrainer::get_ptrainer_module_accessor(module_accessor),
|
||||
*WEAPON_PTRAINER_PTRAINER_GENERATE_ARTICLE_MBALL,
|
||||
false,
|
||||
ArticleOperationTarget(*ARTICLE_OPE_TARGET_ALL),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn on_death(fighter_kind: i32, module_accessor: &mut app::BattleObjectModuleAccessor) {
|
||||
pub unsafe fn on_death(fighter_kind: i32, module_accessor: &mut app::BattleObjectModuleAccessor) {
|
||||
SoundModule::stop_all_sound(module_accessor);
|
||||
// Try moving off-screen so we don't see effects.
|
||||
let pos = Vector3f {
|
||||
x: -300.0,
|
||||
y: -100.0,
|
||||
z: 0.0,
|
||||
};
|
||||
PostureModule::set_pos(module_accessor, &pos);
|
||||
|
||||
// All articles have ID <= 0x25
|
||||
(0..=0x25)
|
||||
.filter(|article_idx| {
|
||||
|
@ -439,7 +438,6 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor)
|
|||
if save_state.state == KillPlayer && !fighter_is_nana {
|
||||
on_ptrainer_death(module_accessor);
|
||||
if !is_dead(module_accessor) {
|
||||
on_death(fighter_kind, module_accessor);
|
||||
StatusModule::change_status_force(module_accessor, *FIGHTER_STATUS_KIND_DEAD, true);
|
||||
}
|
||||
|
||||
|
@ -594,9 +592,10 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor)
|
|||
}
|
||||
if fighter_is_ptrainer {
|
||||
WorkModule::on_flag(
|
||||
get_ptrainer_module_accessor(module_accessor),
|
||||
ptrainer::get_ptrainer_module_accessor(module_accessor),
|
||||
*WEAPON_PTRAINER_PTRAINER_INSTANCE_WORK_ID_FLAG_ENABLE_CHANGE_POKEMON,
|
||||
);
|
||||
save_state.state = WaitForPokemonSwitch;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -636,6 +635,13 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor)
|
|||
}
|
||||
}
|
||||
|
||||
// If we switched last frame, artifacts and sound should be cleaned up, so transition into NoAction
|
||||
if save_state.state == WaitForPokemonSwitch
|
||||
&& ptrainer::is_switched(ptrainer::get_ptrainer_module_accessor(module_accessor))
|
||||
{
|
||||
save_state.state = NoAction;
|
||||
}
|
||||
|
||||
// Save state
|
||||
if button_config::combo_passes(button_config::ButtonCombo::SaveState) {
|
||||
// Don't begin saving state if Nana's delayed input is captured
|
||||
|
|
Loading…
Reference in a new issue