1
0
Fork 0
mirror of https://github.com/jugeeya/UltimateTrainingModpack.git synced 2024-11-27 20:34:03 +00:00

Save States with items (#350)

* initial

* Complete refactor, add save state autoload

* forgot to commit consts folder

* Format Rust code using rustfmt

* Refactor, attempt CPU as well

* Update items.rs

* Format Rust code using rustfmt

* Fix web menu

* Format Rust code using rustfmt

* Fix crashes due to cpu kind not set

* Format Rust code using rustfmt

* Prevent Nana from Spawning with Items (#353)

Simple check before giving items

* somehow this code caused blackscreens on ryujinx?

* remerge

* Link and Diddy fixes, Players spawning with CPU item fixes (#355)

* Format Rust code using rustfmt

* Delete mash buffer queue entirely when spawning items to fix diddy as cpu; nits

* fix merge

* nit

* Format Rust code using rustfmt

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: GradualSyrup <68757075+GradualSyrup@users.noreply.github.com>
This commit is contained in:
jugeeya 2022-05-21 17:57:41 -07:00 committed by GitHub
parent 41ecb8aa38
commit ac78fb4a4d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 669 additions and 9 deletions

View file

@ -1,7 +1,7 @@
set -eu
# Obviously adjust these based on your paths
RYUJINX_APPLICATION_PATH="/mnt/c/Users/Jdsam/Downloads/ryujinx-Release-1.0.0+ba3ae74-win_x64/Ryujinx.exe"
RYUJINX_APPLICATION_PATH="/mnt/c/Users/Jdsam/Downloads/ryujinx-1.1.119-win_x64/publish/Ryujinx.exe"
SMASH_APPLICATION_PATH="C:\Users\Jdsam\Downloads\Super Smash Bros. Ultimate (World) (En,Ja,Fr,De,Es,It,Nl,Zh-Hant,Zh-Hans,Ko,Ru)\Super Smash Bros. Ultimate (World) (En,Ja,Fr,De,Es,It,Nl,Zh-Hant,Zh-Hans,Ko,Ru).xci"
RYUJINX_SMASH_SKYLINE_PLUGINS_PATH="/mnt/c/Users/Jdsam/AppData/Roaming/Ryujinx/mods/contents/01006a800016e000/romfs/skyline/plugins"

View file

@ -12,6 +12,7 @@ pub use crate::common::consts::MENU;
pub static mut DEFAULTS_MENU: TrainingModpackMenu = crate::common::consts::DEFAULTS_MENU;
pub static mut BASE_MENU: TrainingModpackMenu = unsafe { DEFAULTS_MENU };
pub static mut FIGHTER_MANAGER_ADDR: usize = 0;
pub static mut ITEM_MANAGER_ADDR: usize = 0;
pub static mut STAGE_MANAGER_ADDR: usize = 0;
#[cfg(not(feature = "outside_training_mode"))]

View file

@ -166,7 +166,6 @@ pub fn main() {
received_input = true;
});
let b_press = &mut button_presses.b;
let b_prev_press = b_press.prev_frame_is_pressed;
b_press.read_press().then(|| {
received_input = true;
if !app.outer_list {

View file

@ -0,0 +1,553 @@
use crate::common::consts::*;
use crate::common::*;
use crate::training::mash;
use smash::app;
use smash::app::lua_bind::*;
use smash::app::ItemKind;
use smash::app::{ArticleOperationTarget, BattleObjectModuleAccessor, Item};
use smash::cpp::l2c_value::LuaConst;
use smash::lib::lua_const::*;
pub struct CharItem {
pub fighter_kind: LuaConst,
pub item_kind: Option<LuaConst>,
pub article_kind: Option<LuaConst>,
pub variation: Option<LuaConst>,
}
pub const ALL_CHAR_ITEMS: [CharItem; 45] = [
CharItem {
fighter_kind: FIGHTER_KIND_DIDDY,
item_kind: None,
article_kind: Some(FIGHTER_DIDDY_GENERATE_ARTICLE_ITEM_BANANA),
variation: None,
},
CharItem {
// Robin Tome
fighter_kind: FIGHTER_KIND_REFLET,
item_kind: Some(ITEM_KIND_BOOK),
article_kind: None,
variation: None, // TODO: Look at the lua const ITEM_BOOK_STATUS_KIND_BEFORE_BORN
},
CharItem {
// Banjo-Kazooie Grenade Egg
fighter_kind: FIGHTER_KIND_BUDDY,
item_kind: Some(ITEM_KIND_BUDDYBOMB),
article_kind: None,
variation: None,
},
CharItem {
// Turnip
fighter_kind: FIGHTER_KIND_DAISY,
item_kind: None,
article_kind: Some(FIGHTER_DAISY_GENERATE_ARTICLE_DAIKON),
variation: Some(ITEM_VARIATION_DAISYDAIKON_1), // Smile
},
CharItem {
// Turnip
fighter_kind: FIGHTER_KIND_DAISY,
item_kind: None,
article_kind: Some(FIGHTER_DAISY_GENERATE_ARTICLE_DAIKON),
variation: Some(ITEM_VARIATION_DAISYDAIKON_6), // Winky
},
CharItem {
// Turnip
fighter_kind: FIGHTER_KIND_DAISY,
item_kind: None,
article_kind: Some(FIGHTER_DAISY_GENERATE_ARTICLE_DAIKON),
variation: Some(ITEM_VARIATION_DAISYDAIKON_7), // Dot-Eyes
},
CharItem {
// Turnip
fighter_kind: FIGHTER_KIND_DAISY,
item_kind: None,
article_kind: Some(FIGHTER_DAISY_GENERATE_ARTICLE_DAIKON),
variation: Some(ITEM_VARIATION_DAISYDAIKON_8), // Stitch-face
},
CharItem {
// Mr Saturn
fighter_kind: FIGHTER_KIND_DAISY,
item_kind: Some(ITEM_KIND_DOSEISAN),
article_kind: None,
variation: None,
},
CharItem {
// Bob-omb
fighter_kind: FIGHTER_KIND_DAISY,
item_kind: Some(ITEM_KIND_BOMBHEI),
article_kind: None,
variation: Some(ITEM_VARIATION_BOMBHEI_NORMAL),
},
CharItem {
fighter_kind: FIGHTER_KIND_DIDDY,
item_kind: Some(ITEM_KIND_DIDDYPEANUTS),
article_kind: None,
variation: None,
},
CharItem {
// Sheik Sideb Bomb
fighter_kind: FIGHTER_KIND_SHEIK,
item_kind: Some(ITEM_KIND_EXPLOSIONBOMB),
article_kind: None,
variation: None,
},
CharItem {
fighter_kind: FIGHTER_KIND_KROOL,
item_kind: Some(ITEM_KIND_KROOLCROWN),
article_kind: None,
variation: None,
},
CharItem {
fighter_kind: FIGHTER_KIND_LINK,
item_kind: Some(ITEM_KIND_LINKARROW),
article_kind: None,
variation: None,
},
CharItem {
fighter_kind: FIGHTER_KIND_LINK,
item_kind: Some(ITEM_KIND_LINKBOMB),
article_kind: None,
variation: None,
},
CharItem {
fighter_kind: FIGHTER_KIND_KOOPAJR,
item_kind: Some(ITEM_KIND_MECHAKOOPA),
article_kind: None,
variation: None,
},
CharItem {
fighter_kind: FIGHTER_KIND_ROCKMAN,
item_kind: Some(ITEM_KIND_METALBLADE),
article_kind: None,
variation: None,
},
CharItem {
fighter_kind: FIGHTER_KIND_PACMAN,
item_kind: Some(ITEM_KIND_PACMANCHERRY),
article_kind: None,
variation: None,
},
CharItem {
fighter_kind: FIGHTER_KIND_PACMAN,
item_kind: Some(ITEM_KIND_PACMANSTRAWBERRY),
article_kind: None,
variation: None,
},
CharItem {
fighter_kind: FIGHTER_KIND_PACMAN,
item_kind: Some(ITEM_KIND_PACMANORANGE),
article_kind: None,
variation: None,
},
CharItem {
fighter_kind: FIGHTER_KIND_PACMAN,
item_kind: Some(ITEM_KIND_PACMANAPPLE),
article_kind: None,
variation: None,
},
CharItem {
fighter_kind: FIGHTER_KIND_PACMAN,
item_kind: Some(ITEM_KIND_PACMANMELON),
article_kind: None,
variation: None,
},
CharItem {
fighter_kind: FIGHTER_KIND_PACMAN,
item_kind: Some(ITEM_KIND_PACMANBOSS),
article_kind: None,
variation: None,
},
CharItem {
fighter_kind: FIGHTER_KIND_PACMAN,
item_kind: Some(ITEM_KIND_PACMANBELL),
article_kind: None,
variation: None,
},
CharItem {
fighter_kind: FIGHTER_KIND_PACMAN,
item_kind: Some(ITEM_KIND_PACMANKEY),
article_kind: None,
variation: None,
},
CharItem {
// Turnip
fighter_kind: FIGHTER_KIND_PEACH,
item_kind: None,
article_kind: Some(FIGHTER_PEACH_GENERATE_ARTICLE_DAIKON),
variation: Some(ITEM_VARIATION_PEACHDAIKON_1), // Smile
},
CharItem {
// Turnip
fighter_kind: FIGHTER_KIND_PEACH,
item_kind: None,
article_kind: Some(FIGHTER_PEACH_GENERATE_ARTICLE_DAIKON),
variation: Some(ITEM_VARIATION_PEACHDAIKON_6), // Winky
},
CharItem {
// Turnip
fighter_kind: FIGHTER_KIND_PEACH,
item_kind: None,
article_kind: Some(FIGHTER_PEACH_GENERATE_ARTICLE_DAIKON),
variation: Some(ITEM_VARIATION_PEACHDAIKON_7), // Dot-Eyes
},
CharItem {
// Turnip
fighter_kind: FIGHTER_KIND_PEACH,
item_kind: None,
article_kind: Some(FIGHTER_PEACH_GENERATE_ARTICLE_DAIKON),
variation: Some(ITEM_VARIATION_PEACHDAIKON_8), // Stitch-face
},
CharItem {
// Mr Saturn
fighter_kind: FIGHTER_KIND_PEACH,
item_kind: Some(ITEM_KIND_DOSEISAN),
article_kind: None,
variation: None,
},
CharItem {
// Bob-omb
fighter_kind: FIGHTER_KIND_PEACH,
item_kind: Some(ITEM_KIND_BOMBHEI),
article_kind: None,
variation: Some(ITEM_VARIATION_BOMBHEI_NORMAL),
},
CharItem {
fighter_kind: FIGHTER_KIND_RICHTER,
item_kind: Some(ITEM_KIND_RICHTERHOLYWATER),
article_kind: None,
variation: None,
},
CharItem {
fighter_kind: FIGHTER_KIND_ROBOT,
item_kind: Some(ITEM_KIND_ROBOTGYRO),
article_kind: None,
variation: Some(ITEM_VARIATION_ROBOTGYRO_1P),
},
CharItem {
fighter_kind: FIGHTER_KIND_ROBOT,
item_kind: Some(ITEM_KIND_ROBOTGYRO),
article_kind: None,
variation: Some(ITEM_VARIATION_ROBOTGYRO_2P),
},
CharItem {
fighter_kind: FIGHTER_KIND_ROBOT,
item_kind: Some(ITEM_KIND_ROBOTGYRO),
article_kind: None,
variation: Some(ITEM_VARIATION_ROBOTGYRO_3P),
},
CharItem {
fighter_kind: FIGHTER_KIND_ROBOT,
item_kind: Some(ITEM_KIND_ROBOTGYRO),
article_kind: None,
variation: Some(ITEM_VARIATION_ROBOTGYRO_4P),
},
CharItem {
fighter_kind: FIGHTER_KIND_ROBOT,
item_kind: Some(ITEM_KIND_ROBOTGYRO),
article_kind: None,
variation: Some(ITEM_VARIATION_ROBOTGYRO_5P),
},
CharItem {
fighter_kind: FIGHTER_KIND_ROBOT,
item_kind: Some(ITEM_KIND_ROBOTGYRO),
article_kind: None,
variation: Some(ITEM_VARIATION_ROBOTGYRO_6P),
},
CharItem {
fighter_kind: FIGHTER_KIND_ROBOT,
item_kind: Some(ITEM_KIND_ROBOTGYRO),
article_kind: None,
variation: Some(ITEM_VARIATION_ROBOTGYRO_7P),
},
CharItem {
fighter_kind: FIGHTER_KIND_ROBOT,
item_kind: Some(ITEM_KIND_ROBOTGYRO),
article_kind: None,
variation: Some(ITEM_VARIATION_ROBOTGYRO_8P),
},
CharItem {
fighter_kind: FIGHTER_KIND_SIMON,
item_kind: Some(ITEM_KIND_SIMONHOLYWATER),
article_kind: None,
variation: None,
},
CharItem {
fighter_kind: FIGHTER_KIND_SNAKE,
item_kind: Some(ITEM_KIND_SNAKEGRENADE),
article_kind: None,
variation: None,
},
// CharItem {
// // Cardboard Box from Taunt
// fighter_kind: FIGHTER_KIND_SNAKE,
// item_kind: Some(ITEM_KIND_SNAKECBOX),
// article_kind: None,
// variation: None,
// },
CharItem {
// Robin Levin Sword
fighter_kind: FIGHTER_KIND_REFLET,
item_kind: Some(ITEM_KIND_THUNDERSWORD),
article_kind: None,
variation: None,
},
CharItem {
fighter_kind: FIGHTER_KIND_TOONLINK,
item_kind: Some(ITEM_KIND_TOONLINKBOMB),
article_kind: None,
variation: None,
},
// CharItem {
// fighter_kind: FIGHTER_KIND_WARIO,
// item_kind: Some(ITEM_KIND_WARIOBIKE),
// // Pretty sure these other ones are just the bike parts
// // ITEM_KIND_WARIOBIKEA,
// // ITEM_KIND_WARIOBIKEB,
// // ITEM_KIND_WARIOBIKEC,
// // ITEM_KIND_WARIOBIKED,
// // ITEM_KIND_WARIOBIKEE,
// article_kind: None,
// variation: None,
// },
CharItem {
// Villager Wood Chip
fighter_kind: FIGHTER_KIND_MURABITO,
item_kind: Some(ITEM_KIND_WOOD),
article_kind: None,
variation: None,
},
CharItem {
fighter_kind: FIGHTER_KIND_YOUNGLINK,
item_kind: Some(ITEM_KIND_YOUNGLINKBOMB),
article_kind: None,
variation: None,
},
];
pub static mut TURNIP_CHOSEN: Option<u32> = None;
pub static mut TARGET_PLAYER: Option<*mut BattleObjectModuleAccessor> = None;
unsafe fn apply_single_item(player_fighter_kind: i32, item: &CharItem) {
let player_module_accessor = get_module_accessor(FighterId::Player);
let cpu_module_accessor = get_module_accessor(FighterId::CPU);
// Now we make sure the module_accessor we use to generate the item/article is the correct character
let generator_module_accessor = if item.fighter_kind == player_fighter_kind {
player_module_accessor
} else {
cpu_module_accessor
};
let variation = item.variation.as_ref().map(|v| **v).unwrap_or(0);
item.item_kind.as_ref().map(|item_kind| {
let item_kind = **item_kind;
// For Link, use special article generation to link the bomb for detonation
if item_kind == *ITEM_KIND_LINKBOMB {
ArticleModule::generate_article_have_item(
generator_module_accessor,
*FIGHTER_LINK_GENERATE_ARTICLE_LINKBOMB,
*FIGHTER_HAVE_ITEM_WORK_MAIN,
smash::phx::Hash40::new("invalid"),
);
if player_fighter_kind != *FIGHTER_KIND_LINK {
ItemModule::drop_item(cpu_module_accessor, 0.0, 0.0, 0);
//ItemModule::eject_have_item(cpu_module_accessor, 0, false, false);
let item_mgr = *(ITEM_MANAGER_ADDR as *mut *mut app::ItemManager);
let item_ptr = ItemManager::get_active_item(item_mgr, 0);
ItemModule::have_item_instance(
player_module_accessor,
item_ptr as *mut smash::app::Item,
0,
false,
false,
false,
false,
);
}
} else {
ItemModule::have_item(
player_module_accessor,
ItemKind(item_kind),
variation,
0,
false,
false,
);
}
});
item.article_kind.as_ref().map(|article_kind| {
TURNIP_CHOSEN = if [*ITEM_VARIATION_PEACHDAIKON_8, *ITEM_VARIATION_DAISYDAIKON_8]
.contains(&variation)
{
Some(8)
} else if [*ITEM_VARIATION_PEACHDAIKON_7, *ITEM_VARIATION_DAISYDAIKON_7]
.contains(&variation)
{
Some(7)
} else if [*ITEM_VARIATION_PEACHDAIKON_6, *ITEM_VARIATION_DAISYDAIKON_6]
.contains(&variation)
{
Some(6)
} else if [*ITEM_VARIATION_PEACHDAIKON_1, *ITEM_VARIATION_DAISYDAIKON_1]
.contains(&variation)
{
Some(1)
} else {
None
};
let article_kind = **article_kind;
if article_kind == FIGHTER_DIDDY_GENERATE_ARTICLE_ITEM_BANANA {
ArticleModule::generate_article_have_item(
generator_module_accessor,
*FIGHTER_DIDDY_GENERATE_ARTICLE_ITEM_BANANA,
*FIGHTER_HAVE_ITEM_WORK_MAIN,
smash::phx::Hash40::new("invalid"),
);
WorkModule::on_flag(
generator_module_accessor,
*FIGHTER_DIDDY_STATUS_SPECIAL_LW_FLAG_ITEM_THROW,
);
ArticleModule::shoot(
generator_module_accessor,
*FIGHTER_DIDDY_GENERATE_ARTICLE_ITEM_BANANA,
ArticleOperationTarget(*ARTICLE_OPE_TARGET_ALL),
false,
);
// Grab item from the middle of the stage where it gets shot
let item_mgr = *(ITEM_MANAGER_ADDR as *mut *mut app::ItemManager);
let item = ItemManager::get_active_item(item_mgr, 0);
ItemModule::have_item_instance(
player_module_accessor,
item as *mut Item,
0,
false,
false,
false,
false,
);
} else {
TARGET_PLAYER = Some(player_module_accessor); // set so we generate CPU article on the player (in dittos, items always belong to player, even if cpu item is chosen)
ArticleModule::generate_article(
generator_module_accessor, // we want CPU's article
article_kind,
false,
0,
);
TARGET_PLAYER = None;
}
TURNIP_CHOSEN = None;
});
}
pub unsafe fn apply_item(character_item: CharacterItem) {
let player_module_accessor = get_module_accessor(FighterId::Player);
let cpu_module_accessor = get_module_accessor(FighterId::CPU);
let player_fighter_kind = app::utility::get_kind(&mut *player_module_accessor);
let cpu_fighter_kind = app::utility::get_kind(&mut *cpu_module_accessor);
let character_item_num = character_item.as_idx();
let (item_fighter_kind, variation_idx) =
if character_item_num <= CharacterItem::PlayerVariation8.as_idx() {
(
player_fighter_kind,
(character_item_num - CharacterItem::PlayerVariation1.as_idx()) as usize,
)
} else {
(
cpu_fighter_kind,
(character_item_num - CharacterItem::CpuVariation1.as_idx()) as usize,
)
};
ALL_CHAR_ITEMS
.iter()
.filter(|item| item_fighter_kind == item.fighter_kind)
.nth(variation_idx)
.map(|item| apply_single_item(player_fighter_kind, item));
mash::clear_queue();
}
macro_rules! daikon_replace {
($caps_char: ident, $char:ident, $num:literal) => {
paste::paste! {
extern "C" {
#[link_name = "\u{1}_ZN3app11" $char "daikon31" $caps_char "_" $caps_char "DAIKON_DAIKON_" $num "_PROBEv"]
pub fn [<$char daikon_ $num _prob>]() -> f32;
}
#[skyline::hook(replace = [<$char daikon_ $num _prob>])]
pub unsafe fn [<handle_ $char daikon_ $num _prob>]() -> f32 {
let orig = original!()();
if is_training_mode() {
if TURNIP_CHOSEN == Some($num) {
return 58.0;
} else if TURNIP_CHOSEN != None {
return 0.0;
}
}
orig
}
}
};
}
daikon_replace!(PEACH, peach, 8);
daikon_replace!(PEACH, peach, 7);
daikon_replace!(PEACH, peach, 6);
daikon_replace!(PEACH, peach, 5);
daikon_replace!(PEACH, peach, 4);
daikon_replace!(PEACH, peach, 3);
daikon_replace!(PEACH, peach, 2);
daikon_replace!(PEACH, peach, 1);
daikon_replace!(DAISY, daisy, 8);
daikon_replace!(DAISY, daisy, 7);
daikon_replace!(DAISY, daisy, 6);
daikon_replace!(DAISY, daisy, 5);
daikon_replace!(DAISY, daisy, 4);
daikon_replace!(DAISY, daisy, 3);
daikon_replace!(DAISY, daisy, 2);
daikon_replace!(DAISY, daisy, 1);
// GenerateArticleForTarget for Peach/Diddy(/Link?) item creation
static GAFT_OFFSET: usize = 0x03d40a0;
#[skyline::hook(offset = GAFT_OFFSET)]
pub unsafe fn handle_generate_article_for_target(
article_module_accessor: *mut app::BattleObjectModuleAccessor,
int_1: i32,
module_accessor: *mut app::BattleObjectModuleAccessor, // this is always 0x0 normally
bool_1: bool,
int_2: i32,
) -> u64 {
// unknown return value, gets cast to an (Article *)
let target_module_accessor = TARGET_PLAYER.unwrap_or(module_accessor);
let ori = original!()(
article_module_accessor,
int_1,
target_module_accessor,
bool_1,
int_2,
);
return ori;
}
pub fn init() {
skyline::install_hooks!(
handle_peachdaikon_8_prob,
handle_peachdaikon_7_prob,
handle_peachdaikon_6_prob,
handle_peachdaikon_5_prob,
handle_peachdaikon_4_prob,
handle_peachdaikon_3_prob,
handle_peachdaikon_2_prob,
handle_peachdaikon_1_prob,
handle_daisydaikon_8_prob,
handle_daisydaikon_7_prob,
handle_daisydaikon_6_prob,
handle_daisydaikon_5_prob,
handle_daisydaikon_4_prob,
handle_daisydaikon_3_prob,
handle_daisydaikon_2_prob,
handle_daisydaikon_1_prob,
// Items
handle_generate_article_for_target,
);
}

View file

@ -1,6 +1,7 @@
use smash::app::{self};
mod bowser;
pub mod items;
pub mod steve;
/**

View file

@ -87,6 +87,10 @@ pub fn full_reset() {
}
}
pub fn clear_queue() {
unsafe { QUEUE.clear() }
}
pub fn set_aerial(attack: Action) {
unsafe {
CURRENT_AERIAL = attack;

View file

@ -1,5 +1,8 @@
use crate::common::{is_training_mode, menu, FIGHTER_MANAGER_ADDR, STAGE_MANAGER_ADDR};
use crate::common::{
is_training_mode, menu, FIGHTER_MANAGER_ADDR, ITEM_MANAGER_ADDR, STAGE_MANAGER_ADDR,
};
use crate::hitbox_visualizer;
use crate::training::character_specific::items;
use skyline::hooks::{getRegionAddress, InlineCtx, Region};
use skyline::nn::hid::*;
use skyline::nn::ro::LookupSymbol;
@ -485,6 +488,13 @@ pub fn training_mods() {
.as_ptr(),
);
LookupSymbol(
&mut ITEM_MANAGER_ADDR,
"_ZN3lib9SingletonIN3app11ItemManagerEE9instance_E\0"
.as_bytes()
.as_ptr(),
);
smash::params::add_hook(params_main).unwrap();
}
@ -536,4 +546,5 @@ pub fn training_mods() {
throw::init();
menu::init();
buff::init();
items::init();
}

View file

@ -5,14 +5,15 @@ use crate::common::consts::SaveStateMirroring;
use crate::common::is_dead;
use crate::common::MENU;
use crate::training::buff;
use crate::training::character_specific::steve;
use crate::training::charge::{self, ChargeState};
use crate::training::items::apply_item;
use crate::training::reset;
use smash::app::{self, lua_bind::*};
use smash::app::{self, lua_bind::*, Item};
use smash::hash40;
use smash::lib::lua_const::*;
use smash::phx::{Hash40, Vector3f};
use crate::training::character_specific::steve;
use training_mod_consts::CharacterItem;
#[derive(PartialEq)]
enum SaveState {
@ -75,6 +76,7 @@ macro_rules! default_save_state {
};
}
use crate::ITEM_MANAGER_ADDR;
use SaveState::*;
static mut SAVE_STATE_PLAYER: SavedState = default_save_state!();
@ -229,6 +231,16 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor)
};
PostureModule::set_pos(module_accessor, &pos);
let item_mgr = *(ITEM_MANAGER_ADDR as *mut *mut app::ItemManager);
(0..ItemManager::get_num_of_active_item_all(item_mgr)).for_each(|item_idx| {
let item = ItemManager::get_active_item(item_mgr, item_idx);
if item != 0 {
let item = item as *mut Item;
let item_battle_object_id =
smash::app::lua_bind::Item::get_battle_object_id(item) as u32;
ItemManager::remove_item_from_id(item_mgr, item_battle_object_id);
}
});
MotionAnimcmdModule::set_sleep(module_accessor, true);
SoundModule::pause_se_all(module_accessor, true);
ControlModule::stop_rumble(module_accessor, true);
@ -319,6 +331,11 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor)
// If we're done moving, reset percent, handle charges, and apply buffs
if save_state.state == NoAction {
set_damage(module_accessor, save_state.percent);
// Set to held item
if !is_cpu && !fighter_is_nana && MENU.character_item != CharacterItem::None {
apply_item(MENU.character_item);
}
// 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);

View file

@ -918,6 +918,71 @@ impl ToUrlParam for i32 {
}
}
/// Item Selections
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, FromPrimitive, EnumIter, Serialize, Deserialize)]
pub enum CharacterItem {
None = 0,
PlayerVariation1 = 0x1,
PlayerVariation2 = 0x2,
PlayerVariation3 = 0x4,
PlayerVariation4 = 0x8,
PlayerVariation5 = 0x10,
PlayerVariation6 = 0x20,
PlayerVariation7 = 0x40,
PlayerVariation8 = 0x80,
CpuVariation1 = 0x100,
CpuVariation2 = 0x200,
CpuVariation3 = 0x400,
CpuVariation4 = 0x800,
CpuVariation5 = 0x1000,
CpuVariation6 = 0x2000,
CpuVariation7 = 0x4000,
CpuVariation8 = 0x8000,
}
impl CharacterItem {
pub fn as_idx(self) -> u32 {
log_2(self as i32 as u32)
}
pub fn as_str(self) -> Option<&'static str> {
Some(match self {
CharacterItem::PlayerVariation1 => "Player 1st Var.",
CharacterItem::PlayerVariation2 => "Player 2nd Var.",
CharacterItem::PlayerVariation3 => "Player 3rd Var.",
CharacterItem::PlayerVariation4 => "Player 4th Var.",
CharacterItem::PlayerVariation5 => "Player 5th Var.",
CharacterItem::PlayerVariation6 => "Player 6th Var.",
CharacterItem::PlayerVariation7 => "Player 7th Var.",
CharacterItem::PlayerVariation8 => "Player 8th Var.",
CharacterItem::CpuVariation1 => "CPU 1st Var.",
CharacterItem::CpuVariation2 => "CPU 2nd Var.",
CharacterItem::CpuVariation3 => "CPU 3rd Var.",
CharacterItem::CpuVariation4 => "CPU 4th Var.",
CharacterItem::CpuVariation5 => "CPU 5th Var.",
CharacterItem::CpuVariation6 => "CPU 6th Var.",
CharacterItem::CpuVariation7 => "CPU 7th Var.",
CharacterItem::CpuVariation8 => "CPU 8th Var.",
_ => "None",
})
}
pub fn to_url_param(&self) -> String {
(*self as i32).to_string()
}
}
impl ToggleTrait for CharacterItem {
fn to_toggle_strs() -> Vec<&'static str> {
CharacterItem::iter().map(|i| i.as_str().unwrap_or("")).collect()
}
fn to_toggle_vals() -> Vec<usize> {
CharacterItem::iter().map(|i| i as usize).collect()
}
}
// Macro to build the url parameter string
macro_rules! url_params {
(
@ -992,6 +1057,7 @@ url_params! {
pub throw_delay: MedDelay,
pub pummel_delay: MedDelay,
pub buff_state: BuffOption,
pub character_item: CharacterItem,
pub quick_menu: OnOff,
}
}
@ -1055,6 +1121,7 @@ impl TrainingModpackMenu {
throw_delay = MedDelay::from_bits(val),
pummel_delay = MedDelay::from_bits(val),
buff_state = BuffOption::from_bits(val),
character_item = num::FromPrimitive::from_u32(val),
quick_menu = OnOff::from_val(val),
);
}
@ -1120,6 +1187,7 @@ pub static DEFAULTS_MENU: TrainingModpackMenu = TrainingModpackMenu {
throw_delay: MedDelay::empty(),
pummel_delay: MedDelay::empty(),
buff_state: BuffOption::empty(),
character_item: CharacterItem::None,
quick_menu: OnOff::Off,
};
@ -1420,9 +1488,9 @@ pub unsafe fn get_menu() -> UiMenu<'static> {
false, // TODO: Should this be true?
);
defensive_tab.add_submenu_with_toggles::<Defensive>(
"Defensive Toggles",
"Escape Toggles",
"defensive_state",
"Defensive Options: Actions to take after a ledge option, tech option, or mistech option",
"Escape Options: Actions to take after a ledge option, tech option, or mistech option",
false,
);
defensive_tab.add_submenu_with_toggles::<BuffOption>(
@ -1431,6 +1499,12 @@ pub unsafe fn get_menu() -> UiMenu<'static> {
"Buff Options: Buff(s) to be applied to respective character when loading save states",
false,
);
defensive_tab.add_submenu_with_toggles::<CharacterItem>(
"Character Item",
"character_item",
"Character Item: CPU/Player item to hold when loading a save state",
true
);
overall_menu.tabs.push(defensive_tab);
let mut misc_tab = Tab {
@ -1457,7 +1531,7 @@ pub unsafe fn get_menu() -> UiMenu<'static> {
true,
);
misc_tab.add_submenu_with_toggles::<OnOff>(
"Autoload Save States",
"Save States Autoload",
"save_state_autoload",
"Save States Autoload: Load save state when any fighter dies",
true,