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:
parent
68318e3a69
commit
339f822a80
105
src/menu/mod.rs
105
src/menu/mod.rs
|
@ -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;
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
Loading…
Reference in a new issue