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)
|
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
|
// Simple enum for GameData::get_territory_name
|
||||||
|
|
|
@ -94,7 +94,6 @@ pub struct PlayerStatus {
|
||||||
#[br(count = UNLOCK_BITMASK_SIZE)]
|
#[br(count = UNLOCK_BITMASK_SIZE)]
|
||||||
#[bw(pad_size_to = UNLOCK_BITMASK_SIZE)]
|
#[bw(pad_size_to = UNLOCK_BITMASK_SIZE)]
|
||||||
pub unlocks: Vec<u8>,
|
pub unlocks: Vec<u8>,
|
||||||
pub unknown10e: [u8; 28],
|
|
||||||
#[br(count = AETHERYTE_UNLOCK_BITMASK_SIZE)]
|
#[br(count = AETHERYTE_UNLOCK_BITMASK_SIZE)]
|
||||||
#[bw(pad_size_to = AETHERYTE_UNLOCK_BITMASK_SIZE)]
|
#[bw(pad_size_to = AETHERYTE_UNLOCK_BITMASK_SIZE)]
|
||||||
pub aetherytes: Vec<u8>,
|
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;
|
pub const OBFUSCATION_ENABLED_MODE: u8 = 41;
|
||||||
|
|
||||||
/// The size of the unlock bitmask.
|
/// 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.
|
/// The size of the aetheryte unlock bitmask.
|
||||||
// TODO: this can be automatically derived from game data
|
// 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,
|
service_account_id,
|
||||||
path,
|
path,
|
||||||
} => {
|
} => {
|
||||||
|
let mut game_data = connection.gamedata.lock().unwrap();
|
||||||
|
|
||||||
connection
|
connection
|
||||||
.database
|
.database
|
||||||
.import_character(*service_account_id, path);
|
.import_character(&mut game_data, *service_account_id, path);
|
||||||
}
|
}
|
||||||
CustomIpcData::RemakeCharacter {
|
CustomIpcData::RemakeCharacter {
|
||||||
content_id,
|
content_id,
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
CustomizeData, GameData, Position,
|
CustomizeData, GameData, Position,
|
||||||
workdefinitions::{CharaMake, ClientSelectData, RemakeMode},
|
workdefinitions::{CharaMake, ClientSelectData, RemakeMode},
|
||||||
},
|
},
|
||||||
inventory::Inventory,
|
inventory::{Inventory, Item, Storage},
|
||||||
ipc::{
|
ipc::{
|
||||||
lobby::{CharacterDetails, CharacterFlag},
|
lobby::{CharacterDetails, CharacterFlag},
|
||||||
zone::GameMasterRank,
|
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}...");
|
tracing::info!("Importing character backup from {path}...");
|
||||||
|
|
||||||
let file = std::fs::File::open(path).unwrap();
|
let file = std::fs::File::open(path).unwrap();
|
||||||
|
@ -75,6 +75,27 @@ impl WorldDatabase {
|
||||||
month: i32,
|
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)]
|
#[derive(Deserialize)]
|
||||||
struct CharacterJson {
|
struct CharacterJson {
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -82,6 +103,30 @@ impl WorldDatabase {
|
||||||
nameday: NamedayValue,
|
nameday: NamedayValue,
|
||||||
guardian: GenericValue,
|
guardian: GenericValue,
|
||||||
voice: i32,
|
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;
|
let character: CharacterJson;
|
||||||
|
@ -117,8 +162,7 @@ impl WorldDatabase {
|
||||||
unk2: 1,
|
unk2: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: import inventory
|
let (_, actor_id) = self.create_player_data(
|
||||||
self.create_player_data(
|
|
||||||
service_account_id,
|
service_account_id,
|
||||||
&character.name,
|
&character.name,
|
||||||
&chara_make.to_json(),
|
&chara_make.to_json(),
|
||||||
|
@ -127,6 +171,97 @@ impl WorldDatabase {
|
||||||
Inventory::default(),
|
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);
|
tracing::info!("{} added to the world!", character.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue