diff --git a/src/training/character_specific/kirby.rs b/src/training/character_specific/kirby.rs new file mode 100644 index 0000000..f7f1c4c --- /dev/null +++ b/src/training/character_specific/kirby.rs @@ -0,0 +1,579 @@ +use crate::training::charge::ChargeState; +use crate::training::save_states; +use smash::app::{self, lua_bind::*, smashball::is_training_mode}; +use smash::lib::lua_const::*; +use smash::phx::{Hash40, Vector3f}; + +#[repr(C)] +pub struct CopyModule { + _vtable: u64, // maybe, no clue + padding: [u8; 0x17390], + copied_fighter_kind: i32, +} + +// Wait to set up copy ability variables until after CopyStart runs; +static KIRBY_OPFF_OFFSET: usize = 0xb971b0; +#[skyline::hook(offset = KIRBY_OPFF_OFFSET)] +pub unsafe fn handle_copy_start(param1: u64, kirby_fighter: *mut app::Fighter) -> u64 { + if !is_training_mode() || !save_states::is_loading() { + return original!()(param1, kirby_fighter); + } + // Need to check copy start before the function runs, since it will turn it off if it was on + let module_accessor = (*kirby_fighter).battle_object.module_accessor; + let on_copy_start = WorkModule::is_flag(module_accessor, 0x20000104); // *FIGHTER_KIRBY_INSTANCE_WORK_ID_FLAG_COPY_ON_START + // Run optional initial copy setup (it depends on fighter kind) + let ori = original!()(param1, kirby_fighter); + // Now try to set save state variables + if on_copy_start { + let copy_module = + WorkModule::get_int64(module_accessor, 0x10000106) as *const i64 as *const CopyModule; //*FIGHTER_KIRBY_INSTANCE_WORK_ID_INT_COPY_MODULE_ADDRESS + let opponent_fighter_kind = (*copy_module).copied_fighter_kind; + handle_kirby_hat_charge( + &mut *module_accessor, + opponent_fighter_kind, + save_states::get_charge_state(module_accessor), + ); + save_states::end_copy_ability(module_accessor); + } + ori +} + +pub 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, + charge_state: ChargeState, +) -> ChargeState { + if opponent_fighter_kind == FIGHTER_KIND_SAMUS || opponent_fighter_kind == FIGHTER_KIND_SAMUSD { + let shot_charge = WorkModule::get_int( + module_accessor, + *FIGHTER_SAMUS_INSTANCE_WORK_ID_INT_SPECIAL_N_COUNT, + ); + charge_state.int_x(shot_charge) + } + // Sheik Needles + else if opponent_fighter_kind == FIGHTER_KIND_SHEIK { + let my_charge = WorkModule::get_int( + module_accessor, + *FIGHTER_SHEIK_INSTANCE_WORK_ID_INT_NEEDLE_COUNT, + ); + charge_state.int_x(my_charge) + } + // Mewtwo Shadowball + else if opponent_fighter_kind == FIGHTER_KIND_MEWTWO { + let my_charge = WorkModule::get_int( + module_accessor, + *FIGHTER_MEWTWO_INSTANCE_WORK_ID_INT_SHADOWBALL_CHARGE_FRAME, + ); + let prev_frame = WorkModule::get_int( + module_accessor, + *FIGHTER_MEWTWO_INSTANCE_WORK_ID_INT_PREV_SHADOWBALL_CHARGE_FRAME, + ); + let ball_had = WorkModule::is_flag( + module_accessor, + *FIGHTER_MEWTWO_INSTANCE_WORK_ID_FLAG_SHADOWBALL_HAD, + ); + charge_state + .int_x(my_charge) + .int_y(prev_frame) + .has_charge(ball_had) + } + // Squirtle Water Gun + else if opponent_fighter_kind == FIGHTER_KIND_PZENIGAME { + let my_charge = WorkModule::get_int( + module_accessor, + *FIGHTER_PZENIGAME_INSTANCE_WORK_ID_INT_SPECIAL_N_CHARGE, + ); + charge_state.int_x(my_charge) + } + // Olimar Pikmin + else if opponent_fighter_kind == FIGHTER_KIND_PIKMIN { + let pre_pikmin_variation = WorkModule::get_int( + module_accessor, + *FIGHTER_PIKMIN_INSTANCE_WORK_INT_PRE_PIKMIN_VARIATION, + ); + let before_pre_pikmin_variation = WorkModule::get_int( + module_accessor, + *FIGHTER_PIKMIN_INSTANCE_WORK_INT_BEFORE_PRE_PIKMIN_VARIATION, + ); + charge_state + .int_x(pre_pikmin_variation) + .int_y(before_pre_pikmin_variation) + } + // Lucario Aura Sphere + else if opponent_fighter_kind == FIGHTER_KIND_LUCARIO { + let my_charge = WorkModule::get_int( + module_accessor, + *FIGHTER_LUCARIO_INSTANCE_WORK_ID_INT_AURABALL_CHARGE_FRAME, + ); + let prev_frame = WorkModule::get_int( + module_accessor, + *FIGHTER_LUCARIO_INSTANCE_WORK_ID_INT_PREV_AURABALL_CHARGE_FRAME, + ); + let ball_had = WorkModule::is_flag( + module_accessor, + *FIGHTER_LUCARIO_INSTANCE_WORK_ID_FLAG_AURABALL_HAD, + ); + charge_state + .int_x(my_charge) + .int_y(prev_frame) + .has_charge(ball_had) + } + // ROB Gyro/Laser/Fuel + else if opponent_fighter_kind == FIGHTER_KIND_ROBOT { + let laser_charge = WorkModule::get_float( + module_accessor, + *FIGHTER_ROBOT_INSTANCE_WORK_ID_FLOAT_BEAM_ENERGY_VALUE, + ); + charge_state.float_x(laser_charge) + } + // Wii Fit Sun Salutation + else if opponent_fighter_kind == FIGHTER_KIND_WIIFIT { + let my_charge = WorkModule::get_float( + module_accessor, + *FIGHTER_WIIFIT_INSTANCE_WORK_ID_FLOAT_SPECIAL_N_CHARGE_LEVEL_RATIO, + ); + charge_state.float_x(my_charge) + } + // Pac-Man Bonus Fruit + else if opponent_fighter_kind == FIGHTER_KIND_PACMAN { + let my_charge = WorkModule::get_int( + module_accessor, + *FIGHTER_PACMAN_INSTANCE_WORK_ID_INT_SPECIAL_N_CHARGE_RANK, + ); + let fruit_have = WorkModule::is_flag( + module_accessor, + *FIGHTER_PACMAN_INSTANCE_WORK_ID_FLAG_SPECIAL_N_PULL_THROW, + ); + charge_state.int_x(my_charge).has_charge(fruit_have) + } + // Robin Thunder Tome Spells + else if opponent_fighter_kind == FIGHTER_KIND_REFLET { + let my_charge = WorkModule::get_int( + module_accessor, + *FIGHTER_REFLET_INSTANCE_WORK_ID_INT_SPECIAL_N_THUNDER_KIND, + ); + charge_state.int_x(my_charge) + } + // Hero (Ka)frizz(le) + else if opponent_fighter_kind == FIGHTER_KIND_BRAVE { + let my_charge = WorkModule::get_int( + module_accessor, + *FIGHTER_BRAVE_INSTANCE_WORK_ID_INT_SPECIAL_N_HOLD_FRAME, + ); + charge_state.int_x(my_charge) + } + // No charge for this character's copy ability + else { + charge_state + } +} + +pub unsafe fn handle_kirby_hat_charge( + module_accessor: &mut app::BattleObjectModuleAccessor, + opponent_fighter_kind: i32, + charge: ChargeState, +) { + // Samus/Dark Samus Charge Shot - 0 to 112 + if opponent_fighter_kind == FIGHTER_KIND_SAMUS || opponent_fighter_kind == FIGHTER_KIND_SAMUSD { + charge.int_x.map(|shot_charge| { + WorkModule::set_int( + module_accessor, + shot_charge, + *FIGHTER_SAMUS_INSTANCE_WORK_ID_INT_SPECIAL_N_COUNT, + ); + if shot_charge == 112 { + EffectModule::req_common(module_accessor, Hash40::new("charge_max"), 0.0); + let samus_cshot_hash = if opponent_fighter_kind == FIGHTER_KIND_SAMUS { + Hash40::new("samus_cshot_max") + } else { + Hash40::new("samusd_cshot_max") + }; + let joint_hash = Hash40::new("handr"); + let pos = Vector3f { + x: 0.0, + y: 0.0, + z: 0.0, + }; + let rot = Vector3f { + x: 0.0, + y: 0.0, + z: 0.0, + }; + let efh = EffectModule::req_follow( + module_accessor, + samus_cshot_hash, + joint_hash, + &pos, + &rot, + 1.0, + false, + 0, + 0, + 0, + 0, + 0, + false, + false, + ); + WorkModule::set_int( + module_accessor, + efh as i32, + *FIGHTER_SAMUS_INSTANCE_WORK_ID_INT_EFH_CHARGE_MAX, + ); + } + }); + } + // Sheik Needles - 0 to 6 + else if opponent_fighter_kind == FIGHTER_KIND_SHEIK { + charge.int_x.map(|needle_charge| { + WorkModule::set_int( + module_accessor, + needle_charge, + *FIGHTER_SHEIK_INSTANCE_WORK_ID_INT_NEEDLE_COUNT, + ); + if needle_charge == 6 { + EffectModule::req_common(module_accessor, Hash40::new("charge_max"), 0.0); + } + }); + } + // Mewtwo Shadowball - 0 to 120, Boolean + else if opponent_fighter_kind == FIGHTER_KIND_MEWTWO { + charge.int_x.map(|charge_frame| { + WorkModule::set_int( + module_accessor, + charge_frame, + *FIGHTER_MEWTWO_INSTANCE_WORK_ID_INT_SHADOWBALL_CHARGE_FRAME, + ); + }); + charge.int_y.map(|prev_frame| { + WorkModule::set_int( + module_accessor, + prev_frame, + *FIGHTER_MEWTWO_INSTANCE_WORK_ID_INT_PREV_SHADOWBALL_CHARGE_FRAME, + ); + if prev_frame == 120 { + EffectModule::req_common(module_accessor, Hash40::new("charge_max"), 0.0); + let pos = Vector3f { + x: 0.0, + y: 0.0, + z: 0.0, + }; + let rot = Vector3f { + x: 0.0, + y: 0.0, + z: 0.0, + }; + let eff_hash = Hash40 { hash: 0x1ac6d446d8 }; + let joint_hash_l = Hash40 { hash: 0x5e008fd84 }; + let efh_l = EffectModule::req_follow( + module_accessor, + eff_hash, + joint_hash_l, + &pos, + &rot, + 1.0, + false, + 0, + 0, + -1, + 0, + 0, + false, + false, + ); + let joint_hash_r = Hash40 { hash: 0x51a07c0e7 }; + let efh_r = EffectModule::req_follow( + module_accessor, + eff_hash, + joint_hash_r, + &pos, + &rot, + 1.0, + false, + 0, + 0, + -1, + 0, + 0, + false, + false, + ); + WorkModule::set_int( + module_accessor, + efh_l as i32, + *FIGHTER_MEWTWO_INSTANCE_WORK_ID_INT_EF_ID_SHADOWBALL_MAX_L, + ); + WorkModule::set_int( + module_accessor, + efh_r as i32, + *FIGHTER_MEWTWO_INSTANCE_WORK_ID_INT_EF_ID_SHADOWBALL_MAX_R, + ); + } + }); + charge.has_charge.map(|has_shadowball| { + WorkModule::set_flag( + module_accessor, + has_shadowball, + *FIGHTER_MEWTWO_INSTANCE_WORK_ID_FLAG_SHADOWBALL_HAD, + ); + }); + } + // Squirtle Water Gun - 0 to 45 + else if opponent_fighter_kind == FIGHTER_KIND_PZENIGAME { + charge.int_x.map(|water_charge| { + WorkModule::set_int( + module_accessor, + water_charge, + *FIGHTER_PZENIGAME_INSTANCE_WORK_ID_INT_SPECIAL_N_CHARGE, + ); + if water_charge == 45 { + EffectModule::req_common(module_accessor, Hash40::new("charge_max"), 0.0); + } + }); + } + // Olimar Pikmin - 0 to 4 + else if opponent_fighter_kind == FIGHTER_KIND_PIKMIN { + charge.int_x.map(|pre| { + WorkModule::set_int( + module_accessor, + pre, + *FIGHTER_PIKMIN_INSTANCE_WORK_INT_PRE_PIKMIN_VARIATION, + ); + }); + charge.int_y.map(|before_pre| { + WorkModule::set_int( + module_accessor, + before_pre, + *FIGHTER_PIKMIN_INSTANCE_WORK_INT_BEFORE_PRE_PIKMIN_VARIATION, + ); + }); + } + // Lucario Aura Sphere - 0 to 90 + else if opponent_fighter_kind == FIGHTER_KIND_LUCARIO { + charge.int_x.map(|charge_frame| { + WorkModule::set_int( + module_accessor, + charge_frame, + *FIGHTER_LUCARIO_INSTANCE_WORK_ID_INT_AURABALL_CHARGE_FRAME, + ); + }); + charge.int_y.map(|prev_frame| { + WorkModule::set_int( + module_accessor, + prev_frame, + *FIGHTER_LUCARIO_INSTANCE_WORK_ID_INT_PREV_AURABALL_CHARGE_FRAME, + ); + if prev_frame == 90 { + EffectModule::req_common(module_accessor, Hash40::new("charge_max"), 0.0); + let pos = Vector3f { + x: 0.0, + y: 0.0, + z: 0.0, + }; + let rot = Vector3f { + x: 0.0, + y: 0.0, + z: 0.0, + }; + let eff_hash_l = Hash40 { hash: 0x164bf96ca1 }; + let joint_hash_l = Hash40 { hash: 0x5e008fd84 }; + let efh_l = EffectModule::req_follow( + module_accessor, + eff_hash_l, + joint_hash_l, + &pos, + &rot, + 1.0, + false, + 0, + 0, + -1, + 0, + 0, + false, + false, + ); + let eff_hash_r = Hash40 { hash: 0x16b1f651c2 }; + let joint_hash_r = Hash40 { hash: 0x51a07c0e7 }; + let efh_r = EffectModule::req_follow( + module_accessor, + eff_hash_r, + joint_hash_r, + &pos, + &rot, + 1.0, + false, + 0, + 0, + -1, + 0, + 0, + false, + false, + ); + WorkModule::set_int( + module_accessor, + efh_l as i32, + *FIGHTER_LUCARIO_INSTANCE_WORK_ID_INT_EF_ID_AURABALL_MAX_L, + ); + WorkModule::set_int( + module_accessor, + efh_r as i32, + *FIGHTER_LUCARIO_INSTANCE_WORK_ID_INT_EF_ID_AURABALL_MAX_R, + ); + } + }); + charge.has_charge.map(|has_shadowball| { + WorkModule::set_flag( + module_accessor, + has_shadowball, + *FIGHTER_LUCARIO_INSTANCE_WORK_ID_FLAG_AURABALL_HAD, + ); + }); + } + // ROB Laser + else if opponent_fighter_kind == FIGHTER_KIND_ROBOT { + charge.float_x.map(|beam_energy| { + WorkModule::set_float( + module_accessor, + beam_energy, + *FIGHTER_ROBOT_INSTANCE_WORK_ID_FLOAT_BEAM_ENERGY_VALUE, + ); + }); + } + // Wii Fit Sun Salutation - 0 to 1 + else if opponent_fighter_kind == FIGHTER_KIND_WIIFIT { + charge.float_x.map(|sun_ratio| { + WorkModule::set_float( + module_accessor, + sun_ratio, + *FIGHTER_WIIFIT_INSTANCE_WORK_ID_FLOAT_SPECIAL_N_CHARGE_LEVEL_RATIO, + ) + }); + } + // Pac-Man Bonus Fruit - 0 to 12 + else if opponent_fighter_kind == FIGHTER_KIND_PACMAN { + let mut has_key = false; + charge.int_x.map(|charge_rank| { + WorkModule::set_int( + module_accessor, + charge_rank, + *FIGHTER_PACMAN_INSTANCE_WORK_ID_INT_SPECIAL_N_CHARGE_RANK, + ); + + if charge_rank == 12 { + EffectModule::req_common(module_accessor, Hash40::new("charge_max"), 0.0); + has_key = true; + } + }); + charge.has_charge.map(|has_fruit| { + WorkModule::set_flag( + module_accessor, + has_fruit, + *FIGHTER_PACMAN_INSTANCE_WORK_ID_FLAG_SPECIAL_N_PULL_THROW, + ); + if has_key { + WorkModule::set_flag( + module_accessor, + has_key, + *FIGHTER_PACMAN_INSTANCE_WORK_ID_FLAG_SPECIAL_N_MAX_HAVE_ITEM, + ); + } + }); + } + // Robin Thunder Tome Spells - 0 to 3 + else if opponent_fighter_kind == FIGHTER_KIND_REFLET { + charge.int_x.map(|thunder_kind| { + WorkModule::set_int( + module_accessor, + thunder_kind, + *FIGHTER_REFLET_INSTANCE_WORK_ID_INT_SPECIAL_N_THUNDER_KIND, + ); + if thunder_kind == 3 { + EffectModule::req_common(module_accessor, Hash40::new("charge_max"), 0.0); + let eff_hash = Hash40 { hash: 0x12db3e4172 }; + let joint_hash = Hash40 { hash: 0x5eb263e0d }; + let pos = Vector3f { + x: 0.0, + y: 0.0, + z: 0.0, + }; + let rot = Vector3f { + x: 0.0, + y: 0.0, + z: 0.0, + }; + EffectModule::req_follow( + module_accessor, + eff_hash, + joint_hash, + &pos, + &rot, + 1.0, + false, + 0, + 0, + -1, + 0, + 0, + false, + false, + ); + } + }); + } + // Hero (Ka)frizz(le) - 0 to 81 + else if opponent_fighter_kind == FIGHTER_KIND_BRAVE { + EffectModule::remove_common(module_accessor, Hash40::new("charge_max")); + WorkModule::off_flag( + module_accessor, + *FIGHTER_BRAVE_INSTANCE_WORK_ID_FLAG_SPECIAL_N_MAX_EFFECT, + ); + charge.int_x.map(|frizz_charge| { + WorkModule::set_int( + module_accessor, + frizz_charge, + *FIGHTER_BRAVE_INSTANCE_WORK_ID_INT_SPECIAL_N_HOLD_FRAME, + ); + }); + } +} + +pub fn init() { + skyline::install_hooks!(handle_copy_start,); +} diff --git a/src/training/character_specific/mod.rs b/src/training/character_specific/mod.rs index 3a7859d..448df88 100644 --- a/src/training/character_specific/mod.rs +++ b/src/training/character_specific/mod.rs @@ -2,6 +2,7 @@ use smash::app::{self}; mod bowser; pub mod items; +pub mod kirby; pub mod pikmin; pub mod ptrainer; pub mod steve; diff --git a/src/training/charge.rs b/src/training/charge.rs index 90f77b5..239475f 100644 --- a/src/training/charge.rs +++ b/src/training/charge.rs @@ -1,6 +1,6 @@ use crate::common::consts::FighterId; use crate::common::get_module_accessor; -use crate::training::character_specific::pikmin; +use crate::training::character_specific::{kirby, pikmin}; use serde::{Deserialize, Serialize}; use smash::app::{self, lua_bind::*, ArticleOperationTarget, FighterFacial, FighterUtil}; use smash::lib::lua_const::*; @@ -28,27 +28,32 @@ pub struct ChargeState { } impl ChargeState { - fn int_x(mut self, int_x: i32) -> Self { + pub fn int_x(mut self, int_x: i32) -> Self { self.int_x = Some(int_x); self } - fn int_y(mut self, int_y: i32) -> Self { + pub fn int_y(mut self, int_y: i32) -> Self { self.int_y = Some(int_y); self } - fn float_x(mut self, float_x: f32) -> Self { + pub fn int_z(mut self, int_z: i32) -> Self { + self.int_z = Some(int_z); + self + } + + pub fn float_x(mut self, float_x: f32) -> Self { self.float_x = Some(float_x); self } - fn float_y(mut self, float_y: f32) -> Self { + pub fn float_y(mut self, float_y: f32) -> Self { self.float_y = Some(float_y); self } - fn float_z(mut self, float_z: f32) -> Self { + pub fn float_z(mut self, float_z: f32) -> Self { self.float_z = Some(float_z); self } @@ -65,7 +70,7 @@ impl ChargeState { self } - fn has_charge(mut self, has_charge: bool) -> Self { + pub fn has_charge(mut self, has_charge: bool) -> Self { self.has_charge = Some(has_charge); self } @@ -108,7 +113,9 @@ pub unsafe fn get_charge( module_accessor, *FIGHTER_KIRBY_INSTANCE_WORK_ID_INT_COPY_CHARA, ); - charge_state.has_charge(hat_have).int_x(chara_kind) + let kirby_charge = charge_state.has_charge(hat_have).int_z(chara_kind); + // Get the charge of necessary abilities for kirby + kirby::get_kirby_hat_charge(module_accessor, chara_kind, kirby_charge) } // Sheik Needles else if fighter_kind == FIGHTER_KIND_SHEIK { @@ -370,9 +377,10 @@ pub unsafe fn handle_charge( }; // 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); + kirby::is_kirby_hat_okay(opponent_module_accessor, charge.int_z); if opponent_matches_fighter == Some(true) { - copy_setup(module_accessor, 1, charge.int_x.unwrap(), true, false); + copy_setup(module_accessor, 1, charge.int_z.unwrap(), true, false); + //kirby::handle_kirby_hat_charge(module_accessor, charge.int_z.unwrap(), charge); } }); } @@ -926,50 +934,3 @@ 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/debug.rs b/src/training/debug.rs index baaaeef..8178e03 100644 --- a/src/training/debug.rs +++ b/src/training/debug.rs @@ -54,6 +54,11 @@ pub unsafe fn handle_set_float(work_module: &mut WorkModule2, value: f32, addres if !is_training_mode() { original!()(work_module, value, address); } + if address == *FIGHTER_WIIFIT_INSTANCE_WORK_ID_FLOAT_SPECIAL_N_CHARGE_LEVEL_RATIO //*FIGHTER_KIRBY_INSTANCE_WORK_ID_FLAG_COPY_ON_START + && app::utility::get_kind(work_module.owner) == FIGHTER_KIND_KIRBY + { + is_visible_backshield(work_module.owner); + } original!()(work_module, value, address); } @@ -86,7 +91,7 @@ pub fn init() { //handle_on_flag, //handle_set_int, // handle_set_int_64, - // handle_set_float, + handle_set_float, // handle_get_int, //handle_is_flag, ); diff --git a/src/training/mod.rs b/src/training/mod.rs index 39d0bc8..0ccd6f1 100644 --- a/src/training/mod.rs +++ b/src/training/mod.rs @@ -7,7 +7,7 @@ use crate::common::{ use crate::hitbox_visualizer; use crate::input::*; use crate::logging::*; -use crate::training::character_specific::{items, pikmin, ptrainer}; +use crate::training::character_specific::{items, kirby, pikmin, ptrainer}; use skyline::hooks::{getRegionAddress, InlineCtx, Region}; use skyline::nn::ro::LookupSymbol; use smash::app::{self, enSEType, lua_bind::*, utility}; @@ -527,12 +527,17 @@ pub unsafe fn handle_fighter_play_se( original!()(sound_module, my_hash, bool1, bool2, bool3, bool4, se_type) } +pub struct FighterEffectModule { + _vtable: u64, + owner: *mut app::BattleObjectModuleAccessor, +} + 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( - effect_module: *mut FighterEffectModule, + effect_module: &mut FighterEffectModule, eff_hash: Hash40, - eff_hash2: Hash40, + joint_hash: Hash40, pos: *const Vector3f, rot: *const Vector3f, mut size: f32, @@ -549,7 +554,7 @@ pub unsafe fn handle_effect_follow( return original!()( effect_module, eff_hash, - eff_hash2, + joint_hash, pos, rot, size, @@ -570,7 +575,7 @@ pub unsafe fn handle_effect_follow( original!()( effect_module, eff_hash, - eff_hash2, + joint_hash, pos, rot, size, @@ -585,11 +590,6 @@ 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( @@ -931,6 +931,7 @@ pub fn training_mods() { ui::init(); pikmin::init(); ptrainer::init(); + kirby::init(); #[cfg(debug_assertions)] debug::init(); diff --git a/src/training/save_states.rs b/src/training/save_states.rs index 2207fb5..4c28afa 100644 --- a/src/training/save_states.rs +++ b/src/training/save_states.rs @@ -69,9 +69,10 @@ pub enum SaveState { KillPlayer, WaitForAlive, PosMove, - NanaPosMove, ApplyBuff, + NanaPosMove, WaitForPokemonSwitch, + WaitForCopyAbility, } #[derive(Serialize, Deserialize, Copy, Clone, Debug)] @@ -191,6 +192,31 @@ pub unsafe fn get_state_pokemon( (fighter_kind - *FIGHTER_KIND_PZENIGAME) as u32 } +pub unsafe fn get_charge_state( + module_accessor: *mut app::BattleObjectModuleAccessor, +) -> ChargeState { + let selected_slot = get_slot(); + let cpu_module_accessor = get_module_accessor(FighterId::CPU); + if !ptr::eq(module_accessor, cpu_module_accessor) { + save_state_player(selected_slot).charge + } else { + save_state_cpu(selected_slot).charge + } +} + +pub unsafe fn end_copy_ability(module_accessor: *mut app::BattleObjectModuleAccessor) { + let selected_slot = get_slot(); + let cpu_module_accessor = get_module_accessor(FighterId::CPU); + let save_state = if !ptr::eq(module_accessor, cpu_module_accessor) { + save_state_player(selected_slot) + } else { + save_state_cpu(selected_slot) + }; + if save_state.state == WaitForCopyAbility { + save_state.state = NoAction; + } +} + // MIRROR_STATE == 1 -> Do not mirror // MIRROR_STATE == -1 -> Do Mirror static mut MIRROR_STATE: f32 = 1.0; @@ -571,6 +597,10 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor) // Set the charge of special moves if the fighter matches the kind in the save state if save_state.fighter_kind == fighter_kind { charge::handle_charge(module_accessor, fighter_kind, save_state.charge); + // If we ever want to buff kirby, we have to move this to after apply buff + if fighter_kind == FIGHTER_KIND_KIRBY { + save_state.state = WaitForCopyAbility; + } } // Buff the fighter if they're one of the fighters who can be buffed if fighter_is_buffable { @@ -642,6 +672,14 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor) save_state.state = NoAction; } + // Wait for Copy Ability to be applied if needed, exit otherwise + if save_state.state == WaitForCopyAbility { + let is_copy_start = WorkModule::is_flag(module_accessor, 0x20000104); // *FIGHTER_KIRBY_INSTANCE_WORK_ID_FLAG_COPY_ON_START + if !is_copy_start { + 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 diff --git a/training_mod_consts/src/lib.rs b/training_mod_consts/src/lib.rs index 0c49b22..8a0b5ef 100644 --- a/training_mod_consts/src/lib.rs +++ b/training_mod_consts/src/lib.rs @@ -142,7 +142,7 @@ pub static DEFAULTS_MENU: TrainingModpackMenu = TrainingModpackMenu { follow_up: Action::empty(), frame_advantage: OnOff::Off, full_hop: BoolFlag::TRUE, - hitbox_vis: OnOff::On, + hitbox_vis: OnOff::Off, input_display: InputDisplay::Smash, input_display_status: OnOff::Off, hud: OnOff::On,