mirror of
https://github.com/redstrate/Kawari.git
synced 2025-04-21 23:17:45 +00:00
Start sharing the Physis GameData instances
Instead of standalone functions in the common module to perform game data tasks, there is now a shared GameData struct that these functions moved to. This speeds up a few things, and can take advantage of Physis' built-in index caching. I know the current solution isn't ideal (each connection has to mutex lock to access gamedata) but it's at least better than before.
This commit is contained in:
parent
68d3b07acb
commit
37c19ee1b8
5 changed files with 251 additions and 242 deletions
|
@ -1,7 +1,7 @@
|
||||||
|
use kawari::common::GameData;
|
||||||
use kawari::common::custom_ipc::CustomIpcData;
|
use kawari::common::custom_ipc::CustomIpcData;
|
||||||
use kawari::common::custom_ipc::CustomIpcSegment;
|
use kawari::common::custom_ipc::CustomIpcSegment;
|
||||||
use kawari::common::custom_ipc::CustomIpcType;
|
use kawari::common::custom_ipc::CustomIpcType;
|
||||||
use kawari::common::get_world_name;
|
|
||||||
use kawari::config::get_config;
|
use kawari::config::get_config;
|
||||||
use kawari::lobby::LobbyConnection;
|
use kawari::lobby::LobbyConnection;
|
||||||
use kawari::lobby::ipc::{ClientLobbyIpcData, ServerLobbyIpcSegment};
|
use kawari::lobby::ipc::{ClientLobbyIpcData, ServerLobbyIpcSegment};
|
||||||
|
@ -24,7 +24,8 @@ async fn main() {
|
||||||
|
|
||||||
tracing::info!("Server started on {addr}");
|
tracing::info!("Server started on {addr}");
|
||||||
|
|
||||||
let world_name = get_world_name(config.world.world_id);
|
let mut game_data = GameData::new();
|
||||||
|
let world_name = game_data.get_world_name(config.world.world_id);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let (socket, _) = listener.accept().await.unwrap();
|
let (socket, _) = listener.accept().await.unwrap();
|
||||||
|
|
|
@ -2,10 +2,8 @@ use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use kawari::common::custom_ipc::{CustomIpcData, CustomIpcSegment, CustomIpcType};
|
use kawari::common::custom_ipc::{CustomIpcData, CustomIpcSegment, CustomIpcType};
|
||||||
use kawari::common::{
|
use kawari::common::{GameData, timestamp_secs};
|
||||||
Position, determine_initial_starting_zone, get_citystate, get_primary_model_id, get_world_name,
|
use kawari::common::{Position, determine_initial_starting_zone};
|
||||||
};
|
|
||||||
use kawari::common::{get_racial_base_attributes, timestamp_secs};
|
|
||||||
use kawari::config::get_config;
|
use kawari::config::get_config;
|
||||||
use kawari::lobby::CharaMake;
|
use kawari::lobby::CharaMake;
|
||||||
use kawari::oodle::OodleNetwork;
|
use kawari::oodle::OodleNetwork;
|
||||||
|
@ -28,8 +26,6 @@ use kawari::world::{
|
||||||
};
|
};
|
||||||
use kawari::world::{EffectsBuilder, LuaPlayer, PlayerData, StatusEffects, WorldDatabase};
|
use kawari::world::{EffectsBuilder, LuaPlayer, PlayerData, StatusEffects, WorldDatabase};
|
||||||
use mlua::{Function, Lua};
|
use mlua::{Function, Lua};
|
||||||
use physis::common::{Language, Platform};
|
|
||||||
use physis::gamedata::GameData;
|
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::io::AsyncReadExt;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
|
@ -52,6 +48,7 @@ async fn main() {
|
||||||
|
|
||||||
let database = Arc::new(WorldDatabase::new());
|
let database = Arc::new(WorldDatabase::new());
|
||||||
let lua = Arc::new(Mutex::new(Lua::new()));
|
let lua = Arc::new(Mutex::new(Lua::new()));
|
||||||
|
let game_data = Arc::new(Mutex::new(GameData::new()));
|
||||||
|
|
||||||
{
|
{
|
||||||
let lua = lua.lock().unwrap();
|
let lua = lua.lock().unwrap();
|
||||||
|
@ -89,6 +86,7 @@ async fn main() {
|
||||||
|
|
||||||
let database = database.clone();
|
let database = database.clone();
|
||||||
let lua = lua.clone();
|
let lua = lua.clone();
|
||||||
|
let game_data = game_data.clone();
|
||||||
|
|
||||||
let state = PacketState {
|
let state = PacketState {
|
||||||
client_key: None,
|
client_key: None,
|
||||||
|
@ -113,7 +111,7 @@ async fn main() {
|
||||||
|
|
||||||
let mut lua_player = LuaPlayer::default();
|
let mut lua_player = LuaPlayer::default();
|
||||||
|
|
||||||
let config = get_config();
|
/*let config = get_config();
|
||||||
|
|
||||||
let mut game_data =
|
let mut game_data =
|
||||||
GameData::from_existing(Platform::Win32, &config.game_location).unwrap();
|
GameData::from_existing(Platform::Win32, &config.game_location).unwrap();
|
||||||
|
@ -121,7 +119,7 @@ async fn main() {
|
||||||
let exh = game_data.read_excel_sheet_header("Action").unwrap();
|
let exh = game_data.read_excel_sheet_header("Action").unwrap();
|
||||||
let exd = game_data
|
let exd = game_data
|
||||||
.read_excel_sheet("Action", &exh, Language::English, 0)
|
.read_excel_sheet("Action", &exh, Language::English, 0)
|
||||||
.unwrap();
|
.unwrap();*/
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut buf = [0; 2056];
|
let mut buf = [0; 2056];
|
||||||
|
@ -295,9 +293,14 @@ async fn main() {
|
||||||
|
|
||||||
// Stats
|
// Stats
|
||||||
{
|
{
|
||||||
let attributes = get_racial_base_attributes(
|
let attributes;
|
||||||
|
{
|
||||||
|
let mut game_data = game_data.lock().unwrap();
|
||||||
|
|
||||||
|
attributes = game_data.get_racial_base_attributes(
|
||||||
chara_details.chara_make.customize.subrace,
|
chara_details.chara_make.customize.subrace,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let ipc = ServerZoneIpcSegment {
|
let ipc = ServerZoneIpcSegment {
|
||||||
op_code: ServerZoneIpcType::PlayerStats,
|
op_code: ServerZoneIpcType::PlayerStats,
|
||||||
|
@ -396,122 +399,106 @@ async fn main() {
|
||||||
|
|
||||||
// send player spawn
|
// send player spawn
|
||||||
{
|
{
|
||||||
let ipc = ServerZoneIpcSegment {
|
let ipc;
|
||||||
|
{
|
||||||
|
let mut game_data = game_data.lock().unwrap();
|
||||||
|
let equipped = &connection.inventory.equipped;
|
||||||
|
|
||||||
|
ipc = ServerZoneIpcSegment {
|
||||||
op_code: ServerZoneIpcType::PlayerSpawn,
|
op_code: ServerZoneIpcType::PlayerSpawn,
|
||||||
timestamp: timestamp_secs(),
|
timestamp: timestamp_secs(),
|
||||||
data: ServerZoneIpcData::PlayerSpawn(PlayerSpawn {
|
data: ServerZoneIpcData::PlayerSpawn(
|
||||||
account_id: connection.player_data.account_id,
|
PlayerSpawn {
|
||||||
content_id: connection.player_data.content_id,
|
account_id: connection
|
||||||
|
.player_data
|
||||||
|
.account_id,
|
||||||
|
content_id: connection
|
||||||
|
.player_data
|
||||||
|
.content_id,
|
||||||
current_world_id: config.world.world_id,
|
current_world_id: config.world.world_id,
|
||||||
home_world_id: config.world.world_id,
|
home_world_id: config.world.world_id,
|
||||||
gm_rank: GameMasterRank::Debug,
|
gm_rank: GameMasterRank::Debug,
|
||||||
online_status: OnlineStatus::GameMasterBlue,
|
online_status:
|
||||||
|
OnlineStatus::GameMasterBlue,
|
||||||
common: CommonSpawn {
|
common: CommonSpawn {
|
||||||
class_job: connection
|
class_job: connection
|
||||||
.player_data
|
.player_data
|
||||||
.classjob_id,
|
.classjob_id,
|
||||||
name: chara_details.name,
|
name: chara_details.name,
|
||||||
hp_curr: connection.player_data.curr_hp,
|
hp_curr: connection
|
||||||
hp_max: connection.player_data.max_hp,
|
.player_data
|
||||||
mp_curr: connection.player_data.curr_mp,
|
.curr_hp,
|
||||||
mp_max: connection.player_data.max_mp,
|
hp_max: connection
|
||||||
|
.player_data
|
||||||
|
.max_hp,
|
||||||
|
mp_curr: connection
|
||||||
|
.player_data
|
||||||
|
.curr_mp,
|
||||||
|
mp_max: connection
|
||||||
|
.player_data
|
||||||
|
.max_mp,
|
||||||
object_kind: ObjectKind::Player(
|
object_kind: ObjectKind::Player(
|
||||||
PlayerSubKind::Player,
|
PlayerSubKind::Player,
|
||||||
),
|
),
|
||||||
look: chara_details.chara_make.customize,
|
look: chara_details
|
||||||
|
.chara_make
|
||||||
|
.customize,
|
||||||
fc_tag: "LOCAL".to_string(),
|
fc_tag: "LOCAL".to_string(),
|
||||||
display_flags: DisplayFlag::UNK,
|
display_flags: DisplayFlag::UNK,
|
||||||
models: [
|
models: [
|
||||||
get_primary_model_id(
|
game_data.get_primary_model_id(
|
||||||
connection
|
equipped.head.id,
|
||||||
.inventory
|
|
||||||
.equipped
|
|
||||||
.head
|
|
||||||
.id,
|
|
||||||
)
|
)
|
||||||
as u32,
|
as u32,
|
||||||
get_primary_model_id(
|
game_data.get_primary_model_id(
|
||||||
connection
|
equipped.body.id,
|
||||||
.inventory
|
|
||||||
.equipped
|
|
||||||
.body
|
|
||||||
.id,
|
|
||||||
)
|
)
|
||||||
as u32,
|
as u32,
|
||||||
get_primary_model_id(
|
game_data.get_primary_model_id(
|
||||||
connection
|
equipped.hands.id,
|
||||||
.inventory
|
|
||||||
.equipped
|
|
||||||
.hands
|
|
||||||
.id,
|
|
||||||
)
|
)
|
||||||
as u32,
|
as u32,
|
||||||
get_primary_model_id(
|
game_data.get_primary_model_id(
|
||||||
connection
|
equipped.legs.id,
|
||||||
.inventory
|
|
||||||
.equipped
|
|
||||||
.legs
|
|
||||||
.id,
|
|
||||||
)
|
)
|
||||||
as u32,
|
as u32,
|
||||||
get_primary_model_id(
|
game_data.get_primary_model_id(
|
||||||
connection
|
equipped.feet.id,
|
||||||
.inventory
|
|
||||||
.equipped
|
|
||||||
.feet
|
|
||||||
.id,
|
|
||||||
)
|
)
|
||||||
as u32,
|
as u32,
|
||||||
get_primary_model_id(
|
game_data.get_primary_model_id(
|
||||||
connection
|
equipped.ears.id,
|
||||||
.inventory
|
|
||||||
.equipped
|
|
||||||
.ears
|
|
||||||
.id,
|
|
||||||
)
|
)
|
||||||
as u32,
|
as u32,
|
||||||
get_primary_model_id(
|
game_data.get_primary_model_id(
|
||||||
connection
|
equipped.neck.id,
|
||||||
.inventory
|
|
||||||
.equipped
|
|
||||||
.neck
|
|
||||||
.id,
|
|
||||||
)
|
)
|
||||||
as u32,
|
as u32,
|
||||||
get_primary_model_id(
|
game_data.get_primary_model_id(
|
||||||
connection
|
equipped.wrists.id,
|
||||||
.inventory
|
|
||||||
.equipped
|
|
||||||
.wrists
|
|
||||||
.id,
|
|
||||||
)
|
)
|
||||||
as u32,
|
as u32,
|
||||||
get_primary_model_id(
|
game_data.get_primary_model_id(
|
||||||
connection
|
equipped.left_ring.id,
|
||||||
.inventory
|
|
||||||
.equipped
|
|
||||||
.left_ring
|
|
||||||
.id,
|
|
||||||
)
|
)
|
||||||
as u32,
|
as u32,
|
||||||
get_primary_model_id(
|
game_data.get_primary_model_id(
|
||||||
connection
|
equipped.right_ring.id,
|
||||||
.inventory
|
|
||||||
.equipped
|
|
||||||
.right_ring
|
|
||||||
.id,
|
|
||||||
)
|
)
|
||||||
as u32,
|
as u32,
|
||||||
],
|
],
|
||||||
pos: exit_position
|
pos: exit_position
|
||||||
.unwrap_or(Position::default()),
|
.unwrap_or(Position::default()),
|
||||||
rotation: exit_rotation.unwrap_or(0.0),
|
rotation: exit_rotation
|
||||||
|
.unwrap_or(0.0),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
connection
|
connection
|
||||||
.send_segment(PacketSegment {
|
.send_segment(PacketSegment {
|
||||||
|
@ -837,10 +824,10 @@ async fn main() {
|
||||||
ClientZoneIpcData::ActionRequest(request) => {
|
ClientZoneIpcData::ActionRequest(request) => {
|
||||||
tracing::info!("Recieved action request: {:#?}!", request);
|
tracing::info!("Recieved action request: {:#?}!", request);
|
||||||
|
|
||||||
let action_row =
|
/*let action_row =
|
||||||
&exd.read_row(&exh, request.action_id).unwrap()[0];
|
&exd.read_row(&exh, request.action_id).unwrap()[0];
|
||||||
|
|
||||||
println!("Found action: {:#?}", action_row);
|
println!("Found action: {:#?}", action_row);*/
|
||||||
|
|
||||||
let mut effects_builder = None;
|
let mut effects_builder = None;
|
||||||
|
|
||||||
|
@ -858,6 +845,8 @@ async fn main() {
|
||||||
.create_userdata_ref_mut(&mut lua_player)
|
.create_userdata_ref_mut(&mut lua_player)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let config = get_config();
|
||||||
|
|
||||||
let file_name = format!(
|
let file_name = format!(
|
||||||
"{}/{}",
|
"{}/{}",
|
||||||
&config.world.scripts_location,
|
&config.world.scripts_location,
|
||||||
|
@ -993,8 +982,13 @@ async fn main() {
|
||||||
|
|
||||||
let chara_make = CharaMake::from_json(chara_make_json);
|
let chara_make = CharaMake::from_json(chara_make_json);
|
||||||
|
|
||||||
let city_state =
|
let city_state;
|
||||||
get_citystate(chara_make.classjob_id as u16);
|
{
|
||||||
|
let mut game_data = game_data.lock().unwrap();
|
||||||
|
|
||||||
|
city_state = game_data
|
||||||
|
.get_citystate(chara_make.classjob_id as u16);
|
||||||
|
}
|
||||||
|
|
||||||
let (content_id, actor_id) = database.create_player_data(
|
let (content_id, actor_id) = database.create_player_data(
|
||||||
name,
|
name,
|
||||||
|
@ -1087,10 +1081,17 @@ async fn main() {
|
||||||
CustomIpcData::RequestCharacterList { service_account_id } => {
|
CustomIpcData::RequestCharacterList { service_account_id } => {
|
||||||
let config = get_config();
|
let config = get_config();
|
||||||
|
|
||||||
|
let world_name;
|
||||||
|
{
|
||||||
|
let mut game_data = game_data.lock().unwrap();
|
||||||
|
world_name =
|
||||||
|
game_data.get_world_name(config.world.world_id);
|
||||||
|
}
|
||||||
|
|
||||||
let characters = database.get_character_list(
|
let characters = database.get_character_list(
|
||||||
*service_account_id,
|
*service_account_id,
|
||||||
config.world.world_id,
|
config.world.world_id,
|
||||||
&get_world_name(config.world.world_id),
|
&world_name,
|
||||||
);
|
);
|
||||||
|
|
||||||
// send response
|
// send response
|
||||||
|
|
111
src/common/gamedata.rs
Normal file
111
src/common/gamedata.rs
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
use physis::common::{Language, Platform};
|
||||||
|
|
||||||
|
use crate::{common::Attributes, config::get_config};
|
||||||
|
|
||||||
|
/// Convenient methods built on top of Physis to access data relevant to the server
|
||||||
|
pub struct GameData {
|
||||||
|
game_data: physis::gamedata::GameData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameData {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let config = get_config();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
game_data: physis::gamedata::GameData::from_existing(
|
||||||
|
Platform::Win32,
|
||||||
|
&config.game_location,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the world name from an id into the World Excel sheet.
|
||||||
|
pub fn get_world_name(&mut self, world_id: u16) -> String {
|
||||||
|
let exh = self.game_data.read_excel_sheet_header("World").unwrap();
|
||||||
|
let exd = self
|
||||||
|
.game_data
|
||||||
|
.read_excel_sheet("World", &exh, Language::None, 0)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let world_row = &exd.read_row(&exh, world_id as u32).unwrap()[0];
|
||||||
|
|
||||||
|
let physis::exd::ColumnData::String(name) = &world_row.data[1] else {
|
||||||
|
panic!("Unexpected type!");
|
||||||
|
};
|
||||||
|
|
||||||
|
name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the starting city-state from a given class/job id.
|
||||||
|
pub fn get_citystate(&mut self, classjob_id: u16) -> u8 {
|
||||||
|
let exh = self.game_data.read_excel_sheet_header("ClassJob").unwrap();
|
||||||
|
let exd = self
|
||||||
|
.game_data
|
||||||
|
.read_excel_sheet("ClassJob", &exh, Language::English, 0)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let world_row = &exd.read_row(&exh, classjob_id as u32).unwrap()[0];
|
||||||
|
|
||||||
|
let physis::exd::ColumnData::UInt8(town_id) = &world_row.data[33] else {
|
||||||
|
panic!("Unexpected type!");
|
||||||
|
};
|
||||||
|
|
||||||
|
*town_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_racial_base_attributes(&mut self, tribe_id: u8) -> Attributes {
|
||||||
|
// The Tribe Excel sheet only has deltas (e.g. 2 or -2) which are applied to a base 20 number... from somewhere
|
||||||
|
let base_stat = 20;
|
||||||
|
|
||||||
|
let exh = self.game_data.read_excel_sheet_header("Tribe").unwrap();
|
||||||
|
let exd = self
|
||||||
|
.game_data
|
||||||
|
.read_excel_sheet("Tribe", &exh, Language::English, 0)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let tribe_row = &exd.read_row(&exh, tribe_id as u32).unwrap()[0];
|
||||||
|
|
||||||
|
let get_column = |column_index: usize| {
|
||||||
|
let physis::exd::ColumnData::Int8(delta) = &tribe_row.data[column_index] else {
|
||||||
|
panic!("Unexpected type!");
|
||||||
|
};
|
||||||
|
|
||||||
|
*delta
|
||||||
|
};
|
||||||
|
|
||||||
|
Attributes {
|
||||||
|
strength: (base_stat + get_column(4)) as u32,
|
||||||
|
dexterity: (base_stat + get_column(6)) as u32,
|
||||||
|
vitality: (base_stat + get_column(5)) as u32,
|
||||||
|
intelligence: (base_stat + get_column(7)) as u32,
|
||||||
|
mind: (base_stat + get_column(8)) as u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the primary model ID for a given item ID
|
||||||
|
pub fn get_primary_model_id(&mut self, item_id: u32) -> u16 {
|
||||||
|
let exh = self.game_data.read_excel_sheet_header("Item").unwrap();
|
||||||
|
for (i, _) in exh.pages.iter().enumerate() {
|
||||||
|
let exd = self
|
||||||
|
.game_data
|
||||||
|
.read_excel_sheet("Item", &exh, Language::English, i)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if let Some(row) = exd.read_row(&exh, item_id) {
|
||||||
|
let item_row = &row[0];
|
||||||
|
|
||||||
|
let physis::exd::ColumnData::UInt64(id) = &item_row.data[47] else {
|
||||||
|
panic!("Unexpected type!");
|
||||||
|
};
|
||||||
|
|
||||||
|
return *id as u16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: just turn this into an Option<>
|
||||||
|
tracing::warn!("Failed to get model id for {item_id}, this is most likely a bug!");
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,18 +6,15 @@ use std::{
|
||||||
mod customize_data;
|
mod customize_data;
|
||||||
use binrw::binrw;
|
use binrw::binrw;
|
||||||
pub use customize_data::CustomizeData;
|
pub use customize_data::CustomizeData;
|
||||||
use physis::{
|
|
||||||
common::{Language, Platform},
|
|
||||||
gamedata::GameData,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::config::get_config;
|
|
||||||
|
|
||||||
pub mod custom_ipc;
|
pub mod custom_ipc;
|
||||||
|
|
||||||
mod position;
|
mod position;
|
||||||
pub use position::Position;
|
pub use position::Position;
|
||||||
|
|
||||||
|
mod gamedata;
|
||||||
|
pub use gamedata::GameData;
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
#[brw(little)]
|
#[brw(little)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
@ -100,46 +97,6 @@ pub fn timestamp_msecs() -> u64 {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the world name from an id into the World Excel sheet.
|
|
||||||
pub fn get_world_name(world_id: u16) -> String {
|
|
||||||
let config = get_config();
|
|
||||||
|
|
||||||
let mut game_data = GameData::from_existing(Platform::Win32, &config.game_location).unwrap();
|
|
||||||
|
|
||||||
let exh = game_data.read_excel_sheet_header("World").unwrap();
|
|
||||||
let exd = game_data
|
|
||||||
.read_excel_sheet("World", &exh, Language::None, 0)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let world_row = &exd.read_row(&exh, world_id as u32).unwrap()[0];
|
|
||||||
|
|
||||||
let physis::exd::ColumnData::String(name) = &world_row.data[1] else {
|
|
||||||
panic!("Unexpected type!");
|
|
||||||
};
|
|
||||||
|
|
||||||
name.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the starting city-state from a given class/job id.
|
|
||||||
pub fn get_citystate(classjob_id: u16) -> u8 {
|
|
||||||
let config = get_config();
|
|
||||||
|
|
||||||
let mut game_data = GameData::from_existing(Platform::Win32, &config.game_location).unwrap();
|
|
||||||
|
|
||||||
let exh = game_data.read_excel_sheet_header("ClassJob").unwrap();
|
|
||||||
let exd = game_data
|
|
||||||
.read_excel_sheet("ClassJob", &exh, Language::English, 0)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let world_row = &exd.read_row(&exh, classjob_id as u32).unwrap()[0];
|
|
||||||
|
|
||||||
let physis::exd::ColumnData::UInt8(town_id) = &world_row.data[33] else {
|
|
||||||
panic!("Unexpected type!");
|
|
||||||
};
|
|
||||||
|
|
||||||
*town_id
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the initial zone for a given city-state id
|
/// Gets the initial zone for a given city-state id
|
||||||
pub fn determine_initial_starting_zone(citystate_id: u8) -> u16 {
|
pub fn determine_initial_starting_zone(citystate_id: u8) -> u16 {
|
||||||
match citystate_id {
|
match citystate_id {
|
||||||
|
@ -153,35 +110,6 @@ pub fn determine_initial_starting_zone(citystate_id: u8) -> u16 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the primary model ID for a given item ID
|
|
||||||
pub fn get_primary_model_id(item_id: u32) -> u16 {
|
|
||||||
let config = get_config();
|
|
||||||
|
|
||||||
let mut game_data = GameData::from_existing(Platform::Win32, &config.game_location).unwrap();
|
|
||||||
|
|
||||||
let exh = game_data.read_excel_sheet_header("Item").unwrap();
|
|
||||||
for (i, _) in exh.pages.iter().enumerate() {
|
|
||||||
let exd = game_data
|
|
||||||
.read_excel_sheet("Item", &exh, Language::English, i)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if let Some(row) = exd.read_row(&exh, item_id) {
|
|
||||||
let item_row = &row[0];
|
|
||||||
|
|
||||||
let physis::exd::ColumnData::UInt64(id) = &item_row.data[47] else {
|
|
||||||
panic!("Unexpected type!");
|
|
||||||
};
|
|
||||||
|
|
||||||
return *id as u16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: just turn this into an Option<>
|
|
||||||
tracing::warn!("Failed to get model id for {item_id}, this is most likely a bug!");
|
|
||||||
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Attributes {
|
pub struct Attributes {
|
||||||
pub strength: u32,
|
pub strength: u32,
|
||||||
pub dexterity: u32,
|
pub dexterity: u32,
|
||||||
|
@ -190,38 +118,6 @@ pub struct Attributes {
|
||||||
pub mind: u32,
|
pub mind: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_racial_base_attributes(tribe_id: u8) -> Attributes {
|
|
||||||
// The Tribe Excel sheet only has deltas (e.g. 2 or -2) which are applied to a base 20 number... from somewhere
|
|
||||||
let base_stat = 20;
|
|
||||||
|
|
||||||
let config = get_config();
|
|
||||||
|
|
||||||
let mut game_data = GameData::from_existing(Platform::Win32, &config.game_location).unwrap();
|
|
||||||
|
|
||||||
let exh = game_data.read_excel_sheet_header("Tribe").unwrap();
|
|
||||||
let exd = game_data
|
|
||||||
.read_excel_sheet("Tribe", &exh, Language::English, 0)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let tribe_row = &exd.read_row(&exh, tribe_id as u32).unwrap()[0];
|
|
||||||
|
|
||||||
let get_column = |column_index: usize| {
|
|
||||||
let physis::exd::ColumnData::Int8(delta) = &tribe_row.data[column_index] else {
|
|
||||||
panic!("Unexpected type!");
|
|
||||||
};
|
|
||||||
|
|
||||||
*delta
|
|
||||||
};
|
|
||||||
|
|
||||||
Attributes {
|
|
||||||
strength: (base_stat + get_column(4)) as u32,
|
|
||||||
dexterity: (base_stat + get_column(6)) as u32,
|
|
||||||
vitality: (base_stat + get_column(5)) as u32,
|
|
||||||
intelligence: (base_stat + get_column(7)) as u32,
|
|
||||||
mind: (base_stat + get_column(8)) as u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
blowfish::Blowfish,
|
blowfish::Blowfish,
|
||||||
common::{
|
common::{
|
||||||
custom_ipc::{CustomIpcData, CustomIpcSegment, CustomIpcType},
|
custom_ipc::{CustomIpcData, CustomIpcSegment, CustomIpcType},
|
||||||
get_world_name, timestamp_secs,
|
timestamp_secs,
|
||||||
},
|
},
|
||||||
config::get_config,
|
config::get_config,
|
||||||
lobby::CharaMake,
|
lobby::CharaMake,
|
||||||
|
@ -125,7 +125,7 @@ impl LobbyConnection {
|
||||||
index: 0,
|
index: 0,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
icon: 0,
|
icon: 0,
|
||||||
name: get_world_name(config.world.world_id),
|
name: self.world_name.clone(),
|
||||||
}]
|
}]
|
||||||
.to_vec();
|
.to_vec();
|
||||||
// add any empty boys
|
// add any empty boys
|
||||||
|
|
Loading…
Add table
Reference in a new issue