diff --git a/src/lib.rs b/src/lib.rs index c4b5729d..19b5a117 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,14 +18,17 @@ use bevy::{ }; use bevy_egui::{egui, EguiContexts, EguiSet}; use bevy_rapier3d::plugin::PhysicsSet; +use enum_map::{enum_map, EnumMap}; use exe_resource_loader::{ExeResourceCursor, ExeResourceLoader}; use rose_data::{CharacterMotionDatabaseOptions, NpcDatabaseOptions, ZoneId}; use rose_file_readers::{ AruaVfsIndex, HostFilesystemDevice, IrosePhVfsIndex, LtbFile, StbFile, TitanVfsIndex, VfsIndex, VirtualFilesystem, VirtualFilesystemDevice, ZscFile, }; +use rose_game_common::components::{InventoryPageType, INVENTORY_PAGE_SIZE}; use serde::{Deserialize, Serialize}; use std::{ + collections::HashMap, path::{Path, PathBuf}, sync::Arc, }; @@ -122,6 +125,24 @@ pub struct AccountConfig { pub password: String, } +#[derive(Deserialize, Serialize, Clone)] +#[serde(default)] +pub struct CharacterConfig { + slots: EnumMap>, +} + +impl Default for CharacterConfig { + fn default() -> Self { + Self { + slots: enum_map! { + page_type => (0..INVENTORY_PAGE_SIZE) + .map(|index| (page_type, index)) + .collect(), + }, + } + } +} + #[derive(Default, Deserialize, Serialize, Clone)] #[serde(default)] pub struct AutoLoginConfig { @@ -312,6 +333,7 @@ impl Default for GraphicsConfig { #[serde(default)] pub struct Config { pub account: AccountConfig, + pub character: HashMap, pub auto_login: AutoLoginConfig, pub filesystem: FilesystemConfig, pub game: GameConfig, @@ -322,6 +344,20 @@ pub struct Config { pub hotkeys: HotkeysConfig, } +impl Config { + pub fn get_inventory_slots( + &mut self, + username: String, + page: InventoryPageType, + ) -> &mut Vec<(InventoryPageType, usize)> { + &mut self + .character + .entry(username) + .or_insert_with(|| CharacterConfig::default()) + .slots[page] + } +} + pub fn load_config(path: &PathBuf) -> Config { let toml_str = match std::fs::read_to_string(path) { Ok(toml_str) => toml_str, diff --git a/src/ui/ui_inventory_system.rs b/src/ui/ui_inventory_system.rs index cb267109..c44bc3b9 100644 --- a/src/ui/ui_inventory_system.rs +++ b/src/ui/ui_inventory_system.rs @@ -3,17 +3,17 @@ use bevy::{ prelude::{Assets, EventWriter, Events, Local, Query, Res, ResMut, With, World}, }; use bevy_egui::{egui, EguiContexts}; -use enum_map::{enum_map, EnumMap}; - use rose_data::{AmmoIndex, EquipmentIndex, Item, VehiclePartIndex}; use rose_game_common::components::{ - Equipment, Inventory, InventoryPageType, ItemSlot, INVENTORY_PAGE_SIZE, + CharacterInfo, Equipment, Inventory, InventoryPageType, ItemSlot, }; +use std::path::Path; use crate::{ components::{Cooldowns, PlayerCharacter}, events::{NumberInputDialogEvent, PlayerCommandEvent}, resources::{GameData, UiResources}, + save_config, ui::{ tooltips::{PlayerTooltipQuery, PlayerTooltipQueryItem}, ui_add_item_tooltip, @@ -21,6 +21,7 @@ use crate::{ DialogInstance, DragAndDropId, DragAndDropSlot, UiSoundEvent, UiStateDragAndDrop, UiStateWindows, }, + Config, }; const IID_BTN_CLOSE: i32 = 10; @@ -47,31 +48,27 @@ const IID_PANE_INVEN: i32 = 300; pub struct UiStateInventory { dialog_instance: DialogInstance, - item_slot_map: EnumMap>, current_equipment_tab: i32, current_vehicle_tab: i32, current_inventory_tab: i32, minimised: bool, + config_changed: bool, } impl Default for UiStateInventory { fn default() -> Self { Self { dialog_instance: DialogInstance::new("DLGITEM.XML"), - item_slot_map: enum_map! { - page_type => (0..INVENTORY_PAGE_SIZE) - .map(|index| ItemSlot::Inventory(page_type, index)) - .collect(), - }, current_equipment_tab: IID_TAB_EQUIP_AVATAR, current_vehicle_tab: IID_TAB_INVEN_PAT, current_inventory_tab: IID_TAB_INVEN_EQUIP, minimised: false, + config_changed: false, } } } -const EQUIPMENT_GRID_SLOTS: [(rose_game_common::components::ItemSlot, egui::Pos2); 14] = [ +const EQUIPMENT_GRID_SLOTS: [(ItemSlot, egui::Pos2); 14] = [ ( ItemSlot::Equipment(EquipmentIndex::Face), egui::pos2(19.0, 67.0), @@ -222,9 +219,10 @@ fn ui_add_inventory_slot( player_tooltip_data: Option<&PlayerTooltipQueryItem>, game_data: &GameData, ui_resources: &UiResources, - item_slot_map: &mut EnumMap>, + config_changed: &mut bool, ui_state_dnd: &mut UiStateDragAndDrop, player_command_events: &mut EventWriter, + config: &mut Config, ) { let drag_accepts = match inventory_slot { ItemSlot::Inventory(page_type, _) => match page_type { @@ -433,16 +431,20 @@ fn ui_add_inventory_slot( swap_inventory_slots { if page_a == page_b { - let inventory_map = &mut item_slot_map[page_a]; + let inventory_map = + config.get_inventory_slots(player.character_info.name.clone(), page_a); + let source_index = inventory_map .iter() - .position(|slot| slot == &ItemSlot::Inventory(page_a, slot_a)); + .position(|slot| slot == &(page_a, slot_a)); let destination_index = inventory_map .iter() - .position(|slot| slot == &ItemSlot::Inventory(page_b, slot_b)); + .position(|slot| slot == &(page_b, slot_b)); + if let (Some(source_index), Some(destination_index)) = (source_index, destination_index) { inventory_map.swap(source_index, destination_index); + *config_changed = true; } } } @@ -450,6 +452,7 @@ fn ui_add_inventory_slot( #[derive(WorldQuery)] pub struct PlayerQuery<'w> { + character_info: &'w CharacterInfo, equipment: &'w Equipment, inventory: &'w Inventory, cooldowns: &'w Cooldowns, @@ -466,6 +469,7 @@ pub fn ui_inventory_system( dialog_assets: Res>, game_data: Res, ui_resources: Res, + mut config: ResMut, mut player_command_events: EventWriter, mut number_input_dialog_events: EventWriter, ) { @@ -547,9 +551,10 @@ pub fn ui_inventory_system( player_tooltip_data.as_ref(), &game_data, &ui_resources, - &mut ui_state_inventory.item_slot_map, + &mut ui_state_inventory.config_changed, &mut ui_state_dnd, &mut player_command_events, + &mut config, ); } } @@ -578,9 +583,10 @@ pub fn ui_inventory_system( player_tooltip_data.as_ref(), &game_data, &ui_resources, - &mut ui_state_inventory.item_slot_map, + &mut ui_state_inventory.config_changed, &mut ui_state_dnd, &mut player_command_events, + &mut config, ); } } @@ -598,12 +604,15 @@ pub fn ui_inventory_system( for row in 0..6 { for column in 0..5 { - let inventory_slot = - ui_state_inventory.item_slot_map[current_page][column + row * 5]; + let page_slots = config.get_inventory_slots( + player.character_info.name.clone(), + current_page, + ); + let (page_type, index) = page_slots[column + row * 5]; ui_add_inventory_slot( ui, - inventory_slot, + ItemSlot::Inventory(page_type, index), egui::pos2( 12.0 + column as f32 * 41.0, y_start + row as f32 * 41.0, @@ -612,9 +621,10 @@ pub fn ui_inventory_system( player_tooltip_data.as_ref(), &game_data, &ui_resources, - &mut ui_state_inventory.item_slot_map, + &mut ui_state_inventory.config_changed, &mut ui_state_dnd, &mut player_command_events, + &mut config, ); } @@ -637,6 +647,13 @@ pub fn ui_inventory_system( ); }); + if !ui_state_windows.inventory_open && ui_state_inventory.config_changed { + let path = config.filesystem.config_path.clone(); + save_config(&config, Path::new(&path)); + + ui_state_inventory.config_changed = false; + } + if response_close_button.map_or(false, |r| r.clicked()) { ui_state_windows.inventory_open = false; }