mirror of
https://github.com/jugeeya/UltimateTrainingModpack.git
synced 2024-11-30 22:00:16 +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;
|
pub use crate::common::consts::MENU;
|
||||||
use crate::common::consts::*;
|
use crate::common::consts::*;
|
||||||
|
use crate::training::character_specific::ptrainer;
|
||||||
|
|
||||||
pub mod button_config;
|
pub mod button_config;
|
||||||
pub mod consts;
|
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 {
|
pub unsafe fn is_dead(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||||
let status_kind = StatusModule::status_kind(module_accessor);
|
let status_kind = StatusModule::status_kind(module_accessor);
|
||||||
let prev_status_kind = StatusModule::prev_status_kind(module_accessor, 0);
|
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
|
let is_dead_status =
|
||||||
// And the previous status is FIGHTER_STATUS_NONE
|
[*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) {
|
if is_ptrainer(module_accessor) {
|
||||||
[*FIGHTER_STATUS_KIND_DEAD, *FIGHTER_STATUS_KIND_STANDBY].contains(&status_kind)
|
ptrainer_switch_dead = (status_kind == FIGHTER_STATUS_KIND_WAIT
|
||||||
|| (status_kind == FIGHTER_STATUS_KIND_WAIT
|
&& prev_status_kind == FIGHTER_STATUS_KIND_NONE)
|
||||||
&& prev_status_kind == FIGHTER_STATUS_KIND_NONE)
|
&& (StatusModule::status_kind(ptrainer::get_ptrainer_module_accessor(module_accessor))
|
||||||
} else {
|
== *WEAPON_PTRAINER_PTRAINER_STATUS_KIND_RESTART_CHANGE);
|
||||||
[*FIGHTER_STATUS_KIND_DEAD, *FIGHTER_STATUS_KIND_STANDBY].contains(&status_kind)
|
|
||||||
}
|
}
|
||||||
|
is_dead_status || ptrainer_switch_dead
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn is_in_clatter(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
pub unsafe fn is_in_clatter(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||||
|
|
|
@ -3,6 +3,7 @@ use smash::app::{self};
|
||||||
mod bowser;
|
mod bowser;
|
||||||
pub mod items;
|
pub mod items;
|
||||||
pub mod pikmin;
|
pub mod pikmin;
|
||||||
|
pub mod ptrainer;
|
||||||
pub mod steve;
|
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() {
|
pub unsafe fn stop_playback() {
|
||||||
INPUT_RECORD = None;
|
INPUT_RECORD = None;
|
||||||
INPUT_RECORD_FRAME = 0;
|
INPUT_RECORD_FRAME = 0;
|
||||||
|
POSSESSION = Player;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn is_input_neutral(input_frame: usize) -> bool {
|
pub unsafe fn is_input_neutral(input_frame: usize) -> bool {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::common::{
|
||||||
use crate::hitbox_visualizer;
|
use crate::hitbox_visualizer;
|
||||||
use crate::input::*;
|
use crate::input::*;
|
||||||
use crate::logging::*;
|
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::hooks::{getRegionAddress, InlineCtx, Region};
|
||||||
use skyline::nn::ro::LookupSymbol;
|
use skyline::nn::ro::LookupSymbol;
|
||||||
use smash::app::{self, enSEType, lua_bind::*, utility};
|
use smash::app::{self, enSEType, lua_bind::*, utility};
|
||||||
|
@ -31,7 +31,7 @@ pub mod ui;
|
||||||
|
|
||||||
mod air_dodge_direction;
|
mod air_dodge_direction;
|
||||||
mod attack_angle;
|
mod attack_angle;
|
||||||
mod character_specific;
|
pub mod character_specific;
|
||||||
mod fast_fall;
|
mod fast_fall;
|
||||||
mod full_hop;
|
mod full_hop;
|
||||||
pub mod input_delay;
|
pub mod input_delay;
|
||||||
|
@ -42,6 +42,9 @@ mod reset;
|
||||||
pub mod save_states;
|
pub mod save_states;
|
||||||
mod shield_tilt;
|
mod shield_tilt;
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
mod debug;
|
||||||
|
|
||||||
#[skyline::hook(replace = WorkModule::get_param_float)]
|
#[skyline::hook(replace = WorkModule::get_param_float)]
|
||||||
pub unsafe fn handle_get_param_float(
|
pub unsafe fn handle_get_param_float(
|
||||||
module_accessor: &mut app::BattleObjectModuleAccessor,
|
module_accessor: &mut app::BattleObjectModuleAccessor,
|
||||||
|
@ -279,7 +282,7 @@ pub unsafe fn handle_change_motion(
|
||||||
motion_kind
|
motion_kind
|
||||||
};
|
};
|
||||||
|
|
||||||
original!()(
|
let ori = original!()(
|
||||||
module_accessor,
|
module_accessor,
|
||||||
mod_motion_kind,
|
mod_motion_kind,
|
||||||
unk1,
|
unk1,
|
||||||
|
@ -288,7 +291,12 @@ pub unsafe fn handle_change_motion(
|
||||||
unk4,
|
unk4,
|
||||||
unk5,
|
unk5,
|
||||||
unk6,
|
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)]
|
#[skyline::hook(replace = WorkModule::is_enable_transition_term)]
|
||||||
|
@ -494,7 +502,7 @@ static PLAY_SE_OFFSET: usize = 0x04cf6a0;
|
||||||
#[skyline::hook(offset = PLAY_SE_OFFSET)]
|
#[skyline::hook(offset = PLAY_SE_OFFSET)]
|
||||||
pub unsafe fn handle_fighter_play_se(
|
pub unsafe fn handle_fighter_play_se(
|
||||||
sound_module: *mut FighterSoundModule, // pointer to fighter's SoundModule
|
sound_module: *mut FighterSoundModule, // pointer to fighter's SoundModule
|
||||||
my_hash: Hash40,
|
mut my_hash: Hash40,
|
||||||
bool1: bool,
|
bool1: bool,
|
||||||
bool2: bool,
|
bool2: bool,
|
||||||
bool3: bool,
|
bool3: bool,
|
||||||
|
@ -504,49 +512,30 @@ pub unsafe fn handle_fighter_play_se(
|
||||||
if !is_training_mode() {
|
if !is_training_mode() {
|
||||||
return original!()(sound_module, my_hash, bool1, bool2, bool3, bool4, se_type);
|
return original!()(sound_module, my_hash, bool1, bool2, bool3, bool4, se_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Supress Buff Sound Effects while buffing
|
// Supress Buff Sound Effects while buffing
|
||||||
if buff::is_buffing_any() {
|
if buff::is_buffing_any() {
|
||||||
let silent_hash = Hash40::new("se_silent");
|
my_hash = Hash40::new("se_silent");
|
||||||
return original!()(
|
|
||||||
sound_module,
|
|
||||||
silent_hash,
|
|
||||||
bool1,
|
|
||||||
bool2,
|
|
||||||
bool3,
|
|
||||||
bool4,
|
|
||||||
se_type,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Supress Kirby Copy Ability SFX when loading Save State
|
// Supress Kirby Copy Ability SFX when loading Save State
|
||||||
if my_hash.hash == 0x1453dd86e4 || my_hash.hash == 0x14bdd3e7c8 {
|
if my_hash.hash == 0x1453dd86e4 || my_hash.hash == 0x14bdd3e7c8 {
|
||||||
let module_accessor = (*sound_module).owner;
|
let module_accessor = (*sound_module).owner;
|
||||||
if StatusModule::status_kind(module_accessor) != FIGHTER_KIRBY_STATUS_KIND_SPECIAL_N_DRINK {
|
if StatusModule::status_kind(module_accessor) != FIGHTER_KIRBY_STATUS_KIND_SPECIAL_N_DRINK {
|
||||||
let silent_hash = Hash40::new("se_silent");
|
my_hash = Hash40::new("se_silent");
|
||||||
return original!()(
|
|
||||||
sound_module,
|
|
||||||
silent_hash,
|
|
||||||
bool1,
|
|
||||||
bool2,
|
|
||||||
bool3,
|
|
||||||
bool4,
|
|
||||||
se_type,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
my_hash = ptrainer::handle_pokemon_sound_effect(my_hash);
|
||||||
original!()(sound_module, my_hash, bool1, bool2, bool3, bool4, se_type)
|
original!()(sound_module, my_hash, bool1, bool2, bool3, bool4, se_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
static FOLLOW_REQ_OFFSET: usize = 0x044f860;
|
static FOLLOW_REQ_OFFSET: usize = 0x044f860;
|
||||||
#[skyline::hook(offset = FOLLOW_REQ_OFFSET)] // hooked to prevent score gfx from playing when loading save states
|
#[skyline::hook(offset = FOLLOW_REQ_OFFSET)] // hooked to prevent score gfx from playing when loading save states
|
||||||
pub unsafe fn handle_effect_follow(
|
pub unsafe fn handle_effect_follow(
|
||||||
module_accessor: &mut app::BattleObjectModuleAccessor,
|
effect_module: *mut FighterEffectModule,
|
||||||
eff_hash: Hash40,
|
eff_hash: Hash40,
|
||||||
eff_hash2: Hash40,
|
eff_hash2: Hash40,
|
||||||
pos: *const Vector3f,
|
pos: *const Vector3f,
|
||||||
rot: *const Vector3f,
|
rot: *const Vector3f,
|
||||||
size: f32,
|
mut size: f32,
|
||||||
arg5: bool,
|
arg5: bool,
|
||||||
arg6: u32,
|
arg6: u32,
|
||||||
arg7: i32,
|
arg7: i32,
|
||||||
|
@ -558,7 +547,7 @@ pub unsafe fn handle_effect_follow(
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
if !is_training_mode() {
|
if !is_training_mode() {
|
||||||
return original!()(
|
return original!()(
|
||||||
module_accessor,
|
effect_module,
|
||||||
eff_hash,
|
eff_hash,
|
||||||
eff_hash2,
|
eff_hash2,
|
||||||
pos,
|
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
|
// 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() {
|
if eff_hash == Hash40::new("sys_score_aura") && save_states::is_loading() {
|
||||||
return original!()(
|
size = 0.0
|
||||||
module_accessor,
|
|
||||||
eff_hash,
|
|
||||||
eff_hash2,
|
|
||||||
pos,
|
|
||||||
rot,
|
|
||||||
0.0,
|
|
||||||
arg5,
|
|
||||||
arg6,
|
|
||||||
arg7,
|
|
||||||
arg8,
|
|
||||||
arg9,
|
|
||||||
arg10,
|
|
||||||
arg11,
|
|
||||||
arg12,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
original!()(
|
original!()(
|
||||||
module_accessor,
|
effect_module,
|
||||||
eff_hash,
|
eff_hash,
|
||||||
eff_hash2,
|
eff_hash2,
|
||||||
pos,
|
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
|
#[skyline::hook(replace = EffectModule::req)] // hooked to prevent death gfx from playing when loading save states
|
||||||
pub unsafe fn handle_effect(
|
pub unsafe fn handle_effect(
|
||||||
module_accessor: &mut app::BattleObjectModuleAccessor,
|
module_accessor: &mut app::BattleObjectModuleAccessor,
|
||||||
|
@ -773,15 +841,6 @@ unsafe fn handle_final_input_mapping(
|
||||||
input_record::handle_final_input_mapping(player_idx, out);
|
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() {
|
pub fn training_mods() {
|
||||||
info!("Applying training mods.");
|
info!("Applying training mods.");
|
||||||
|
|
||||||
|
@ -863,11 +922,16 @@ pub fn training_mods() {
|
||||||
handle_final_input_mapping,
|
handle_final_input_mapping,
|
||||||
// Charge
|
// Charge
|
||||||
handle_article_get_int,
|
handle_article_get_int,
|
||||||
handle_get_module_accessor,
|
handle_fighter_effect,
|
||||||
|
handle_fighter_joint_effect,
|
||||||
);
|
);
|
||||||
|
|
||||||
items::init();
|
items::init();
|
||||||
input_record::init();
|
input_record::init();
|
||||||
ui::init();
|
ui::init();
|
||||||
pikmin::init();
|
pikmin::init();
|
||||||
|
ptrainer::init();
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
debug::init();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,12 @@ use std::collections::HashMap;
|
||||||
use log::info;
|
use log::info;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use serde::{Deserialize, Serialize};
|
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::cpp::l2c_value::LuaConst;
|
||||||
use smash::hash40;
|
use smash::hash40;
|
||||||
use smash::lib::lua_const::*;
|
use smash::lib::lua_const::*;
|
||||||
use smash::phx::{Hash40, Vector3f};
|
use smash::phx::{Hash40, Vector3f};
|
||||||
|
use std::ptr;
|
||||||
use training_mod_consts::{CharacterItem, SaveDamage};
|
use training_mod_consts::{CharacterItem, SaveDamage};
|
||||||
|
|
||||||
use SaveState::*;
|
use SaveState::*;
|
||||||
|
@ -22,11 +23,12 @@ use crate::common::consts::RecordTrigger;
|
||||||
use crate::common::consts::SaveStateMirroring;
|
use crate::common::consts::SaveStateMirroring;
|
||||||
//TODO: Cleanup above
|
//TODO: Cleanup above
|
||||||
use crate::common::consts::SAVE_STATES_TOML_PATH;
|
use crate::common::consts::SAVE_STATES_TOML_PATH;
|
||||||
|
use crate::common::get_module_accessor;
|
||||||
use crate::common::is_dead;
|
use crate::common::is_dead;
|
||||||
use crate::common::MENU;
|
use crate::common::MENU;
|
||||||
use crate::is_operation_cpu;
|
use crate::is_operation_cpu;
|
||||||
use crate::training::buff;
|
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::charge::{self, ChargeState};
|
||||||
use crate::training::input_record;
|
use crate::training::input_record;
|
||||||
use crate::training::items::apply_item;
|
use crate::training::items::apply_item;
|
||||||
|
@ -69,6 +71,7 @@ pub enum SaveState {
|
||||||
PosMove,
|
PosMove,
|
||||||
NanaPosMove,
|
NanaPosMove,
|
||||||
ApplyBuff,
|
ApplyBuff,
|
||||||
|
WaitForPokemonSwitch,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
#[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]
|
&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 not mirror
|
||||||
// MIRROR_STATE == -1 -> Do Mirror
|
// MIRROR_STATE == -1 -> Do Mirror
|
||||||
static mut MIRROR_STATE: f32 = 1.0;
|
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) {
|
unsafe fn on_ptrainer_death(module_accessor: &mut app::BattleObjectModuleAccessor) {
|
||||||
if !is_ptrainer(module_accessor) {
|
if !is_ptrainer(module_accessor) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let ptrainer_module_accessor = ptrainer::get_ptrainer_module_accessor(module_accessor);
|
||||||
WorkModule::off_flag(
|
WorkModule::off_flag(
|
||||||
get_ptrainer_module_accessor(module_accessor),
|
ptrainer_module_accessor,
|
||||||
*WEAPON_PTRAINER_PTRAINER_INSTANCE_WORK_ID_FLAG_ENABLE_CHANGE_POKEMON,
|
*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);
|
MotionModule::set_rate(ptrainer_module_accessor, 1000.0);
|
||||||
if ArticleModule::is_exist(
|
if let Some(ptrainer_masterball_module_accessor) =
|
||||||
ptrainer_module_accessor,
|
ptrainer::get_ptrainer_mball_module_accessor(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);
|
|
||||||
MotionModule::set_rate(ptrainer_masterball_module_accessor, 1000.0);
|
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);
|
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
|
// All articles have ID <= 0x25
|
||||||
(0..=0x25)
|
(0..=0x25)
|
||||||
.filter(|article_idx| {
|
.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 {
|
if save_state.state == KillPlayer && !fighter_is_nana {
|
||||||
on_ptrainer_death(module_accessor);
|
on_ptrainer_death(module_accessor);
|
||||||
if !is_dead(module_accessor) {
|
if !is_dead(module_accessor) {
|
||||||
on_death(fighter_kind, module_accessor);
|
|
||||||
StatusModule::change_status_force(module_accessor, *FIGHTER_STATUS_KIND_DEAD, true);
|
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 {
|
if fighter_is_ptrainer {
|
||||||
WorkModule::on_flag(
|
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,
|
*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
|
// Save state
|
||||||
if button_config::combo_passes(button_config::ButtonCombo::SaveState) {
|
if button_config::combo_passes(button_config::ButtonCombo::SaveState) {
|
||||||
// Don't begin saving state if Nana's delayed input is captured
|
// Don't begin saving state if Nana's delayed input is captured
|
||||||
|
|
Loading…
Reference in a new issue