use core::time::Duration;
use crate::abi::StatModifierData;
use crate::buffs::BuffTypeId;
use crate::item_data::ItemInstance;
use super::defs::{Stat, StatDef, StatKey, StatType};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[non_exhaustive]
pub enum Op {
Add,
Multiply,
Max,
Override,
}
impl Op {
#[must_use]
pub const fn to_raw(self) -> u8 {
match self {
Self::Add => StatModifierData::ADD,
Self::Multiply => StatModifierData::MULTIPLY,
Self::Max => StatModifierData::MAX,
Self::Override => StatModifierData::OVERRIDE,
}
}
#[must_use]
pub const fn from_raw(raw: u8) -> Option<Self> {
match raw {
StatModifierData::ADD => Some(Self::Add),
StatModifierData::MULTIPLY => Some(Self::Multiply),
StatModifierData::MAX => Some(Self::Max),
StatModifierData::OVERRIDE => Some(Self::Override),
_ => None,
}
}
}
#[must_use]
pub fn aggregate(def: &StatDef, modifiers: impl IntoIterator<Item = (Op, f32)>) -> f32 {
let mut add = 0.0f32;
let mut factor = 1.0f32;
let mut floor = f32::NEG_INFINITY;
let mut replaced = None;
for (op, value) in modifiers {
if !value.is_finite() {
continue;
}
match op {
Op::Add => add += value,
Op::Multiply => factor *= value,
Op::Max => floor = floor.max(value),
Op::Override => replaced = Some(value),
}
}
let value = replaced.unwrap_or_else(|| ((def.default + add) * factor).max(floor));
let value = if value.is_finite() {
value.clamp(def.min, def.max)
} else {
def.default
};
match def.ty {
StatType::Flag => f32::from(u8::from(value != 0.0)),
StatType::Additive | StatType::Multiplicative => value,
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Modifier {
pub stat: Stat,
pub op: Op,
pub value: f32,
}
impl Modifier {
#[must_use]
pub const fn new(stat: Stat, op: Op, value: f32) -> Self {
Self { stat, op, value }
}
pub(super) const fn to_data(self) -> StatModifierData {
StatModifierData {
stat: self.stat.index,
op: self.op.to_raw(),
reserved: [0; 3],
value: self.value,
}
}
#[must_use]
pub const fn to_item(self) -> ItemModifier {
ItemModifier {
stat: self.stat.key,
op: self.op,
value: self.value,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct ItemModifier {
pub stat: StatKey,
pub op: Op,
pub value: f32,
}
#[derive(Clone, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct ItemStats {
pub modifiers: Vec<ItemModifier>,
pub affixes: Vec<crate::affixes::Rolled>,
pub rolled: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Lasting {
UntilRemoved,
For(Duration),
Saved,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum ModifyError {
#[error("only the server keeps modifiers")]
NotServer,
#[error("only players keep saved modifiers")]
NotPlayer,
#[error("modifier refused (status {0})")]
Refused(u32),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Origin {
Item(ItemInstance),
Buff(BuffTypeId),
Mod {
index: u32,
key: u32,
},
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
pub struct Source {
pub origin: Origin,
pub stat: u32,
pub op: Op,
pub value: f32,
}