mirror of
https://github.com/jugeeya/UltimateTrainingModpack.git
synced 2025-03-14 02:16:10 +00:00
Save State Slots across game load + Fixes galore: overwrite button config if invalid; exclusive button combos; remove wait when opening menu (#476)
* Overwrite button config if invalid; exclusive button combos * Tons of fixes * Fix save state settings load from file
This commit is contained in:
parent
bf78f06f7d
commit
94ff25a921
8 changed files with 192 additions and 119 deletions
|
@ -2,6 +2,10 @@ use lazy_static::lazy_static;
|
|||
use serde::Deserialize;
|
||||
use smash::app::lua_bind::ControlModule;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use log::info;
|
||||
use strum::IntoEnumIterator;
|
||||
use strum_macros::EnumIter;
|
||||
use toml;
|
||||
|
||||
lazy_static! {
|
||||
|
@ -43,7 +47,7 @@ static mut BUTTON_COMBO_CONFIG: BtnComboConfig = BtnComboConfig {
|
|||
},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, EnumIter, PartialEq)]
|
||||
pub enum ButtonCombo {
|
||||
OpenMenu,
|
||||
SaveState,
|
||||
|
@ -68,45 +72,37 @@ struct BtnComboConfig {
|
|||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct TopLevelBtnComboConfig {
|
||||
pub struct TopLevelBtnComboConfig {
|
||||
button_config: BtnComboConfig,
|
||||
}
|
||||
|
||||
pub fn validate_config(data: &str) -> bool {
|
||||
let conf: TopLevelBtnComboConfig =
|
||||
toml::from_str(data).expect("Custom button config has invalid schema");
|
||||
let conf = conf.button_config;
|
||||
let configs = [conf.open_menu, conf.save_state, conf.load_state,
|
||||
conf.previous_save_state_slot, conf.next_save_state_slot];
|
||||
let bad_keys = configs
|
||||
.iter()
|
||||
.flat_map(|btn_list| {
|
||||
btn_list
|
||||
.hold
|
||||
.iter()
|
||||
.chain(btn_list.press.iter())
|
||||
.filter(|x| !BUTTON_MAPPING.contains_key(x.to_uppercase().as_str()))
|
||||
})
|
||||
.collect::<Vec<&String>>();
|
||||
pub fn load_from_file() {
|
||||
let combo_path = "sd:/TrainingModpack/training_modpack.toml";
|
||||
info!("Checking for previous button combo settings in training_modpack.toml...");
|
||||
let mut valid_button_config = false;
|
||||
if fs::metadata(combo_path).is_ok() {
|
||||
info!("Previous button combo settings found. Loading...");
|
||||
let combo_conf =
|
||||
fs::read_to_string(combo_path).unwrap_or_else(|_| panic!("Could not read {}", combo_path));
|
||||
let conf: Result<TopLevelBtnComboConfig, toml::de::Error> = toml::from_str(&combo_conf);
|
||||
if let Ok(conf) = conf {
|
||||
if validate_config(conf) {
|
||||
save_all_btn_config_from_toml(&combo_conf);
|
||||
valid_button_config = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !bad_keys.is_empty() {
|
||||
skyline::error::show_error(
|
||||
0x71,
|
||||
"Training Modpack custom button\nconfiguration is invalid!\0",
|
||||
&format!(
|
||||
"The following keys are invalid in\nsd:/TrainingModpack/training_modpack.toml:\n\
|
||||
{:?}\n\nPossible Keys: {:#?}\0",
|
||||
&bad_keys,
|
||||
BUTTON_MAPPING.keys()
|
||||
),
|
||||
);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
if !valid_button_config {
|
||||
info!("No previous button combo file found. Creating...");
|
||||
fs::write(combo_path, DEFAULT_BTN_CONFIG)
|
||||
.expect("Failed to write button config conf file");
|
||||
save_all_btn_config_from_defaults();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_all_btn_config_from_defaults() {
|
||||
|
||||
fn save_all_btn_config_from_defaults() {
|
||||
let conf = TopLevelBtnComboConfig {
|
||||
button_config: BtnComboConfig {
|
||||
open_menu: BtnList {
|
||||
|
@ -138,7 +134,7 @@ pub fn save_all_btn_config_from_defaults() {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn save_all_btn_config_from_toml(data: &str) {
|
||||
fn save_all_btn_config_from_toml(data: &str) {
|
||||
let conf: TopLevelBtnComboConfig = toml::from_str(data).expect("Could not parse button config");
|
||||
unsafe {
|
||||
// This println is necessary. Why?.......
|
||||
|
@ -147,44 +143,92 @@ pub fn save_all_btn_config_from_toml(data: &str) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn combo_passes(
|
||||
fn validate_config(conf: TopLevelBtnComboConfig) -> bool {
|
||||
let conf = conf.button_config;
|
||||
let configs = [conf.open_menu, conf.save_state, conf.load_state,
|
||||
conf.previous_save_state_slot, conf.next_save_state_slot];
|
||||
let bad_keys = configs
|
||||
.iter()
|
||||
.flat_map(|btn_list| {
|
||||
btn_list
|
||||
.hold
|
||||
.iter()
|
||||
.chain(btn_list.press.iter())
|
||||
.filter(|x| !BUTTON_MAPPING.contains_key(x.to_uppercase().as_str()))
|
||||
})
|
||||
.collect::<Vec<&String>>();
|
||||
|
||||
if !bad_keys.is_empty() {
|
||||
skyline::error::show_error(
|
||||
0x71,
|
||||
"Training Modpack custom button\nconfiguration is invalid!\0",
|
||||
&format!(
|
||||
"The following keys are invalid in\nsd:/TrainingModpack/training_modpack.toml:\n\
|
||||
{:?}\n\nPossible Keys: {:#?}\0",
|
||||
&bad_keys,
|
||||
BUTTON_MAPPING.keys()
|
||||
),
|
||||
);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn get_combo_keys(combo: ButtonCombo) -> (&'static Vec<String>, &'static Vec<String>) {
|
||||
match combo {
|
||||
ButtonCombo::OpenMenu => (
|
||||
&BUTTON_COMBO_CONFIG.open_menu.hold,
|
||||
&BUTTON_COMBO_CONFIG.open_menu.press,
|
||||
),
|
||||
ButtonCombo::SaveState => (
|
||||
&BUTTON_COMBO_CONFIG.save_state.hold,
|
||||
&BUTTON_COMBO_CONFIG.save_state.press,
|
||||
),
|
||||
ButtonCombo::LoadState => (
|
||||
&BUTTON_COMBO_CONFIG.load_state.hold,
|
||||
&BUTTON_COMBO_CONFIG.load_state.press,
|
||||
),
|
||||
ButtonCombo::PrevSaveStateSlot => (
|
||||
&BUTTON_COMBO_CONFIG.previous_save_state_slot.hold,
|
||||
&BUTTON_COMBO_CONFIG.previous_save_state_slot.press,
|
||||
),
|
||||
ButtonCombo::NextSaveStateSlot => (
|
||||
&BUTTON_COMBO_CONFIG.next_save_state_slot.hold,
|
||||
&BUTTON_COMBO_CONFIG.next_save_state_slot.press,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn combo_passes(
|
||||
module_accessor: *mut smash::app::BattleObjectModuleAccessor,
|
||||
combo: ButtonCombo,
|
||||
) -> bool {
|
||||
unsafe {
|
||||
let (hold, press) = match combo {
|
||||
ButtonCombo::OpenMenu => (
|
||||
&BUTTON_COMBO_CONFIG.open_menu.hold,
|
||||
&BUTTON_COMBO_CONFIG.open_menu.press,
|
||||
),
|
||||
ButtonCombo::SaveState => (
|
||||
&BUTTON_COMBO_CONFIG.save_state.hold,
|
||||
&BUTTON_COMBO_CONFIG.save_state.press,
|
||||
),
|
||||
ButtonCombo::LoadState => (
|
||||
&BUTTON_COMBO_CONFIG.load_state.hold,
|
||||
&BUTTON_COMBO_CONFIG.load_state.press,
|
||||
),
|
||||
ButtonCombo::PrevSaveStateSlot => (
|
||||
&BUTTON_COMBO_CONFIG.previous_save_state_slot.hold,
|
||||
&BUTTON_COMBO_CONFIG.previous_save_state_slot.press,
|
||||
),
|
||||
ButtonCombo::NextSaveStateSlot => (
|
||||
&BUTTON_COMBO_CONFIG.next_save_state_slot.hold,
|
||||
&BUTTON_COMBO_CONFIG.next_save_state_slot.press,
|
||||
),
|
||||
};
|
||||
hold.iter()
|
||||
let (hold, press) = get_combo_keys(combo);
|
||||
let this_combo_passes = hold.iter()
|
||||
.map(|hold| *BUTTON_MAPPING.get(&*hold.to_uppercase()).unwrap())
|
||||
.all(|hold| ControlModule::check_button_on(module_accessor, hold))
|
||||
&& press
|
||||
.iter()
|
||||
.map(|press| *BUTTON_MAPPING.get(&*press.to_uppercase()).unwrap())
|
||||
.all(|press| ControlModule::check_button_trigger(module_accessor, press))
|
||||
.all(|press| ControlModule::check_button_trigger(module_accessor, press));
|
||||
|
||||
this_combo_passes
|
||||
}
|
||||
}
|
||||
|
||||
pub const DEFAULT_BTN_CONFIG: &str = r#"[button_config]
|
||||
pub fn combo_passes_exclusive(
|
||||
module_accessor: *mut smash::app::BattleObjectModuleAccessor,
|
||||
combo: ButtonCombo
|
||||
) -> bool {
|
||||
let other_combo_passes = ButtonCombo::iter()
|
||||
.filter(|other_combo| *other_combo != combo)
|
||||
.any(|other_combo| combo_passes(module_accessor, other_combo));
|
||||
combo_passes(module_accessor, combo) && !other_combo_passes
|
||||
}
|
||||
|
||||
const DEFAULT_BTN_CONFIG: &str = r#"[button_config]
|
||||
# Available Options:
|
||||
#
|
||||
# ATTACK
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::fs;
|
||||
use crate::common::*;
|
||||
use crate::events::{Event, EVENT_QUEUE};
|
||||
use crate::logging::*;
|
||||
|
@ -13,11 +14,33 @@ const MENU_CLOSE_WAIT_FRAMES : u32 = 60;
|
|||
pub static mut QUICK_MENU_ACTIVE: bool = false;
|
||||
|
||||
pub unsafe fn menu_condition(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
button_config::combo_passes(module_accessor, button_config::ButtonCombo::OpenMenu)
|
||||
button_config::combo_passes_exclusive(module_accessor, button_config::ButtonCombo::OpenMenu)
|
||||
}
|
||||
|
||||
const MENU_CONF_PATH: &str = "sd:/TrainingModpack/training_modpack_menu.json";
|
||||
|
||||
pub fn load_from_file() {
|
||||
let menu_conf_path = "sd:/TrainingModpack/training_modpack_menu.json";
|
||||
info!("Checking for previous menu in training_modpack_menu.json...");
|
||||
if fs::metadata(menu_conf_path).is_ok() {
|
||||
let menu_conf = fs::read_to_string(menu_conf_path)
|
||||
.unwrap_or_else(|_| panic!("Could not remove {}", menu_conf_path));
|
||||
if let Ok(menu_conf_json) = serde_json::from_str::<MenuJsonStruct>(&menu_conf) {
|
||||
unsafe {
|
||||
MENU = menu_conf_json.menu;
|
||||
DEFAULTS_MENU = menu_conf_json.defaults_menu;
|
||||
info!("Previous menu found. Loading...");
|
||||
}
|
||||
} else {
|
||||
warn!("Previous menu found but is invalid. Deleting...");
|
||||
fs::remove_file(menu_conf_path)
|
||||
.unwrap_or_else(|_| panic!("{} has invalid schema but could not be deleted!", menu_conf_path));
|
||||
}
|
||||
} else {
|
||||
info!("No previous menu file found.");
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn set_menu_from_json(message: &str) {
|
||||
let response = serde_json::from_str::<MenuJsonStruct>(message);
|
||||
info!("Received menu message: {message}");
|
||||
|
@ -160,10 +183,6 @@ pub fn handle_get_npad_state(state: *mut NpadGcState, _controller_id: *const u32
|
|||
// BUTTON_PRESSES.down.is_pressed = (*state).Buttons & ((1 << 15) | (1 << 19)) > 0;
|
||||
// BUTTON_PRESSES.up.is_pressed = (*state).Buttons & ((1 << 13) | (1 << 17)) > 0;
|
||||
|
||||
if FRAME_COUNTER < MENU_INPUT_WAIT_FRAMES {
|
||||
return;
|
||||
}
|
||||
|
||||
if (*state).Buttons & (1 << 0) > 0 {
|
||||
BUTTON_PRESSES.a.is_pressed = true;
|
||||
}
|
||||
|
@ -179,7 +198,8 @@ pub fn handle_get_npad_state(state: *mut NpadGcState, _controller_id: *const u32
|
|||
if (*state).Buttons & (1 << 7) > 0 {
|
||||
BUTTON_PRESSES.r.is_pressed = true;
|
||||
}
|
||||
if (*state).Buttons & (1 << 8) > 0 {
|
||||
// Special case for frame-by-frame
|
||||
if FRAME_COUNTER < MENU_INPUT_WAIT_FRAMES && (*state).Buttons & (1 << 8) > 0 {
|
||||
BUTTON_PRESSES.zl.is_pressed = true;
|
||||
}
|
||||
if (*state).Buttons & (1 << 9) > 0 {
|
||||
|
|
40
src/lib.rs
40
src/lib.rs
|
@ -31,7 +31,6 @@ use std::fs;
|
|||
|
||||
use crate::logging::*;
|
||||
use crate::menu::quick_menu_loop;
|
||||
use training_mod_consts::MenuJsonStruct;
|
||||
use crate::training::ui::notifications::notification;
|
||||
|
||||
fn nro_main(nro: &NroInfo<'_>) {
|
||||
|
@ -103,43 +102,8 @@ pub fn main() {
|
|||
info!("Performing version check...");
|
||||
release::version_check();
|
||||
|
||||
let menu_conf_path = "sd:/TrainingModpack/training_modpack_menu.json";
|
||||
info!("Checking for previous menu in training_modpack_menu.json...");
|
||||
if fs::metadata(menu_conf_path).is_ok() {
|
||||
let menu_conf = fs::read_to_string(menu_conf_path)
|
||||
.unwrap_or_else(|_| panic!("Could not remove {}", menu_conf_path));
|
||||
if let Ok(menu_conf_json) = serde_json::from_str::<MenuJsonStruct>(&menu_conf) {
|
||||
unsafe {
|
||||
MENU = menu_conf_json.menu;
|
||||
DEFAULTS_MENU = menu_conf_json.defaults_menu;
|
||||
info!("Previous menu found. Loading...");
|
||||
}
|
||||
} else {
|
||||
warn!("Previous menu found but is invalid. Deleting...");
|
||||
fs::remove_file(menu_conf_path)
|
||||
.unwrap_or_else(|_| panic!("{} has invalid schema but could not be deleted!", menu_conf_path));
|
||||
}
|
||||
} else {
|
||||
info!("No previous menu file found.");
|
||||
}
|
||||
|
||||
let combo_path = "sd:/TrainingModpack/training_modpack.toml";
|
||||
info!("Checking for previous button combo settings in training_modpack.toml...");
|
||||
if fs::metadata(combo_path).is_ok() {
|
||||
info!("Previous button combo settings found. Loading...");
|
||||
let combo_conf =
|
||||
fs::read_to_string(combo_path).unwrap_or_else(|_| panic!("Could not remove {}", combo_path));
|
||||
if button_config::validate_config(&combo_conf) {
|
||||
button_config::save_all_btn_config_from_toml(&combo_conf);
|
||||
} else {
|
||||
button_config::save_all_btn_config_from_defaults();
|
||||
}
|
||||
} else {
|
||||
info!("No previous button combo file found. Creating...");
|
||||
fs::write(combo_path, button_config::DEFAULT_BTN_CONFIG)
|
||||
.expect("Failed to write button config conf file");
|
||||
button_config::save_all_btn_config_from_defaults();
|
||||
}
|
||||
menu::load_from_file();
|
||||
button_config::load_from_file();
|
||||
|
||||
std::thread::spawn(|| loop {
|
||||
std::thread::sleep(std::time::Duration::from_secs(10));
|
||||
|
|
Binary file not shown.
|
@ -1,7 +1,8 @@
|
|||
use serde::{Serialize, Deserialize};
|
||||
use smash::app::{self, lua_bind::*};
|
||||
use smash::lib::lua_const::*;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||
pub struct SteveState {
|
||||
pub mat_g1: i32,
|
||||
pub mat_wood: i32,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use serde::{Serialize, Deserialize};
|
||||
use smash::app::{self, lua_bind::*, ArticleOperationTarget, FighterFacial, FighterUtil};
|
||||
use smash::lib::lua_const::*;
|
||||
use smash::phx::{Hash40, Vector3f};
|
||||
|
||||
#[derive(Default, Copy, Clone)]
|
||||
#[derive(Serialize, Deserialize, Default, Copy, Clone, Debug)]
|
||||
pub struct ChargeState {
|
||||
pub int_x: Option<i32>,
|
||||
pub int_y: Option<i32>,
|
||||
|
|
|
@ -33,7 +33,7 @@ pub(crate) mod input_delay;
|
|||
mod input_record;
|
||||
mod mash;
|
||||
mod reset;
|
||||
mod save_states;
|
||||
pub(crate) mod save_states;
|
||||
mod shield_tilt;
|
||||
|
||||
#[skyline::hook(replace = WorkModule::get_param_float)]
|
||||
|
|
|
@ -14,11 +14,14 @@ use crate::training::items::apply_item;
|
|||
use crate::training::reset;
|
||||
use crate::{is_ptrainer, ITEM_MANAGER_ADDR};
|
||||
use SaveState::*;
|
||||
use parking_lot::Mutex;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use smash::app::{self, lua_bind::*, Item};
|
||||
use smash::hash40;
|
||||
use smash::lib::lua_const::*;
|
||||
use smash::phx::{Hash40, Vector3f};
|
||||
use std::collections::HashMap;
|
||||
use log::info;
|
||||
use training_mod_consts::{CharacterItem, SaveDamage};
|
||||
use crate::training::ui::notifications;
|
||||
|
||||
|
@ -27,7 +30,7 @@ extern "C" {
|
|||
pub fn stage_id() -> i32;
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
#[derive(Serialize, Deserialize, PartialEq, Copy, Clone, Debug)]
|
||||
enum SaveState {
|
||||
Save,
|
||||
NoAction,
|
||||
|
@ -37,7 +40,7 @@ enum SaveState {
|
|||
ApplyBuff,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||
struct SavedState {
|
||||
x: f32,
|
||||
y: f32,
|
||||
|
@ -89,20 +92,55 @@ macro_rules! default_save_state {
|
|||
};
|
||||
}
|
||||
|
||||
// static mut SAVE_STATE_PLAYER: SavedState = default_save_state!();
|
||||
// static mut SAVE_STATE_CPU: SavedState = default_save_state!();
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||
pub struct SaveStateSlots {
|
||||
player: [SavedState; NUM_SAVE_STATE_SLOTS],
|
||||
cpu: [SavedState; NUM_SAVE_STATE_SLOTS],
|
||||
}
|
||||
|
||||
const NUM_SAVE_STATE_SLOTS : usize = 5;
|
||||
static mut SAVE_STATE_PLAYER : [SavedState; NUM_SAVE_STATE_SLOTS] = [default_save_state!(); NUM_SAVE_STATE_SLOTS];
|
||||
static mut SAVE_STATE_CPU : [SavedState; NUM_SAVE_STATE_SLOTS] = [default_save_state!(); NUM_SAVE_STATE_SLOTS];
|
||||
// I actually had to do it this way, a simple load-from-file in main() caused crashes.
|
||||
lazy_static::lazy_static! {
|
||||
static ref SAVE_STATE_SLOTS : Mutex<SaveStateSlots> = Mutex::new(load_from_file());
|
||||
}
|
||||
static mut SAVE_STATE_SLOT : usize = 0;
|
||||
|
||||
pub fn load_from_file() -> SaveStateSlots {
|
||||
let defaults = SaveStateSlots{
|
||||
player: [default_save_state!(); NUM_SAVE_STATE_SLOTS],
|
||||
cpu: [default_save_state!(); NUM_SAVE_STATE_SLOTS],
|
||||
};
|
||||
|
||||
let save_states_path = "sd:/TrainingModpack/save_states.toml";
|
||||
info!("Checking for previous save state settings in save_states.toml...");
|
||||
if std::fs::metadata(save_states_path).is_err() {
|
||||
return defaults;
|
||||
}
|
||||
|
||||
info!("Previous save state settings found. Loading...");
|
||||
if let Ok(data) = std::fs::read_to_string(save_states_path) {
|
||||
let input_slots = toml::from_str::<SaveStateSlots>(&data);
|
||||
if let Ok(input_slots) = input_slots {
|
||||
return input_slots;
|
||||
}
|
||||
}
|
||||
|
||||
defaults
|
||||
}
|
||||
|
||||
pub unsafe fn save_to_file() {
|
||||
let save_states_str = toml::to_string_pretty(&*SAVE_STATE_SLOTS.data_ptr())
|
||||
.expect("Error serializing save state information");
|
||||
std::fs::write("sd:/TrainingModpack/save_states.toml", save_states_str)
|
||||
.expect("Could not write save state information to file");
|
||||
}
|
||||
|
||||
unsafe fn save_state_player() -> &'static mut SavedState {
|
||||
&mut SAVE_STATE_PLAYER[SAVE_STATE_SLOT]
|
||||
&mut (*SAVE_STATE_SLOTS.data_ptr()).player[SAVE_STATE_SLOT]
|
||||
}
|
||||
|
||||
unsafe fn save_state_cpu() -> &'static mut SavedState {
|
||||
&mut SAVE_STATE_CPU[SAVE_STATE_SLOT]
|
||||
&mut (*SAVE_STATE_SLOTS.data_ptr()).cpu[SAVE_STATE_SLOT]
|
||||
}
|
||||
|
||||
// MIRROR_STATE == 1 -> Do not mirror
|
||||
|
@ -256,7 +294,7 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor)
|
|||
.contains(&fighter_kind);
|
||||
|
||||
if !is_operation_cpu(module_accessor) &&
|
||||
button_config::combo_passes(module_accessor, button_config::ButtonCombo::PrevSaveStateSlot) {
|
||||
button_config::combo_passes_exclusive(module_accessor, button_config::ButtonCombo::PrevSaveStateSlot) {
|
||||
SAVE_STATE_SLOT = if SAVE_STATE_SLOT == 0 {
|
||||
NUM_SAVE_STATE_SLOTS - 1
|
||||
} else {
|
||||
|
@ -269,7 +307,7 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor)
|
|||
}
|
||||
|
||||
if !is_operation_cpu(module_accessor) &&
|
||||
button_config::combo_passes(module_accessor, button_config::ButtonCombo::NextSaveStateSlot) {
|
||||
button_config::combo_passes_exclusive(module_accessor, button_config::ButtonCombo::NextSaveStateSlot) {
|
||||
SAVE_STATE_SLOT = (SAVE_STATE_SLOT + 1) % NUM_SAVE_STATE_SLOTS;
|
||||
notifications::clear_notifications("Save State");
|
||||
notifications::notification("Save State".to_string(), format!("Switched to Slot {SAVE_STATE_SLOT}"), 120);
|
||||
|
@ -284,7 +322,7 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor)
|
|||
let mut triggered_reset: bool = false;
|
||||
if !is_operation_cpu(module_accessor) {
|
||||
triggered_reset =
|
||||
button_config::combo_passes(module_accessor, button_config::ButtonCombo::LoadState);
|
||||
button_config::combo_passes_exclusive(module_accessor, button_config::ButtonCombo::LoadState);
|
||||
}
|
||||
if (autoload_reset || triggered_reset) && !fighter_is_nana {
|
||||
if save_state.state == NoAction {
|
||||
|
@ -531,7 +569,7 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor)
|
|||
}
|
||||
|
||||
// Grab + Dpad down: Save state
|
||||
if button_config::combo_passes(module_accessor, button_config::ButtonCombo::SaveState) {
|
||||
if button_config::combo_passes_exclusive(module_accessor, button_config::ButtonCombo::SaveState) {
|
||||
// Don't begin saving state if Nana's delayed input is captured
|
||||
MIRROR_STATE = 1.0;
|
||||
save_state_player().state = Save;
|
||||
|
@ -580,5 +618,10 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor)
|
|||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
// If both chars finished saving by now
|
||||
if save_state_player().state != Save && save_state_cpu().state != Save {
|
||||
save_to_file();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue