mirror of
https://github.com/jugeeya/UltimateTrainingModpack.git
synced 2024-11-28 04:44:06 +00:00
Use ui2d-based Quick Menu; show dmg percentage on save state % reload with random damage (#450)
* 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 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
9a7b5fdf85
commit
52e3528292
10 changed files with 921 additions and 186 deletions
1
.github/workflows/rust.yml
vendored
1
.github/workflows/rust.yml
vendored
|
@ -113,7 +113,6 @@ jobs:
|
||||||
cp libparam_hook.nro ${{env.SMASH_PLUGIN_DIR}}/libparam_hook.nro
|
cp libparam_hook.nro ${{env.SMASH_PLUGIN_DIR}}/libparam_hook.nro
|
||||||
cp libnro_hook.nro ${{env.SMASH_PLUGIN_DIR}}/libnro_hook.nro
|
cp libnro_hook.nro ${{env.SMASH_PLUGIN_DIR}}/libnro_hook.nro
|
||||||
cp libnn_hid_hook.nro ${{env.SMASH_PLUGIN_DIR}}/libnn_hid_hook.nro
|
cp libnn_hid_hook.nro ${{env.SMASH_PLUGIN_DIR}}/libnn_hid_hook.nro
|
||||||
mv static/libtraining_modpack_menu.nro ${{env.SMASH_PLUGIN_DIR}}/libtraining_modpack_menu.nro
|
|
||||||
cp -r static/* ${{env.SMASH_WEB_DIR}}
|
cp -r static/* ${{env.SMASH_WEB_DIR}}
|
||||||
zip -r training_modpack_beta.zip atmosphere
|
zip -r training_modpack_beta.zip atmosphere
|
||||||
- name: Update Release
|
- name: Update Release
|
||||||
|
|
|
@ -262,8 +262,7 @@ SD Card Root
|
||||||
├── libnn_hid_hook.nro
|
├── libnn_hid_hook.nro
|
||||||
├── libnro_hook.nro
|
├── libnro_hook.nro
|
||||||
├── libparam_hook.nro
|
├── libparam_hook.nro
|
||||||
├── libtraining_modpack.nro
|
└── libtraining_modpack.nro
|
||||||
└── libtraining_modpack_menu.nro
|
|
||||||
```
|
```
|
||||||
|
|
||||||
To install a beta version of the modpack, follow the same procedure using the [latest beta release](https://github.com/jugeeya/UltimateTrainingModpack/tree/beta) on Github. Beta releases may have additional features and bugfixes, but are subject to change.
|
To install a beta version of the modpack, follow the same procedure using the [latest beta release](https://github.com/jugeeya/UltimateTrainingModpack/tree/beta) on Github. Beta releases may have additional features and bugfixes, but are subject to change.
|
||||||
|
|
|
@ -2,7 +2,6 @@ use crate::common::*;
|
||||||
use crate::events::{Event, EVENT_QUEUE};
|
use crate::events::{Event, EVENT_QUEUE};
|
||||||
use crate::training::frame_counter;
|
use crate::training::frame_counter;
|
||||||
|
|
||||||
use owo_colors::OwoColorize;
|
|
||||||
use ramhorns::Template;
|
use ramhorns::Template;
|
||||||
use skyline::info::get_program_id;
|
use skyline::info::get_program_id;
|
||||||
use skyline::nn::hid::NpadGcState;
|
use skyline::nn::hid::NpadGcState;
|
||||||
|
@ -11,7 +10,6 @@ use skyline_web::{Background, BootDisplay, WebSession, Webpage};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use training_mod_consts::{MenuJsonStruct, TrainingModpackMenu};
|
use training_mod_consts::{MenuJsonStruct, TrainingModpackMenu};
|
||||||
use training_mod_tui::Color;
|
|
||||||
|
|
||||||
static mut FRAME_COUNTER_INDEX: usize = 0;
|
static mut FRAME_COUNTER_INDEX: usize = 0;
|
||||||
pub static mut QUICK_MENU_FRAME_COUNTER_INDEX: usize = 0;
|
pub static mut QUICK_MENU_FRAME_COUNTER_INDEX: usize = 0;
|
||||||
|
@ -273,32 +271,18 @@ pub fn handle_get_npad_state(state: *mut NpadGcState, _controller_id: *const u32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
use lazy_static::lazy_static;
|
||||||
#[link_name = "render_text_to_screen"]
|
use parking_lot::Mutex;
|
||||||
pub fn render_text_to_screen_cstr(str: *const skyline::libc::c_char);
|
|
||||||
|
|
||||||
#[link_name = "set_should_display_text_to_screen"]
|
lazy_static! {
|
||||||
pub fn set_should_display_text_to_screen(toggle: bool);
|
pub static ref QUICK_MENU_APP: Mutex<training_mod_tui::App<'static>> =
|
||||||
}
|
Mutex::new(training_mod_tui::App::new(unsafe { get_menu() }));
|
||||||
|
|
||||||
macro_rules! c_str {
|
|
||||||
($l:tt) => {
|
|
||||||
[$l.as_bytes(), "\u{0}".as_bytes()].concat().as_ptr()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_text_to_screen(s: &str) {
|
|
||||||
unsafe {
|
|
||||||
render_text_to_screen_cstr(c_str!(s));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn quick_menu_loop() {
|
pub unsafe fn quick_menu_loop() {
|
||||||
loop {
|
loop {
|
||||||
std::thread::sleep(std::time::Duration::from_secs(10));
|
std::thread::sleep(std::time::Duration::from_secs(10));
|
||||||
let menu = get_menu();
|
let mut app = QUICK_MENU_APP.lock();
|
||||||
|
|
||||||
let mut app = training_mod_tui::App::new(menu);
|
|
||||||
|
|
||||||
let backend = training_mod_tui::TestBackend::new(75, 15);
|
let backend = training_mod_tui::TestBackend::new(75, 15);
|
||||||
let mut terminal = training_mod_tui::Terminal::new(backend).unwrap();
|
let mut terminal = training_mod_tui::Terminal::new(backend).unwrap();
|
||||||
|
@ -357,47 +341,15 @@ pub unsafe fn quick_menu_loop() {
|
||||||
|
|
||||||
has_slept_millis = 16;
|
has_slept_millis = 16;
|
||||||
if !QUICK_MENU_ACTIVE {
|
if !QUICK_MENU_ACTIVE {
|
||||||
app = training_mod_tui::App::new(get_menu());
|
|
||||||
set_should_display_text_to_screen(false);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if !received_input {
|
if !received_input {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut view = String::new();
|
terminal
|
||||||
|
|
||||||
let frame_res = terminal
|
|
||||||
.draw(|f| json_response = training_mod_tui::ui(f, &mut app))
|
.draw(|f| json_response = training_mod_tui::ui(f, &mut app))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
use std::fmt::Write;
|
|
||||||
for (i, cell) in frame_res.buffer.content().iter().enumerate() {
|
|
||||||
match cell.fg {
|
|
||||||
Color::Black => write!(&mut view, "{}", &cell.symbol.black()),
|
|
||||||
Color::Blue => write!(&mut view, "{}", &cell.symbol.blue()),
|
|
||||||
Color::LightBlue => write!(&mut view, "{}", &cell.symbol.bright_blue()),
|
|
||||||
Color::Cyan => write!(&mut view, "{}", &cell.symbol.cyan()),
|
|
||||||
Color::LightCyan => write!(&mut view, "{}", &cell.symbol.cyan()),
|
|
||||||
Color::Red => write!(&mut view, "{}", &cell.symbol.red()),
|
|
||||||
Color::LightRed => write!(&mut view, "{}", &cell.symbol.bright_red()),
|
|
||||||
Color::LightGreen => write!(&mut view, "{}", &cell.symbol.bright_green()),
|
|
||||||
Color::Green => write!(&mut view, "{}", &cell.symbol.green()),
|
|
||||||
Color::Yellow => write!(&mut view, "{}", &cell.symbol.yellow()),
|
|
||||||
Color::LightYellow => write!(&mut view, "{}", &cell.symbol.bright_yellow()),
|
|
||||||
Color::Magenta => write!(&mut view, "{}", &cell.symbol.magenta()),
|
|
||||||
Color::LightMagenta => {
|
|
||||||
write!(&mut view, "{}", &cell.symbol.bright_magenta())
|
|
||||||
}
|
|
||||||
_ => write!(&mut view, "{}", &cell.symbol),
|
|
||||||
}
|
|
||||||
.unwrap();
|
|
||||||
if i % frame_res.area.width as usize == frame_res.area.width as usize - 1 {
|
|
||||||
writeln!(&mut view).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeln!(&mut view).unwrap();
|
|
||||||
|
|
||||||
render_text_to_screen(view.as_str());
|
|
||||||
received_input = false;
|
received_input = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -229,6 +229,16 @@ pub unsafe fn entry_count() -> i32 {
|
||||||
FighterManager::entry_count(fighter_manager)
|
FighterManager::entry_count(fighter_manager)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub unsafe fn get_player_dmg_digits(p: FighterId) -> (u8, u8, u8, u8) {
|
||||||
|
let module_accessor = get_module_accessor(p);
|
||||||
|
let dmg = DamageModule::damage(module_accessor, 0);
|
||||||
|
let hundreds = dmg as u16 / 100;
|
||||||
|
let tens = (dmg as u16 - hundreds * 100) / 10;
|
||||||
|
let ones = (dmg as u16) - (hundreds * 100) - (tens * 10);
|
||||||
|
let dec = ((dmg * 10.0) as u16) - (hundreds * 1000) - (tens * 100) - ones * 10;
|
||||||
|
(hundreds as u8, tens as u8, ones as u8, dec as u8)
|
||||||
|
}
|
||||||
|
|
||||||
pub unsafe fn get_fighter_distance() -> f32 {
|
pub unsafe fn get_fighter_distance() -> f32 {
|
||||||
let player_module_accessor = get_module_accessor(FighterId::Player);
|
let player_module_accessor = get_module_accessor(FighterId::Player);
|
||||||
let cpu_module_accessor = get_module_accessor(FighterId::CPU);
|
let cpu_module_accessor = get_module_accessor(FighterId::CPU);
|
||||||
|
|
Binary file not shown.
|
@ -1,10 +1,15 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use bitfield_struct::bitfield;
|
use bitfield_struct::bitfield;
|
||||||
|
|
||||||
mod resources;
|
mod resources;
|
||||||
pub use resources::*;
|
pub use resources::*;
|
||||||
|
|
||||||
|
use crate::common::get_player_dmg_digits;
|
||||||
|
use crate::consts::FighterId;
|
||||||
|
|
||||||
macro_rules! c_str {
|
macro_rules! c_str {
|
||||||
($l:tt) => {
|
($l:tt) => {
|
||||||
[$l.as_bytes(), "\u{0}".as_bytes()].concat().as_ptr()
|
[$l.as_bytes(), "\u{0}".as_bytes()].concat().as_ptr()
|
||||||
|
@ -49,13 +54,13 @@ pub struct AnimTransform {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnimTransform {
|
impl AnimTransform {
|
||||||
pub unsafe fn parse_anim_transform(&mut self) {
|
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_data_start = (*self).res_animation_block as u64;
|
||||||
let res_animation_block = &*(*self).res_animation_block;
|
let res_animation_block = &*(*self).res_animation_block;
|
||||||
let mut anim_cont_offsets = (res_animation_block_data_start
|
let mut anim_cont_offsets = (res_animation_block_data_start
|
||||||
+ res_animation_block.anim_cont_offsets_offset as u64)
|
+ res_animation_block.anim_cont_offsets_offset as u64)
|
||||||
as *const u32;
|
as *const u32;
|
||||||
for anim_cont_idx in 0..res_animation_block.anim_cont_count {
|
for _anim_cont_idx in 0..res_animation_block.anim_cont_count {
|
||||||
let anim_cont_offset = *anim_cont_offsets;
|
let anim_cont_offset = *anim_cont_offsets;
|
||||||
let res_animation_cont = (res_animation_block_data_start + anim_cont_offset as u64)
|
let res_animation_cont = (res_animation_block_data_start + anim_cont_offset as u64)
|
||||||
as *const ResAnimationContent;
|
as *const ResAnimationContent;
|
||||||
|
@ -63,19 +68,28 @@ impl AnimTransform {
|
||||||
let name = skyline::try_from_c_str((*res_animation_cont).name.as_ptr())
|
let name = skyline::try_from_c_str((*res_animation_cont).name.as_ptr())
|
||||||
.unwrap_or("UNKNOWN".to_string());
|
.unwrap_or("UNKNOWN".to_string());
|
||||||
let anim_type = (*res_animation_cont).anim_content_type;
|
let anim_type = (*res_animation_cont).anim_content_type;
|
||||||
let frame = (*self).frame;
|
|
||||||
println!(
|
|
||||||
"animTransform/resAnimationContent_{anim_cont_idx}: {name} of type {anim_type} on frame {frame}",
|
|
||||||
);
|
|
||||||
// AnimContentType 1 == MATERIAL
|
// AnimContentType 1 == MATERIAL
|
||||||
if (name == "dig_3_anim" || name == "set_dmg_num_3") && anim_type == 1 {
|
if layout_name.is_some() && name.starts_with("set_dmg_num") && anim_type == 1 {
|
||||||
(*self).frame = 4.0;
|
let layout_name = layout_name.unwrap();
|
||||||
|
let (hundreds, tens, ones, dec) = get_player_dmg_digits(match layout_name {
|
||||||
|
"p1" => FighterId::Player,
|
||||||
|
"p2" => FighterId::CPU,
|
||||||
|
_ => panic!("Unknown layout name: {}", layout_name),
|
||||||
|
});
|
||||||
|
|
||||||
|
if name == "set_dmg_num_3" {
|
||||||
|
self.frame = hundreds as f32;
|
||||||
}
|
}
|
||||||
if (name == "dig_2_anim" || name == "set_dmg_num_2") && anim_type == 1 {
|
if name == "set_dmg_num_2" {
|
||||||
(*self).frame = 2.0;
|
self.frame = tens as f32;
|
||||||
|
}
|
||||||
|
if name == "set_dmg_num_1" {
|
||||||
|
self.frame = ones as f32;
|
||||||
|
}
|
||||||
|
if name == "set_dmg_num_dec" {
|
||||||
|
self.frame = dec as f32;
|
||||||
}
|
}
|
||||||
if (name == "dig_1_anim" || name == "set_dmg_num_1") && anim_type == 1 {
|
|
||||||
(*self).frame = 8.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
anim_cont_offsets = anim_cont_offsets.add(1);
|
anim_cont_offsets = anim_cont_offsets.add(1);
|
||||||
|
@ -91,14 +105,17 @@ pub struct AnimTransformNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnimTransformNode {
|
impl AnimTransformNode {
|
||||||
pub unsafe fn iterate_anim_list(&mut self) {
|
pub unsafe fn iterate_anim_list(&mut self, layout_name: Option<&str>) {
|
||||||
let mut curr = self as *mut AnimTransformNode;
|
let mut curr = self as *mut AnimTransformNode;
|
||||||
let mut _anim_idx = 0;
|
let mut _anim_idx = 0;
|
||||||
while !curr.is_null() {
|
while !curr.is_null() {
|
||||||
// Only if valid
|
// Only if valid
|
||||||
if curr != (*curr).next {
|
if curr != (*curr).next {
|
||||||
let anim_transform = (curr as *mut u64).add(2) as *mut AnimTransform;
|
let anim_transform = (curr as *mut u64).add(2) as *mut AnimTransform;
|
||||||
anim_transform.as_mut().unwrap().parse_anim_transform();
|
anim_transform
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.parse_anim_transform(layout_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
curr = (*curr).next;
|
curr = (*curr).next;
|
||||||
|
@ -146,6 +163,20 @@ pub struct Pane {
|
||||||
user_data: [skyline::libc::c_char; 9],
|
user_data: [skyline::libc::c_char; 9],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum PaneFlag {
|
||||||
|
Visible,
|
||||||
|
InfluencedAlpha,
|
||||||
|
LocationAdjust,
|
||||||
|
UserAllocated,
|
||||||
|
IsGlobalMatrixDirty,
|
||||||
|
UserMatrix,
|
||||||
|
UserGlobalMatrix,
|
||||||
|
IsConstantBufferReady,
|
||||||
|
Max,
|
||||||
|
}
|
||||||
|
|
||||||
impl Pane {
|
impl Pane {
|
||||||
pub unsafe fn find_pane_by_name_recursive(&self, s: &str) -> Option<&mut Pane> {
|
pub unsafe fn find_pane_by_name_recursive(&self, s: &str) -> Option<&mut Pane> {
|
||||||
find_pane_by_name_recursive(self, c_str!(s)).as_mut()
|
find_pane_by_name_recursive(self, c_str!(s)).as_mut()
|
||||||
|
@ -167,9 +198,32 @@ impl Pane {
|
||||||
pane_append_child(self, child as *const Pane);
|
pane_append_child(self, child as *const Pane);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Detach from current parent pane
|
||||||
|
pub unsafe fn detach(&self) {
|
||||||
|
pane_remove_child(self.parent, self as *const Pane);
|
||||||
|
}
|
||||||
|
|
||||||
pub unsafe fn as_parts(&mut self) -> *mut Parts {
|
pub unsafe fn as_parts(&mut self) -> *mut Parts {
|
||||||
self as *mut Pane as *mut Parts
|
self as *mut Pane as *mut Parts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub unsafe fn as_picture(&mut self) -> &mut Picture {
|
||||||
|
&mut *(self as *mut Pane as *mut Picture)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn as_textbox(&mut self) -> &mut TextBox {
|
||||||
|
&mut *(self as *mut Pane as *mut TextBox)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn set_visible(&mut self, visible: bool) {
|
||||||
|
if visible {
|
||||||
|
self.alpha = 255;
|
||||||
|
self.global_alpha = 255;
|
||||||
|
} else {
|
||||||
|
self.alpha = 0;
|
||||||
|
self.global_alpha = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -181,15 +235,43 @@ pub struct Parts {
|
||||||
pub layout: *mut Layout,
|
pub layout: *mut Layout,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Deref for Parts {
|
||||||
|
type Target = Pane;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.pane
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Parts {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.pane
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct Picture {
|
pub struct Picture {
|
||||||
pub pane: Pane,
|
pub pane: Pane,
|
||||||
material: *mut u8,
|
pub material: *mut Material,
|
||||||
vertex_colors: [[u8; 4]; 4],
|
pub vertex_colors: [[u8; 4]; 4],
|
||||||
shared_memory: *mut u8,
|
shared_memory: *mut u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Deref for Picture {
|
||||||
|
type Target = Pane;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.pane
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Picture {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.pane
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[bitfield(u16)]
|
#[bitfield(u16)]
|
||||||
pub struct TextBoxBits {
|
pub struct TextBoxBits {
|
||||||
#[bits(2)]
|
#[bits(2)]
|
||||||
|
@ -216,7 +298,7 @@ pub struct TextBoxBits {
|
||||||
pub struct TextBox {
|
pub struct TextBox {
|
||||||
pub pane: Pane,
|
pub pane: Pane,
|
||||||
// Actually a union
|
// Actually a union
|
||||||
m_text_buf: *const skyline::libc::c_char,
|
pub m_text_buf: *mut skyline::libc::c_char,
|
||||||
m_p_text_id: *const skyline::libc::c_char,
|
m_p_text_id: *const skyline::libc::c_char,
|
||||||
m_text_colors: [[u8; 4]; 2],
|
m_text_colors: [[u8; 4]; 2],
|
||||||
m_p_font: *const skyline::libc::c_void,
|
m_p_font: *const skyline::libc::c_void,
|
||||||
|
@ -229,12 +311,12 @@ pub struct TextBox {
|
||||||
m_p_tag_processor: *const skyline::libc::c_char,
|
m_p_tag_processor: *const skyline::libc::c_char,
|
||||||
|
|
||||||
m_text_buf_len: u16,
|
m_text_buf_len: u16,
|
||||||
m_text_len: u16,
|
pub m_text_len: u16,
|
||||||
|
|
||||||
m_bits: TextBoxBits,
|
m_bits: TextBoxBits,
|
||||||
m_text_position: u8,
|
m_text_position: u8,
|
||||||
|
|
||||||
m_is_utf8: bool,
|
pub m_is_utf8: bool,
|
||||||
|
|
||||||
m_italic_ratio: f32,
|
m_italic_ratio: f32,
|
||||||
|
|
||||||
|
@ -271,6 +353,33 @@ impl TextBox {
|
||||||
self.m_bits.set_is_ptdirty(1);
|
self.m_bits.set_is_ptdirty(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub unsafe fn set_material_white_color(&mut self, r: f32, g: f32, b: f32, a: f32) {
|
||||||
|
(*self.m_p_material).set_white_color(r, g, b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn set_material_black_color(&mut self, r: f32, g: f32, b: f32, a: f32) {
|
||||||
|
(*self.m_p_material).set_black_color(r, g, b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn set_default_material_colors(&mut self) {
|
||||||
|
self.set_material_white_color(255.0, 255.0, 255.0, 255.0);
|
||||||
|
self.set_material_black_color(0.0, 0.0, 0.0, 255.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for TextBox {
|
||||||
|
type Target = Pane;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.pane
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for TextBox {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.pane
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -313,14 +422,14 @@ pub enum MaterialFlags {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Material {
|
pub struct Material {
|
||||||
vtable: u64,
|
vtable: u64,
|
||||||
m_colors: MaterialColor,
|
pub m_colors: MaterialColor,
|
||||||
// Actually a struct
|
// Actually a struct
|
||||||
m_mem_cap: u32,
|
m_mem_cap: u32,
|
||||||
// Actually a struct
|
// Actually a struct
|
||||||
m_mem_count: u32,
|
m_mem_count: u32,
|
||||||
m_p_mem: *mut skyline::libc::c_void,
|
m_p_mem: *mut skyline::libc::c_void,
|
||||||
m_p_shader_info: *const skyline::libc::c_void,
|
m_p_shader_info: *const skyline::libc::c_void,
|
||||||
m_p_name: *const skyline::libc::c_char,
|
pub m_p_name: *const skyline::libc::c_char,
|
||||||
m_vertex_shader_constant_buffer_offset: u32,
|
m_vertex_shader_constant_buffer_offset: u32,
|
||||||
m_pixel_shader_constant_buffer_offset: u32,
|
m_pixel_shader_constant_buffer_offset: u32,
|
||||||
m_p_user_shader_constant_buffer_information: *const skyline::libc::c_void,
|
m_p_user_shader_constant_buffer_information: *const skyline::libc::c_void,
|
||||||
|
@ -370,13 +479,10 @@ impl Material {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct RawLayout {
|
pub struct Window {
|
||||||
pub anim_trans_list: AnimTransformNode,
|
pub pane: Pane,
|
||||||
pub root_pane: *const Pane,
|
// TODO
|
||||||
group_container: u64,
|
|
||||||
layout_size: f64,
|
|
||||||
pub layout_name: *const skyline::libc::c_char,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
@ -398,7 +504,11 @@ pub struct GroupContainer {}
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Layout {
|
pub struct Layout {
|
||||||
vtable: u64,
|
vtable: u64,
|
||||||
pub raw_layout: RawLayout,
|
pub anim_trans_list: AnimTransformNode,
|
||||||
|
pub root_pane: *const Pane,
|
||||||
|
group_container: u64,
|
||||||
|
layout_size: f64,
|
||||||
|
pub layout_name: *const skyline::libc::c_char,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[skyline::from_offset(0x59970)]
|
#[skyline::from_offset(0x59970)]
|
||||||
|
|
|
@ -6,12 +6,22 @@ pub struct ResVec2 {
|
||||||
y: f32,
|
y: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ResVec2 {
|
||||||
|
pub fn default() -> ResVec2 {
|
||||||
|
ResVec2 { x: 0.0, y: 0.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(x: f32, y: f32) -> ResVec2 {
|
||||||
|
ResVec2 { x, y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct ResVec3 {
|
pub struct ResVec3 {
|
||||||
x: f32,
|
pub x: f32,
|
||||||
y: f32,
|
pub y: f32,
|
||||||
z: f32,
|
pub z: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResVec3 {
|
impl ResVec3 {
|
||||||
|
@ -103,6 +113,11 @@ impl ResPane {
|
||||||
self.pos = pos;
|
self.pos = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_size(&mut self, size: ResVec2) {
|
||||||
|
self.size_x = size.x;
|
||||||
|
self.size_y = size.y;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn name_matches(&self, other: &str) -> bool {
|
pub fn name_matches(&self, other: &str) -> bool {
|
||||||
self.name
|
self.name
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -113,6 +128,37 @@ impl ResPane {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum TextBoxFlag {
|
||||||
|
ShadowEnabled,
|
||||||
|
ForceAssignTextLength,
|
||||||
|
InvisibleBorderEnabled,
|
||||||
|
DoubleDrawnBorderEnabled,
|
||||||
|
PerCharacterTransformEnabled,
|
||||||
|
CenterCeilingEnabled,
|
||||||
|
LineWidthOffsetEnabled,
|
||||||
|
ExtendedTagEnabled,
|
||||||
|
PerCharacterTransformSplitByCharWidth,
|
||||||
|
PerCharacterTransformAutoShadowAlpha,
|
||||||
|
DrawFromRightToLeft,
|
||||||
|
PerCharacterTransformOriginToCenter,
|
||||||
|
KeepingFontScaleEnabled,
|
||||||
|
PerCharacterTransformFixSpace,
|
||||||
|
PerCharacterTransformSplitByCharWidthInsertSpaceEnabled,
|
||||||
|
MaxTextBoxFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum TextAlignment {
|
||||||
|
Synchronous,
|
||||||
|
Left,
|
||||||
|
Center,
|
||||||
|
Right,
|
||||||
|
MaxTextAlignment,
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct ResTextBox {
|
pub struct ResTextBox {
|
||||||
|
@ -120,7 +166,7 @@ pub struct ResTextBox {
|
||||||
text_buf_bytes: u16,
|
text_buf_bytes: u16,
|
||||||
text_str_bytes: u16,
|
text_str_bytes: u16,
|
||||||
material_idx: u16,
|
material_idx: u16,
|
||||||
font_idx: u16,
|
pub font_idx: u16,
|
||||||
text_position: u8,
|
text_position: u8,
|
||||||
text_alignment: u8,
|
text_alignment: u8,
|
||||||
text_box_flag: u16,
|
text_box_flag: u16,
|
||||||
|
@ -148,6 +194,16 @@ pub struct ResTextBox {
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ResTextBox {
|
||||||
|
pub fn enable_shadow(&mut self) {
|
||||||
|
self.text_box_flag |= 0x1 << TextBoxFlag::ShadowEnabled as u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn text_alignment(&mut self, align: TextAlignment) {
|
||||||
|
self.text_alignment = align as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct ResPicture {
|
pub struct ResPicture {
|
||||||
|
@ -168,3 +224,109 @@ pub struct ResPictureWithTex<const TEX_COORD_COUNT: usize> {
|
||||||
pub picture: ResPicture,
|
pub picture: ResPicture,
|
||||||
tex_coords: [[ResVec2; TEX_COORD_COUNT]; 4],
|
tex_coords: [[ResVec2; TEX_COORD_COUNT]; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct ResParts {
|
||||||
|
pub pane: ResPane,
|
||||||
|
pub property_count: u32,
|
||||||
|
magnify: ResVec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
struct ResPartsProperty {
|
||||||
|
name: [skyline::libc::c_char; 24],
|
||||||
|
usage_flag: u8,
|
||||||
|
basic_usage_flag: u8,
|
||||||
|
material_usage_flag: u8,
|
||||||
|
system_ext_user_data_override_flag: u8,
|
||||||
|
property_offset: u32,
|
||||||
|
ext_user_data_offset: u32,
|
||||||
|
pane_basic_info_offset: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct ResPartsWithProperty<const PROPERTY_COUNT: usize> {
|
||||||
|
pub parts: ResParts,
|
||||||
|
property_table: [ResPartsProperty; PROPERTY_COUNT],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
struct ResWindowInflation {
|
||||||
|
left: i16,
|
||||||
|
right: i16,
|
||||||
|
top: i16,
|
||||||
|
bottom: i16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct ResWindowFrameSize {
|
||||||
|
left: u16,
|
||||||
|
right: u16,
|
||||||
|
top: u16,
|
||||||
|
bottom: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct ResWindowContent {
|
||||||
|
vtx_cols: [ResColor; 4],
|
||||||
|
material_idx: u16,
|
||||||
|
tex_coord_count: u8,
|
||||||
|
padding: [u8; 1],
|
||||||
|
/* Additional Info
|
||||||
|
nn::util::Float2 texCoords[texCoordCount][VERTEX_MAX];
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct ResWindowContentWithTexCoords<const TEX_COORD_COUNT: usize> {
|
||||||
|
pub window_content: ResWindowContent,
|
||||||
|
// This has to be wrong.
|
||||||
|
// Should be [[ResVec2; TEX_COORD_COUNT]; 4]?
|
||||||
|
tex_coords: [[ResVec3; TEX_COORD_COUNT]; 1],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct ResWindowFrame {
|
||||||
|
material_idx: u16,
|
||||||
|
texture_flip: u8,
|
||||||
|
padding: [u8; 1],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct ResWindow {
|
||||||
|
pub pane: ResPane,
|
||||||
|
inflation: ResWindowInflation,
|
||||||
|
frame_size: ResWindowFrameSize,
|
||||||
|
frame_count: u8,
|
||||||
|
window_flags: u8,
|
||||||
|
padding: [u8; 2],
|
||||||
|
content_offset: u32,
|
||||||
|
frame_offset_table_offset: u32,
|
||||||
|
content: ResWindowContent,
|
||||||
|
/* Additional Info
|
||||||
|
|
||||||
|
ResWindowContent content;
|
||||||
|
|
||||||
|
detail::uint32_t frameOffsetTable[frameCount];
|
||||||
|
ResWindowFrame frames;
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct ResWindowWithTexCoordsAndFrames<const TEX_COORD_COUNT: usize, const FRAME_COUNT: usize> {
|
||||||
|
pub window: ResWindow,
|
||||||
|
content: ResWindowContentWithTexCoords<TEX_COORD_COUNT>,
|
||||||
|
frame_offset_table: [u32; FRAME_COUNT],
|
||||||
|
frames: [ResWindowFrame; FRAME_COUNT],
|
||||||
|
}
|
||||||
|
|
|
@ -1,38 +1,298 @@
|
||||||
use crate::training::combo::FRAME_ADVANTAGE;
|
use crate::common::get_player_dmg_digits;
|
||||||
|
use crate::common::MENU;
|
||||||
|
use crate::consts::FighterId;
|
||||||
use crate::training::ui::*;
|
use crate::training::ui::*;
|
||||||
use training_mod_consts::OnOff;
|
use crate::{common::menu::QUICK_MENU_ACTIVE, training::combo::FRAME_ADVANTAGE};
|
||||||
|
use training_mod_consts::{OnOff, SaveDamage};
|
||||||
|
use training_mod_tui::gauge::GaugeState;
|
||||||
|
|
||||||
|
pub static NUM_DISPLAY_PANES: usize = 1;
|
||||||
|
pub static NUM_MENU_TEXT_OPTIONS: usize = 27;
|
||||||
|
pub static NUM_MENU_TEXT_SLIDERS: usize = 4;
|
||||||
|
pub static NUM_MENU_TABS: usize = 3;
|
||||||
|
|
||||||
#[skyline::hook(offset = 0x4b620)]
|
#[skyline::hook(offset = 0x4b620)]
|
||||||
pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64) {
|
pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64) {
|
||||||
let layout_name = skyline::from_c_str((*layout).raw_layout.layout_name);
|
let layout_name = skyline::from_c_str((*layout).layout_name);
|
||||||
let layout_root_pane = &*(*layout).raw_layout.root_pane;
|
let root_pane = &*(*layout).root_pane;
|
||||||
let _anim_list = &mut (*layout).raw_layout.anim_trans_list;
|
|
||||||
// anim_list.iterate_anim_list();
|
|
||||||
|
|
||||||
if layout_name == "info_training" {
|
// Update percentage display as soon as possible on death,
|
||||||
if let Some(parent) = layout_root_pane.find_pane_by_name_recursive("trMod_disp_0") {
|
// only if we have random save state damage active
|
||||||
if crate::common::MENU.frame_advantage == OnOff::On {
|
if crate::common::is_training_mode()
|
||||||
parent.alpha = 255;
|
&& (MENU.save_damage_cpu == SaveDamage::RANDOM
|
||||||
parent.global_alpha = 255;
|
|| MENU.save_damage_player == SaveDamage::RANDOM)
|
||||||
|
&& 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 =
|
||||||
|
skyline::from_c_str((*(*parent.as_parts()).layout).layout_name);
|
||||||
|
let anim_list = &mut (*(*parent.as_parts()).layout).anim_trans_list;
|
||||||
|
|
||||||
|
let mut has_altered_anim_list = false;
|
||||||
|
let (hundreds, tens, _, _) = get_player_dmg_digits(match *player_name {
|
||||||
|
"p1" => FighterId::Player,
|
||||||
|
"p2" => FighterId::CPU,
|
||||||
|
_ => panic!("Unknown player name: {}", player_name),
|
||||||
|
});
|
||||||
|
|
||||||
|
for dmg_num_s in &[
|
||||||
|
"set_dmg_num_3",
|
||||||
|
"dig_3",
|
||||||
|
"dig_3_anim",
|
||||||
|
"set_dmg_num_2",
|
||||||
|
"dig_2",
|
||||||
|
"dig_2_anim",
|
||||||
|
"set_dmg_num_1",
|
||||||
|
"dig_1",
|
||||||
|
"dig_1_anim",
|
||||||
|
"set_dmg_num_p",
|
||||||
|
"dig_dec",
|
||||||
|
"dig_dec_anim_00",
|
||||||
|
"set_dmg_num_dec",
|
||||||
|
"dig_dec_anim_01",
|
||||||
|
"dig_0_anim",
|
||||||
|
"set_dmg_p",
|
||||||
|
] {
|
||||||
|
if let Some(dmg_num) = parent.find_pane_by_name_recursive(dmg_num_s) {
|
||||||
|
if (dmg_num_s.contains('3') && hundreds == 0)
|
||||||
|
|| (dmg_num_s.contains('2') && hundreds == 0 && tens == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if *dmg_num_s == "set_dmg_p" {
|
||||||
|
dmg_num.pos_y = 0.0;
|
||||||
|
} else if *dmg_num_s == "set_dmg_num_p" {
|
||||||
|
dmg_num.pos_y = -4.0;
|
||||||
|
} else if *dmg_num_s == "dig_dec" {
|
||||||
|
dmg_num.pos_y = -16.0;
|
||||||
} else {
|
} else {
|
||||||
parent.alpha = 0;
|
dmg_num.pos_y = 0.0;
|
||||||
parent.global_alpha = 0;
|
}
|
||||||
|
|
||||||
|
if dmg_num.alpha != 255 || dmg_num.global_alpha != 255 {
|
||||||
|
dmg_num.set_visible(true);
|
||||||
|
if !has_altered_anim_list {
|
||||||
|
anim_list.iterate_anim_list(Some(player_name));
|
||||||
|
has_altered_anim_list = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(header) = layout_root_pane.find_pane_by_name_recursive("trMod_disp_0_header") {
|
for death_explosion_s in &[
|
||||||
|
"set_fxui_dead1",
|
||||||
|
"set_fxui_dead2",
|
||||||
|
"set_fxui_dead3",
|
||||||
|
"set_fxui_fire",
|
||||||
|
] {
|
||||||
|
if let Some(death_explosion) =
|
||||||
|
parent.find_pane_by_name_recursive(death_explosion_s)
|
||||||
|
{
|
||||||
|
death_explosion.set_visible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update training mod displays
|
||||||
|
if layout_name == "info_training" {
|
||||||
|
// Update frame advantage
|
||||||
|
if let Some(parent) = root_pane.find_pane_by_name_recursive("trMod_disp_0") {
|
||||||
|
parent.set_visible(crate::common::MENU.frame_advantage == OnOff::On);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(header) = root_pane.find_pane_by_name_recursive("trMod_disp_0_header") {
|
||||||
header.set_text_string("Frame Advantage");
|
header.set_text_string("Frame Advantage");
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(text) = layout_root_pane.find_pane_by_name_recursive("trMod_disp_0_txt") {
|
if let Some(text) = root_pane.find_pane_by_name_recursive("trMod_disp_0_txt") {
|
||||||
text.set_text_string(format!("{FRAME_ADVANTAGE}").as_str());
|
text.set_text_string(format!("{FRAME_ADVANTAGE}").as_str());
|
||||||
let text = text as *mut Pane as *mut TextBox;
|
let text = text.as_textbox();
|
||||||
if FRAME_ADVANTAGE < 0 {
|
if FRAME_ADVANTAGE < 0 {
|
||||||
(*text).set_color(200, 8, 8, 255);
|
text.set_color(200, 8, 8, 255);
|
||||||
} else if FRAME_ADVANTAGE == 0 {
|
} else if FRAME_ADVANTAGE == 0 {
|
||||||
(*text).set_color(0, 0, 0, 255);
|
text.set_color(0, 0, 0, 255);
|
||||||
} else {
|
} else {
|
||||||
(*text).set_color(31, 198, 0, 255);
|
text.set_color(31, 198, 0, 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update menu display
|
||||||
|
// Grabbing lock as read-only, essentially
|
||||||
|
let app = &*crate::common::menu::QUICK_MENU_APP.data_ptr();
|
||||||
|
|
||||||
|
if let Some(quit_button) = root_pane.find_pane_by_name_recursive("btn_finish") {
|
||||||
|
// Normally at (-804, 640)
|
||||||
|
// Comes down to (-804, 514)
|
||||||
|
if QUICK_MENU_ACTIVE {
|
||||||
|
quit_button.pos_y = 514.0;
|
||||||
|
}
|
||||||
|
if let Some(quit_txt) = quit_button.find_pane_by_name_recursive("set_txt_00") {
|
||||||
|
quit_txt.set_text_string(if QUICK_MENU_ACTIVE {
|
||||||
|
"Modpack Menu"
|
||||||
|
} else {
|
||||||
|
// Awkward. We should get the o.g. translation for non-english games
|
||||||
|
// Or create our own textbox here so we don't step on their toes.
|
||||||
|
"Quit Training"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let menu_pane = root_pane.find_pane_by_name_recursive("trMod_menu").unwrap();
|
||||||
|
menu_pane.set_visible(QUICK_MENU_ACTIVE);
|
||||||
|
|
||||||
|
// Make all invisible first
|
||||||
|
(0..NUM_MENU_TEXT_OPTIONS).for_each(|idx| {
|
||||||
|
let x = idx % 3;
|
||||||
|
let y = idx / 3;
|
||||||
|
root_pane
|
||||||
|
.find_pane_by_name_recursive(format!("trMod_menu_opt_{x}_{y}").as_str())
|
||||||
|
.map(|text| text.set_visible(false));
|
||||||
|
root_pane
|
||||||
|
.find_pane_by_name_recursive(format!("trMod_menu_check_{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())
|
||||||
|
.map(|text| text.set_visible(false));
|
||||||
|
});
|
||||||
|
|
||||||
|
let app_tabs = &app.tabs.items;
|
||||||
|
let tab_selected = app.tabs.state.selected().unwrap();
|
||||||
|
let prev_tab = if tab_selected == 0 {
|
||||||
|
app_tabs.len() - 1
|
||||||
|
} else {
|
||||||
|
tab_selected - 1
|
||||||
|
};
|
||||||
|
let next_tab = if tab_selected == app_tabs.len() - 1 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
tab_selected + 1
|
||||||
|
};
|
||||||
|
let tab_titles = [prev_tab, tab_selected, next_tab].map(|idx| app_tabs[idx]);
|
||||||
|
|
||||||
|
(0..NUM_MENU_TABS).for_each(|idx| {
|
||||||
|
root_pane
|
||||||
|
.find_pane_by_name_recursive(format!("trMod_menu_tab_{idx}").as_str())
|
||||||
|
.map(|text| text.set_text_string(tab_titles[idx]));
|
||||||
|
});
|
||||||
|
|
||||||
|
if app.outer_list {
|
||||||
|
let tab_selected = app.tab_selected();
|
||||||
|
let tab = app.menu_items.get(tab_selected).unwrap();
|
||||||
|
|
||||||
|
(0..NUM_MENU_TEXT_OPTIONS)
|
||||||
|
// Valid options in this submenu
|
||||||
|
.filter_map(|idx| tab.idx_to_list_idx_opt(idx))
|
||||||
|
.map(|(list_section, list_idx)| {
|
||||||
|
(
|
||||||
|
list_section,
|
||||||
|
list_idx,
|
||||||
|
root_pane
|
||||||
|
.find_pane_by_name_recursive(
|
||||||
|
&format!("trMod_menu_opt_{list_section}_{list_idx}").to_owned(),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.for_each(|(list_section, list_idx, text)| {
|
||||||
|
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();
|
||||||
|
text.set_text_string(submenu.submenu_title);
|
||||||
|
text.set_visible(true);
|
||||||
|
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())
|
||||||
|
{
|
||||||
|
footer.set_text_string(submenu.help_text);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
text.set_color(0, 0, 0, 255);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} 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() {
|
||||||
|
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();
|
||||||
|
if let Some(text) = root_pane.find_pane_by_name_recursive(
|
||||||
|
format!("trMod_menu_opt_{list_section}_{idx}").as_str(),
|
||||||
|
) {
|
||||||
|
let text = text.as_textbox();
|
||||||
|
text.set_text_string(name);
|
||||||
|
if is_selected {
|
||||||
|
text.set_color(0x27, 0x4E, 0x13, 255);
|
||||||
|
} else {
|
||||||
|
text.set_color(0, 0, 0, 255);
|
||||||
|
}
|
||||||
|
text.set_visible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(check) = root_pane.find_pane_by_name_recursive(
|
||||||
|
format!("trMod_menu_check_{list_section}_{idx}").as_str(),
|
||||||
|
) {
|
||||||
|
if *checked {
|
||||||
|
let check = check.as_textbox();
|
||||||
|
|
||||||
|
check.set_text_string("+");
|
||||||
|
check.set_visible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let (_title, _help_text, gauge_vals) = app.sub_menu_strs_for_slider();
|
||||||
|
let abs_min = gauge_vals.abs_min;
|
||||||
|
let abs_max = gauge_vals.abs_max;
|
||||||
|
let selected_min = gauge_vals.selected_min;
|
||||||
|
let selected_max = gauge_vals.selected_max;
|
||||||
|
if let Some(text) = root_pane.find_pane_by_name_recursive("trMod_menu_slider_0") {
|
||||||
|
let text = text.as_textbox();
|
||||||
|
text.set_visible(true);
|
||||||
|
text.set_text_string(&format!("{abs_min}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(text) = root_pane.find_pane_by_name_recursive("trMod_menu_slider_1") {
|
||||||
|
let text = text.as_textbox();
|
||||||
|
text.set_visible(true);
|
||||||
|
text.set_text_string(&format!("{selected_min}"));
|
||||||
|
match gauge_vals.state {
|
||||||
|
GaugeState::MinHover => text.set_color(200, 8, 8, 255),
|
||||||
|
GaugeState::MinSelected => text.set_color(8, 200, 8, 255),
|
||||||
|
_ => text.set_color(0, 0, 0, 255),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(text) = root_pane.find_pane_by_name_recursive("trMod_menu_slider_2") {
|
||||||
|
let text = text.as_textbox();
|
||||||
|
text.set_visible(true);
|
||||||
|
text.set_text_string(&format!("{selected_max}"));
|
||||||
|
match gauge_vals.state {
|
||||||
|
GaugeState::MaxHover => text.set_color(200, 8, 8, 255),
|
||||||
|
GaugeState::MaxSelected => text.set_color(8, 200, 8, 255),
|
||||||
|
_ => text.set_color(0, 0, 0, 255),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(text) = root_pane.find_pane_by_name_recursive("trMod_menu_slider_3") {
|
||||||
|
let text = text.as_textbox();
|
||||||
|
text.set_visible(true);
|
||||||
|
text.set_text_string(&format!("{abs_max}"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,9 +311,26 @@ pub unsafe fn layout_build_parts_impl(
|
||||||
build_res_set: *const u8,
|
build_res_set: *const u8,
|
||||||
kind: u32,
|
kind: u32,
|
||||||
) -> *mut Pane {
|
) -> *mut Pane {
|
||||||
let layout_name = skyline::from_c_str((*layout).raw_layout.layout_name);
|
let layout_name = skyline::from_c_str((*layout).layout_name);
|
||||||
let _kind_str: String = kind.to_le_bytes().map(|b| b as char).iter().collect();
|
let _kind_str: String = kind.to_le_bytes().map(|b| b as char).iter().collect();
|
||||||
|
|
||||||
|
macro_rules! build {
|
||||||
|
($block: ident, $resTyp: ty, $kind:ident, $typ: ty) => {
|
||||||
|
paste::paste! {
|
||||||
|
&mut *(original!()(
|
||||||
|
layout,
|
||||||
|
out_build_result_information,
|
||||||
|
device,
|
||||||
|
&mut $block as *mut $resTyp as *mut u8,
|
||||||
|
parts_build_data_set,
|
||||||
|
build_arg_set,
|
||||||
|
build_res_set,
|
||||||
|
$kind,
|
||||||
|
) as *mut $typ)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if layout_name != "info_training" {
|
if layout_name != "info_training" {
|
||||||
return original!()(
|
return original!()(
|
||||||
layout,
|
layout,
|
||||||
|
@ -67,11 +344,265 @@ pub unsafe fn layout_build_parts_impl(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let root_pane = (*layout).raw_layout.root_pane;
|
let root_pane = &*(*layout).root_pane;
|
||||||
|
|
||||||
let block = data as *mut ResPane;
|
let block = data as *mut ResPane;
|
||||||
let num_display_panes = 1;
|
let menu_pos = ResVec3::new(-360.0, 440.0, 0.0);
|
||||||
(0..num_display_panes).for_each(|idx| {
|
|
||||||
|
// 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");
|
||||||
|
// Overall menu pane @ 0,0 to reason about positions globally
|
||||||
|
menu_pane_block.set_pos(ResVec3::default());
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
if (*block).name_matches("pic_help_bg_00") {
|
||||||
|
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();
|
||||||
|
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();
|
||||||
|
|
||||||
|
menu_pane.append_child(pic_menu_pane);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Menu footer text
|
||||||
|
if (*block).name_matches("set_txt_help_00") {
|
||||||
|
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 text_pane = build!(text_block, ResTextBox, kind, TextBox);
|
||||||
|
text_pane.pane.set_text_string(format!("Footer!").as_str());
|
||||||
|
// 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);
|
||||||
|
text_pane.detach();
|
||||||
|
menu_pane.append_child(text_pane);
|
||||||
|
}
|
||||||
|
|
||||||
|
(0..NUM_MENU_TABS).for_each(|txt_idx| {
|
||||||
|
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;
|
||||||
|
let mut text_block = (*block).clone();
|
||||||
|
text_block.enable_shadow();
|
||||||
|
text_block.text_alignment(TextAlignment::Center);
|
||||||
|
|
||||||
|
let x = txt_idx;
|
||||||
|
text_block
|
||||||
|
.pane
|
||||||
|
.set_name(format!("trMod_menu_tab_{x}").as_str());
|
||||||
|
|
||||||
|
let mut x_offset = x as f32 * 300.0;
|
||||||
|
// Center current tab since we don't have a help key
|
||||||
|
if x == 1 {
|
||||||
|
x_offset -= 25.0;
|
||||||
|
}
|
||||||
|
text_block.pane.set_pos(ResVec3::new(
|
||||||
|
menu_pos.x - 25.0 + x_offset,
|
||||||
|
menu_pos.y + 75.0,
|
||||||
|
0.0,
|
||||||
|
));
|
||||||
|
let text_pane = build!(text_block, ResTextBox, kind, TextBox);
|
||||||
|
text_pane
|
||||||
|
.pane
|
||||||
|
.set_text_string(format!("Tab {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(255, 255, 255, 255);
|
||||||
|
if txt_idx == 1 {
|
||||||
|
text_pane.set_color(255, 255, 0, 255);
|
||||||
|
}
|
||||||
|
text_pane.detach();
|
||||||
|
menu_pane.append_child(text_pane);
|
||||||
|
|
||||||
|
let mut help_block = (*block).clone();
|
||||||
|
// Font Idx 2 = nintendo64 which contains nice symbols
|
||||||
|
help_block.font_idx = 2;
|
||||||
|
|
||||||
|
let x = txt_idx;
|
||||||
|
help_block
|
||||||
|
.pane
|
||||||
|
.set_name(format!("trMod_menu_tab_help_{x}").as_str());
|
||||||
|
|
||||||
|
let x_offset = x as f32 * 300.0;
|
||||||
|
help_block.pane.set_pos(ResVec3::new(
|
||||||
|
menu_pos.x - 250.0 + x_offset,
|
||||||
|
menu_pos.y + 75.0,
|
||||||
|
0.0,
|
||||||
|
));
|
||||||
|
let help_pane = build!(help_block, ResTextBox, kind, TextBox);
|
||||||
|
help_pane.pane.set_text_string(format!("abcd").as_str());
|
||||||
|
let it = help_pane.m_text_buf as *mut u16;
|
||||||
|
match txt_idx {
|
||||||
|
// Left Tab: ZL
|
||||||
|
0 => {
|
||||||
|
*it = 0xE0E6;
|
||||||
|
*(it.add(1)) = 0x0;
|
||||||
|
help_pane.m_text_len = 1;
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
*it = 0x0;
|
||||||
|
help_pane.m_text_len = 1;
|
||||||
|
}
|
||||||
|
// Right Tab: ZR
|
||||||
|
2 => {
|
||||||
|
*it = 0xE0E7;
|
||||||
|
*(it.add(1)) = 0x0;
|
||||||
|
help_pane.m_text_len = 1;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure Material Colors are not hardcoded so we can just use SetTextColor.
|
||||||
|
help_pane.set_default_material_colors();
|
||||||
|
help_pane.set_color(255, 255, 255, 255);
|
||||||
|
help_pane.detach();
|
||||||
|
menu_pane.append_child(help_pane);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
(0..NUM_MENU_TEXT_OPTIONS).for_each(|txt_idx| {
|
||||||
|
let x = txt_idx % 3;
|
||||||
|
let y = txt_idx / 3;
|
||||||
|
|
||||||
|
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;
|
||||||
|
let mut text_block = (*block).clone();
|
||||||
|
text_block.enable_shadow();
|
||||||
|
text_block.text_alignment(TextAlignment::Center);
|
||||||
|
|
||||||
|
text_block
|
||||||
|
.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;
|
||||||
|
text_block.pane.set_pos(ResVec3::new(
|
||||||
|
menu_pos.x - 450.0 + x_offset,
|
||||||
|
menu_pos.y - 50.0 - y_offset,
|
||||||
|
0.0,
|
||||||
|
));
|
||||||
|
let text_pane = build!(text_block, ResTextBox, kind, TextBox);
|
||||||
|
text_pane
|
||||||
|
.pane
|
||||||
|
.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.detach();
|
||||||
|
menu_pane.append_child(text_pane);
|
||||||
|
|
||||||
|
let mut check_block = (*block).clone();
|
||||||
|
// Font Idx 2 = nintendo64 which contains nice symbols
|
||||||
|
check_block.font_idx = 2;
|
||||||
|
|
||||||
|
check_block
|
||||||
|
.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.y - 50.0 - y_offset,
|
||||||
|
0.0,
|
||||||
|
));
|
||||||
|
let check_pane = build!(check_block, ResTextBox, kind, TextBox);
|
||||||
|
check_pane
|
||||||
|
.pane
|
||||||
|
.set_text_string(format!("Check {txt_idx}!").as_str());
|
||||||
|
// Ensure Material Colors are not hardcoded so we can just use SetTextColor.
|
||||||
|
check_pane.set_default_material_colors();
|
||||||
|
check_pane.set_color(0, 0, 0, 255);
|
||||||
|
check_pane.detach();
|
||||||
|
menu_pane.append_child(check_pane);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Slider visualization
|
||||||
|
(0..NUM_MENU_TEXT_SLIDERS).for_each(|idx| {
|
||||||
|
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;
|
||||||
|
let mut text_block = (*block).clone();
|
||||||
|
text_block.enable_shadow();
|
||||||
|
text_block.text_alignment(TextAlignment::Center);
|
||||||
|
|
||||||
|
text_block
|
||||||
|
.pane
|
||||||
|
.set_name(format!("trMod_menu_slider_{idx}").as_str());
|
||||||
|
|
||||||
|
let x_offset = idx as f32 * 250.0;
|
||||||
|
text_block.pane.set_pos(ResVec3::new(
|
||||||
|
menu_pos.x - 450.0 + x_offset,
|
||||||
|
menu_pos.y - 150.0,
|
||||||
|
0.0,
|
||||||
|
));
|
||||||
|
let text_pane = build!(text_block, ResTextBox, kind, TextBox);
|
||||||
|
text_pane
|
||||||
|
.pane
|
||||||
|
.set_text_string(format!("Slider {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.detach();
|
||||||
|
menu_pane.append_child(text_pane);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Display panes
|
||||||
|
(0..NUM_DISPLAY_PANES).for_each(|idx| {
|
||||||
let mod_prefix = "trMod_disp_";
|
let mod_prefix = "trMod_disp_";
|
||||||
let parent_name = format!("{mod_prefix}{idx}");
|
let parent_name = format!("{mod_prefix}{idx}");
|
||||||
let pic_name = format!("{mod_prefix}{idx}_base");
|
let pic_name = format!("{mod_prefix}{idx}_base");
|
||||||
|
@ -83,39 +614,21 @@ pub unsafe fn layout_build_parts_impl(
|
||||||
let mut pic_block = (*block).clone();
|
let mut pic_block = (*block).clone();
|
||||||
pic_block.picture.pane.set_name(pic_name.as_str());
|
pic_block.picture.pane.set_name(pic_name.as_str());
|
||||||
pic_block.picture.pane.set_pos(ResVec3::default());
|
pic_block.picture.pane.set_pos(ResVec3::default());
|
||||||
let pic_pane = original!()(
|
let pic_pane = build!(pic_block, ResPictureWithTex<1>, kind, Picture);
|
||||||
layout,
|
pic_pane.detach();
|
||||||
out_build_result_information,
|
|
||||||
device,
|
|
||||||
&mut pic_block as *mut ResPictureWithTex<1> as *mut u8,
|
|
||||||
parts_build_data_set,
|
|
||||||
build_arg_set,
|
|
||||||
build_res_set,
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
(*(*pic_pane).parent).remove_child(&*pic_pane);
|
|
||||||
|
|
||||||
// pic is loaded first, we can create our parent pane here.
|
// pic is loaded first, we can create our parent pane here.
|
||||||
let disp_pane_kind = u32::from_le_bytes([b'p', b'a', b'n', b'1']);
|
let disp_pane_kind = u32::from_le_bytes([b'p', b'a', b'n', b'1']);
|
||||||
let mut disp_pane_block = ResPane::new(parent_name.as_str());
|
let mut disp_pane_block = ResPane::new(parent_name.as_str());
|
||||||
disp_pane_block.set_pos(ResVec3::new(806.0, 390.0 - (idx as f32 * 110.0), 0.0));
|
disp_pane_block.set_pos(ResVec3::new(806.0, 390.0 - (idx as f32 * 110.0), 0.0));
|
||||||
let disp_pane = original!()(
|
let disp_pane = build!(disp_pane_block, ResPane, disp_pane_kind, Pane);
|
||||||
layout,
|
disp_pane.detach();
|
||||||
out_build_result_information,
|
root_pane.append_child(disp_pane);
|
||||||
device,
|
disp_pane.append_child(pic_pane);
|
||||||
&mut disp_pane_block as *mut ResPane as *mut u8,
|
|
||||||
parts_build_data_set,
|
|
||||||
build_arg_set,
|
|
||||||
build_res_set,
|
|
||||||
disp_pane_kind,
|
|
||||||
);
|
|
||||||
(*(*disp_pane).parent).remove_child(&*disp_pane);
|
|
||||||
(*root_pane).append_child(&*disp_pane);
|
|
||||||
(*disp_pane).append_child(&*pic_pane);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*block).name_matches("set_txt_num_01") {
|
if (*block).name_matches("set_txt_num_01") {
|
||||||
let disp_pane = (*root_pane)
|
let disp_pane = root_pane
|
||||||
.find_pane_by_name(parent_name.as_str(), true)
|
.find_pane_by_name(parent_name.as_str(), true)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -123,27 +636,18 @@ pub unsafe fn layout_build_parts_impl(
|
||||||
let mut text_block = (*block).clone();
|
let mut text_block = (*block).clone();
|
||||||
text_block.pane.set_name(txt_name.as_str());
|
text_block.pane.set_name(txt_name.as_str());
|
||||||
text_block.pane.set_pos(ResVec3::new(-10.0, -25.0, 0.0));
|
text_block.pane.set_pos(ResVec3::new(-10.0, -25.0, 0.0));
|
||||||
let text_pane = original!()(
|
let text_pane = build!(text_block, ResTextBox, kind, TextBox);
|
||||||
layout,
|
text_pane
|
||||||
out_build_result_information,
|
.pane
|
||||||
device,
|
.set_text_string(format!("Pane {idx}!").as_str());
|
||||||
&mut text_block as *mut ResTextBox as *mut u8,
|
|
||||||
parts_build_data_set,
|
|
||||||
build_arg_set,
|
|
||||||
build_res_set,
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
(*text_pane).set_text_string(format!("Pane {idx}!").as_str());
|
|
||||||
// Ensure Material Colors are not hardcoded so we can just use SetTextColor.
|
// Ensure Material Colors are not hardcoded so we can just use SetTextColor.
|
||||||
(*((*(text_pane as *mut TextBox)).m_p_material))
|
text_pane.set_default_material_colors();
|
||||||
.set_white_color(255.0, 255.0, 255.0, 255.0);
|
text_pane.detach();
|
||||||
(*((*(text_pane as *mut TextBox)).m_p_material)).set_black_color(0.0, 0.0, 0.0, 255.0);
|
disp_pane.append_child(text_pane);
|
||||||
(*(*text_pane).parent).remove_child(&*text_pane);
|
|
||||||
(*disp_pane).append_child(&*text_pane);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*block).name_matches("txt_cap_01") {
|
if (*block).name_matches("txt_cap_01") {
|
||||||
let disp_pane = (*root_pane)
|
let disp_pane = root_pane
|
||||||
.find_pane_by_name(parent_name.as_str(), true)
|
.find_pane_by_name(parent_name.as_str(), true)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -151,26 +655,16 @@ pub unsafe fn layout_build_parts_impl(
|
||||||
let mut header_block = (*block).clone();
|
let mut header_block = (*block).clone();
|
||||||
header_block.pane.set_name(header_name.as_str());
|
header_block.pane.set_name(header_name.as_str());
|
||||||
header_block.pane.set_pos(ResVec3::new(0.0, 25.0, 0.0));
|
header_block.pane.set_pos(ResVec3::new(0.0, 25.0, 0.0));
|
||||||
let header_pane = original!()(
|
let header_pane = build!(header_block, ResTextBox, kind, TextBox);
|
||||||
layout,
|
header_pane
|
||||||
out_build_result_information,
|
.pane
|
||||||
device,
|
.set_text_string(format!("Header {idx}").as_str());
|
||||||
&mut header_block as *mut ResTextBox as *mut u8,
|
|
||||||
parts_build_data_set,
|
|
||||||
build_arg_set,
|
|
||||||
build_res_set,
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
(*header_pane).set_text_string(format!("Header {idx}").as_str());
|
|
||||||
// Ensure Material Colors are not hardcoded so we can just use SetTextColor.
|
// Ensure Material Colors are not hardcoded so we can just use SetTextColor.
|
||||||
(*((*(header_pane as *mut TextBox)).m_p_material))
|
header_pane.set_default_material_colors();
|
||||||
.set_white_color(255.0, 255.0, 255.0, 255.0);
|
|
||||||
(*((*(header_pane as *mut TextBox)).m_p_material))
|
|
||||||
.set_black_color(0.0, 0.0, 0.0, 255.0);
|
|
||||||
// Header should be white text
|
// Header should be white text
|
||||||
(*(header_pane as *mut TextBox)).set_color(255, 255, 255, 255);
|
header_pane.set_color(255, 255, 255, 255);
|
||||||
(*(*header_pane).parent).remove_child(&*header_pane);
|
header_pane.detach();
|
||||||
(*disp_pane).append_child(&*header_pane);
|
disp_pane.append_child(header_pane);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ use std::collections::HashMap;
|
||||||
use serde_json::{Map, json};
|
use serde_json::{Map, json};
|
||||||
pub use tui::{backend::TestBackend, style::Color, Terminal};
|
pub use tui::{backend::TestBackend, style::Color, Terminal};
|
||||||
|
|
||||||
mod gauge;
|
pub mod gauge;
|
||||||
mod list;
|
mod list;
|
||||||
|
|
||||||
use crate::gauge::{DoubleEndedGauge, GaugeState};
|
use crate::gauge::{DoubleEndedGauge, GaugeState};
|
||||||
|
@ -95,7 +95,7 @@ impl<'a> App<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the id of the currently selected tab
|
/// Returns the id of the currently selected tab
|
||||||
fn tab_selected(&self) -> &str {
|
pub fn tab_selected(&self) -> &str {
|
||||||
self.tabs
|
self.tabs
|
||||||
.items
|
.items
|
||||||
.get(self.tabs.state.selected().unwrap())
|
.get(self.tabs.state.selected().unwrap())
|
||||||
|
@ -221,7 +221,7 @@ impl<'a> App<'a> {
|
||||||
/// 3: ListState for toggles, ListState::new() for slider
|
/// 3: ListState for toggles, ListState::new() for slider
|
||||||
/// TODO: Refactor return type into a nice struct
|
/// TODO: Refactor return type into a nice struct
|
||||||
pub fn sub_menu_strs_and_states(
|
pub fn sub_menu_strs_and_states(
|
||||||
&mut self,
|
&self,
|
||||||
) -> (&str, &str, Vec<(Vec<(bool, &str)>, ListState)>) {
|
) -> (&str, &str, Vec<(Vec<(bool, &str)>, ListState)>) {
|
||||||
(
|
(
|
||||||
self.sub_menu_selected().submenu_title,
|
self.sub_menu_selected().submenu_title,
|
||||||
|
@ -254,7 +254,7 @@ impl<'a> App<'a> {
|
||||||
/// 1: Help text
|
/// 1: Help text
|
||||||
/// 2: Reference to self.selected_sub_menu_slider
|
/// 2: Reference to self.selected_sub_menu_slider
|
||||||
/// TODO: Refactor return type into a nice struct
|
/// TODO: Refactor return type into a nice struct
|
||||||
pub fn sub_menu_strs_for_slider(&mut self) -> (&str, &str, &DoubleEndedGauge) {
|
pub fn sub_menu_strs_for_slider(&self) -> (&str, &str, &DoubleEndedGauge) {
|
||||||
let slider = match SubMenuType::from_str(self.sub_menu_selected()._type) {
|
let slider = match SubMenuType::from_str(self.sub_menu_selected()._type) {
|
||||||
SubMenuType::SLIDER => &self.selected_sub_menu_slider,
|
SubMenuType::SLIDER => &self.selected_sub_menu_slider,
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -780,6 +780,12 @@ pub fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect settings
|
// Collect settings
|
||||||
|
to_json(app)
|
||||||
|
|
||||||
|
// TODO: Add saveDefaults
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_json(app: &App) -> String {
|
||||||
let mut settings = Map::new();
|
let mut settings = Map::new();
|
||||||
for key in app.menu_items.keys() {
|
for key in app.menu_items.keys() {
|
||||||
for list in &app.menu_items.get(key).unwrap().lists {
|
for list in &app.menu_items.get(key).unwrap().lists {
|
||||||
|
@ -803,6 +809,4 @@ pub fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
serde_json::to_string(&settings).unwrap()
|
serde_json::to_string(&settings).unwrap()
|
||||||
|
|
||||||
// TODO: Add saveDefaults
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,16 +15,21 @@ impl<T: Clone> MultiStatefulList<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn idx_to_list_idx(&self, idx: usize) -> (usize, usize) {
|
pub fn idx_to_list_idx(&self, idx: usize) -> (usize, usize) {
|
||||||
|
self.idx_to_list_idx_opt(idx).unwrap_or((0, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn idx_to_list_idx_opt(&self, idx: usize) -> Option<(usize, usize)> {
|
||||||
for list_section in 0..self.lists.len() {
|
for list_section in 0..self.lists.len() {
|
||||||
let list_section_min_idx = (self.total_len as f32 / self.lists.len() as f32).ceil() as usize * list_section;
|
let list_section_min_idx = (self.total_len as f32 / self.lists.len() as f32).ceil() as usize * list_section;
|
||||||
let list_section_max_idx = std::cmp::min(
|
let list_section_max_idx = std::cmp::min(
|
||||||
(self.total_len as f32 / self.lists.len() as f32).ceil() as usize * (list_section + 1),
|
(self.total_len as f32 / self.lists.len() as f32).ceil() as usize * (list_section + 1),
|
||||||
self.total_len);
|
self.total_len);
|
||||||
if (list_section_min_idx..list_section_max_idx).contains(&idx) {
|
if (list_section_min_idx..list_section_max_idx).contains(&idx) {
|
||||||
return (list_section, idx - list_section_min_idx)
|
return Some((list_section, idx - list_section_min_idx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(0, 0)
|
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_idx_to_idx(&self, list_idx: (usize, usize)) -> usize {
|
fn list_idx_to_idx(&self, list_idx: (usize, usize)) -> usize {
|
||||||
|
|
Loading…
Reference in a new issue