1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2024-11-26 23:33:01 +00:00

Added save select menu (#58)

This commit is contained in:
dawndus 2022-02-06 12:23:24 -05:00 committed by GitHub
parent 2223358991
commit 693155ca6a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 243 additions and 26 deletions

View file

@ -1,17 +1,18 @@
use std::cell::Cell;
use crate::common::{Color, Rect};
use crate::components::draw_common::{draw_number, Alignment};
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::framework::graphics;
use crate::input::combined_menu_controller::CombinedMenuController;
use crate::menu::save_select_menu::MenuSaveInfo;
use crate::shared_game_state::{MenuCharacter, SharedGameState};
pub mod pause_menu;
pub mod save_select_menu;
pub mod settings_menu;
pub struct MenuSaveInfo {}
pub enum MenuEntry {
Hidden,
Active(String),
@ -402,6 +403,55 @@ impl Menu {
graphics::draw_rect(ctx, bar_rect, Color::new(1.0, 1.0, 1.0, 1.0))?;
}
}
MenuEntry::NewSave => {
state.font.draw_text(
"New Save".chars(),
self.x as f32 + 20.0,
y,
&state.constants,
&mut state.texture_set,
ctx,
)?;
}
MenuEntry::SaveData(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;
// Lifebar
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "TextBox")?;
batch.add_rect(right_edge - 60.0, y, &Rect::new_size(0, 40, 24, 8));
batch.add_rect(right_edge - 36.0, y, &Rect::new_size(24, 40, 40, 8));
batch.add_rect(right_edge - 36.0, y, &Rect::new_size(0, 24, bar_width, 8));
state.font.draw_text(
name.chars(),
self.x as f32 + 20.0,
y,
&state.constants,
&mut state.texture_set,
ctx,
)?;
// Weapons
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "ArmsImage")?;
for weapon_slot in 0..save.weapon_count {
let wtype = save.weapon_id[weapon_slot];
let pos_x = weapon_slot as f32 * 16.0 - (16 * save.weapon_count.saturating_sub(4)) as f32;
let mut rect = Rect::new(0, 0, 0, 16);
if wtype != 0 {
rect.left = wtype as u16 * 16;
rect.right = rect.left + 16;
batch.add_rect(right_edge + pos_x - 60.0, y + 8.0, &rect);
}
}
batch.draw(ctx)?;
draw_number(right_edge - 36.0, y, save.life as usize, Alignment::Right, state, ctx)?;
}
_ => {}
}
@ -453,6 +503,8 @@ impl Menu {
let mut y = self.y as f32 + 8.0;
for (idx, entry) in self.entries.iter_mut().enumerate() {
let entry_bounds = Rect::new_size(self.x, y as isize, self.width as isize, entry.height() as isize);
let right_entry_bounds =
Rect::new_size(self.x + self.width as isize, y as isize, self.width as isize, entry.height() as isize);
y += entry.height() as f32;
match entry {
@ -460,6 +512,8 @@ impl Menu {
| MenuEntry::Toggle(_, _)
| MenuEntry::Options(_, _, _)
| MenuEntry::DescriptiveOptions(_, _, _, _)
| MenuEntry::SaveData(_)
| MenuEntry::NewSave
if (self.selected == idx && controller.trigger_ok())
|| state.touch_controls.consume_click_in(entry_bounds) =>
{
@ -478,11 +532,16 @@ impl Menu {
state.sound_manager.play_sfx(1);
return MenuSelectionResult::Right(self.selected, entry, 1);
}
MenuEntry::DescriptiveOptions(_, _, _, _) if self.selected == idx && controller.trigger_left() => {
MenuEntry::DescriptiveOptions(_, _, _, _)
if (self.selected == idx && controller.trigger_left())
|| state.touch_controls.consume_click_in(right_entry_bounds) =>
{
state.sound_manager.play_sfx(1);
return MenuSelectionResult::Left(self.selected, entry, -1);
}
MenuEntry::DescriptiveOptions(_, _, _, _) if self.selected == idx && controller.trigger_right() => {
MenuEntry::DescriptiveOptions(_, _, _, _) | MenuEntry::SaveData(_)
if self.selected == idx && controller.trigger_right() =>
{
state.sound_manager.play_sfx(1);
return MenuSelectionResult::Right(self.selected, entry, 1);
}

View file

@ -0,0 +1,144 @@
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::framework::filesystem;
use crate::input::combined_menu_controller::CombinedMenuController;
use crate::menu::MenuEntry;
use crate::menu::{Menu, MenuSelectionResult};
use crate::profile::GameProfile;
use crate::shared_game_state::SharedGameState;
#[derive(Clone, Copy)]
pub struct MenuSaveInfo {
pub current_map: u32,
pub max_life: u16,
pub life: u16,
pub weapon_count: usize,
pub weapon_id: [u32; 8],
}
impl Default for MenuSaveInfo {
fn default() -> Self {
MenuSaveInfo { current_map: 0, max_life: 0, life: 0, weapon_count: 0, weapon_id: [0; 8] }
}
}
pub enum CurrentMenu {
SaveMenu,
DeleteConfirm,
}
pub struct SaveSelectMenu {
pub saves: [MenuSaveInfo; 3],
current_menu: CurrentMenu,
save_menu: Menu,
delete_confirm: Menu,
}
impl SaveSelectMenu {
pub fn new() -> SaveSelectMenu {
SaveSelectMenu {
saves: [MenuSaveInfo::default(); 3],
current_menu: CurrentMenu::SaveMenu,
save_menu: Menu::new(0, 0, 200, 0),
delete_confirm: Menu::new(0, 0, 75, 0),
}
}
pub fn init(&mut self, state: &mut SharedGameState, ctx: &Context) -> GameResult {
for (iter, save) in self.saves.iter_mut().enumerate() {
if let Ok(data) = filesystem::user_open(ctx, state.get_save_filename(iter + 1)) {
let loaded_save = GameProfile::load_from_save(data)?;
save.current_map = loaded_save.current_map;
save.max_life = loaded_save.max_life;
save.life = loaded_save.life;
save.weapon_count = loaded_save.weapon_data.iter().filter(|weapon| weapon.weapon_id != 0).count();
save.weapon_id = loaded_save.weapon_data.map(|weapon| weapon.weapon_id);
self.save_menu.push_entry(MenuEntry::SaveData(*save));
} else {
self.save_menu.push_entry(MenuEntry::NewSave);
}
}
self.save_menu.push_entry(MenuEntry::Active("< Back".to_owned()));
self.save_menu.push_entry(MenuEntry::Disabled("Press Right to Delete".to_owned()));
self.delete_confirm.push_entry(MenuEntry::Disabled("Delete?".to_owned()));
self.delete_confirm.push_entry(MenuEntry::Active("Yes".to_owned()));
self.delete_confirm.push_entry(MenuEntry::Active("No".to_owned()));
self.delete_confirm.selected = 2;
self.update_sizes(state);
Ok(())
}
fn update_sizes(&mut self, state: &SharedGameState) {
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.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
}
pub fn tick(
&mut self,
exit_action: &mut dyn FnMut(),
controller: &mut CombinedMenuController,
state: &mut SharedGameState,
ctx: &mut Context,
) -> GameResult {
self.update_sizes(state);
match self.current_menu {
CurrentMenu::SaveMenu => match self.save_menu.tick(controller, state) {
MenuSelectionResult::Selected(0, _) => {
state.save_slot = 1;
state.load_or_start_game(ctx)?;
}
MenuSelectionResult::Selected(1, _) => {
state.save_slot = 2;
state.load_or_start_game(ctx)?;
}
MenuSelectionResult::Selected(2, _) => {
state.save_slot = 3;
state.load_or_start_game(ctx)?;
}
MenuSelectionResult::Selected(3, _) | MenuSelectionResult::Canceled => exit_action(),
MenuSelectionResult::Right(slot, _, _) => {
if slot <= 2 {
self.current_menu = CurrentMenu::DeleteConfirm;
self.delete_confirm.selected = 2;
}
}
_ => (),
},
CurrentMenu::DeleteConfirm => match self.delete_confirm.tick(controller, state) {
MenuSelectionResult::Selected(1, _) => {
state.sound_manager.play_sfx(17); // Player Death sfx
filesystem::user_delete(ctx, state.get_save_filename(self.save_menu.selected + 1))?;
self.save_menu.entries[self.save_menu.selected] = MenuEntry::NewSave;
self.current_menu = CurrentMenu::SaveMenu;
}
MenuSelectionResult::Selected(2, _) | MenuSelectionResult::Canceled => {
self.current_menu = CurrentMenu::SaveMenu;
}
_ => (),
},
}
Ok(())
}
pub fn draw(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
match self.current_menu {
CurrentMenu::SaveMenu => {
self.save_menu.draw(state, ctx)?;
}
CurrentMenu::DeleteConfirm => {
self.delete_confirm.draw(state, ctx)?;
}
}
Ok(())
}
}

View file

@ -9,6 +9,7 @@ use crate::framework::graphics;
use crate::input::combined_menu_controller::CombinedMenuController;
use crate::input::touch_controls::TouchControlType;
use crate::map::Map;
use crate::menu::save_select_menu::SaveSelectMenu;
use crate::menu::settings_menu::SettingsMenu;
use crate::menu::{Menu, MenuEntry, MenuSelectionResult};
use crate::scene::Scene;
@ -33,7 +34,7 @@ pub struct TitleScene {
current_menu: CurrentMenu,
main_menu: Menu,
option_menu: SettingsMenu,
save_select_menu: Menu,
save_select_menu: SaveSelectMenu,
background: Background,
frame: Frame,
nikumaru_rec: NikumaruCounter,
@ -67,7 +68,7 @@ impl TitleScene {
current_menu: CurrentMenu::MainMenu,
main_menu: Menu::new(0, 0, 100, 0),
option_menu: SettingsMenu::new(),
save_select_menu: Menu::new(0, 0, 200, 0),
save_select_menu: SaveSelectMenu::new(),
background: Background::new(),
frame: Frame::new(),
nikumaru_rec: NikumaruCounter::new(),
@ -126,8 +127,7 @@ impl Scene for TitleScene {
self.controller.add(state.settings.create_player1_controller());
self.controller.add(state.settings.create_player2_controller());
self.main_menu.push_entry(MenuEntry::Active("New game".to_string()));
self.main_menu.push_entry(MenuEntry::Active("Load game".to_string()));
self.main_menu.push_entry(MenuEntry::Active("Start Game".to_string()));
self.main_menu.push_entry(MenuEntry::Active("Options".to_string()));
if cfg!(feature = "editor") {
self.main_menu.push_entry(MenuEntry::Active("Editor".to_string()));
@ -138,11 +138,7 @@ impl Scene for TitleScene {
self.option_menu.init(state, ctx)?;
self.save_select_menu.push_entry(MenuEntry::NewSave);
self.save_select_menu.push_entry(MenuEntry::NewSave);
self.save_select_menu.push_entry(MenuEntry::NewSave);
self.save_select_menu.push_entry(MenuEntry::Active("Delete a save".to_string()));
self.save_select_menu.push_entry(MenuEntry::Active("Back".to_string()));
self.save_select_menu.init(state, ctx)?;
self.controller.update(state, ctx)?;
self.controller.update_trigger();
@ -166,20 +162,12 @@ impl Scene for TitleScene {
match self.current_menu {
CurrentMenu::MainMenu => match self.main_menu.tick(&mut self.controller, state) {
MenuSelectionResult::Selected(0, _) => {
state.reset();
state.sound_manager.play_song(0, &state.constants, &state.settings, ctx)?;
self.tick = 1;
self.current_menu = CurrentMenu::StartGame;
self.current_menu = CurrentMenu::SaveSelectMenu;
}
MenuSelectionResult::Selected(1, _) => {
state.sound_manager.play_song(0, &state.constants, &state.settings, ctx)?;
self.tick = 1;
self.current_menu = CurrentMenu::LoadGame;
}
MenuSelectionResult::Selected(2, _) => {
self.current_menu = CurrentMenu::OptionMenu;
}
MenuSelectionResult::Selected(3, _) => {
MenuSelectionResult::Selected(2, _) => {
// this comment is just there because rustfmt removes parenthesis around the match case and breaks compilation
#[cfg(feature = "editor")]
{
@ -187,7 +175,7 @@ impl Scene for TitleScene {
state.next_scene = Some(Box::new(EditorScene::new()));
}
}
MenuSelectionResult::Selected(4, _) => {
MenuSelectionResult::Selected(3, _) => {
state.shutdown();
}
_ => {}
@ -207,6 +195,17 @@ impl Scene for TitleScene {
self.update_menu_cursor(state, ctx)?;
}
}
CurrentMenu::SaveSelectMenu => {
let cm = &mut self.current_menu;
self.save_select_menu.tick(
&mut || {
*cm = CurrentMenu::MainMenu;
},
&mut self.controller,
state,
ctx,
)?;
}
CurrentMenu::StartGame => {
if self.tick == 10 {
state.reset_skip_flags();
@ -253,6 +252,7 @@ impl Scene for TitleScene {
match self.current_menu {
CurrentMenu::MainMenu => self.main_menu.draw(state, ctx)?,
CurrentMenu::OptionMenu => self.option_menu.draw(state, ctx)?,
CurrentMenu::SaveSelectMenu => self.save_select_menu.draw(state, ctx)?,
_ => {}
}

View file

@ -184,6 +184,7 @@ pub struct SharedGameState {
pub lua: LuaScriptingState,
pub sound_manager: SoundManager,
pub settings: Settings,
pub save_slot: usize,
pub shutdown: bool,
}
@ -293,6 +294,7 @@ impl SharedGameState {
lua: LuaScriptingState::new(),
sound_manager,
settings,
save_slot: 1,
shutdown: false,
})
}
@ -384,7 +386,11 @@ impl SharedGameState {
}
pub fn save_game(&mut self, game_scene: &mut GameScene, ctx: &mut Context) -> GameResult {
if let Ok(data) = filesystem::open_options(ctx, "/Profile.dat", OpenOptions::new().write(true).create(true)) {
if let Ok(data) = filesystem::open_options(
ctx,
self.get_save_filename(self.save_slot),
OpenOptions::new().write(true).create(true),
) {
let profile = GameProfile::dump(self, game_scene);
profile.write_save(data)?;
} else {
@ -395,7 +401,7 @@ impl SharedGameState {
}
pub fn load_or_start_game(&mut self, ctx: &mut Context) -> GameResult {
if let Ok(data) = filesystem::user_open(ctx, "/Profile.dat") {
if let Ok(data) = filesystem::user_open(ctx, self.get_save_filename(self.save_slot)) {
match GameProfile::load_from_save(data) {
Ok(profile) => {
self.reset();
@ -529,4 +535,12 @@ impl SharedGameState {
false
}
}
pub fn get_save_filename(&self, slot: usize) -> String {
if slot == 1 {
"/Profile.dat".to_owned()
} else {
format!("/Profile{}.dat", slot)
}
}
}