1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-06-30 03:37:45 +00:00

Fix bad assumption with EXP/Level array indices

They don't always correspond to their classjob id, because jobs share
the same EXP/Levels as their base classes.
This commit is contained in:
Joshua Goins 2025-06-28 14:15:17 -04:00
parent 8f61fc36bd
commit 66da9abc90
2 changed files with 84 additions and 51 deletions

View file

@ -20,6 +20,7 @@ pub struct GameData {
pub game_data: physis::gamedata::GameData,
pub item_exh: EXH,
pub item_pages: Vec<EXD>,
pub classjob_exp_indexes: Vec<i8>,
}
impl Default for GameData {
@ -46,10 +47,21 @@ impl GameData {
);
}
let mut classjob_exp_indexes = Vec::new();
let sheet = ClassJobSheet::read_from(&mut game_data, Language::English).unwrap();
// TODO: ids are hardcoded until we have API in Icarus to do this
for i in 0..43 {
let row = sheet.get_row(i).unwrap();
classjob_exp_indexes.push(*row.ExpArrayIndex().into_i8().unwrap());
}
Self {
game_data,
item_exh,
item_pages,
classjob_exp_indexes,
}
}
@ -314,11 +326,8 @@ impl GameData {
}
/// 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()
pub fn get_exp_array_index(&self, classjob_id: u16) -> Option<i8> {
self.classjob_exp_indexes.get(classjob_id as usize).copied()
}
}

View file

@ -84,24 +84,6 @@ pub struct PlayerData {
pub aetherytes: Vec<u8>,
}
impl PlayerData {
pub fn current_level(&self) -> i32 {
self.classjob_levels[self.classjob_id as usize]
}
pub fn set_current_level(&mut self, level: i32) {
self.classjob_levels[self.classjob_id as usize] = level;
}
pub fn current_exp(&self) -> u32 {
self.classjob_exp[self.classjob_id as usize]
}
pub fn set_current_exp(&mut self, exp: u32) {
self.classjob_exp[self.classjob_id as usize] = exp;
}
}
/// Represents a single connection between an instance of the client and the world server
pub struct ZoneConnection {
pub config: WorldConfig,
@ -315,19 +297,24 @@ impl ZoneConnection {
}
pub async fn update_class_info(&mut self) {
let ipc = ServerZoneIpcSegment {
op_code: ServerZoneIpcType::UpdateClassInfo,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::UpdateClassInfo(UpdateClassInfo {
class_id: self.player_data.classjob_id,
synced_level: self.player_data.current_level() as u16,
class_level: self.player_data.current_level() as u16,
current_level: self.player_data.current_level() as u16,
current_exp: self.player_data.current_exp(),
let ipc;
{
let game_data = self.gamedata.lock().unwrap();
ipc = ServerZoneIpcSegment {
op_code: ServerZoneIpcType::UpdateClassInfo,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::UpdateClassInfo(UpdateClassInfo {
class_id: self.player_data.classjob_id,
synced_level: self.current_level(&game_data) as u16,
class_level: self.current_level(&game_data) as u16,
current_level: self.current_level(&game_data) as u16,
current_exp: self.current_exp(&game_data),
..Default::default()
}),
..Default::default()
}),
..Default::default()
};
};
}
self.send_segment(PacketSegment {
source_actor: self.player_data.actor_id,
@ -748,7 +735,7 @@ impl ZoneConnection {
}
}
Task::SetLevel { level } => {
self.player_data.set_current_level(*level);
self.set_current_level(*level);
self.update_class_info().await;
}
Task::ChangeWeather { id } => {
@ -875,21 +862,26 @@ impl ZoneConnection {
list[..self.status_effects.status_effects.len()]
.copy_from_slice(&self.status_effects.status_effects);
let ipc = ServerZoneIpcSegment {
op_code: ServerZoneIpcType::StatusEffectList,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::StatusEffectList(StatusEffectList {
statues: list,
classjob_id: self.player_data.classjob_id,
level: self.player_data.current_level() as u8,
curr_hp: self.player_data.curr_hp,
max_hp: self.player_data.max_hp,
curr_mp: self.player_data.curr_mp,
max_mp: self.player_data.max_mp,
let ipc;
{
let game_data = self.gamedata.lock().unwrap();
ipc = ServerZoneIpcSegment {
op_code: ServerZoneIpcType::StatusEffectList,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::StatusEffectList(StatusEffectList {
statues: list,
classjob_id: self.player_data.classjob_id,
level: self.current_level(&game_data) as u8,
curr_hp: self.player_data.curr_hp,
max_hp: self.player_data.max_hp,
curr_mp: self.player_data.curr_mp,
max_mp: self.player_data.max_mp,
..Default::default()
}),
..Default::default()
}),
..Default::default()
};
};
}
self.send_segment(PacketSegment {
source_actor: self.player_data.actor_id,
@ -1019,7 +1011,7 @@ impl ZoneConnection {
hp_max: self.player_data.max_hp,
mp_curr: self.player_data.curr_mp,
mp_max: self.player_data.max_mp,
level: self.player_data.current_level() as u8,
level: self.current_level(&game_data) as u8,
object_kind: ObjectKind::Player(PlayerSubKind::Player),
look: chara_details.chara_make.customize,
display_flags: DisplayFlag::UNK,
@ -1168,4 +1160,36 @@ impl ZoneConnection {
})
.await;
}
pub fn current_level(&self, game_data: &GameData) -> i32 {
let index = game_data
.get_exp_array_index(self.player_data.classjob_id as u16)
.unwrap();
self.player_data.classjob_levels[index as usize]
}
pub fn set_current_level(&mut self, level: i32) {
let game_data = self.gamedata.lock().unwrap();
let index = game_data
.get_exp_array_index(self.player_data.classjob_id as u16)
.unwrap();
self.player_data.classjob_levels[index as usize] = level;
}
pub fn current_exp(&self, game_data: &GameData) -> u32 {
let index = game_data
.get_exp_array_index(self.player_data.classjob_id as u16)
.unwrap();
self.player_data.classjob_exp[index as usize]
}
pub fn set_current_exp(&mut self, exp: u32) {
let game_data = self.gamedata.lock().unwrap();
let index = game_data
.get_exp_array_index(self.player_data.classjob_id as u16)
.unwrap();
self.player_data.classjob_exp[index as usize] = exp;
}
}