use std::ops::{Deref, DerefMut};
use crate::bits::{BitReader, BitWriter};
use crate::data::ExternalData;
use crate::enums::CharacterClass;
use crate::error::{Error, Result};
use crate::StatId;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Stat {
pub id: StatId,
pub layer: i32,
pub value: i64,
}
impl Stat {
pub const fn new(id: StatId, value: i64) -> Stat {
Stat { id, layer: 0, value }
}
pub const fn with_layer(id: StatId, layer: i32, value: i64) -> Stat {
Stat { id, layer, value }
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct PlayerStats(pub Vec<Stat>);
impl PlayerStats {
pub const MAGIC: u16 = 0x6667;
pub fn get(&self, id: StatId) -> i64 {
self.0.iter().find(|s| s.id == id).map_or(0, |s| s.value)
}
pub fn set(&mut self, id: StatId, value: i64) {
self.set_with_layer(id, 0, value);
}
pub fn set_with_layer(&mut self, id: StatId, layer: i32, value: i64) {
let stat = Stat { id, layer, value };
match self.0.iter_mut().find(|s| s.id == id) {
Some(existing) => *existing = stat,
None => self.0.push(stat),
}
}
pub fn read(r: &mut BitReader<'_>, data: &(impl ExternalData + ?Sized), save_version: u32) -> Result<Self> {
let magic = r.read_u16()?;
if magic != Self::MAGIC {
return Err(Error::InvalidMagic { section: "stats section", found: magic as u32 });
}
let mut stats = Vec::with_capacity(16);
loop {
let id = StatId(r.read_bits(9)? as u16);
if id == StatId::TERMINATOR {
break;
}
let info = data.stat_info(id, save_version)?;
let layer = if info.csv_param > 0 { r.read_signed_bits(info.csv_param as u32)? } else { 0 };
let value = if info.csv_bits < 32 && info.csv_signed {
r.read_signed_bits(info.csv_bits as u32)? as i64
} else {
r.read_bits(info.csv_bits as u32)? as i64
};
stats.push(Stat { id, layer, value });
}
r.align_to_byte();
Ok(PlayerStats(stats))
}
pub fn write(&self, w: &mut BitWriter<'_>, data: &(impl ExternalData + ?Sized), save_version: u32) -> Result<()> {
w.write_u16(Self::MAGIC)?;
for stat in &self.0 {
let info = data.stat_info(stat.id, save_version)?;
let bits = info.csv_bits as u32;
let mut value = stat.value;
if bits < 32 {
if info.csv_signed {
let max = 1i64.wrapping_shl(bits.wrapping_sub(1));
let min = max.wrapping_neg();
value = value.clamp(min, max.wrapping_sub(1));
} else {
value = value.clamp(0, 1i64.wrapping_shl(bits) - 1);
}
}
w.write_bits(stat.id.0 as u32, 9)?;
if info.csv_param > 0 {
w.write_bits(stat.layer as u32, info.csv_param as u32)?;
}
if bits < 32 && info.csv_signed {
w.write_signed_bits(value as i32, bits)?;
} else {
w.write_bits(value as u32, bits)?;
}
}
w.write_bits(StatId::TERMINATOR.0 as u32, 9)?;
w.align_to_byte();
Ok(())
}
}
impl Deref for PlayerStats {
type Target = Vec<Stat>;
fn deref(&self) -> &Vec<Stat> {
&self.0
}
}
impl DerefMut for PlayerStats {
fn deref_mut(&mut self) -> &mut Vec<Stat> {
&mut self.0
}
}
impl<'a> IntoIterator for &'a PlayerStats {
type Item = &'a Stat;
type IntoIter = std::slice::Iter<'a, Stat>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl FromIterator<Stat> for PlayerStats {
fn from_iter<T: IntoIterator<Item = Stat>>(iter: T) -> Self {
PlayerStats(iter.into_iter().collect())
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SkillsSection {
pub class: CharacterClass,
pub levels: Vec<u8>,
}
impl SkillsSection {
pub const MAGIC: u16 = 0x6669;
pub const SKILLS_PER_CLASS: usize = 30;
pub const CLASS_SKILL_OFFSETS: [u16; 8] = [6, 36, 66, 96, 126, 221, 251, 373];
pub fn skill_id_offset(&self) -> Option<u16> {
Self::CLASS_SKILL_OFFSETS.get(self.class.to_raw() as usize).copied()
}
pub fn level(&self, index: usize) -> u8 {
self.levels.get(index).copied().unwrap_or(0)
}
pub fn set_level(&mut self, index: usize, level: u8) {
if let Some(l) = self.levels.get_mut(index) {
*l = level;
}
}
pub fn skill_id(&self, index: usize) -> Option<u16> {
if index >= Self::SKILLS_PER_CLASS {
return None;
}
Some(self.skill_id_offset()? + index as u16)
}
pub fn skill_index(&self, skill_id: u16) -> Option<usize> {
let index = skill_id.checked_sub(self.skill_id_offset()?)? as usize;
(index < Self::SKILLS_PER_CLASS).then_some(index)
}
pub fn read(r: &mut BitReader<'_>, class: CharacterClass, count: usize) -> Result<Self> {
let magic = r.read_u16()?;
if magic != Self::MAGIC {
return Err(Error::InvalidMagic { section: "skills section", found: magic as u32 });
}
Ok(SkillsSection { class, levels: r.read_slice(count)?.to_vec() })
}
pub fn write(&self, w: &mut BitWriter<'_>) -> Result<()> {
w.write_u16(Self::MAGIC)?;
w.write_bytes(&self.levels)
}
}
impl std::ops::Index<usize> for SkillsSection {
type Output = u8;
fn index(&self, index: usize) -> &u8 {
&self.levels[index]
}
}
impl std::ops::IndexMut<usize> for SkillsSection {
fn index_mut(&mut self, index: usize) -> &mut u8 {
&mut self.levels[index]
}
}