mirror of
https://github.com/jugeeya/UltimateTrainingModpack.git
synced 2024-11-20 00:46:34 +00:00
Add UI backing to text options in menu, always do new damage display (#451)
* A bunch of things * Current progress * Fix for ResAnimationContent * Figure out Parts* * Cleanup, just because * New pane working!!! * New null pane for hierarchy * Success with parent pane * Generate multiple panes * Multiple panes, almost working text color * MaterialColor test, but fails * Forgot bitfield-struct * Vtable for material. Fixes SetWhiteColor! * Refactor color changing, change naming scheme * Just Frame Advantage * Merge * Delete T_test.txt * Delete set_txt_num_01.txt * Delete libtraining_modpack.nro * Format Rust code using rustfmt * Ignore shell scripts in repo languages * General refactor, add basis for quick menu * Small refactor, fix ordering of submenu options * Toggles, sliders * Tons of progress... * Correct dmg updater, remove old quick menu backend * Fix damage percentage display * Small QoL * Format Rust code using rustfmt * More edits. Use Quit Training button as Modpack Menu header * Finish merge * Format Rust code using rustfmt * Use vanilla backgrounds for text options * Format ui_hacks, also always do new percent display * Fix merge * That was the most awful merge ever * Address clippy warnings * Format Rust code using rustfmt Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
52e3528292
commit
ed425d78e5
29 changed files with 411 additions and 341 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -6,8 +6,5 @@
|
|||
"--workspace",
|
||||
"--message-format=json",
|
||||
"--all-features",
|
||||
"--",
|
||||
"-A",
|
||||
"clippy::borrow_interior_mutable_const"
|
||||
],
|
||||
}
|
|
@ -154,7 +154,7 @@ pub fn combo_passes(
|
|||
}
|
||||
}
|
||||
|
||||
pub const DEFAULT_BTN_CONFIG: &'static str = r#"[button_config]
|
||||
pub const DEFAULT_BTN_CONFIG: &str = r#"[button_config]
|
||||
# Available Options:
|
||||
#
|
||||
# ATTACK
|
||||
|
|
|
@ -41,7 +41,7 @@ impl Uuid {
|
|||
pub fn to_str(&self) -> String {
|
||||
self.data
|
||||
.iter()
|
||||
.map(|i| format!("{:02x}", i))
|
||||
.map(|i| format!("{i:02x}"))
|
||||
.collect::<String>()
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ impl Event {
|
|||
user_uid
|
||||
.id
|
||||
.iter()
|
||||
.map(|i| format!("{:02x}", i))
|
||||
.map(|i| format!("{i:02x}"))
|
||||
.collect::<Vec<String>>()
|
||||
.join(""),
|
||||
)
|
||||
|
@ -109,7 +109,7 @@ impl Event {
|
|||
device_uuid
|
||||
.data
|
||||
.iter()
|
||||
.map(|i| format!("{:02x}", i))
|
||||
.map(|i| format!("{i:02x}"))
|
||||
.collect::<Vec<String>>()
|
||||
.join(""),
|
||||
)
|
||||
|
@ -137,7 +137,7 @@ impl Event {
|
|||
session_id_hash
|
||||
.hash
|
||||
.iter()
|
||||
.map(|i| format!("{:02x}", i))
|
||||
.map(|i| format!("{i:02x}"))
|
||||
.collect::<Vec<String>>()
|
||||
.join(""),
|
||||
)
|
||||
|
|
|
@ -54,8 +54,8 @@ pub unsafe fn write_menu() {
|
|||
let program_id = get_program_id();
|
||||
let htdocs_dir = "training_modpack";
|
||||
let menu_html_path = Path::new("sd:/atmosphere/contents")
|
||||
.join(&format!("{:016X}", program_id))
|
||||
.join(&format!("manual_html/html-document/{}.htdocs/", htdocs_dir))
|
||||
.join(format!("{program_id:016X}"))
|
||||
.join(format!("manual_html/html-document/{htdocs_dir}.htdocs/"))
|
||||
.join("training_menu.html");
|
||||
|
||||
let write_resp = fs::write(menu_html_path, data);
|
||||
|
@ -94,11 +94,10 @@ pub unsafe fn set_menu_from_json(message: &str) {
|
|||
skyline::error::show_error(
|
||||
0x70,
|
||||
"Could not parse the menu response!\nPlease send a screenshot of the details page to the developers.\n\0",
|
||||
&*format!("{:#?}\0", message)
|
||||
&format!("{message:#?}\0")
|
||||
);
|
||||
};
|
||||
if MENU.quick_menu == OnOff::Off {
|
||||
if is_emulator() {
|
||||
if MENU.quick_menu == OnOff::Off && is_emulator() {
|
||||
skyline::error::show_error(
|
||||
0x69,
|
||||
"Cannot use web menu on emulator.\n\0",
|
||||
|
@ -106,7 +105,6 @@ pub unsafe fn set_menu_from_json(message: &str) {
|
|||
);
|
||||
MENU.quick_menu = OnOff::On;
|
||||
}
|
||||
}
|
||||
EVENT_QUEUE.push(Event::menu_open(message.to_string()));
|
||||
}
|
||||
|
||||
|
@ -151,14 +149,6 @@ pub struct ButtonPress {
|
|||
}
|
||||
|
||||
impl ButtonPress {
|
||||
pub fn default() -> ButtonPress {
|
||||
ButtonPress {
|
||||
prev_frame_is_pressed: false,
|
||||
is_pressed: false,
|
||||
lockout_frames: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_press(&mut self) -> bool {
|
||||
let is_pressed = self.is_pressed;
|
||||
if self.is_pressed {
|
||||
|
|
|
@ -26,7 +26,7 @@ extern "C" {
|
|||
|
||||
#[cfg(feature = "outside_training_mode")]
|
||||
pub fn is_training_mode() -> bool {
|
||||
return true;
|
||||
true
|
||||
}
|
||||
|
||||
pub fn get_category(module_accessor: &mut app::BattleObjectModuleAccessor) -> i32 {
|
||||
|
@ -60,7 +60,7 @@ pub fn is_operation_cpu(module_accessor: &mut app::BattleObjectModuleAccessor) -
|
|||
}
|
||||
|
||||
let entry_id_int =
|
||||
WorkModule::get_int(module_accessor, *FIGHTER_INSTANCE_WORK_ID_INT_ENTRY_ID) as i32;
|
||||
WorkModule::get_int(module_accessor, *FIGHTER_INSTANCE_WORK_ID_INT_ENTRY_ID);
|
||||
|
||||
if entry_id_int == 0 {
|
||||
return false;
|
||||
|
@ -76,13 +76,13 @@ pub fn is_operation_cpu(module_accessor: &mut app::BattleObjectModuleAccessor) -
|
|||
}
|
||||
|
||||
pub fn is_grounded(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let situation_kind = unsafe { StatusModule::situation_kind(module_accessor) as i32 };
|
||||
let situation_kind = unsafe { StatusModule::situation_kind(module_accessor) };
|
||||
|
||||
situation_kind == SITUATION_KIND_GROUND
|
||||
}
|
||||
|
||||
pub fn is_airborne(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let situation_kind = unsafe { StatusModule::situation_kind(module_accessor) as i32 };
|
||||
let situation_kind = unsafe { StatusModule::situation_kind(module_accessor) };
|
||||
|
||||
situation_kind == SITUATION_KIND_AIR
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ pub fn is_in_footstool(module_accessor: &mut app::BattleObjectModuleAccessor) ->
|
|||
}
|
||||
|
||||
pub fn is_shielding(module_accessor: *mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let status_kind = unsafe { StatusModule::status_kind(module_accessor) as i32 };
|
||||
let status_kind = unsafe { StatusModule::status_kind(module_accessor) };
|
||||
|
||||
(*FIGHTER_STATUS_KIND_GUARD_ON..=*FIGHTER_STATUS_KIND_GUARD_DAMAGE).contains(&status_kind)
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ pub unsafe fn is_ptrainer(module_accessor: &mut app::BattleObjectModuleAccessor)
|
|||
}
|
||||
|
||||
pub unsafe fn is_dead(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let status_kind = StatusModule::status_kind(module_accessor) as i32;
|
||||
let status_kind = StatusModule::status_kind(module_accessor);
|
||||
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
|
||||
// And the previous status is FIGHTER_STATUS_NONE
|
||||
|
|
|
@ -168,11 +168,10 @@ fn print_char(
|
|||
for segment_char in segment_str.chars() {
|
||||
let mut index = segment_char as i32 - 'a' as i32;
|
||||
|
||||
let segment: [f32; 5];
|
||||
if is_facing_left {
|
||||
index = SEGMENT_REV[index as usize] as i32 - 'a' as i32;
|
||||
}
|
||||
segment = SEGMENT_DICT[index as usize];
|
||||
let segment = SEGMENT_DICT[index as usize];
|
||||
|
||||
const SIZE_MULT: f32 = 0.5;
|
||||
|
||||
|
|
|
@ -38,26 +38,23 @@ pub fn version_check() {
|
|||
// Display dialog box on launch if changing versions
|
||||
DialogOk::ok(
|
||||
format!(
|
||||
"Thank you for installing version {} of the Training Modpack.\n\n\
|
||||
"Thank you for installing version {CURRENT_VERSION} of the Training Modpack.\n\n\
|
||||
Due to a breaking change in this version, your menu selections and defaults must be reset once.\n\n\
|
||||
Please refer to the Github page and the Discord server for a full list of recent features, bugfixes, and other changes.",
|
||||
CURRENT_VERSION
|
||||
Please refer to the Github page and the Discord server for a full list of recent features, bugfixes, and other changes."
|
||||
)
|
||||
);
|
||||
// Remove old menu selections, silently ignoring errors (i.e. if the file doesn't exist)
|
||||
fs::remove_file("sd:/TrainingModpack/training_modpack_menu.conf").unwrap_or({});
|
||||
fs::remove_file("sd:/TrainingModpack/training_modpack_menu.json").unwrap_or({});
|
||||
fs::remove_file("sd:/TrainingModpack/training_modpack_menu_defaults.conf")
|
||||
.unwrap_or({});
|
||||
fs::remove_file("sd:/TrainingModpack/training_modpack_menu.conf").unwrap();
|
||||
fs::remove_file("sd:/TrainingModpack/training_modpack_menu.json").unwrap();
|
||||
fs::remove_file("sd:/TrainingModpack/training_modpack_menu_defaults.conf").unwrap();
|
||||
record_current_version(VERSION_FILE_PATH);
|
||||
}
|
||||
VersionCheck::NoFile => {
|
||||
// Display dialog box on fresh installation
|
||||
DialogOk::ok(
|
||||
format!(
|
||||
"Thank you for installing version {} of the Training Modpack.\n\n\
|
||||
Please refer to the Github page and the Discord server for a full list of features and instructions on how to utilize the improved Training Mode.",
|
||||
CURRENT_VERSION
|
||||
"Thank you for installing version {CURRENT_VERSION} of the Training Modpack.\n\n\
|
||||
Please refer to the Github page and the Discord server for a full list of features and instructions on how to utilize the improved Training Mode."
|
||||
)
|
||||
);
|
||||
record_current_version(VERSION_FILE_PATH);
|
||||
|
|
|
@ -75,7 +75,7 @@ fn get_hazard_hook_address() -> usize {
|
|||
}
|
||||
}
|
||||
|
||||
flag_pos as usize
|
||||
flag_pos
|
||||
}
|
||||
|
||||
// 8.1.0 Defaults
|
||||
|
@ -129,8 +129,7 @@ unsafe fn validate_hazards_addrs() -> std::result::Result<(), ()> {
|
|||
pub fn hazard_manager() {
|
||||
println!("[Training Modpack] Applying hazard control mods.");
|
||||
unsafe {
|
||||
match validate_hazards_addrs() {
|
||||
Ok(()) => {
|
||||
if let Ok(()) = validate_hazards_addrs() {
|
||||
HAZARD_FLAG_ADDRESS = get_hazard_flag_address() as *mut u8;
|
||||
LOAD_ADDRESS = get_hazard_hook_address();
|
||||
A64InlineHook(
|
||||
|
@ -138,7 +137,5 @@ pub fn hazard_manager() {
|
|||
hazard_intercept as *const skyline::libc::c_void,
|
||||
);
|
||||
}
|
||||
Err(()) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,7 +161,7 @@ pub unsafe fn get_command_flag_cat(module_accessor: &mut app::BattleObjectModule
|
|||
return;
|
||||
}
|
||||
|
||||
let status_kind = StatusModule::status_kind(module_accessor) as i32;
|
||||
let status_kind = StatusModule::status_kind(module_accessor);
|
||||
if (*FIGHTER_STATUS_KIND_CATCH..=*FIGHTER_STATUS_KIND_CATCH_TURN).contains(&status_kind) {
|
||||
return;
|
||||
}
|
||||
|
@ -231,11 +231,11 @@ unsafe fn mod_handle_attack(lua_state: u64) {
|
|||
let mut hitbox_params: Vec<L2CValue> =
|
||||
(0..36).map(|i| l2c_agent.pop_lua_stack(i + 1)).collect();
|
||||
l2c_agent.clear_lua_stack();
|
||||
for (i, mut x) in hitbox_params.iter_mut().enumerate().take(36) {
|
||||
for (i, x) in hitbox_params.iter_mut().enumerate().take(36) {
|
||||
if i == 20 {
|
||||
l2c_agent.push_lua_stack(&mut L2CValue::new_num(-999.0));
|
||||
} else {
|
||||
l2c_agent.push_lua_stack(&mut x);
|
||||
l2c_agent.push_lua_stack(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -335,7 +335,7 @@ unsafe fn mod_handle_catch(lua_state: u64) {
|
|||
size.get_num(),
|
||||
center,
|
||||
capsule_center,
|
||||
ID_COLORS[(id.get_int() + 3 % 8) as usize],
|
||||
ID_COLORS[((id.get_int() + 3) % 8) as usize],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
14
src/lib.rs
14
src/lib.rs
|
@ -5,11 +5,15 @@
|
|||
#![feature(c_variadic)]
|
||||
#![allow(
|
||||
clippy::borrow_interior_mutable_const,
|
||||
clippy::declare_interior_mutable_const,
|
||||
clippy::not_unsafe_ptr_arg_deref,
|
||||
clippy::missing_safety_doc,
|
||||
clippy::wrong_self_convention,
|
||||
clippy::option_map_unit_fn,
|
||||
clippy::float_cmp
|
||||
clippy::float_cmp,
|
||||
clippy::fn_null_check,
|
||||
// Look into why for this one
|
||||
clippy::transmute_num_to_bytes
|
||||
)]
|
||||
|
||||
pub mod common;
|
||||
|
@ -66,7 +70,7 @@ pub fn main() {
|
|||
},
|
||||
};
|
||||
|
||||
let err_msg = format!("thread has panicked at '{}', {}", msg, location);
|
||||
let err_msg = format!("thread has panicked at '{msg}', {location}");
|
||||
skyline::error::show_error(
|
||||
69,
|
||||
"Skyline plugin has panicked! Please open the details and send a screenshot to the developer, then close the game.\n",
|
||||
|
@ -108,7 +112,7 @@ pub fn main() {
|
|||
let menu_conf_path = "sd:/TrainingModpack/training_modpack_menu.json";
|
||||
log!("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();
|
||||
let menu_conf = fs::read_to_string(menu_conf_path).unwrap();
|
||||
if let Ok(menu_conf_json) = serde_json::from_str::<MenuJsonStruct>(&menu_conf) {
|
||||
unsafe {
|
||||
MENU = menu_conf_json.menu;
|
||||
|
@ -130,7 +134,7 @@ pub fn main() {
|
|||
log!("Checking for previous button combo settings in training_modpack.toml...");
|
||||
if fs::metadata(combo_path).is_ok() {
|
||||
log!("Previous button combo settings found. Loading...");
|
||||
let combo_conf = fs::read_to_string(&combo_path).unwrap();
|
||||
let combo_conf = fs::read_to_string(combo_path).unwrap();
|
||||
if button_config::validate_config(&combo_conf) {
|
||||
button_config::save_all_btn_config_from_toml(&combo_conf);
|
||||
} else {
|
||||
|
@ -161,7 +165,7 @@ pub fn main() {
|
|||
event.event_name, event.device_id, event.event_time
|
||||
);
|
||||
|
||||
let url = format!("{}{}", host, path);
|
||||
let url = format!("{host}{path}");
|
||||
minreq::post(url).with_json(&event).unwrap().send().ok();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -519,14 +519,14 @@ pub unsafe fn handle_generate_article_for_target(
|
|||
) -> u64 {
|
||||
// unknown return value, gets cast to an (Article *)
|
||||
let target_module_accessor = TARGET_PLAYER.unwrap_or(module_accessor);
|
||||
let ori = original!()(
|
||||
|
||||
original!()(
|
||||
article_module_accessor,
|
||||
int_1,
|
||||
target_module_accessor,
|
||||
bool_1,
|
||||
int_2,
|
||||
);
|
||||
return ori;
|
||||
)
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
|
|
|
@ -2,7 +2,7 @@ use smash::app::{self, lua_bind::*, ArticleOperationTarget, FighterFacial, Fight
|
|||
use smash::lib::lua_const::*;
|
||||
use smash::phx::{Hash40, Vector3f};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Default, Copy, Clone)]
|
||||
pub struct ChargeState {
|
||||
pub int_x: Option<i32>,
|
||||
pub int_y: Option<i32>,
|
||||
|
@ -44,19 +44,6 @@ impl ChargeState {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for ChargeState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
int_x: None,
|
||||
int_y: None,
|
||||
float_x: None,
|
||||
float_y: None,
|
||||
float_z: None,
|
||||
has_charge: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn get_charge(
|
||||
module_accessor: &mut app::BattleObjectModuleAccessor,
|
||||
fighter_kind: i32,
|
||||
|
@ -282,12 +269,11 @@ pub unsafe fn handle_charge(
|
|||
);
|
||||
if shot_charge == 112 {
|
||||
EffectModule::req_common(module_accessor, Hash40::new("charge_max"), 0.0);
|
||||
let samus_cshot_hash;
|
||||
if fighter_kind == FIGHTER_KIND_SAMUS {
|
||||
samus_cshot_hash = Hash40::new("samus_cshot_max");
|
||||
let samus_cshot_hash = if fighter_kind == FIGHTER_KIND_SAMUS {
|
||||
Hash40::new("samus_cshot_max")
|
||||
} else {
|
||||
samus_cshot_hash = Hash40::new("samusd_cshot_max");
|
||||
}
|
||||
Hash40::new("samusd_cshot_max")
|
||||
};
|
||||
let joint_hash = Hash40::new("armr");
|
||||
let pos = Vector3f {
|
||||
x: 7.98004,
|
||||
|
|
|
@ -76,7 +76,7 @@ pub unsafe fn hook_start_clatter(
|
|||
// Most of the time this is 8 frames, but could be less depending on
|
||||
// the status (e.g. freeze is 4 frames / input)
|
||||
if is_training_mode() && is_operation_cpu(module_accessor) {
|
||||
CLATTER_STEP = manual_recovery_rate.clone();
|
||||
CLATTER_STEP = manual_recovery_rate;
|
||||
}
|
||||
original!()(
|
||||
module_accessor,
|
||||
|
|
|
@ -55,8 +55,7 @@ pub unsafe fn is_enable_transition_term(
|
|||
transition_term: i32,
|
||||
is: bool,
|
||||
) {
|
||||
let entry_id_int =
|
||||
WorkModule::get_int(module_accessor, *FIGHTER_INSTANCE_WORK_ID_INT_ENTRY_ID) as i32;
|
||||
let entry_id_int = WorkModule::get_int(module_accessor, *FIGHTER_INSTANCE_WORK_ID_INT_ENTRY_ID);
|
||||
|
||||
if entry_id_int != (FighterId::Player as i32) {
|
||||
return;
|
||||
|
@ -92,8 +91,7 @@ pub unsafe fn is_enable_transition_term(
|
|||
}
|
||||
|
||||
pub unsafe fn get_command_flag_cat(module_accessor: &mut app::BattleObjectModuleAccessor) {
|
||||
let entry_id_int =
|
||||
WorkModule::get_int(module_accessor, *FIGHTER_INSTANCE_WORK_ID_INT_ENTRY_ID) as i32;
|
||||
let entry_id_int = WorkModule::get_int(module_accessor, *FIGHTER_INSTANCE_WORK_ID_INT_ENTRY_ID);
|
||||
// do only once.
|
||||
if entry_id_int != (FighterId::Player as i32) {
|
||||
return;
|
||||
|
|
|
@ -73,7 +73,7 @@ pub fn should_reverse_angle(direction: &Direction) -> bool {
|
|||
let player_module_accessor = get_module_accessor(FighterId::Player);
|
||||
unsafe {
|
||||
PostureModule::pos_x(player_module_accessor) > PostureModule::pos_x(cpu_module_accessor)
|
||||
&& ![Direction::LEFT, Direction::RIGHT].contains(&direction)
|
||||
&& ![Direction::LEFT, Direction::RIGHT].contains(direction)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ unsafe fn should_return_none_in_check_button(
|
|||
}
|
||||
|
||||
// Only need to hold during jump squat
|
||||
let status_kind = StatusModule::status_kind(module_accessor) as i32;
|
||||
let status_kind = StatusModule::status_kind(module_accessor);
|
||||
if status_kind != FIGHTER_STATUS_KIND_JUMP_SQUAT {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -24,8 +24,7 @@ pub unsafe fn p1_controller_id() -> u32 {
|
|||
|
||||
pub fn handle_get_npad_state(state: *mut NpadGcState, controller_id: *const u32) {
|
||||
unsafe {
|
||||
if crate::common::is_training_mode() {
|
||||
if *controller_id == p1_controller_id() {
|
||||
if crate::common::is_training_mode() && *controller_id == p1_controller_id() {
|
||||
let mut delayed_states = P1_DELAYED_NPAD_STATES.lock();
|
||||
let actual_state = *state;
|
||||
|
||||
|
@ -48,4 +47,3 @@ pub fn handle_get_npad_state(state: *mut NpadGcState, controller_id: *const u32)
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,8 +23,7 @@ pub enum InputRecordState {
|
|||
use InputRecordState::*;
|
||||
|
||||
pub unsafe fn get_command_flag_cat(module_accessor: &mut BattleObjectModuleAccessor) {
|
||||
let entry_id_int =
|
||||
WorkModule::get_int(module_accessor, *FIGHTER_INSTANCE_WORK_ID_INT_ENTRY_ID) as i32;
|
||||
let entry_id_int = WorkModule::get_int(module_accessor, *FIGHTER_INSTANCE_WORK_ID_INT_ENTRY_ID);
|
||||
|
||||
if entry_id_int == 0 {
|
||||
// Attack + Dpad Right: Playback
|
||||
|
|
|
@ -58,7 +58,7 @@ fn roll_ledge_case() {
|
|||
}
|
||||
|
||||
pub unsafe fn force_option(module_accessor: &mut app::BattleObjectModuleAccessor) {
|
||||
if StatusModule::situation_kind(module_accessor) as i32 != *SITUATION_KIND_CLIFF {
|
||||
if StatusModule::situation_kind(module_accessor) != *SITUATION_KIND_CLIFF {
|
||||
// No longer on ledge, so re-roll the ledge case and reset the delay counter for next time
|
||||
reset_ledge_case();
|
||||
reset_ledge_delay();
|
||||
|
@ -113,7 +113,7 @@ pub unsafe fn is_enable_transition_term(
|
|||
return None;
|
||||
}
|
||||
// Only handle ledge scenarios from menu
|
||||
if StatusModule::status_kind(_module_accessor) as i32 != *FIGHTER_STATUS_KIND_CLIFF_WAIT
|
||||
if StatusModule::status_kind(_module_accessor) != *FIGHTER_STATUS_KIND_CLIFF_WAIT
|
||||
|| MENU.ledge_state == LedgeOption::empty()
|
||||
{
|
||||
return None;
|
||||
|
|
|
@ -143,7 +143,7 @@ unsafe fn check_buffer(module_accessor: &mut app::BattleObjectModuleAccessor) {
|
|||
|
||||
unsafe fn should_buffer(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let fighter_distance = get_fighter_distance();
|
||||
if MENU.mash_triggers.contains(MashTrigger::ALWAYS)
|
||||
MENU.mash_triggers.contains(MashTrigger::ALWAYS)
|
||||
|| (MENU.mash_triggers.contains(MashTrigger::HIT) && is_in_hitstun(module_accessor))
|
||||
// BLOCK handled in shield.rs
|
||||
|| (MENU.mash_triggers.contains(MashTrigger::PARRY) && is_in_parry(module_accessor))
|
||||
|
@ -158,13 +158,7 @@ unsafe fn should_buffer(module_accessor: &mut app::BattleObjectModuleAccessor) -
|
|||
|| (MENU.mash_triggers.contains(MashTrigger::GROUNDED) && is_grounded(module_accessor))
|
||||
|| (MENU.mash_triggers.contains(MashTrigger::AIRBORNE) && is_airborne(module_accessor))
|
||||
|| (MENU.mash_triggers.contains(MashTrigger::DISTANCE_CLOSE) && fighter_distance < DISTANCE_CLOSE_THRESHOLD)
|
||||
|| (MENU.mash_triggers.contains(MashTrigger::DISTANCE_MID) && fighter_distance < DISTANCE_MID_THRESHOLD)
|
||||
|| (MENU.mash_triggers.contains(MashTrigger::DISTANCE_FAR) && fighter_distance < DISTANCE_FAR_THRESHOLD)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|| (MENU.mash_triggers.contains(MashTrigger::DISTANCE_MID) && fighter_distance < DISTANCE_MID_THRESHOLD) || (MENU.mash_triggers.contains(MashTrigger::DISTANCE_FAR) && fighter_distance < DISTANCE_FAR_THRESHOLD)
|
||||
}
|
||||
|
||||
// Temp Translation
|
||||
|
|
|
@ -360,11 +360,9 @@ pub unsafe fn handle_check_doyle_summon_dispatch(
|
|||
if !is_training_mode() {
|
||||
return ori;
|
||||
}
|
||||
if ori == *FIGHTER_JACK_STATUS_KIND_SUMMON as u64 {
|
||||
if buff::is_buffing(module_accessor) {
|
||||
if ori == *FIGHTER_JACK_STATUS_KIND_SUMMON as u64 && buff::is_buffing(module_accessor) {
|
||||
return 4294967295;
|
||||
}
|
||||
}
|
||||
ori
|
||||
}
|
||||
|
||||
|
@ -384,7 +382,7 @@ static STALE_MENU_OFFSET: usize = 0x013e88a0;
|
|||
#[skyline::hook(offset=STALE_MENU_OFFSET, inline)]
|
||||
unsafe fn stale_menu_handle(ctx: &mut InlineCtx) {
|
||||
// Set the text pointer to where "mel_training_on" is located
|
||||
let on_text_ptr = ((getRegionAddress(Region::Text) as u64) + (0x42b215e)) as u64;
|
||||
let on_text_ptr = (getRegionAddress(Region::Text) as u64) + 0x42b215e;
|
||||
let x1 = ctx.registers[1].x.as_mut();
|
||||
*x1 = on_text_ptr;
|
||||
}
|
||||
|
@ -467,9 +465,9 @@ static CAN_FUTTOBI_BACK_OFFSET: usize = 0x0260f950; // can_futtobi_back, checks
|
|||
pub unsafe fn handle_star_ko(my_long_ptr: &mut u64) -> bool {
|
||||
let ori = original!()(my_long_ptr);
|
||||
if !is_training_mode() {
|
||||
return ori;
|
||||
ori
|
||||
} else {
|
||||
return false;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -127,11 +127,9 @@ pub unsafe fn get_param_int(
|
|||
if param_hash == hash40("rebirth_move_frame") {
|
||||
return Some(0);
|
||||
}
|
||||
if param_hash == hash40("rebirth_move_frame_trainer") {
|
||||
if is_killing() {
|
||||
if param_hash == hash40("rebirth_move_frame_trainer") && is_killing() {
|
||||
return Some(0);
|
||||
}
|
||||
}
|
||||
if param_hash == hash40("rebirth_wait_frame") {
|
||||
return Some(0);
|
||||
}
|
||||
|
@ -142,13 +140,12 @@ pub unsafe fn get_param_int(
|
|||
return Some(0);
|
||||
}
|
||||
}
|
||||
if param_type == hash40("param_mball") {
|
||||
if param_hash == hash40("change_fly_frame") {
|
||||
if is_killing() {
|
||||
if param_type == hash40("param_mball")
|
||||
&& param_hash == hash40("change_fly_frame")
|
||||
&& is_killing()
|
||||
{
|
||||
return Some(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
@ -203,7 +200,7 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor)
|
|||
return;
|
||||
}
|
||||
|
||||
let status = StatusModule::status_kind(module_accessor) as i32;
|
||||
let status = StatusModule::status_kind(module_accessor);
|
||||
let is_cpu = WorkModule::get_int(module_accessor, *FIGHTER_INSTANCE_WORK_ID_INT_ENTRY_ID)
|
||||
== FighterId::CPU as i32;
|
||||
let save_state = if is_cpu {
|
||||
|
@ -318,7 +315,7 @@ pub unsafe fn save_states(module_accessor: &mut app::BattleObjectModuleAccessor)
|
|||
|
||||
// move to correct pos
|
||||
if save_state.state == PosMove || save_state.state == NanaPosMove {
|
||||
let status_kind = StatusModule::status_kind(module_accessor) as i32;
|
||||
let status_kind = StatusModule::status_kind(module_accessor);
|
||||
if save_state.state == NanaPosMove
|
||||
&& (!fighter_is_nana || (status_kind == FIGHTER_STATUS_KIND_STANDBY))
|
||||
{
|
||||
|
|
|
@ -44,8 +44,8 @@ pub unsafe fn check_hit_stop_delay_command(
|
|||
// If there is a non-neutral direction picked,
|
||||
// modify the SDI angle Vector2f as a side-effect
|
||||
// and return 1 so the CPU knows that an SDI input occurred
|
||||
(*sdi_direction).x = (angle.cos() as f32).into();
|
||||
(*sdi_direction).y = (angle.sin() as f32).into();
|
||||
(*sdi_direction).x = angle.cos() as f32;
|
||||
(*sdi_direction).y = angle.sin() as f32;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,9 +70,7 @@ unsafe fn handle_oos_offset(module_accessor: &mut app::BattleObjectModuleAccesso
|
|||
SHIELD_DELAY = MENU.reaction_time.get_random().into_delay();
|
||||
|
||||
// Decrease offset once if needed
|
||||
if MULTI_HIT_OFFSET > 0 {
|
||||
MULTI_HIT_OFFSET -= 1;
|
||||
}
|
||||
MULTI_HIT_OFFSET = MULTI_HIT_OFFSET.saturating_sub(1);
|
||||
|
||||
// Mark that we were in shield stun, so we don't decrease again
|
||||
WAS_IN_SHIELDSTUN = true;
|
||||
|
|
|
@ -199,7 +199,7 @@ pub unsafe fn get_command_flag_cat(module_accessor: &mut app::BattleObjectModule
|
|||
return;
|
||||
}
|
||||
|
||||
let status = StatusModule::status_kind(module_accessor) as i32;
|
||||
let status = StatusModule::status_kind(module_accessor);
|
||||
let mut requested_status: i32 = 0;
|
||||
if [
|
||||
*FIGHTER_STATUS_KIND_DOWN_WAIT,
|
||||
|
|
|
@ -89,9 +89,9 @@ pub unsafe fn get_command_flag_throw_direction(
|
|||
return 0;
|
||||
}
|
||||
|
||||
if StatusModule::status_kind(module_accessor) as i32 != *FIGHTER_STATUS_KIND_CATCH_WAIT
|
||||
&& StatusModule::status_kind(module_accessor) as i32 != *FIGHTER_STATUS_KIND_CATCH_PULL
|
||||
&& StatusModule::status_kind(module_accessor) as i32 != *FIGHTER_STATUS_KIND_CATCH_ATTACK
|
||||
if StatusModule::status_kind(module_accessor) != *FIGHTER_STATUS_KIND_CATCH_WAIT
|
||||
&& StatusModule::status_kind(module_accessor) != *FIGHTER_STATUS_KIND_CATCH_PULL
|
||||
&& StatusModule::status_kind(module_accessor) != *FIGHTER_STATUS_KIND_CATCH_ATTACK
|
||||
{
|
||||
// No longer holding character, so re-roll the throw case and reset the delay counter for next time
|
||||
reset_throw_case();
|
||||
|
@ -132,7 +132,7 @@ pub unsafe fn get_command_flag_throw_direction(
|
|||
}
|
||||
|
||||
// (this conditional would need to be changed to speed up pummelling)
|
||||
if StatusModule::status_kind(module_accessor) as i32 == *FIGHTER_STATUS_KIND_CATCH_WAIT {
|
||||
if StatusModule::status_kind(module_accessor) == *FIGHTER_STATUS_KIND_CATCH_WAIT {
|
||||
let status = *FIGHTER_STATUS_KIND_CATCH_ATTACK; //.unwrap_or(0);
|
||||
StatusModule::change_status_request_from_script(module_accessor, status, true);
|
||||
}
|
||||
|
|
|
@ -55,8 +55,8 @@ pub struct AnimTransform {
|
|||
|
||||
impl AnimTransform {
|
||||
pub unsafe fn parse_anim_transform(&mut self, layout_name: Option<&str>) {
|
||||
let res_animation_block_data_start = (*self).res_animation_block as u64;
|
||||
let res_animation_block = &*(*self).res_animation_block;
|
||||
let res_animation_block_data_start = self.res_animation_block as u64;
|
||||
let res_animation_block = &*self.res_animation_block;
|
||||
let mut anim_cont_offsets = (res_animation_block_data_start
|
||||
+ res_animation_block.anim_cont_offsets_offset as u64)
|
||||
as *const u32;
|
||||
|
@ -70,8 +70,8 @@ impl AnimTransform {
|
|||
let anim_type = (*res_animation_cont).anim_content_type;
|
||||
|
||||
// AnimContentType 1 == MATERIAL
|
||||
if layout_name.is_some() && name.starts_with("set_dmg_num") && anim_type == 1 {
|
||||
let layout_name = layout_name.unwrap();
|
||||
if name.starts_with("set_dmg_num") && anim_type == 1 {
|
||||
if let Some(layout_name) = layout_name {
|
||||
let (hundreds, tens, ones, dec) = get_player_dmg_digits(match layout_name {
|
||||
"p1" => FighterId::Player,
|
||||
"p2" => FighterId::CPU,
|
||||
|
@ -91,6 +91,7 @@ impl AnimTransform {
|
|||
self.frame = dec as f32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anim_cont_offsets = anim_cont_offsets.add(1);
|
||||
}
|
||||
|
@ -224,6 +225,14 @@ impl Pane {
|
|||
self.global_alpha = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_name(&self) -> String {
|
||||
self.name
|
||||
.iter()
|
||||
.take_while(|b| **b != 0)
|
||||
.map(|b| *b as char)
|
||||
.collect::<String>()
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -410,12 +419,12 @@ pub enum MaterialColorType {
|
|||
#[repr(C)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum MaterialFlags {
|
||||
FlagsUserAllocated,
|
||||
FlagsTextureOnly,
|
||||
FlagsThresholdingAlphaInterpolation,
|
||||
FlagsBlackColorFloat,
|
||||
FlagsWhiteColorFloat,
|
||||
FlagsDynamicAllocatedColorData,
|
||||
UserAllocated,
|
||||
TextureOnly,
|
||||
ThresholdingAlphaInterpolation,
|
||||
BlackColorFloat,
|
||||
WhiteColorFloat,
|
||||
DynamicAllocatedColorData,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -458,9 +467,9 @@ impl Material {
|
|||
|
||||
pub fn set_color(&mut self, color_type: MaterialColorType, r: f32, g: f32, b: f32, a: f32) {
|
||||
let (is_float_flag, idx) = if color_type == MaterialColorType::BlackColor {
|
||||
(MaterialFlags::FlagsBlackColorFloat as u8, 0)
|
||||
(MaterialFlags::BlackColorFloat as u8, 0)
|
||||
} else {
|
||||
(MaterialFlags::FlagsWhiteColorFloat as u8, 1)
|
||||
(MaterialFlags::WhiteColorFloat as u8, 1)
|
||||
};
|
||||
if self.m_flag & (0x1 << is_float_flag) != 0 {
|
||||
self.set_color_float(idx, r, g, b, a);
|
||||
|
|
|
@ -118,13 +118,16 @@ impl ResPane {
|
|||
self.size_y = size.y;
|
||||
}
|
||||
|
||||
pub fn name_matches(&self, other: &str) -> bool {
|
||||
pub fn get_name(&self) -> String {
|
||||
self.name
|
||||
.iter()
|
||||
.take_while(|b| **b != 0)
|
||||
.map(|b| *b as char)
|
||||
.collect::<String>()
|
||||
== other
|
||||
}
|
||||
|
||||
pub fn name_matches(&self, other: &str) -> bool {
|
||||
self.get_name() == other
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,7 +149,7 @@ enum TextBoxFlag {
|
|||
KeepingFontScaleEnabled,
|
||||
PerCharacterTransformFixSpace,
|
||||
PerCharacterTransformSplitByCharWidthInsertSpaceEnabled,
|
||||
MaxTextBoxFlag,
|
||||
Max,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use crate::common::get_player_dmg_digits;
|
||||
use crate::common::MENU;
|
||||
use crate::consts::FighterId;
|
||||
use crate::training::ui::*;
|
||||
use crate::{common::menu::QUICK_MENU_ACTIVE, training::combo::FRAME_ADVANTAGE};
|
||||
use training_mod_consts::{OnOff, SaveDamage};
|
||||
use training_mod_consts::OnOff;
|
||||
use training_mod_tui::gauge::GaugeState;
|
||||
|
||||
pub static NUM_DISPLAY_PANES: usize = 1;
|
||||
|
@ -11,18 +10,60 @@ pub static NUM_MENU_TEXT_OPTIONS: usize = 27;
|
|||
pub static NUM_MENU_TEXT_SLIDERS: usize = 4;
|
||||
pub static NUM_MENU_TABS: usize = 3;
|
||||
|
||||
pub static mut HAS_SORTED_MENU_CHILDREN: bool = false;
|
||||
|
||||
// Sort all panes in under menu pane such that text and check options
|
||||
// are last
|
||||
pub unsafe fn all_menu_panes_sorted(root_pane: &Pane) -> Vec<&mut Pane> {
|
||||
let mut panes = (0..NUM_MENU_TEXT_OPTIONS)
|
||||
.flat_map(|idx| {
|
||||
let x = idx % 3;
|
||||
let y = idx / 3;
|
||||
[
|
||||
root_pane
|
||||
.find_pane_by_name_recursive(format!("trMod_menu_opt_{x}_{y}").as_str())
|
||||
.unwrap(),
|
||||
root_pane
|
||||
.find_pane_by_name_recursive(format!("trMod_menu_check_{x}_{y}").as_str())
|
||||
.unwrap(),
|
||||
root_pane
|
||||
.find_pane_by_name_recursive(format!("trMod_menu_bg_left_{x}_{y}").as_str())
|
||||
.unwrap(),
|
||||
root_pane
|
||||
.find_pane_by_name_recursive(format!("trMod_menu_bg_back_{x}_{y}").as_str())
|
||||
.unwrap(),
|
||||
]
|
||||
})
|
||||
.collect::<Vec<&mut Pane>>();
|
||||
|
||||
panes.append(
|
||||
&mut (0..NUM_MENU_TEXT_SLIDERS)
|
||||
.map(|idx| {
|
||||
root_pane
|
||||
.find_pane_by_name_recursive(format!("trMod_menu_slider_{idx}").as_str())
|
||||
.unwrap()
|
||||
})
|
||||
.collect::<Vec<&mut Pane>>(),
|
||||
);
|
||||
|
||||
panes.sort_by(|a, _| {
|
||||
if a.get_name().contains("opt") || a.get_name().contains("check") {
|
||||
std::cmp::Ordering::Greater
|
||||
} else {
|
||||
std::cmp::Ordering::Less
|
||||
}
|
||||
});
|
||||
|
||||
panes
|
||||
}
|
||||
|
||||
#[skyline::hook(offset = 0x4b620)]
|
||||
pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64) {
|
||||
let layout_name = skyline::from_c_str((*layout).layout_name);
|
||||
let root_pane = &*(*layout).root_pane;
|
||||
|
||||
// Update percentage display as soon as possible on death,
|
||||
// only if we have random save state damage active
|
||||
if crate::common::is_training_mode()
|
||||
&& (MENU.save_damage_cpu == SaveDamage::RANDOM
|
||||
|| MENU.save_damage_player == SaveDamage::RANDOM)
|
||||
&& layout_name == "info_melee"
|
||||
{
|
||||
// Update percentage display as soon as possible on death
|
||||
if crate::common::is_training_mode() && layout_name == "info_melee" {
|
||||
for player_name in &["p1", "p2"] {
|
||||
if let Some(parent) = root_pane.find_pane_by_name_recursive(player_name) {
|
||||
let _p1_layout_name =
|
||||
|
@ -111,13 +152,11 @@ pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64)
|
|||
if let Some(text) = root_pane.find_pane_by_name_recursive("trMod_disp_0_txt") {
|
||||
text.set_text_string(format!("{FRAME_ADVANTAGE}").as_str());
|
||||
let text = text.as_textbox();
|
||||
if FRAME_ADVANTAGE < 0 {
|
||||
text.set_color(200, 8, 8, 255);
|
||||
} else if FRAME_ADVANTAGE == 0 {
|
||||
text.set_color(0, 0, 0, 255);
|
||||
} else {
|
||||
text.set_color(31, 198, 0, 255);
|
||||
}
|
||||
match FRAME_ADVANTAGE {
|
||||
x if x < 0 => text.set_color(200, 8, 8, 255),
|
||||
x if x == 0 => text.set_color(0, 0, 0, 255),
|
||||
_ => text.set_color(31, 198, 0, 255),
|
||||
};
|
||||
}
|
||||
|
||||
// Update menu display
|
||||
|
@ -144,6 +183,15 @@ pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64)
|
|||
let menu_pane = root_pane.find_pane_by_name_recursive("trMod_menu").unwrap();
|
||||
menu_pane.set_visible(QUICK_MENU_ACTIVE);
|
||||
|
||||
if !HAS_SORTED_MENU_CHILDREN {
|
||||
let sorted_panes = all_menu_panes_sorted(root_pane);
|
||||
// Place in sorted order such that backings are behind, etc.
|
||||
sorted_panes.iter().for_each(|p| menu_pane.remove_child(p));
|
||||
sorted_panes.iter().for_each(|p| menu_pane.append_child(p));
|
||||
|
||||
HAS_SORTED_MENU_CHILDREN = true;
|
||||
}
|
||||
|
||||
// Make all invisible first
|
||||
(0..NUM_MENU_TEXT_OPTIONS).for_each(|idx| {
|
||||
let x = idx % 3;
|
||||
|
@ -154,10 +202,16 @@ pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64)
|
|||
root_pane
|
||||
.find_pane_by_name_recursive(format!("trMod_menu_check_{x}_{y}").as_str())
|
||||
.map(|text| text.set_visible(false));
|
||||
root_pane
|
||||
.find_pane_by_name_recursive(format!("trMod_menu_bg_left_{x}_{y}").as_str())
|
||||
.map(|text| text.set_visible(false));
|
||||
root_pane
|
||||
.find_pane_by_name_recursive(format!("trMod_menu_bg_back_{x}_{y}").as_str())
|
||||
.map(|text| text.set_visible(false));
|
||||
});
|
||||
(0..NUM_MENU_TEXT_SLIDERS).for_each(|idx| {
|
||||
root_pane
|
||||
.find_pane_by_name_recursive(&format!("trMod_menu_slider_{idx}").as_str())
|
||||
.find_pane_by_name_recursive(format!("trMod_menu_slider_{idx}").as_str())
|
||||
.map(|text| text.set_visible(false));
|
||||
});
|
||||
|
||||
|
@ -194,12 +248,22 @@ pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64)
|
|||
list_idx,
|
||||
root_pane
|
||||
.find_pane_by_name_recursive(
|
||||
&format!("trMod_menu_opt_{list_section}_{list_idx}").to_owned(),
|
||||
format!("trMod_menu_opt_{list_section}_{list_idx}").as_str(),
|
||||
)
|
||||
.unwrap(),
|
||||
root_pane
|
||||
.find_pane_by_name_recursive(
|
||||
format!("trMod_menu_bg_left_{list_section}_{list_idx}").as_str(),
|
||||
)
|
||||
.unwrap(),
|
||||
root_pane
|
||||
.find_pane_by_name_recursive(
|
||||
format!("trMod_menu_bg_back_{list_section}_{list_idx}").as_str(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
})
|
||||
.for_each(|(list_section, list_idx, text)| {
|
||||
.for_each(|(list_section, list_idx, text, bg_left, bg_back)| {
|
||||
let list = &tab.lists[list_section];
|
||||
let submenu = &list.items[list_idx];
|
||||
let is_selected = list.state.selected().filter(|s| *s == list_idx).is_some();
|
||||
|
@ -208,27 +272,28 @@ pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64)
|
|||
let text = text.as_textbox();
|
||||
if is_selected {
|
||||
text.set_color(0x27, 0x4E, 0x13, 255);
|
||||
if let Some(footer) = root_pane
|
||||
.find_pane_by_name_recursive(&format!("trMod_menu_footer_txt").as_str())
|
||||
if let Some(footer) =
|
||||
root_pane.find_pane_by_name_recursive("trMod_menu_footer_txt")
|
||||
{
|
||||
footer.set_text_string(submenu.help_text);
|
||||
}
|
||||
} else {
|
||||
text.set_color(0, 0, 0, 255);
|
||||
text.set_color(255, 255, 255, 255);
|
||||
}
|
||||
|
||||
bg_left.set_visible(true);
|
||||
bg_back.set_visible(true);
|
||||
});
|
||||
} else {
|
||||
if matches!(app.selected_sub_menu_slider.state, GaugeState::None) {
|
||||
} else if matches!(app.selected_sub_menu_slider.state, GaugeState::None) {
|
||||
let (_title, _help_text, mut sub_menu_str_lists) = app.sub_menu_strs_and_states();
|
||||
for list_section in 0..sub_menu_str_lists.len() {
|
||||
(0..sub_menu_str_lists.len()).for_each(|list_section| {
|
||||
let sub_menu_str = sub_menu_str_lists[list_section].0.clone();
|
||||
let sub_menu_state = &mut sub_menu_str_lists[list_section].1;
|
||||
sub_menu_str
|
||||
.iter()
|
||||
.enumerate()
|
||||
.for_each(|(idx, (checked, name))| {
|
||||
let is_selected =
|
||||
sub_menu_state.selected().filter(|s| *s == idx).is_some();
|
||||
let is_selected = sub_menu_state.selected().filter(|s| *s == idx).is_some();
|
||||
if let Some(text) = root_pane.find_pane_by_name_recursive(
|
||||
format!("trMod_menu_opt_{list_section}_{idx}").as_str(),
|
||||
) {
|
||||
|
@ -237,11 +302,23 @@ pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64)
|
|||
if is_selected {
|
||||
text.set_color(0x27, 0x4E, 0x13, 255);
|
||||
} else {
|
||||
text.set_color(0, 0, 0, 255);
|
||||
text.set_color(255, 255, 255, 255);
|
||||
}
|
||||
text.set_visible(true);
|
||||
}
|
||||
|
||||
if let Some(bg_left) = root_pane.find_pane_by_name_recursive(
|
||||
format!("trMod_menu_bg_left_{list_section}_{idx}").as_str(),
|
||||
) {
|
||||
bg_left.set_visible(true);
|
||||
}
|
||||
|
||||
if let Some(bg_back) = root_pane.find_pane_by_name_recursive(
|
||||
format!("trMod_menu_bg_back_{list_section}_{idx}").as_str(),
|
||||
) {
|
||||
bg_back.set_visible(true);
|
||||
}
|
||||
|
||||
if let Some(check) = root_pane.find_pane_by_name_recursive(
|
||||
format!("trMod_menu_check_{list_section}_{idx}").as_str(),
|
||||
) {
|
||||
|
@ -253,7 +330,7 @@ pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64)
|
|||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let (_title, _help_text, gauge_vals) = app.sub_menu_strs_for_slider();
|
||||
let abs_min = gauge_vals.abs_min;
|
||||
|
@ -295,11 +372,14 @@ pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64)
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
original!()(layout, draw_info, cmd_buffer);
|
||||
}
|
||||
|
||||
pub static mut MENU_PANE_PTR: u64 = 0;
|
||||
pub static mut HAS_CREATED_OPT_BG: bool = false;
|
||||
pub static mut HAS_CREATED_OPT_BG_BACK: bool = false;
|
||||
|
||||
#[skyline::hook(offset = 0x493a0)]
|
||||
pub unsafe fn layout_build_parts_impl(
|
||||
layout: *mut Layout,
|
||||
|
@ -331,6 +411,72 @@ pub unsafe fn layout_build_parts_impl(
|
|||
};
|
||||
}
|
||||
|
||||
let root_pane = &*(*layout).root_pane;
|
||||
let block = data as *mut ResPane;
|
||||
let menu_pos = ResVec3::new(-360.0, 440.0, 0.0);
|
||||
|
||||
if layout_name == "info_training_btn0_00_item" {
|
||||
if !HAS_CREATED_OPT_BG && (*block).name_matches("icn_bg_main") {
|
||||
(0..NUM_MENU_TEXT_OPTIONS).for_each(|txt_idx| {
|
||||
let x = txt_idx % 3;
|
||||
let y = txt_idx / 3;
|
||||
|
||||
let x_offset = x as f32 * 500.0;
|
||||
let y_offset = y as f32 * 85.0;
|
||||
|
||||
let block = block as *mut ResPictureWithTex<2>;
|
||||
let mut pic_menu_block = *block;
|
||||
pic_menu_block
|
||||
.picture
|
||||
.pane
|
||||
.set_name(format!("trMod_menu_bg_left_{x}_{y}").as_str());
|
||||
pic_menu_block.picture.pane.scale_x /= 1.5;
|
||||
pic_menu_block.picture.pane.set_pos(ResVec3::new(
|
||||
menu_pos.x - 400.0 - 195.0 + x_offset,
|
||||
menu_pos.y - 50.0 - y_offset,
|
||||
0.0,
|
||||
));
|
||||
let pic_menu_pane = build!(pic_menu_block, ResPictureWithTex<2>, kind, Picture);
|
||||
pic_menu_pane.detach();
|
||||
if MENU_PANE_PTR != 0 {
|
||||
(*(MENU_PANE_PTR as *mut Pane)).append_child(pic_menu_pane);
|
||||
HAS_CREATED_OPT_BG = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if !HAS_CREATED_OPT_BG_BACK && (*block).name_matches("btn_bg") {
|
||||
(0..NUM_MENU_TEXT_OPTIONS).for_each(|txt_idx| {
|
||||
let x = txt_idx % 3;
|
||||
let y = txt_idx / 3;
|
||||
|
||||
let x_offset = x as f32 * 500.0;
|
||||
let y_offset = y as f32 * 85.0;
|
||||
|
||||
let block = block as *mut ResWindowWithTexCoordsAndFrames<1, 4>;
|
||||
|
||||
let mut pic_menu_block = *block;
|
||||
pic_menu_block
|
||||
.window
|
||||
.pane
|
||||
.set_name(format!("trMod_menu_bg_back_{x}_{y}").as_str());
|
||||
pic_menu_block.window.pane.scale_x /= 2.0;
|
||||
pic_menu_block.window.pane.set_pos(ResVec3::new(
|
||||
menu_pos.x - 400.0 + x_offset,
|
||||
menu_pos.y - 50.0 - y_offset,
|
||||
0.0,
|
||||
));
|
||||
let pic_menu_pane =
|
||||
build!(pic_menu_block, ResWindowWithTexCoordsAndFrames<1,4>, kind, Window);
|
||||
pic_menu_pane.pane.detach();
|
||||
if MENU_PANE_PTR != 0 {
|
||||
(*(MENU_PANE_PTR as *mut Pane)).append_child(&pic_menu_pane.pane);
|
||||
HAS_CREATED_OPT_BG_BACK = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if layout_name != "info_training" {
|
||||
return original!()(
|
||||
layout,
|
||||
|
@ -344,24 +490,8 @@ pub unsafe fn layout_build_parts_impl(
|
|||
);
|
||||
}
|
||||
|
||||
let root_pane = &*(*layout).root_pane;
|
||||
let block = data as *mut ResPane;
|
||||
let menu_pos = ResVec3::new(-360.0, 440.0, 0.0);
|
||||
|
||||
// Menu creation
|
||||
if (*block).name_matches("pic_numbase_01") {
|
||||
let block = block as *mut ResPictureWithTex<1>;
|
||||
// For menu backing
|
||||
let mut pic_menu_block = (*block).clone();
|
||||
pic_menu_block.picture.pane.set_name("trMod_menu_base");
|
||||
pic_menu_block.picture.pane.set_pos(menu_pos);
|
||||
pic_menu_block
|
||||
.picture
|
||||
.pane
|
||||
.set_size(ResVec2::new(1400.0, 1600.0));
|
||||
let pic_menu_pane = build!(pic_menu_block, ResPictureWithTex<1>, kind, Picture);
|
||||
pic_menu_pane.detach();
|
||||
|
||||
// pic is loaded first, we can create our parent pane here.
|
||||
let menu_pane_kind = u32::from_le_bytes([b'p', b'a', b'n', b'1']);
|
||||
let mut menu_pane_block = ResPane::new("trMod_menu");
|
||||
|
@ -370,31 +500,9 @@ pub unsafe fn layout_build_parts_impl(
|
|||
let menu_pane = build!(menu_pane_block, ResPane, menu_pane_kind, Pane);
|
||||
menu_pane.detach();
|
||||
root_pane.append_child(menu_pane);
|
||||
menu_pane.append_child(pic_menu_pane);
|
||||
if MENU_PANE_PTR == 0 {
|
||||
MENU_PANE_PTR = menu_pane as *mut Pane as u64;
|
||||
}
|
||||
|
||||
// Menu header
|
||||
// TODO: Copy "Quit Training" window and text
|
||||
if (*block).name_matches("set_txt_num_01") {
|
||||
let menu_pane = root_pane.find_pane_by_name("trMod_menu", true).unwrap();
|
||||
|
||||
let block = data as *mut ResTextBox;
|
||||
|
||||
// Header
|
||||
let mut text_block = (*block).clone();
|
||||
text_block.pane.size_x = text_block.pane.size_x * 2.0;
|
||||
text_block.pane.set_name("trMod_menu_header");
|
||||
|
||||
text_block
|
||||
.pane
|
||||
.set_pos(ResVec3::new(menu_pos.x - 525.0, menu_pos.y + 75.0, 0.0));
|
||||
let text_pane = build!(text_block, ResTextBox, kind, TextBox);
|
||||
text_pane.pane.set_text_string("Modpack Menu");
|
||||
// Ensure Material Colors are not hardcoded so we can just use SetTextColor.
|
||||
text_pane.set_default_material_colors();
|
||||
text_pane.set_color(200, 8, 8, 255);
|
||||
text_pane.detach();
|
||||
menu_pane.append_child(text_pane);
|
||||
}
|
||||
|
||||
// Menu footer background
|
||||
|
@ -402,7 +510,7 @@ pub unsafe fn layout_build_parts_impl(
|
|||
let menu_pane = root_pane.find_pane_by_name("trMod_menu", true).unwrap();
|
||||
let block = block as *mut ResPictureWithTex<1>;
|
||||
// For menu backing
|
||||
let mut pic_menu_block = (*block).clone();
|
||||
let mut pic_menu_block = *block;
|
||||
pic_menu_block.picture.pane.set_name("trMod_menu_footer_bg");
|
||||
let pic_menu_pane = build!(pic_menu_block, ResPictureWithTex<1>, kind, Picture);
|
||||
pic_menu_pane.detach();
|
||||
|
@ -415,13 +523,11 @@ pub unsafe fn layout_build_parts_impl(
|
|||
let menu_pane = root_pane.find_pane_by_name("trMod_menu", true).unwrap();
|
||||
|
||||
let block = data as *mut ResTextBox;
|
||||
let mut text_block = (*block).clone();
|
||||
text_block
|
||||
.pane
|
||||
.set_name(format!("trMod_menu_footer_txt").as_str());
|
||||
let mut text_block = *block;
|
||||
text_block.pane.set_name("trMod_menu_footer_txt");
|
||||
|
||||
let text_pane = build!(text_block, ResTextBox, kind, TextBox);
|
||||
text_pane.pane.set_text_string(format!("Footer!").as_str());
|
||||
text_pane.pane.set_text_string("Footer!");
|
||||
// Ensure Material Colors are not hardcoded so we can just use SetTextColor.
|
||||
text_pane.set_default_material_colors();
|
||||
text_pane.set_color(255, 255, 255, 255);
|
||||
|
@ -434,7 +540,7 @@ pub unsafe fn layout_build_parts_impl(
|
|||
let menu_pane = root_pane.find_pane_by_name("trMod_menu", true).unwrap();
|
||||
|
||||
let block = data as *mut ResTextBox;
|
||||
let mut text_block = (*block).clone();
|
||||
let mut text_block = *block;
|
||||
text_block.enable_shadow();
|
||||
text_block.text_alignment(TextAlignment::Center);
|
||||
|
||||
|
@ -466,7 +572,7 @@ pub unsafe fn layout_build_parts_impl(
|
|||
text_pane.detach();
|
||||
menu_pane.append_child(text_pane);
|
||||
|
||||
let mut help_block = (*block).clone();
|
||||
let mut help_block = *block;
|
||||
// Font Idx 2 = nintendo64 which contains nice symbols
|
||||
help_block.font_idx = 2;
|
||||
|
||||
|
@ -482,7 +588,7 @@ pub unsafe fn layout_build_parts_impl(
|
|||
0.0,
|
||||
));
|
||||
let help_pane = build!(help_block, ResTextBox, kind, TextBox);
|
||||
help_pane.pane.set_text_string(format!("abcd").as_str());
|
||||
help_pane.pane.set_text_string("abcd");
|
||||
let it = help_pane.m_text_buf as *mut u16;
|
||||
match txt_idx {
|
||||
// Left Tab: ZL
|
||||
|
@ -520,7 +626,7 @@ pub unsafe fn layout_build_parts_impl(
|
|||
let menu_pane = root_pane.find_pane_by_name("trMod_menu", true).unwrap();
|
||||
|
||||
let block = data as *mut ResTextBox;
|
||||
let mut text_block = (*block).clone();
|
||||
let mut text_block = *block;
|
||||
text_block.enable_shadow();
|
||||
text_block.text_alignment(TextAlignment::Center);
|
||||
|
||||
|
@ -528,10 +634,10 @@ pub unsafe fn layout_build_parts_impl(
|
|||
.pane
|
||||
.set_name(format!("trMod_menu_opt_{x}_{y}").as_str());
|
||||
|
||||
let x_offset = x as f32 * 400.0;
|
||||
let y_offset = y as f32 * 75.0;
|
||||
let x_offset = x as f32 * 500.0;
|
||||
let y_offset = y as f32 * 85.0;
|
||||
text_block.pane.set_pos(ResVec3::new(
|
||||
menu_pos.x - 450.0 + x_offset,
|
||||
menu_pos.x - 480.0 + x_offset,
|
||||
menu_pos.y - 50.0 - y_offset,
|
||||
0.0,
|
||||
));
|
||||
|
@ -541,11 +647,11 @@ pub unsafe fn layout_build_parts_impl(
|
|||
.set_text_string(format!("Opt {txt_idx}!").as_str());
|
||||
// Ensure Material Colors are not hardcoded so we can just use SetTextColor.
|
||||
text_pane.set_default_material_colors();
|
||||
text_pane.set_color(0, 0, 0, 255);
|
||||
text_pane.set_color(255, 255, 255, 255);
|
||||
text_pane.detach();
|
||||
menu_pane.append_child(text_pane);
|
||||
|
||||
let mut check_block = (*block).clone();
|
||||
let mut check_block = *block;
|
||||
// Font Idx 2 = nintendo64 which contains nice symbols
|
||||
check_block.font_idx = 2;
|
||||
|
||||
|
@ -553,7 +659,7 @@ pub unsafe fn layout_build_parts_impl(
|
|||
.pane
|
||||
.set_name(format!("trMod_menu_check_{x}_{y}").as_str());
|
||||
check_block.pane.set_pos(ResVec3::new(
|
||||
menu_pos.x - 675.0 + x_offset,
|
||||
menu_pos.x - 375.0 + x_offset,
|
||||
menu_pos.y - 50.0 - y_offset,
|
||||
0.0,
|
||||
));
|
||||
|
@ -575,7 +681,7 @@ pub unsafe fn layout_build_parts_impl(
|
|||
let menu_pane = root_pane.find_pane_by_name("trMod_menu", true).unwrap();
|
||||
|
||||
let block = data as *mut ResTextBox;
|
||||
let mut text_block = (*block).clone();
|
||||
let mut text_block = *block;
|
||||
text_block.enable_shadow();
|
||||
text_block.text_alignment(TextAlignment::Center);
|
||||
|
||||
|
@ -611,7 +717,7 @@ pub unsafe fn layout_build_parts_impl(
|
|||
|
||||
if (*block).name_matches("pic_numbase_01") {
|
||||
let block = block as *mut ResPictureWithTex<1>;
|
||||
let mut pic_block = (*block).clone();
|
||||
let mut pic_block = *block;
|
||||
pic_block.picture.pane.set_name(pic_name.as_str());
|
||||
pic_block.picture.pane.set_pos(ResVec3::default());
|
||||
let pic_pane = build!(pic_block, ResPictureWithTex<1>, kind, Picture);
|
||||
|
@ -633,7 +739,7 @@ pub unsafe fn layout_build_parts_impl(
|
|||
.unwrap();
|
||||
|
||||
let block = data as *mut ResTextBox;
|
||||
let mut text_block = (*block).clone();
|
||||
let mut text_block = *block;
|
||||
text_block.pane.set_name(txt_name.as_str());
|
||||
text_block.pane.set_pos(ResVec3::new(-10.0, -25.0, 0.0));
|
||||
let text_pane = build!(text_block, ResTextBox, kind, TextBox);
|
||||
|
@ -652,7 +758,7 @@ pub unsafe fn layout_build_parts_impl(
|
|||
.unwrap();
|
||||
|
||||
let block = data as *mut ResTextBox;
|
||||
let mut header_block = (*block).clone();
|
||||
let mut header_block = *block;
|
||||
header_block.pane.set_name(header_name.as_str());
|
||||
header_block.pane.set_pos(ResVec3::new(0.0, 25.0, 0.0));
|
||||
let header_pane = build!(header_block, ResTextBox, kind, TextBox);
|
||||
|
|
Loading…
Reference in a new issue