From 9ffe3c7ceb1ab0f376df8100e25a04b33eef8ca4 Mon Sep 17 00:00:00 2001 From: GradualSyrup <68757075+GradualSyrup@users.noreply.github.com> Date: Mon, 4 Sep 2023 23:24:37 -0500 Subject: [PATCH] Save Sora Spell (#623) * Initial implementation, getting the wrong pointer somewhere * Working build w/ Kirby * Sound Fix * appease clippy --- src/common/mod.rs | 8 +++++++ src/training/character_specific/kirby.rs | 29 +++++++++++++++++++++++ src/training/charge.rs | 30 +++++++++++++++++++++++- src/training/mod.rs | 8 +++++++ 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/common/mod.rs b/src/common/mod.rs index e39960c..8827bfd 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -34,6 +34,9 @@ pub fn is_training_mode() -> bool { true } +#[skyline::from_offset(0x3ac540)] +pub fn get_battle_object_from_id(battle_object_id: u32) -> *mut app::BattleObject; + pub fn get_category(module_accessor: &app::BattleObjectModuleAccessor) -> i32 { (module_accessor.battle_object_id >> 28) as u8 as i32 } @@ -42,6 +45,11 @@ pub fn is_emulator() -> bool { unsafe { skyline::hooks::getRegionAddress(skyline::hooks::Region::Text) as u64 == 0x8004000 } } +pub unsafe fn try_get_battle_object(battle_object_id: u32) -> Option<&'static app::BattleObject> { + let battle_object_ptr = get_battle_object_from_id(battle_object_id); + battle_object_ptr.as_ref() +} + pub fn get_module_accessor(fighter_id: FighterId) -> *mut app::BattleObjectModuleAccessor { try_get_module_accessor(fighter_id).unwrap() } diff --git a/src/training/character_specific/kirby.rs b/src/training/character_specific/kirby.rs index f7f1c4c..7cc59c8 100644 --- a/src/training/character_specific/kirby.rs +++ b/src/training/character_specific/kirby.rs @@ -1,3 +1,4 @@ +use crate::common::try_get_battle_object; use crate::training::charge::ChargeState; use crate::training::save_states; use smash::app::{self, lua_bind::*, smashball::is_training_mode}; @@ -197,6 +198,14 @@ pub unsafe fn get_kirby_hat_charge( ); charge_state.int_x(my_charge) } + // Sora Spell + else if opponent_fighter_kind == FIGHTER_KIND_TRAIL { + let my_charge = WorkModule::get_int( + module_accessor, + *FIGHTER_TRAIL_INSTANCE_WORK_ID_INT_SPECIAL_N_MAGIC_KIND, + ); + charge_state.int_x(my_charge) + } // No charge for this character's copy ability else { charge_state @@ -572,6 +581,26 @@ pub unsafe fn handle_kirby_hat_charge( ); }); } + // Sora Spell - 0 to 2 + else if opponent_fighter_kind == FIGHTER_KIND_TRAIL { + charge.int_x.map(|spell_kind| { + let prev_spell_kind = (spell_kind + 1) % 3; + WorkModule::set_int( + module_accessor, + prev_spell_kind, + *FIGHTER_TRAIL_INSTANCE_WORK_ID_INT_SPECIAL_N_MAGIC_KIND, + ); + // app::FighterSpecializer_Trail::change_magic() doesn't actually run if the below flag isn't set + WorkModule::on_flag( + module_accessor, + *FIGHTER_TRAIL_STATUS_SPECIAL_N1_FLAG_CHANGE_MAGIC, + ); + if let Some(battle_object) = try_get_battle_object(module_accessor.battle_object_id) { + let fighter = battle_object as *const app::BattleObject as *mut app::Fighter; + app::FighterSpecializer_Trail::change_magic(fighter); + } + }); + } } pub fn init() { diff --git a/src/training/charge.rs b/src/training/charge.rs index 239475f..6e45fd5 100644 --- a/src/training/charge.rs +++ b/src/training/charge.rs @@ -1,5 +1,5 @@ use crate::common::consts::FighterId; -use crate::common::get_module_accessor; +use crate::common::{get_module_accessor, try_get_battle_object}; use crate::training::character_specific::{kirby, pikmin}; use serde::{Deserialize, Serialize}; use smash::app::{self, lua_bind::*, ArticleOperationTarget, FighterFacial, FighterUtil}; @@ -274,6 +274,14 @@ pub unsafe fn get_charge( *FIGHTER_MIIGUNNER_INSTANCE_WORK_ID_INT_GUNNER_CHARGE_COUNT, ); charge_state.int_x(my_charge) + } + // Sora Spell + else if fighter_kind == FIGHTER_KIND_TRAIL { + let my_charge = WorkModule::get_int( + module_accessor, + *FIGHTER_TRAIL_INSTANCE_WORK_ID_INT_SPECIAL_N_MAGIC_KIND, + ); + charge_state.int_x(my_charge) } else { charge_state } @@ -933,4 +941,24 @@ pub unsafe fn handle_charge( ); }); } + // Sora Spell - 0 to 2 + else if fighter_kind == FIGHTER_KIND_TRAIL { + charge.int_x.map(|spell_kind| { + let prev_spell_kind = (spell_kind + 1) % 3; // Should have used this for pikmin idk what I was on + WorkModule::set_int( + module_accessor, + prev_spell_kind, + *FIGHTER_TRAIL_INSTANCE_WORK_ID_INT_SPECIAL_N_MAGIC_KIND, + ); + // app::FighterSpecializer_Trail::change_magic() doesn't actually run if the below flag isn't set + WorkModule::on_flag( + module_accessor, + *FIGHTER_TRAIL_STATUS_SPECIAL_N1_FLAG_CHANGE_MAGIC, + ); + if let Some(battle_object) = try_get_battle_object(module_accessor.battle_object_id) { + let fighter = battle_object as *const app::BattleObject as *mut app::Fighter; + app::FighterSpecializer_Trail::change_magic(fighter); + } + }); + } } diff --git a/src/training/mod.rs b/src/training/mod.rs index f59faae..5ba6e75 100644 --- a/src/training/mod.rs +++ b/src/training/mod.rs @@ -523,6 +523,14 @@ pub unsafe fn handle_fighter_play_se( my_hash = Hash40::new("se_silent"); } } + // Supress Sora Magic Switch SFX when loading Save State + if my_hash.hash == 0x156fdf29ba { + let module_accessor = (*sound_module).owner; + // Kirby and Sora's Special N statuses are all here or higher + if StatusModule::status_kind(module_accessor) < *FIGHTER_TRAIL_STATUS_KIND_SPECIAL_N1 { + 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) }