mirror of
https://github.com/redstrate/Kawari.git
synced 2025-06-30 11:47:45 +00:00
Support importing even more data from Auracite
This includes unlocked class jobs, levels, your inventory and unlock flags.
This commit is contained in:
parent
72bff848b5
commit
8f61fc36bd
5 changed files with 151 additions and 7 deletions
|
@ -312,6 +312,14 @@ impl GameData {
|
|||
|
||||
self.get_weather_rate(*weather_rate_id as u32)
|
||||
}
|
||||
|
||||
/// Gets the array index used in EXP & levels.
|
||||
pub fn get_exp_array_index(&mut self, classjob_id: u16) -> Option<i8> {
|
||||
let sheet = ClassJobSheet::read_from(&mut self.game_data, Language::English)?;
|
||||
let row = sheet.get_row(classjob_id as u32)?;
|
||||
|
||||
row.ExpArrayIndex().into_i8().copied()
|
||||
}
|
||||
}
|
||||
|
||||
// Simple enum for GameData::get_territory_name
|
||||
|
|
|
@ -94,7 +94,6 @@ pub struct PlayerStatus {
|
|||
#[br(count = UNLOCK_BITMASK_SIZE)]
|
||||
#[bw(pad_size_to = UNLOCK_BITMASK_SIZE)]
|
||||
pub unlocks: Vec<u8>,
|
||||
pub unknown10e: [u8; 28],
|
||||
#[br(count = AETHERYTE_UNLOCK_BITMASK_SIZE)]
|
||||
#[bw(pad_size_to = AETHERYTE_UNLOCK_BITMASK_SIZE)]
|
||||
pub aetherytes: Vec<u8>,
|
||||
|
|
|
@ -70,7 +70,7 @@ pub fn get_supported_expac_versions() -> HashMap<&'static str, Version<'static>>
|
|||
pub const OBFUSCATION_ENABLED_MODE: u8 = 41;
|
||||
|
||||
/// The size of the unlock bitmask.
|
||||
pub const UNLOCK_BITMASK_SIZE: usize = 64;
|
||||
pub const UNLOCK_BITMASK_SIZE: usize = 92;
|
||||
|
||||
/// The size of the aetheryte unlock bitmask.
|
||||
// TODO: this can be automatically derived from game data
|
||||
|
|
|
@ -194,9 +194,11 @@ pub async fn handle_custom_ipc(connection: &mut ZoneConnection, data: &CustomIpc
|
|||
service_account_id,
|
||||
path,
|
||||
} => {
|
||||
let mut game_data = connection.gamedata.lock().unwrap();
|
||||
|
||||
connection
|
||||
.database
|
||||
.import_character(*service_account_id, path);
|
||||
.import_character(&mut game_data, *service_account_id, path);
|
||||
}
|
||||
CustomIpcData::RemakeCharacter {
|
||||
content_id,
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
CustomizeData, GameData, Position,
|
||||
workdefinitions::{CharaMake, ClientSelectData, RemakeMode},
|
||||
},
|
||||
inventory::Inventory,
|
||||
inventory::{Inventory, Item, Storage},
|
||||
ipc::{
|
||||
lobby::{CharacterDetails, CharacterFlag},
|
||||
zone::GameMasterRank,
|
||||
|
@ -57,7 +57,7 @@ impl WorldDatabase {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn import_character(&self, service_account_id: u32, path: &str) {
|
||||
pub fn import_character(&self, game_data: &mut GameData, service_account_id: u32, path: &str) {
|
||||
tracing::info!("Importing character backup from {path}...");
|
||||
|
||||
let file = std::fs::File::open(path).unwrap();
|
||||
|
@ -75,6 +75,27 @@ impl WorldDatabase {
|
|||
month: i32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ClassJobLevelValue {
|
||||
level: i32,
|
||||
exp: Option<u32>,
|
||||
value: i32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct InventoryItem {
|
||||
slot: i32,
|
||||
quantity: u32,
|
||||
condition: i32,
|
||||
id: u32,
|
||||
glamour_id: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct InventoryContainer {
|
||||
items: Vec<InventoryItem>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct CharacterJson {
|
||||
name: String,
|
||||
|
@ -82,6 +103,30 @@ impl WorldDatabase {
|
|||
nameday: NamedayValue,
|
||||
guardian: GenericValue,
|
||||
voice: i32,
|
||||
classjob_levels: Vec<ClassJobLevelValue>,
|
||||
|
||||
inventory1: InventoryContainer,
|
||||
inventory2: InventoryContainer,
|
||||
inventory3: InventoryContainer,
|
||||
inventory4: InventoryContainer,
|
||||
equipped_items: InventoryContainer,
|
||||
|
||||
currency: InventoryContainer,
|
||||
|
||||
armory_off_hand: InventoryContainer,
|
||||
armory_head: InventoryContainer,
|
||||
armory_body: InventoryContainer,
|
||||
armory_hands: InventoryContainer,
|
||||
armory_legs: InventoryContainer,
|
||||
armory_ear: InventoryContainer,
|
||||
armory_neck: InventoryContainer,
|
||||
armory_wrist: InventoryContainer,
|
||||
armory_rings: InventoryContainer,
|
||||
armory_soul_crystal: InventoryContainer,
|
||||
armory_main_hand: InventoryContainer,
|
||||
|
||||
unlock_flags: Vec<u8>,
|
||||
unlock_aetherytes: Vec<u8>,
|
||||
}
|
||||
|
||||
let character: CharacterJson;
|
||||
|
@ -117,8 +162,7 @@ impl WorldDatabase {
|
|||
unk2: 1,
|
||||
};
|
||||
|
||||
// TODO: import inventory
|
||||
self.create_player_data(
|
||||
let (_, actor_id) = self.create_player_data(
|
||||
service_account_id,
|
||||
&character.name,
|
||||
&chara_make.to_json(),
|
||||
|
@ -127,6 +171,97 @@ impl WorldDatabase {
|
|||
Inventory::default(),
|
||||
);
|
||||
|
||||
let mut player_data = self.find_player_data(actor_id);
|
||||
|
||||
// import jobs
|
||||
for classjob in &character.classjob_levels {
|
||||
// find the array index of the job
|
||||
let index = game_data
|
||||
.get_exp_array_index(classjob.value as u16)
|
||||
.unwrap();
|
||||
|
||||
player_data.classjob_levels[index as usize] = classjob.level;
|
||||
if let Some(exp) = classjob.exp {
|
||||
player_data.classjob_exp[index as usize] = exp;
|
||||
}
|
||||
}
|
||||
|
||||
let process_inventory_container =
|
||||
|container: &InventoryContainer, target: &mut dyn Storage| {
|
||||
for item in &container.items {
|
||||
if item.slot as u32 > target.max_slots() {
|
||||
continue;
|
||||
}
|
||||
*target.get_slot_mut(item.slot as u16) = Item {
|
||||
quantity: item.quantity,
|
||||
id: item.id,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// import inventory
|
||||
process_inventory_container(&character.inventory1, &mut player_data.inventory.pages[0]);
|
||||
process_inventory_container(&character.inventory2, &mut player_data.inventory.pages[1]);
|
||||
process_inventory_container(&character.inventory3, &mut player_data.inventory.pages[2]);
|
||||
process_inventory_container(&character.inventory4, &mut player_data.inventory.pages[3]);
|
||||
process_inventory_container(
|
||||
&character.equipped_items,
|
||||
&mut player_data.inventory.equipped,
|
||||
);
|
||||
|
||||
process_inventory_container(&character.currency, &mut player_data.inventory.currency);
|
||||
|
||||
process_inventory_container(
|
||||
&character.armory_off_hand,
|
||||
&mut player_data.inventory.armoury_off_hand,
|
||||
);
|
||||
process_inventory_container(
|
||||
&character.armory_head,
|
||||
&mut player_data.inventory.armoury_head,
|
||||
);
|
||||
process_inventory_container(
|
||||
&character.armory_body,
|
||||
&mut player_data.inventory.armoury_body,
|
||||
);
|
||||
process_inventory_container(
|
||||
&character.armory_hands,
|
||||
&mut player_data.inventory.armoury_hands,
|
||||
);
|
||||
process_inventory_container(
|
||||
&character.armory_legs,
|
||||
&mut player_data.inventory.armoury_legs,
|
||||
);
|
||||
process_inventory_container(
|
||||
&character.armory_ear,
|
||||
&mut player_data.inventory.armoury_earring,
|
||||
);
|
||||
process_inventory_container(
|
||||
&character.armory_neck,
|
||||
&mut player_data.inventory.armoury_necklace,
|
||||
);
|
||||
process_inventory_container(
|
||||
&character.armory_wrist,
|
||||
&mut player_data.inventory.armoury_bracelet,
|
||||
);
|
||||
process_inventory_container(
|
||||
&character.armory_rings,
|
||||
&mut player_data.inventory.armoury_rings,
|
||||
);
|
||||
process_inventory_container(
|
||||
&character.armory_soul_crystal,
|
||||
&mut player_data.inventory.armoury_soul_crystal,
|
||||
);
|
||||
process_inventory_container(
|
||||
&character.armory_main_hand,
|
||||
&mut player_data.inventory.armoury_main_hand,
|
||||
);
|
||||
|
||||
// import unlock flags
|
||||
player_data.unlocks = character.unlock_flags;
|
||||
player_data.aetherytes = character.unlock_aetherytes;
|
||||
|
||||
self.commit_player_data(&player_data);
|
||||
|
||||
tracing::info!("{} added to the world!", character.name);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue