1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2025-06-19 00:24:44 +00:00

Better Save Menu UX (fixes #111)

This commit is contained in:
dawnDus 2022-04-10 15:57:19 -04:00
parent 68318e3a69
commit 339f822a80
No known key found for this signature in database
GPG key ID: 972AABDE81848F21
3 changed files with 113 additions and 62 deletions

View file

@ -13,6 +13,7 @@ pub mod pause_menu;
pub mod save_select_menu;
pub mod settings_menu;
#[derive(Clone)]
pub enum MenuEntry {
Hidden,
Active(String),
@ -23,6 +24,7 @@ pub enum MenuEntry {
DescriptiveOptions(String, usize, Vec<String>, Vec<String>),
OptionsBar(String, f32),
SaveData(MenuSaveInfo),
SaveDataSingle(MenuSaveInfo),
NewSave,
}
@ -38,6 +40,7 @@ impl MenuEntry {
MenuEntry::DescriptiveOptions(_, _, _, _) => 16.0,
MenuEntry::OptionsBar(_, _) => 16.0,
MenuEntry::SaveData(_) => 32.0,
MenuEntry::SaveDataSingle(_) => 32.0,
MenuEntry::NewSave => 32.0,
}
}
@ -53,6 +56,7 @@ impl MenuEntry {
MenuEntry::DescriptiveOptions(_, _, _, _) => true,
MenuEntry::OptionsBar(_, _) => true,
MenuEntry::SaveData(_) => true,
MenuEntry::SaveDataSingle(_) => true,
MenuEntry::NewSave => true,
}
}
@ -76,6 +80,7 @@ pub struct Menu {
anim_num: u16,
anim_wait: u16,
custom_cursor: Cell<bool>,
pub draw_cursor: bool,
}
impl Menu {
@ -90,6 +95,7 @@ impl Menu {
anim_wait: 0,
entries: Vec::new(),
custom_cursor: Cell::new(true),
draw_cursor: true,
}
}
@ -152,6 +158,7 @@ impl Menu {
width = width.max(entry_width);
}
MenuEntry::SaveData(_) => {}
MenuEntry::SaveDataSingle(_) => {}
MenuEntry::NewSave => {}
}
}
@ -271,59 +278,61 @@ impl Menu {
entry_y = self.entries[0..(self.selected)].iter().map(|e| e.height()).sum::<f64>().max(0.0) as u16;
}
if self.custom_cursor.get() {
if let Ok(batch) = state.texture_set.get_or_load_batch(ctx, &state.constants, "MenuCursor") {
rect.left = self.anim_num * 16;
rect.top = 16;
rect.right = rect.left + 16;
rect.bottom = rect.top + 16;
if self.draw_cursor {
if self.custom_cursor.get() {
if let Ok(batch) = state.texture_set.get_or_load_batch(ctx, &state.constants, "MenuCursor") {
rect.left = self.anim_num * 16;
rect.top = 16;
rect.right = rect.left + 16;
rect.bottom = rect.top + 16;
batch.add_rect(self.x as f32, self.y as f32 + 3.0 + entry_y as f32, &rect);
batch.add_rect(self.x as f32, self.y as f32 + 3.0 + entry_y as f32, &rect);
batch.draw(ctx)?;
} else {
self.custom_cursor.set(false);
}
}
if !self.custom_cursor.get() {
let menu_texture: &str;
let character_rect: [Rect<u16>; 4];
match state.menu_character {
MenuCharacter::Quote => {
menu_texture = "MyChar";
character_rect = state.constants.title.cursor_quote;
}
MenuCharacter::Curly => {
menu_texture = "Npc/NpcRegu";
character_rect = state.constants.title.cursor_curly;
}
MenuCharacter::Toroko => {
menu_texture = "Npc/NpcRegu";
character_rect = state.constants.title.cursor_toroko;
}
MenuCharacter::King => {
menu_texture = "Npc/NpcRegu";
character_rect = state.constants.title.cursor_king;
}
MenuCharacter::Sue => {
menu_texture = "Npc/NpcRegu";
character_rect = state.constants.title.cursor_sue;
}
}
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, menu_texture)?;
batch.add_rect(
self.x as f32,
self.y as f32 + 4.0 + entry_y as f32,
&character_rect[self.anim_num as usize],
);
batch.draw(ctx)?;
} else {
self.custom_cursor.set(false);
}
}
if !self.custom_cursor.get() {
let menu_texture: &str;
let character_rect: [Rect<u16>; 4];
match state.menu_character {
MenuCharacter::Quote => {
menu_texture = "MyChar";
character_rect = state.constants.title.cursor_quote;
}
MenuCharacter::Curly => {
menu_texture = "Npc/NpcRegu";
character_rect = state.constants.title.cursor_curly;
}
MenuCharacter::Toroko => {
menu_texture = "Npc/NpcRegu";
character_rect = state.constants.title.cursor_toroko;
}
MenuCharacter::King => {
menu_texture = "Npc/NpcRegu";
character_rect = state.constants.title.cursor_king;
}
MenuCharacter::Sue => {
menu_texture = "Npc/NpcRegu";
character_rect = state.constants.title.cursor_sue;
}
}
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, menu_texture)?;
batch.add_rect(
self.x as f32,
self.y as f32 + 4.0 + entry_y as f32,
&character_rect[self.anim_num as usize],
);
batch.draw(ctx)?;
}
y = self.y as f32 + 8.0;
for entry in &self.entries {
match entry {
@ -481,7 +490,7 @@ impl Menu {
ctx,
)?;
}
MenuEntry::SaveData(save) => {
MenuEntry::SaveData(save) | MenuEntry::SaveDataSingle(save) => {
let name = &state.stages[save.current_map as usize].name;
let bar_width = (save.life as f32 / save.max_life as f32 * 39.0) as u16;
let right_edge = self.x as f32 + self.width as f32 - 4.0;

View file

@ -26,13 +26,16 @@ pub enum CurrentMenu {
SaveMenu,
DifficultyMenu,
DeleteConfirm,
LoadConfirm,
}
pub struct SaveSelectMenu {
pub saves: [MenuSaveInfo; 3],
current_menu: CurrentMenu,
save_menu: Menu,
save_detailed: Menu,
difficulty_menu: Menu,
delete_confirm: Menu,
load_confirm: Menu,
skip_difficulty_menu: bool,
}
@ -42,16 +45,20 @@ impl SaveSelectMenu {
saves: [MenuSaveInfo::default(); 3],
current_menu: CurrentMenu::SaveMenu,
save_menu: Menu::new(0, 0, 230, 0),
save_detailed: Menu::new(0, 0, 230, 0),
difficulty_menu: Menu::new(0, 0, 130, 0),
delete_confirm: Menu::new(0, 0, 75, 0),
load_confirm: Menu::new(0, 0, 75, 0),
skip_difficulty_menu: false,
}
}
pub fn init(&mut self, state: &mut SharedGameState, ctx: &Context) -> GameResult {
self.save_menu = Menu::new(0, 0, 230, 0);
self.save_detailed = Menu::new(0, 0, 230, 0);
self.difficulty_menu = Menu::new(0, 0, 130, 0);
self.delete_confirm = Menu::new(0, 0, 75, 0);
self.load_confirm = Menu::new(0, 0, 75, 0);
self.skip_difficulty_menu = false;
for (iter, save) in self.saves.iter_mut().enumerate() {
@ -72,7 +79,6 @@ impl SaveSelectMenu {
}
self.save_menu.push_entry(MenuEntry::Active(state.t("common.back")));
self.save_menu.push_entry(MenuEntry::Disabled(state.t("menus.save_menu.delete_info")));
self.difficulty_menu.push_entry(MenuEntry::Disabled(state.t("menus.difficulty_menu.title")));
self.difficulty_menu.push_entry(MenuEntry::Active(state.t("menus.difficulty_menu.easy")));
@ -88,6 +94,15 @@ impl SaveSelectMenu {
self.delete_confirm.selected = 2;
self.load_confirm.push_entry(MenuEntry::Active(state.t("menus.main_menu.start")));
self.load_confirm.push_entry(MenuEntry::Active(state.t("menus.save_menu.delete_confirm")));
self.load_confirm.push_entry(MenuEntry::Active(state.t("common.back")));
self.save_detailed.draw_cursor = false;
if let MenuEntry::SaveData(save) = self.save_menu.entries[0] {
self.save_detailed.push_entry(MenuEntry::SaveDataSingle(save));
}
self.update_sizes(state);
Ok(())
@ -101,7 +116,7 @@ impl SaveSelectMenu {
self.save_menu.update_width(state);
self.save_menu.update_height();
self.save_menu.x = ((state.canvas_size.0 - self.save_menu.width as f32) / 2.0).floor() as isize;
self.save_menu.y = 30 + ((state.canvas_size.1 - self.save_menu.height as f32) / 2.0).floor() as isize;
self.save_menu.y = ((state.canvas_size.1 - self.save_menu.height as f32) / 2.0).floor() as isize;
self.difficulty_menu.update_width(state);
self.difficulty_menu.update_height();
@ -112,7 +127,17 @@ impl SaveSelectMenu {
self.delete_confirm.update_width(state);
self.delete_confirm.update_height();
self.delete_confirm.x = ((state.canvas_size.0 - self.delete_confirm.width as f32) / 2.0).floor() as isize;
self.delete_confirm.y = 30 + ((state.canvas_size.1 - self.delete_confirm.height as f32) / 2.0).floor() as isize
self.delete_confirm.y = 30 + ((state.canvas_size.1 - self.delete_confirm.height as f32) / 2.0).floor() as isize;
self.load_confirm.update_width(state);
self.load_confirm.update_height();
self.load_confirm.x = ((state.canvas_size.0 - self.load_confirm.width as f32) / 2.0).floor() as isize;
self.load_confirm.y = 30 + ((state.canvas_size.1 - self.load_confirm.height as f32) / 2.0).floor() as isize;
self.save_detailed.update_width(state);
self.save_detailed.update_height();
self.save_detailed.x = ((state.canvas_size.0 - self.save_detailed.width as f32) / 2.0).floor() as isize;
self.save_detailed.y = -40 + ((state.canvas_size.1 - self.save_detailed.height as f32) / 2.0).floor() as isize;
}
pub fn tick(
@ -129,12 +154,15 @@ impl SaveSelectMenu {
MenuSelectionResult::Selected(slot, _) => {
state.save_slot = slot + 1;
if self.skip_difficulty_menu {
state.reload_resources(ctx)?;
state.load_or_start_game(ctx)?;
} else if let Ok(_) =
if let Ok(_) =
filesystem::user_open(ctx, state.get_save_filename(state.save_slot).unwrap_or("".to_string()))
{
if let MenuEntry::SaveData(save) = self.save_menu.entries[slot] {
self.save_detailed.entries[0] = MenuEntry::SaveDataSingle(save);
}
self.current_menu = CurrentMenu::LoadConfirm;
self.load_confirm.selected = 0;
} else if self.skip_difficulty_menu {
state.reload_resources(ctx)?;
state.load_or_start_game(ctx)?;
} else {
@ -142,12 +170,6 @@ impl SaveSelectMenu {
self.current_menu = CurrentMenu::DifficultyMenu;
}
}
MenuSelectionResult::Right(slot, _, _) => {
if slot <= 2 {
self.current_menu = CurrentMenu::DeleteConfirm;
self.delete_confirm.selected = 2;
}
}
_ => (),
},
CurrentMenu::DifficultyMenu => match self.difficulty_menu.tick(controller, state) {
@ -181,6 +203,21 @@ impl SaveSelectMenu {
self.save_menu.entries[self.save_menu.selected] = MenuEntry::NewSave;
self.current_menu = CurrentMenu::SaveMenu;
}
MenuSelectionResult::Selected(2, _) | MenuSelectionResult::Canceled => {
self.current_menu = CurrentMenu::LoadConfirm;
self.load_confirm.selected = 0;
}
_ => (),
},
CurrentMenu::LoadConfirm => match self.load_confirm.tick(controller, state) {
MenuSelectionResult::Selected(0, _) => {
state.reload_resources(ctx)?;
state.load_or_start_game(ctx)?;
}
MenuSelectionResult::Selected(1, _) => {
self.current_menu = CurrentMenu::DeleteConfirm;
self.delete_confirm.selected = 2;
}
MenuSelectionResult::Selected(2, _) | MenuSelectionResult::Canceled => {
self.current_menu = CurrentMenu::SaveMenu;
}
@ -200,8 +237,13 @@ impl SaveSelectMenu {
self.difficulty_menu.draw(state, ctx)?;
}
CurrentMenu::DeleteConfirm => {
self.save_detailed.draw(state, ctx)?;
self.delete_confirm.draw(state, ctx)?;
}
CurrentMenu::LoadConfirm => {
self.save_detailed.draw(state, ctx)?;
self.load_confirm.draw(state, ctx)?;
}
}
Ok(())
}

View file

@ -352,7 +352,7 @@ impl Scene for TitleScene {
fn draw(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
self.background.draw(state, ctx, &self.frame, &self.textures, &self.stage)?;
{
if self.current_menu != CurrentMenu::SaveSelectMenu {
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "Title")?;
batch.add_rect(
((state.canvas_size.0 - state.constants.title.logo_rect.width() as f32) / 2.0).floor(),