1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-06-30 11:47: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 game_data: physis::gamedata::GameData,
pub item_exh: EXH, pub item_exh: EXH,
pub item_pages: Vec<EXD>, pub item_pages: Vec<EXD>,
pub classjob_exp_indexes: Vec<i8>,
} }
impl Default for GameData { 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 { Self {
game_data, game_data,
item_exh, item_exh,
item_pages, item_pages,
classjob_exp_indexes,
} }
} }
@ -314,11 +326,8 @@ impl GameData {
} }
/// Gets the array index used in EXP & levels. /// Gets the array index used in EXP & levels.
pub fn get_exp_array_index(&mut self, classjob_id: u16) -> Option<i8> { pub fn get_exp_array_index(&self, classjob_id: u16) -> Option<i8> {
let sheet = ClassJobSheet::read_from(&mut self.game_data, Language::English)?; self.classjob_exp_indexes.get(classjob_id as usize).copied()
let row = sheet.get_row(classjob_id as u32)?;
row.ExpArrayIndex().into_i8().copied()
} }
} }

View file

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