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

Update UI for quickmenu slider values (#458)

* Updated devcontainer to remove unused config

* Added slider menu background and text labels

* Finished layout of UI components

* Reordered slider values

* Fixed alignment of labels

* Adjusted label postion to be centered vertically

* TEST for colors

* Updated UI to look better with colours

* Changed menu to only have 'min' & 'max', fixed layout

* Created macro for slider button labels, cleaned up warnings

* Updated default case to panic

* Added text outline and shadow, updated text colours to match the game

* Updated default cases to panic
This commit is contained in:
Matthew Edell 2023-02-02 14:02:59 -05:00 committed by GitHub
parent f762850bbf
commit 24441c2b26
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 337 additions and 37 deletions

View file

@ -10,7 +10,6 @@
"remoteEnv": {
"LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}"
},
"customizations": {
"vscode": {
"extensions": [

View file

@ -73,7 +73,7 @@ pub unsafe fn parse_anim_transform(anim_transform: &mut AnimTransform, layout_na
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_TEXT_SLIDERS: usize = 2;
pub static NUM_MENU_TABS: usize = 3;
pub static mut HAS_SORTED_MENU_CHILDREN: bool = false;
@ -106,6 +106,27 @@ const BG_LEFT_OFF_BLACK_COLOR: ResColor = ResColor {
a: 0,
};
const BG_LEFT_SELECTED_BLACK_COLOR: ResColor = ResColor {
r: 240,
g: 154,
b: 7,
a: 0,
};
const BG_LEFT_SELECTED_WHITE_COLOR: ResColor = ResColor {
r: 255,
g: 166,
b: 7,
a: 255,
};
const BLACK: ResColor = ResColor {
r: 0,
g: 0,
b: 0,
a: 255,
};
macro_rules! menu_text_name_fmt {
($x:ident, $y:ident) => {
format!("trMod_menu_opt_{}_{}", $x, $y).as_str()
@ -136,6 +157,12 @@ macro_rules! menu_text_slider_fmt {
};
}
macro_rules! menu_slider_label_fmt {
($x:ident) => {
format!("trMod_menu_slider_{}_lbl", $x).as_str()
};
}
// 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> {
@ -170,6 +197,16 @@ pub unsafe fn all_menu_panes_sorted(root_pane: &Pane) -> Vec<&mut Pane> {
.collect::<Vec<&mut Pane>>(),
);
panes.append(
&mut (0..NUM_MENU_TEXT_SLIDERS)
.map(|idx| {
root_pane
.find_pane_by_name_recursive(menu_slider_label_fmt!(idx))
.unwrap()
})
.collect::<Vec<&mut Pane>>(),
);
panes.sort_by(|a, _| {
if a.get_name().contains("opt") || a.get_name().contains("check") {
std::cmp::Ordering::Greater
@ -344,8 +381,16 @@ pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64)
root_pane
.find_pane_by_name_recursive(menu_text_slider_fmt!(idx))
.map(|text| text.set_visible(false));
root_pane
.find_pane_by_name_recursive(format!("trMod_menu_slider_{}_lbl", idx).as_str())
.map(|text| text.set_visible(false));
});
root_pane
.find_pane_by_name_recursive("slider_menu")
.map(|pane| pane.set_visible(false));
let app_tabs = &app.tabs.items;
let tab_selected = app.tabs.state.selected().unwrap();
let prev_tab = if tab_selected == 0 {
@ -473,43 +518,114 @@ 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;
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(pane) = root_pane.find_pane_by_name_recursive("slider_menu") {
pane.set_visible(true);
}
if let Some(text) = root_pane.find_pane_by_name_recursive("trMod_menu_slider_1") {
if let Some(text) = root_pane.find_pane_by_name_recursive("slider_title") {
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),
text.set_text_string(&format!("{_title}"));
}
(0..NUM_MENU_TEXT_SLIDERS).for_each(|index| {
if let Some(text_pane) = root_pane.find_pane_by_name_recursive(
format!("trMod_menu_slider_{}_lbl", index).as_str(),
) {
let text_pane = text_pane.as_textbox();
text_pane.set_visible(true);
match index {
0 => {
text_pane.set_text_string("Min");
match gauge_vals.state {
GaugeState::MinHover | GaugeState::MinSelected => {
text_pane.m_bits |= 1 << TextBoxFlag::ShadowEnabled as u8;
text_pane.m_bits = text_pane.m_bits & !(1 << TextBoxFlag::InvisibleBorderEnabled as u8);
text_pane.set_color(255, 255, 255, 255);
}
_ => {
text_pane.m_bits = text_pane.m_bits & !(1 << TextBoxFlag::ShadowEnabled as u8);
text_pane.m_bits |= 1 << TextBoxFlag::InvisibleBorderEnabled as u8;
text_pane.set_color(85, 89, 92, 255);
}
}
}
1 => {
text_pane.set_text_string("Max");
match gauge_vals.state {
GaugeState::MaxHover | GaugeState::MaxSelected => {
text_pane.m_bits |= 1 << TextBoxFlag::ShadowEnabled as u8;
text_pane.m_bits = text_pane.m_bits & !(1 << TextBoxFlag::InvisibleBorderEnabled as u8);
text_pane.set_color(255, 255, 255, 255);
}
_ => {
text_pane.m_bits |= 1 << TextBoxFlag::InvisibleBorderEnabled as u8;
text_pane.m_bits = text_pane.m_bits & !(1 << TextBoxFlag::ShadowEnabled as u8);
text_pane.set_color(85, 89, 92, 255);
}
}
}
_ => panic!("Unexpected slider label index {}!", index),
}
}
}
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_pane) = root_pane
.find_pane_by_name_recursive(format!("trMod_menu_slider_{}", index).as_str())
{
let text_pane = text_pane.as_textbox();
text_pane.set_visible(true);
match index {
0 => text_pane.set_text_string(&format!("{selected_min}")),
1 => text_pane.set_text_string(&format!("{selected_max}")),
_ => panic!("Unexpected slider label index {}!", index),
}
}
}
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}"));
}
if let Some(bg_left) = root_pane
.find_pane_by_name_recursive(format!("slider_btn_fg_{}", index).as_str())
{
let bg_left_material = &mut *bg_left.as_picture().material;
match index {
0 => match gauge_vals.state {
GaugeState::MinHover => {
bg_left_material.set_white_res_color(BG_LEFT_ON_WHITE_COLOR);
bg_left_material.set_black_res_color(BG_LEFT_ON_BLACK_COLOR);
}
GaugeState::MinSelected => {
bg_left_material.set_white_res_color(BG_LEFT_SELECTED_WHITE_COLOR);
bg_left_material.set_black_res_color(BG_LEFT_SELECTED_BLACK_COLOR);
}
_ => {
bg_left_material.set_white_res_color(BG_LEFT_OFF_WHITE_COLOR);
bg_left_material.set_black_res_color(BG_LEFT_OFF_BLACK_COLOR);
}
},
1 => match gauge_vals.state {
GaugeState::MaxHover => {
bg_left_material.set_white_res_color(BG_LEFT_ON_WHITE_COLOR);
bg_left_material.set_black_res_color(BG_LEFT_ON_BLACK_COLOR);
}
GaugeState::MaxSelected => {
bg_left_material.set_white_res_color(BG_LEFT_SELECTED_WHITE_COLOR);
bg_left_material.set_black_res_color(BG_LEFT_SELECTED_BLACK_COLOR);
}
_ => {
bg_left_material.set_white_res_color(BG_LEFT_OFF_WHITE_COLOR);
bg_left_material.set_black_res_color(BG_LEFT_OFF_BLACK_COLOR);
}
},
_ => panic!("Unexpected slider label index {}!", index),
}
bg_left.set_visible(true);
}
});
}
}
@ -519,6 +635,8 @@ pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64)
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;
pub static mut HAS_CREATED_SLIDER_BG: bool = false;
pub static mut HAS_CREATED_SLIDER_BG_BACK: bool = false;
#[skyline::hook(offset = 0x493a0)]
pub unsafe fn layout_build_parts_impl(
@ -608,6 +726,81 @@ pub unsafe fn layout_build_parts_impl(
}
});
}
if !HAS_CREATED_SLIDER_BG && (*block).name_matches("icn_bg_main") {
(0..NUM_MENU_TEXT_SLIDERS).for_each(|index| {
let x = index % 2;
if MENU_PANE_PTR != 0 {
let slider_root = (*(MENU_PANE_PTR as *mut Pane))
.find_pane_by_name("slider_menu", true)
.unwrap();
let slider_bg = (*(MENU_PANE_PTR as *mut Pane))
.find_pane_by_name("slider_ui_container", true)
.unwrap();
let x_offset = x as f32 * 345.0;
let block = block as *mut ResPictureWithTex<2>;
let mut pic_menu_block = *block;
pic_menu_block.set_name(format!("slider_btn_fg_{}", index).as_str());
pic_menu_block.picture.scale_x /= 1.85;
pic_menu_block.picture.scale_y /= 1.25;
pic_menu_block.set_pos(ResVec3::new(
slider_root.pos_x - 842.5 + x_offset,
slider_root.pos_y + slider_bg.size_y * 0.458,
0.0,
));
let pic_menu_pane = build!(pic_menu_block, ResPictureWithTex<2>, kind, Picture);
pic_menu_pane.detach();
slider_root.append_child(pic_menu_pane);
HAS_CREATED_SLIDER_BG = true;
}
});
}
if !HAS_CREATED_SLIDER_BG_BACK && (*block).name_matches("btn_bg") {
(0..NUM_MENU_TEXT_SLIDERS).for_each(|index| {
let x = index % 2;
if MENU_PANE_PTR != 0 {
let slider_root = (*(MENU_PANE_PTR as *mut Pane))
.find_pane_by_name("slider_menu", true)
.unwrap();
let slider_bg = (*(MENU_PANE_PTR as *mut Pane))
.find_pane_by_name("slider_ui_container", true)
.unwrap();
let size_y = 90.0;
let x_offset = x as f32 * 345.0;
let block = block as *mut ResWindowWithTexCoordsAndFrames<1, 4>;
let mut bg_block = *block;
bg_block.set_name(format!("slider_item_btn_{}", index).as_str());
bg_block.scale_x /= 2.0;
bg_block.set_size(ResVec2::new(605.0, size_y));
bg_block.set_pos(ResVec3::new(
slider_root.pos_x - 700.0 + x_offset,
slider_root.pos_y + slider_bg.size_y * 0.458,
0.0,
));
let bg_pane = build!(bg_block, ResWindowWithTexCoordsAndFrames<1,4>, kind, Window);
bg_pane.detach();
slider_root.append_child(bg_pane);
HAS_CREATED_SLIDER_BG_BACK = true;
}
});
}
}
if layout_name != "info_training" {
@ -638,6 +831,8 @@ pub unsafe fn layout_build_parts_impl(
HAS_CREATED_OPT_BG = false;
HAS_CREATED_OPT_BG_BACK = false;
HAS_SORTED_MENU_CHILDREN = false;
HAS_CREATED_SLIDER_BG = false;
HAS_CREATED_SLIDER_BG_BACK = false;
}
}
@ -798,30 +993,136 @@ pub unsafe fn layout_build_parts_impl(
});
// Slider visualization
// UI Backing
let slider_root_name = "slider_menu";
let slider_container_name = "slider_ui_container";
if (*block).name_matches("pic_numbase_01") {
let menu_pane = root_pane.find_pane_by_name("trMod_menu", true).unwrap();
let slider_ui_root_pane_kind = u32::from_le_bytes([b'p', b'a', b'n', b'1']);
let mut slider_ui_root_block = ResPane::new(slider_root_name);
slider_ui_root_block.set_pos(ResVec3::default());
let slider_ui_root = build!(
slider_ui_root_block,
ResPane,
slider_ui_root_pane_kind,
Pane
);
slider_ui_root.detach();
menu_pane.append_child(slider_ui_root);
let block = data as *mut ResPictureWithTex<1>;
let mut picture_block = *block;
picture_block.set_name(slider_container_name);
picture_block.set_size(ResVec2::new(675.0, 300.0));
picture_block.set_pos(ResVec3::new(-530.0, 180.0, 0.0));
picture_block.tex_coords = [
[ResVec2::new(0.0, 0.0)],
[ResVec2::new(1.0, 0.0)],
[ResVec2::new(0.0, 1.5)],
[ResVec2::new(1.0, 1.5)],
];
let picture_pane = build!(picture_block, ResPictureWithTex<1>, kind, Picture);
picture_pane.detach();
slider_ui_root.append_child(picture_pane);
}
if (*block).name_matches("txt_cap_01") {
let container_pane = root_pane.find_pane_by_name(slider_root_name, true).unwrap();
let block = data as *mut ResTextBox;
let mut title_block = *block;
title_block.set_name("slider_title");
title_block.set_pos(ResVec3::new(-530.0, 285.0, 0.0));
title_block.set_size(ResVec2::new(550.0, 100.0));
title_block.font_size = ResVec2::new(50.0, 100.0);
let title_pane = build!(title_block, ResTextBox, kind, TextBox);
title_pane.set_text_string(format!("Slider Title").as_str());
// Ensure Material Colors are not hardcoded so we can just use SetTextColor.
title_pane.set_default_material_colors();
// Header should be white text
title_pane.set_color(255, 255, 255, 255);
title_pane.detach();
container_pane.append_child(title_pane);
}
(0..NUM_MENU_TEXT_SLIDERS).for_each(|idx| {
let x = idx % 2;
let label_x_offset = x as f32 * 345.0;
if (*block).name_matches("set_txt_num_01") {
let menu_pane = root_pane.find_pane_by_name("trMod_menu", true).unwrap();
let slider_root_pane = root_pane.find_pane_by_name(slider_root_name, true).unwrap();
let slider_container = root_pane
.find_pane_by_name(slider_container_name, true)
.unwrap();
let block = data as *mut ResTextBox;
let mut text_block = *block;
text_block.enable_shadow();
text_block.text_alignment(TextAlignment::Center);
text_block.set_name(menu_text_slider_fmt!(idx));
let x_offset = idx as f32 * 250.0;
let value_x_offset = x as f32 * 345.0;
text_block.set_pos(ResVec3::new(
menu_pos.x - 450.0 + x_offset,
menu_pos.y - 150.0,
slider_root_pane.pos_x - 675.0 + value_x_offset,
slider_root_pane.pos_y + (slider_container.size_y * 0.458),
0.0,
));
let text_pane = build!(text_block, ResTextBox, kind, TextBox);
text_pane.set_text_string(format!("Slider {idx}!").as_str());
text_pane.set_text_string(format!("Slider opt {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);
slider_root_pane.append_child(text_pane);
let mut label_block = *block;
label_block.text_alignment(TextAlignment::Center);
label_block.set_name(menu_slider_label_fmt!(idx));
label_block.set_pos(ResVec3::new(
slider_root_pane.pos_x - 750.0 + label_x_offset,
slider_root_pane.pos_y + slider_container.size_y * 0.458 + 5.0,
0.0,
));
label_block.font_size = ResVec2::new(25.0, 50.0);
// Aligns text to the center horizontally
label_block.text_position = 4;
label_block.shadow_offset = ResVec2::new(4.0, -3.0);
label_block.shadow_cols = [BLACK, BLACK];
label_block.shadow_scale = ResVec2::new(1.0, 1.0);
let label_pane = build!(label_block, ResTextBox, kind, TextBox);
label_pane.set_text_string(format!("Slider opt {idx}!").as_str());
// Ensure Material Colors are not hardcoded so we can just use SetTextColor.
label_pane.set_default_material_colors();
label_pane.set_color(85, 89, 92, 255);
// Turns on text outline
label_pane.m_bits = label_pane.m_bits & !(1 << TextBoxFlag::InvisibleBorderEnabled as u8);
label_pane.detach();
slider_root_pane.append_child(label_pane);
}
});