mirror of
https://github.com/doukutsu-rs/doukutsu-rs.git
synced 2026-08-24 13:02:49 -07:00
Implement export of the profile [ci skip]
This commit is contained in:
Generated
+881
-2
File diff suppressed because it is too large
Load Diff
@@ -82,6 +82,7 @@ pelite = { version = ">=0.9.2", default-features = false, features = ["std"] }
|
||||
sdl2 = { git = "https://github.com/doukutsu-rs/rust-sdl2.git", rev = "244ae85833cff4f97ab4b58331741be20e422bd7", optional = true, features = ["unsafe_textures", "bundled", "static-link"] }
|
||||
sdl2-sys = { git = "https://github.com/doukutsu-rs/rust-sdl2.git", rev = "244ae85833cff4f97ab4b58331741be20e422bd7", optional = true, features = ["bundled", "static-link"] }
|
||||
rc-box = "1.2.0"
|
||||
rfd = "0.15.4"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_derive = "1"
|
||||
serde_cbor = { version = "0.11", optional = true }
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"yes": "Yes",
|
||||
"no": "No",
|
||||
"on": "ON",
|
||||
"off": "OFF"
|
||||
"off": "OFF",
|
||||
"choose_file": "No file selected…"
|
||||
},
|
||||
"menus": {
|
||||
"main_menu": {
|
||||
@@ -67,7 +68,28 @@
|
||||
"save_manage_menu": {
|
||||
"import_export_save": "Import/Export Save...",
|
||||
"import_format": "Import format",
|
||||
"export_format": "Export format"
|
||||
"export_format": "Export format",
|
||||
"action_type": {
|
||||
"entry": "Action:",
|
||||
"import": "Import",
|
||||
"export": "Export"
|
||||
},
|
||||
"save_location": {
|
||||
"import": "Import location",
|
||||
"export": "Export location"
|
||||
},
|
||||
"save_format": {
|
||||
"entry": "Save format:",
|
||||
"freeware": "Freeware",
|
||||
"plus": "Cave Story+ (PC)",
|
||||
"switch": "Cave Story+ (Switch)",
|
||||
"auto": "Auto-detect"
|
||||
},
|
||||
"file_filters": {
|
||||
"freeware": "Cave Story save",
|
||||
"plus": "Cave Story+ save",
|
||||
"switch": "Cave Story+ save"
|
||||
}
|
||||
},
|
||||
"options_menu": {
|
||||
"graphics": "Graphics...",
|
||||
|
||||
+203
-436
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ use crate::framework::{filesystem, graphics};
|
||||
use crate::game::caret::{Caret, CaretType};
|
||||
use crate::game::npc::NPCTable;
|
||||
use crate::game::player::TargetPlayer;
|
||||
use crate::game::profile::{GameProfile, SaveContainer, SaveFormat, SaveSlot};
|
||||
use crate::game::profile::{GameProfile, SaveContainer, SaveFormat, SaveParams, SaveSlot};
|
||||
use crate::game::scripting::tsc::credit_script::{CreditScript, CreditScriptVM};
|
||||
use crate::game::scripting::tsc::text_script::{
|
||||
ScriptMode, TextScript, TextScriptEncoding, TextScriptExecutionState, TextScriptVM,
|
||||
@@ -697,9 +697,7 @@ impl SharedGameState {
|
||||
|
||||
let mut save_container = SaveContainer::load(ctx, self)?;
|
||||
save_container.set_profile(slot, profile);
|
||||
//save_container.write_save(ctx, state, SaveFormat::Generic, None, None);
|
||||
//save_container.write_save(ctx, state, self.settings.save_format, None, None);
|
||||
save_container.save(ctx, self)?;
|
||||
save_container.save(ctx, self, SaveParams::default())?;
|
||||
} else {
|
||||
log::info!("Mod has saves disabled.");
|
||||
}
|
||||
|
||||
@@ -183,8 +183,6 @@ impl Weapon {
|
||||
self.refire_timer = 4;
|
||||
}
|
||||
|
||||
// todo lua hook
|
||||
|
||||
match self.wtype {
|
||||
WeaponType::None => {}
|
||||
WeaponType::Snake => self.tick_snake(player, player_id, bullet_manager, state),
|
||||
|
||||
+5
-1
@@ -86,8 +86,12 @@ impl Locale {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ts(&self, key: &str) -> String {
|
||||
self.t(key).to_owned()
|
||||
}
|
||||
|
||||
pub fn tt(&self, key: &str, args: &[(&str, &str)]) -> String {
|
||||
let mut string = self.t(key).to_owned();
|
||||
let mut string = self.ts(key);
|
||||
|
||||
for (key, value) in args.iter() {
|
||||
string = string.replace(&format!("{{{}}}", key), &value);
|
||||
|
||||
+107
-12
@@ -1,4 +1,6 @@
|
||||
use std::cell::Cell;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::common::{Color, Rect};
|
||||
use crate::components::draw_common::{draw_number, Alignment};
|
||||
@@ -9,6 +11,7 @@ use crate::game::shared_game_state::{GameDifficulty, MenuCharacter, SharedGameSt
|
||||
use crate::graphics::font::Font;
|
||||
use crate::input::combined_menu_controller::CombinedMenuController;
|
||||
use crate::menu::save_select_menu::MenuSaveInfo;
|
||||
use crate::util::file_picker::{open_file_picker, FilePickerParams};
|
||||
|
||||
pub mod controls_menu;
|
||||
pub mod coop_menu;
|
||||
@@ -17,6 +20,7 @@ pub mod save_select_menu;
|
||||
pub mod settings_menu;
|
||||
|
||||
const MENU_MIN_PADDING: f32 = 30.0;
|
||||
const MENU_DISABLED_COLOR: (u8, u8, u8, u8) = (0xa0, 0xa0, 0xff, 0xff);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ControlMenuData {
|
||||
@@ -43,6 +47,7 @@ pub enum MenuEntry {
|
||||
PlayerSkin,
|
||||
Control(String, ControlMenuData),
|
||||
Spacer(f64),
|
||||
FilePicker(String, bool, FilePickerParams, Option<Vec<PathBuf>>), // text, display selected file names, params, selected files/dirs
|
||||
}
|
||||
|
||||
impl MenuEntry {
|
||||
@@ -64,6 +69,7 @@ impl MenuEntry {
|
||||
MenuEntry::PlayerSkin => 24.0,
|
||||
MenuEntry::Control(_, _) => 16.0,
|
||||
MenuEntry::Spacer(height) => *height,
|
||||
MenuEntry::FilePicker(_, _, _, _) => 16.0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +91,7 @@ impl MenuEntry {
|
||||
MenuEntry::PlayerSkin => true,
|
||||
MenuEntry::Control(_, _) => true,
|
||||
MenuEntry::Spacer(_) => false,
|
||||
MenuEntry::FilePicker(_, _, _, _) => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,6 +104,7 @@ pub enum MenuSelectionResult<'a, T: std::cmp::PartialEq> {
|
||||
Right(T, &'a mut MenuEntry, i16),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Menu<T: std::cmp::PartialEq> {
|
||||
pub x: isize,
|
||||
pub y: isize,
|
||||
@@ -154,6 +162,26 @@ impl<T: std::cmp::PartialEq + std::default::Default + Clone> Menu<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_entry(&self, id: T) -> Option<&MenuEntry> {
|
||||
for i in 0..self.entries.len() {
|
||||
if self.entries[i].0 == id {
|
||||
return Some(&self.entries[i].1);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_entry_mut(&mut self, id: T) -> Option<&mut MenuEntry> {
|
||||
for i in 0..self.entries.len() {
|
||||
if self.entries[i].0 == id {
|
||||
return Some(&mut self.entries[i].1);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn update_width(&mut self, state: &SharedGameState) {
|
||||
let mut width = self.width as f32;
|
||||
|
||||
@@ -216,7 +244,25 @@ impl<T: std::cmp::PartialEq + std::default::Default + Clone> Menu<T> {
|
||||
MenuEntry::NewSave => {}
|
||||
MenuEntry::PlayerSkin => {}
|
||||
MenuEntry::Control(_, _) => {}
|
||||
MenuEntry::Spacer(_) => {}
|
||||
MenuEntry::Spacer(_) => {},
|
||||
MenuEntry::FilePicker(entry, display, _, selection) => {
|
||||
let mut entry_with_selection = entry.clone();
|
||||
|
||||
if *display {
|
||||
entry_with_selection.push_str(" ");
|
||||
|
||||
let filename = selection.as_ref()
|
||||
.and_then(|files| files.first())
|
||||
.and_then(|file| file.file_name())
|
||||
.and_then(|filename| filename.to_str())
|
||||
.unwrap_or(state.loc.t("common.choose_file"));
|
||||
|
||||
entry_with_selection.push_str(filename);
|
||||
}
|
||||
|
||||
let entry_width = state.font.builder().compute_width(&entry_with_selection) + 32.0;
|
||||
width = width.max(entry_width);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,21 +399,22 @@ impl<T: std::cmp::PartialEq + std::default::Default + Clone> Menu<T> {
|
||||
batch.draw(ctx)?;
|
||||
|
||||
let options_x = if self.center_options {
|
||||
let mut longest_option_width = 20.0;
|
||||
let mut longest_entry_width = 20.0;
|
||||
|
||||
for (_, entry) in &self.entries {
|
||||
match entry {
|
||||
let text_width = match entry {
|
||||
MenuEntry::Options(text, _, _) | MenuEntry::Active(text) => {
|
||||
let text_width = state.font.builder().compute_width(text) + 32.0;
|
||||
if text_width > longest_option_width {
|
||||
longest_option_width = text_width;
|
||||
}
|
||||
state.font.builder().compute_width(text) + 32.0
|
||||
}
|
||||
_ => {}
|
||||
_ => 0.0
|
||||
};
|
||||
|
||||
if text_width > longest_entry_width {
|
||||
longest_entry_width = text_width;
|
||||
}
|
||||
}
|
||||
|
||||
(state.canvas_size.0 / 2.0) - (longest_option_width / 2.0)
|
||||
(state.canvas_size.0 / 2.0) - (longest_entry_width / 2.0)
|
||||
} else {
|
||||
self.x as f32
|
||||
};
|
||||
@@ -471,7 +518,7 @@ impl<T: std::cmp::PartialEq + std::default::Default + Clone> Menu<T> {
|
||||
let mut builder = state.font.builder().position(x, local_y);
|
||||
|
||||
if !*is_white {
|
||||
builder = builder.color((0xa0, 0xa0, 0xff, 0xff));
|
||||
builder = builder.color(MENU_DISABLED_COLOR);
|
||||
}
|
||||
|
||||
builder.draw(&line, ctx, &state.constants, &mut state.texture_set)?;
|
||||
@@ -482,7 +529,7 @@ impl<T: std::cmp::PartialEq + std::default::Default + Clone> Menu<T> {
|
||||
y += entry.height() as f32 * (lines.len() - 1) as f32;
|
||||
}
|
||||
MenuEntry::Disabled(name) => {
|
||||
state.font.builder().position(self.x as f32 + 20.0, y).color((0xa0, 0xa0, 0xff, 0xff)).draw(
|
||||
state.font.builder().position(self.x as f32 + 20.0, y).color(MENU_DISABLED_COLOR).draw(
|
||||
name,
|
||||
ctx,
|
||||
&state.constants,
|
||||
@@ -548,7 +595,7 @@ impl<T: std::cmp::PartialEq + std::default::Default + Clone> Menu<T> {
|
||||
.font
|
||||
.builder()
|
||||
.position(self.x as f32 + 20.0, y + 16.0)
|
||||
.color((0xc0, 0xc0, 0xff, 0xff))
|
||||
.color(MENU_DISABLED_COLOR)
|
||||
.draw(description_text, ctx, &state.constants, &mut state.texture_set)?;
|
||||
}
|
||||
MenuEntry::OptionsBar(name, percent) => {
|
||||
@@ -739,6 +786,42 @@ impl<T: std::cmp::PartialEq + std::default::Default + Clone> Menu<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
MenuEntry::FilePicker(name, display, _, selection) => {
|
||||
// TODO: fix text is rendered out of the text box bounds if the filename is too long.
|
||||
|
||||
let name_text_len = state.font.builder().compute_width(name);
|
||||
let value_text = selection.as_ref()
|
||||
.and_then(|files| files.first())
|
||||
.and_then(|file| file.file_name())
|
||||
.and_then(|filename| filename.to_str())
|
||||
.unwrap_or(state.loc.t("common.choose_file"));
|
||||
|
||||
// Draw the entry as disabled on unsupported platforms
|
||||
let text_color = if cfg!(not(any(target_os = "android", target_os = "horizon"))) {
|
||||
state.font.builder().get_color()
|
||||
} else {
|
||||
MENU_DISABLED_COLOR
|
||||
};
|
||||
|
||||
state.font.builder().position(self.x as f32 + 20.0, y)
|
||||
.draw(
|
||||
name,
|
||||
ctx,
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
)?;
|
||||
|
||||
if *display {
|
||||
state.font.builder()
|
||||
.position(self.x as f32 + 25.0 + name_text_len, y)
|
||||
.draw(
|
||||
value_text,
|
||||
ctx,
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -882,6 +965,18 @@ impl<T: std::cmp::PartialEq + std::default::Default + Clone> Menu<T> {
|
||||
return MenuSelectionResult::Selected(idx, entry);
|
||||
}
|
||||
}
|
||||
MenuEntry::FilePicker(_, _, params, selection) => {
|
||||
if self.selected == idx && controller.trigger_ok()
|
||||
|| state.touch_controls.consume_click_in(entry_bounds)
|
||||
{
|
||||
state.sound_manager.play_sfx(18);
|
||||
self.selected = idx.clone();
|
||||
|
||||
*selection = open_file_picker(params);
|
||||
|
||||
return MenuSelectionResult::Selected(idx, entry);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
+238
-27
@@ -1,12 +1,15 @@
|
||||
use pelite::pe::imports::Import;
|
||||
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::filesystem;
|
||||
use crate::game::profile::{GameProfile, SaveContainer, SaveFormat, SaveSlot};
|
||||
use crate::game::profile::{GameProfile, SaveContainer, SaveFormat, SaveParams, SaveSlot};
|
||||
use crate::game::shared_game_state::{GameDifficulty, SharedGameState};
|
||||
use crate::input::combined_menu_controller::CombinedMenuController;
|
||||
use crate::menu::coop_menu::PlayerCountMenu;
|
||||
use crate::menu::MenuEntry;
|
||||
use crate::menu::{Menu, MenuSelectionResult};
|
||||
use crate::util::file_picker::FilePickerParams;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct MenuSaveInfo {
|
||||
@@ -91,19 +94,141 @@ impl Default for LoadConfirmMenuEntry {
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum ImportExportMenuEntry {
|
||||
Format(SaveFormat),
|
||||
ExportLocation,
|
||||
Format,
|
||||
Import,
|
||||
Export,
|
||||
Back,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum ImportExportLocation {
|
||||
Filesystem,
|
||||
// TODO: add some Switch emulators
|
||||
}
|
||||
|
||||
impl Default for ImportExportLocation {
|
||||
fn default() -> Self {
|
||||
Self::Filesystem
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MenuExportInfo {
|
||||
pub location: ImportExportLocation,
|
||||
pub format: SaveFormat,
|
||||
pub picker_params: FilePickerParams,
|
||||
pub save_params: SaveParams,
|
||||
}
|
||||
|
||||
impl Default for MenuExportInfo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
location: ImportExportLocation::Filesystem,
|
||||
format: SaveFormat::Freeware,
|
||||
picker_params: FilePickerParams::new(),
|
||||
save_params: SaveParams::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MenuExportInfo {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn set_picker_params(&mut self, state: &SharedGameState, is_export: bool) {
|
||||
let filename = SaveContainer::get_save_filename(&self.format, None).split_off(1);
|
||||
|
||||
let mut params = match self.format {
|
||||
SaveFormat::Freeware => {
|
||||
FilePickerParams::new()
|
||||
.pick_dirs(true)
|
||||
}
|
||||
SaveFormat::Plus | SaveFormat::Switch => {
|
||||
FilePickerParams::new()
|
||||
.file_name(Some(filename))
|
||||
.filter(state.loc.ts("menus.save_manage_menu.file_filters.plus"), vec![
|
||||
"dat".to_owned()
|
||||
])
|
||||
}
|
||||
SaveFormat::Generic => {
|
||||
FilePickerParams::new()
|
||||
.file_name(Some(filename))
|
||||
.filter(state.loc.ts("menus.save_manage_menu.file_filters.generic"), vec![
|
||||
"json".to_owned()
|
||||
])
|
||||
}
|
||||
};
|
||||
|
||||
if is_export {
|
||||
params = params.save(true);
|
||||
};
|
||||
|
||||
if let Some(fs_container) = &state.fs_container {
|
||||
params = params.starting_dir(Some(fs_container.user_path.clone()));
|
||||
}
|
||||
|
||||
self.picker_params = params;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl ImportExportMenuEntry {
|
||||
fn format_from_value(val: usize) -> Option<SaveFormat> {
|
||||
match val {
|
||||
0 => None, // Auto
|
||||
1 => Some(SaveFormat::Freeware),
|
||||
2 => Some(SaveFormat::Plus),
|
||||
3 => Some(SaveFormat::Switch),
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
fn fpicker_from_format(state: &SharedGameState, format: SaveFormat, is_export: bool) -> FilePickerParams {
|
||||
let filename = SaveContainer::get_save_filename(&format, None).split_off(1);
|
||||
|
||||
let mut params = match format {
|
||||
SaveFormat::Freeware => {
|
||||
FilePickerParams::new()
|
||||
.pick_dirs(true)
|
||||
}
|
||||
SaveFormat::Plus | SaveFormat::Switch => {
|
||||
FilePickerParams::new()
|
||||
.file_name(Some(filename))
|
||||
.filter(state.loc.ts("menus.save_manage_menu.file_filters.plus"), vec![
|
||||
"dat".to_owned()
|
||||
])
|
||||
}
|
||||
SaveFormat::Generic => {
|
||||
FilePickerParams::new()
|
||||
.file_name(Some(filename))
|
||||
.filter(state.loc.ts("menus.save_manage_menu.file_filters.generic"), vec![
|
||||
"json".to_owned()
|
||||
])
|
||||
}
|
||||
};
|
||||
|
||||
if is_export && format != SaveFormat::Freeware {
|
||||
params = params.save(true);
|
||||
};
|
||||
|
||||
if let Some(fs_container) = &state.fs_container {
|
||||
params = params.starting_dir(Some(fs_container.user_path.clone()));
|
||||
}
|
||||
|
||||
params
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ImportExportMenuEntry {
|
||||
fn default() -> Self {
|
||||
ImportExportMenuEntry::Format(SaveFormat::Freeware)
|
||||
ImportExportMenuEntry::Format
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SaveSelectMenu {
|
||||
pub saves: [MenuSaveInfo; 3],
|
||||
pub export_info: MenuExportInfo,
|
||||
current_menu: CurrentMenu,
|
||||
save_menu: Menu<SaveMenuEntry>,
|
||||
save_detailed: Menu<usize>,
|
||||
@@ -119,6 +244,7 @@ impl SaveSelectMenu {
|
||||
pub fn new() -> SaveSelectMenu {
|
||||
SaveSelectMenu {
|
||||
saves: [MenuSaveInfo::default(); 3],
|
||||
export_info: MenuExportInfo::default(),
|
||||
current_menu: CurrentMenu::SaveMenu,
|
||||
save_menu: Menu::new(0, 0, 230, 0),
|
||||
coop_menu: PlayerCountMenu::new(),
|
||||
@@ -139,6 +265,7 @@ impl SaveSelectMenu {
|
||||
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.import_export_menu = Menu::new(0, 0, 75, 0);
|
||||
self.skip_difficulty_menu = false;
|
||||
|
||||
let mut should_mutate_selection = true;
|
||||
@@ -147,7 +274,7 @@ impl SaveSelectMenu {
|
||||
for (iter, save) in self.saves.iter_mut().enumerate() {
|
||||
if let Some(slot) = state.get_save_slot(iter + 1) {
|
||||
if let Some(loaded_profile) = save_container.get_profile(slot) {
|
||||
log::debug!("Loading save select menu. Iter - {}. {}", iter, loaded_profile.is_empty());
|
||||
log::trace!("Loading save select menu. Iter - {}. {}", iter, loaded_profile.is_empty());
|
||||
save.current_map = loaded_profile.current_map;
|
||||
save.max_life = loaded_profile.max_life;
|
||||
save.life = loaded_profile.life;
|
||||
@@ -179,7 +306,7 @@ impl SaveSelectMenu {
|
||||
}
|
||||
}
|
||||
|
||||
self.save_menu.push_entry(SaveMenuEntry::ImportExport, MenuEntry::Disabled(state.loc.t("menus.save_manage_menu.import_export_save").to_owned()));
|
||||
self.save_menu.push_entry(SaveMenuEntry::ImportExport, MenuEntry::Active(state.loc.ts("menus.save_manage_menu.import_export_save")));
|
||||
self.save_menu.push_entry(SaveMenuEntry::Back, MenuEntry::Active(state.loc.t("common.back").to_owned()));
|
||||
|
||||
self.difficulty_menu.push_entry(
|
||||
@@ -203,8 +330,6 @@ impl SaveSelectMenu {
|
||||
|
||||
self.difficulty_menu.selected = DifficultyMenuEntry::Difficulty(GameDifficulty::Normal);
|
||||
|
||||
//self.coop_menu.init(state, ctx);
|
||||
|
||||
self.delete_confirm.push_entry(
|
||||
DeleteConfirmMenuEntry::Title,
|
||||
MenuEntry::Disabled(state.loc.t("menus.save_menu.delete_confirm").to_owned()),
|
||||
@@ -233,6 +358,37 @@ impl SaveSelectMenu {
|
||||
self.save_detailed.push_entry(0, MenuEntry::SaveDataSingle(save));
|
||||
}
|
||||
|
||||
self.export_info.format = state.settings.save_format;
|
||||
//self.export_info.set_picker_params(state, true);
|
||||
|
||||
self.import_export_menu.push_entry(
|
||||
ImportExportMenuEntry::Format,
|
||||
MenuEntry::Options(
|
||||
state.loc.ts("menus.save_manage_menu.save_format.entry"),
|
||||
0,
|
||||
vec![
|
||||
state.loc.ts("menus.save_manage_menu.save_format.auto"),
|
||||
state.loc.ts("menus.save_manage_menu.save_format.freeware"),
|
||||
state.loc.ts("menus.save_manage_menu.save_format.plus"),
|
||||
state.loc.ts("menus.save_manage_menu.save_format.switch"),
|
||||
]
|
||||
)
|
||||
);
|
||||
self.import_export_menu.push_entry(
|
||||
ImportExportMenuEntry::Import,
|
||||
MenuEntry::Disabled(state.loc.ts("menus.save_manage_menu.action_type.import"))
|
||||
);
|
||||
self.import_export_menu.push_entry(
|
||||
ImportExportMenuEntry::Export,
|
||||
MenuEntry::FilePicker(
|
||||
state.loc.ts("menus.save_manage_menu.action_type.export"),
|
||||
false,
|
||||
ImportExportMenuEntry::fpicker_from_format(state, self.export_info.format, true),
|
||||
None
|
||||
)
|
||||
);
|
||||
self.import_export_menu.push_entry(ImportExportMenuEntry::Back, MenuEntry::Active(state.loc.ts("common.back")));
|
||||
|
||||
self.update_sizes(state);
|
||||
|
||||
Ok(())
|
||||
@@ -268,6 +424,11 @@ impl SaveSelectMenu {
|
||||
self.save_detailed.update_height(state);
|
||||
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;
|
||||
|
||||
self.import_export_menu.update_width(state);
|
||||
self.import_export_menu.update_height(state);
|
||||
self.import_export_menu.x = ((state.canvas_size.0 - self.import_export_menu.width as f32) / 2.0).floor() as isize;
|
||||
self.import_export_menu.y = ((state.canvas_size.1 - self.import_export_menu.height as f32) / 2.0).floor() as isize;
|
||||
}
|
||||
|
||||
pub fn tick(
|
||||
@@ -301,19 +462,10 @@ impl SaveSelectMenu {
|
||||
|
||||
self.current_menu = CurrentMenu::LoadConfirm;
|
||||
self.load_confirm.selected = LoadConfirmMenuEntry::Start;
|
||||
/*
|
||||
if let Ok(_) =
|
||||
filesystem::user_open(ctx, state.get_save_filename(state.save_slot).unwrap_or(String::new()))
|
||||
{
|
||||
if let (_, MenuEntry::SaveData(save)) = self.save_menu.entries[slot] {
|
||||
self.save_detailed.entries.clear();
|
||||
self.save_detailed.push_entry(0, MenuEntry::SaveDataSingle(save));
|
||||
}
|
||||
|
||||
self.current_menu = CurrentMenu::LoadConfirm;
|
||||
self.load_confirm.selected = LoadConfirmMenuEntry::Start;
|
||||
}
|
||||
*/
|
||||
}
|
||||
MenuSelectionResult::Selected(SaveMenuEntry::ImportExport, _) => {
|
||||
self.current_menu = CurrentMenu::ImportExport;
|
||||
self.import_export_menu.selected = ImportExportMenuEntry::Back;
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
@@ -344,12 +496,9 @@ impl SaveSelectMenu {
|
||||
match self.save_menu.selected {
|
||||
SaveMenuEntry::Load(slot) => {
|
||||
state.sound_manager.play_sfx(17); // Player Death sfx
|
||||
//filesystem::user_delete(ctx, state.get_save_filename(slot + 1).unwrap_or(String::new()))?;
|
||||
let mut save = SaveContainer::load(ctx, state)?;
|
||||
//let mod_id = state.get_save_mod_id();
|
||||
save.delete_profile(&ctx, state.get_save_slot(slot + 1).unwrap());
|
||||
//save.write_save(ctx, state, SaveFormat::Generic, None, None);
|
||||
save.save(ctx, state);
|
||||
save.save(ctx, state, SaveParams::default())?;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
@@ -381,7 +530,67 @@ impl SaveSelectMenu {
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
CurrentMenu::ImportExport => todo!(),
|
||||
CurrentMenu::ImportExport => match self.import_export_menu.tick(controller, state) {
|
||||
MenuSelectionResult::Selected(ImportExportMenuEntry::Format, toggle)
|
||||
| MenuSelectionResult::Right(ImportExportMenuEntry::Format, toggle, _) => {
|
||||
if let MenuEntry::Options(_, value, _) = toggle {
|
||||
*value = match *value {
|
||||
0..3 => *value + 1,
|
||||
3 => 0,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let format = ImportExportMenuEntry::format_from_value(*value).unwrap_or(state.settings.save_format);
|
||||
self.export_info.format = format;
|
||||
self.export_info.picker_params = ImportExportMenuEntry::fpicker_from_format(state, format, true);
|
||||
|
||||
let export_file_picker = self.import_export_menu.get_entry_mut(ImportExportMenuEntry::Export).unwrap();
|
||||
if let MenuEntry::FilePicker(_, _, params, _) = export_file_picker {
|
||||
*params = self.export_info.picker_params.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
MenuSelectionResult::Left(ImportExportMenuEntry::Format, toggle, _) => {
|
||||
if let MenuEntry::Options(_, value, _) = toggle {
|
||||
*value = match *value {
|
||||
1..=3 => *value - 1,
|
||||
0 => 3,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let format = ImportExportMenuEntry::format_from_value(*value).unwrap_or(state.settings.save_format);
|
||||
self.export_info.format = format;
|
||||
self.export_info.picker_params = ImportExportMenuEntry::fpicker_from_format(state, format, true);
|
||||
|
||||
let export_file_picker = self.import_export_menu.get_entry_mut(ImportExportMenuEntry::Export).unwrap();
|
||||
if let MenuEntry::FilePicker(_, _, params, _) = export_file_picker {
|
||||
*params = self.export_info.picker_params.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
MenuSelectionResult::Selected(ImportExportMenuEntry::Export, entry) => {
|
||||
let out_path = if let MenuEntry::FilePicker(_, _, _, selection) = entry {
|
||||
if let Some(location) = selection {
|
||||
location.first()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else { None };
|
||||
|
||||
if out_path.is_none() {
|
||||
// Export path is not selected, so we break export operation
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
||||
let mut save_container = SaveContainer::load(ctx, state)?;
|
||||
save_container.export(state, ctx, self.export_info.format, self.export_info.save_params.clone(), out_path.unwrap().clone())?;
|
||||
}
|
||||
MenuSelectionResult::Selected(ImportExportMenuEntry::Back, _) | MenuSelectionResult::Canceled => {
|
||||
self.current_menu = CurrentMenu::SaveMenu;
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -406,7 +615,9 @@ impl SaveSelectMenu {
|
||||
self.save_detailed.draw(state, ctx)?;
|
||||
self.load_confirm.draw(state, ctx)?;
|
||||
}
|
||||
CurrentMenu::ImportExport => todo!()
|
||||
CurrentMenu::ImportExport => {
|
||||
self.import_export_menu.draw(state, ctx)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "horizon")))]
|
||||
use rfd::FileDialog;
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct FilePickerParams {
|
||||
pub save: bool, /// Opens save file dialog
|
||||
pub multiple: bool,
|
||||
pub pick_dirs: bool,
|
||||
pub file_name: Option<String>, /// Starting file name.
|
||||
pub starting_dir: Option<PathBuf>,
|
||||
pub filters: HashMap<String, Vec<String>>, // filter name -> file extensions
|
||||
}
|
||||
|
||||
impl FilePickerParams {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn save(mut self, save: bool) -> Self {
|
||||
self.save = save;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn multiple(mut self, multiple: bool) -> Self {
|
||||
self.multiple = multiple;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn pick_dirs(mut self, pick_dirs: bool) -> Self {
|
||||
self.pick_dirs = pick_dirs;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn file_name(mut self, file_name: Option<String>) -> Self {
|
||||
self.file_name = file_name;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn starting_dir(mut self, starting_dir: Option<PathBuf>) -> Self {
|
||||
self.starting_dir = starting_dir;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn filter(mut self, name: String, ext: Vec<String>) -> Self {
|
||||
self.filters.insert(name, ext);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "horizon")))]
|
||||
pub fn open_file_picker(params: &FilePickerParams) -> Option<Vec<PathBuf>> {
|
||||
log::trace!("Call a file picker dialog with params: {:?}", params.clone());
|
||||
let mut dialog = FileDialog::new();
|
||||
|
||||
if let Some(filename) = params.file_name.clone() {
|
||||
dialog = dialog.set_file_name(filename);
|
||||
}
|
||||
|
||||
if let Some(dir) = params.starting_dir.clone() {
|
||||
dialog = dialog.set_directory(dir);
|
||||
}
|
||||
|
||||
for filter in params.filters.iter() {
|
||||
dialog = dialog.add_filter(filter.0, filter.1);
|
||||
}
|
||||
|
||||
let selected_files = match (params.pick_dirs, params.multiple, params.save) {
|
||||
(_, _, true) => dialog.save_file().map(|path| vec![path]),
|
||||
(true, false, _) => dialog.pick_folder().map(|path| vec![path]),
|
||||
(true, true, _) => dialog.pick_folders(),
|
||||
(false, false, _) => dialog.pick_file().map(|path| vec![path]),
|
||||
(false, true, _) => dialog.pick_files(),
|
||||
};
|
||||
|
||||
log::trace!("Selected file entries: {:?}", selected_files.clone());
|
||||
|
||||
return selected_files;
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "horizon"))]
|
||||
pub fn open_file_picker(params: &FileChooserParams) -> Option<Vec<PathBuf>> {
|
||||
None
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod bitvec;
|
||||
pub mod browser;
|
||||
pub mod file_picker;
|
||||
pub mod rng;
|
||||
|
||||
Reference in New Issue
Block a user