From ebe639314352d7a0b974ed35dd4f8853de268b81 Mon Sep 17 00:00:00 2001 From: GradualSyrup <68757075+GradualSyrup@users.noreply.github.com> Date: Fri, 1 Sep 2023 13:10:03 -0500 Subject: [PATCH] Add Kirby Hats to Save States (#615) * 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 * fmt fmt fmt --- src/training/charge.rs | 88 ++++++++++++++++++++++++++++++++++++++++++ src/training/mod.rs | 25 +++++++++++- 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/src/training/charge.rs b/src/training/charge.rs index abef0f3..90f77b5 100644 --- a/src/training/charge.rs +++ b/src/training/charge.rs @@ -1,8 +1,20 @@ +use crate::common::consts::FighterId; +use crate::common::get_module_accessor; use crate::training::character_specific::pikmin; use serde::{Deserialize, Serialize}; use smash::app::{self, lua_bind::*, ArticleOperationTarget, FighterFacial, FighterUtil}; use smash::lib::lua_const::*; use smash::phx::{Hash40, Vector3f}; +use std::ptr; + +#[skyline::from_offset(0xba0e60)] +fn copy_setup( + module_accessor: *mut app::BattleObjectModuleAccessor, + int: i32, + fighter_kind: i32, + bool_1: bool, + bool_2: bool, +); #[derive(Serialize, Deserialize, Default, Copy, Clone, Debug)] pub struct ChargeState { @@ -88,6 +100,16 @@ pub unsafe fn get_charge( ); charge_state.int_x(my_charge) } + // Kirby Copy Abilities + else if fighter_kind == FIGHTER_KIND_KIRBY { + let hat_have = + WorkModule::is_flag(module_accessor, *FIGHTER_KIRBY_INSTANCE_WORK_ID_FLAG_COPY); + let chara_kind = WorkModule::get_int( + module_accessor, + *FIGHTER_KIRBY_INSTANCE_WORK_ID_INT_COPY_CHARA, + ); + charge_state.has_charge(hat_have).int_x(chara_kind) + } // Sheik Needles else if fighter_kind == FIGHTER_KIND_SHEIK { let my_charge = WorkModule::get_int( @@ -335,6 +357,25 @@ pub unsafe fn handle_charge( } }); } + // Kirby Copy Abilities + else if fighter_kind == FIGHTER_KIND_KIRBY { + charge.has_charge.map(|_has_copy_ability| { + let cpu_module_accessor = &mut *get_module_accessor(FighterId::CPU); + let player_module_accessor = &mut *get_module_accessor(FighterId::Player); + let opponent_module_accessor: &mut app::BattleObjectModuleAccessor = + if ptr::eq(module_accessor, player_module_accessor) { + cpu_module_accessor + } else { + player_module_accessor + }; + // Only try to set up Copy Ability when the current opponent matches the type of fighter from the save state + let opponent_matches_fighter = + is_kirby_hat_okay(opponent_module_accessor, charge.int_x); + if opponent_matches_fighter == Some(true) { + copy_setup(module_accessor, 1, charge.int_x.unwrap(), true, false); + } + }); + } // Sheik Needles - 0 to 6 else if fighter_kind == FIGHTER_KIND_SHEIK { charge.int_x.map(|needle_charge| { @@ -885,3 +926,50 @@ pub unsafe fn handle_charge( }); } } + +unsafe fn is_kirby_hat_okay( + opponent_module_accessor: &mut app::BattleObjectModuleAccessor, + save_state_fighter_option: Option, +) -> Option { + let mut opponent_fighter_kind = app::utility::get_kind(opponent_module_accessor); + let save_state_fighter_kind = save_state_fighter_option?; + if opponent_fighter_kind == save_state_fighter_kind { + return Some(true); + } + // We have a fighter but they don't match - see if it's an accepted transformation + let trainer_kinds = [ + *FIGHTER_KIND_PZENIGAME, + *FIGHTER_KIND_PFUSHIGISOU, + *FIGHTER_KIND_PLIZARDON, + -1, // Fighter Kind while switching pokemon + ]; + let element_kinds = [*FIGHTER_KIND_EFLAME, *FIGHTER_KIND_ELIGHT]; + if opponent_fighter_kind == -1 { + let trainer_boid = LinkModule::get_parent_object_id( + opponent_module_accessor, + *FIGHTER_POKEMON_LINK_NO_PTRAINER, + ) as u32; + if trainer_boid != *BATTLE_OBJECT_ID_INVALID as u32 + && app::sv_battle_object::is_active(trainer_boid) + { + opponent_fighter_kind = *FIGHTER_KIND_PZENIGAME; // ptrainer is in the match, so assume we have a ptrainer fighter + } + } + let both_trainer = trainer_kinds.contains(&opponent_fighter_kind) + && trainer_kinds.contains(&save_state_fighter_kind); + let both_element = element_kinds.contains(&opponent_fighter_kind) + && element_kinds.contains(&save_state_fighter_kind); + Some(both_trainer || both_element) +} + +pub unsafe fn _get_kirby_hat_charge( + _module_accessor: &mut app::BattleObjectModuleAccessor, + _opponent_fighter_kind: i32, +) { +} + +pub unsafe fn _handle_kirby_hat_charge( + _module_accessor: &mut app::BattleObjectModuleAccessor, + _opponent_fighter_kind: i32, +) { +} diff --git a/src/training/mod.rs b/src/training/mod.rs index 7ffb43b..3dfaa93 100644 --- a/src/training/mod.rs +++ b/src/training/mod.rs @@ -482,11 +482,17 @@ pub unsafe fn handle_se( ) } +#[repr(C)] +pub struct FighterSoundModule { + vtable: u64, + owner: *mut app::BattleObjectModuleAccessor, +} + 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( - sound_module: u64, // pointer to fighter's SoundModule + sound_module: *mut FighterSoundModule, // pointer to fighter's SoundModule my_hash: Hash40, bool1: bool, bool2: bool, @@ -511,6 +517,23 @@ pub unsafe fn handle_fighter_play_se( se_type, ); } + + // 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, + ); + } + } original!()(sound_module, my_hash, bool1, bool2, bool3, bool4, se_type) }