use crate::abi::{Header, StatDefData, StatRef, Str};
use crate::content::items::StatFormat;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum StatType {
Additive,
Multiplicative,
Flag,
}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct StatDef {
pub ty: StatType,
pub default: f32,
pub min: f32,
pub max: f32,
pub text: String,
pub format: StatFormat,
}
impl StatDef {
fn of(ty: StatType, default: f32, text: &str, format: StatFormat) -> Self {
Self {
ty,
default,
min: f32::MIN,
max: f32::MAX,
text: text.to_owned(),
format,
}
}
#[must_use]
pub fn additive(text: &str) -> Self {
Self::of(StatType::Additive, 0.0, text, StatFormat::Signed)
}
#[must_use]
pub fn multiplicative(text: &str) -> Self {
Self::of(StatType::Multiplicative, 1.0, text, StatFormat::Percent)
}
#[must_use]
pub fn flag(text: &str) -> Self {
let mut def = Self::of(StatType::Flag, 0.0, text, StatFormat::Number);
def.min = 0.0;
def.max = 1.0;
def
}
#[must_use]
pub const fn range(mut self, min: f32, max: f32) -> Self {
self.min = min;
self.max = max;
self
}
#[must_use]
pub const fn starting_at(mut self, value: f32) -> Self {
self.default = value;
self
}
#[must_use]
pub const fn format(mut self, format: StatFormat) -> Self {
self.format = format;
self
}
pub(super) fn check(&self) -> Result<(), String> {
let finite = self.default.is_finite() && self.min.is_finite() && self.max.is_finite();
if !finite || self.min > self.max || !(self.min..=self.max).contains(&self.default) {
return Err(format!(
"default {} is not within {}..{}",
self.default, self.min, self.max
));
}
Ok(())
}
#[must_use]
pub fn to_data(&self) -> StatDefData {
StatDefData {
header: Header::of::<StatDefData>(),
ty: match self.ty {
StatType::Additive => StatDefData::ADDITIVE,
StatType::Multiplicative => StatDefData::MULTIPLICATIVE,
StatType::Flag => StatDefData::FLAG,
},
format: match self.format {
StatFormat::Number => StatDefData::NUMBER,
StatFormat::Signed => StatDefData::SIGNED,
StatFormat::Percent => StatDefData::PERCENT,
},
reserved: 0,
default: self.default,
min: self.min,
max: self.max,
text: Str::new(&self.text),
}
}
#[must_use]
pub unsafe fn from_data(data: &StatDefData) -> Option<Self> {
let ty = match data.ty {
StatDefData::ADDITIVE => StatType::Additive,
StatDefData::MULTIPLICATIVE => StatType::Multiplicative,
StatDefData::FLAG => StatType::Flag,
_ => return None,
};
let format = match data.format {
StatDefData::SIGNED => StatFormat::Signed,
StatDefData::PERCENT => StatFormat::Percent,
_ => StatFormat::Number,
};
let text = unsafe { data.text.as_str() }?;
let def = Self {
ty,
default: data.default,
min: data.min,
max: data.max,
text: text.to_owned(),
format,
};
def.check().is_ok().then_some(def)
}
}
#[derive(
Clone,
Copy,
Debug,
Default,
PartialEq,
Eq,
Hash,
PartialOrd,
Ord,
serde::Serialize,
serde::Deserialize,
)]
pub struct StatKey(pub u32);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Stat {
pub(super) index: u32,
pub(super) key: StatKey,
}
impl Stat {
#[must_use]
pub const fn key(self) -> StatKey {
self.key
}
#[must_use]
pub const fn index(self) -> u32 {
self.index
}
#[must_use]
pub const fn from_ref(stat: StatRef) -> Self {
Self {
index: stat.index,
key: StatKey(stat.key),
}
}
}