use crate::modifier::StatModifier;
use core::mem::MaybeUninit;
#[cfg(not(feature = "sync"))]
type ReferenceCounted<T> = std::rc::Rc<T>;
#[cfg(not(feature = "sync"))]
type Weak<T> = std::rc::Weak<T>;
#[cfg(feature = "sync")]
type ReferenceCounted<T> = std::sync::Arc<T>;
#[cfg(feature = "sync")]
type Weak<T> = std::sync::Weak<T>;
pub type StatModifierHandle = ReferenceCounted<StatModifierHandleTag>;
pub struct StatModifierHandleTag;
pub struct Stat<const M: usize> {
pub base_value: f32,
value: f32,
modifiers: [Option<ModifierMeta>; M],
}
struct ModifierMeta {
modifier: StatModifier,
order: i32,
owner_modifier_weak: Weak<StatModifierHandleTag>,
}
#[derive(Debug, Clone, Copy)]
pub struct ModifiersFullError;
impl<const M: usize> Stat<M> {
pub fn new(base_value: f32) -> Self {
let mut modifiers: [MaybeUninit<Option<ModifierMeta>>; M] =
unsafe { MaybeUninit::uninit().assume_init() };
modifiers[..].iter_mut().for_each(|elem| {
elem.write(None);
});
let modifiers = unsafe {
modifiers
.as_ptr()
.cast::<[Option<ModifierMeta>; M]>()
.read()
};
Self {
base_value,
value: base_value,
modifiers,
}
}
pub fn add_modifier(
&mut self,
modifier: StatModifier,
) -> Result<StatModifierHandle, ModifiersFullError> {
self.update_modifiers();
match self.modifiers.iter_mut().find(|m| m.is_none()) {
Some(modifier_option) => {
let key = ReferenceCounted::new(StatModifierHandleTag);
*modifier_option = Some(ModifierMeta {
order: modifier.default_order(),
modifier,
owner_modifier_weak: ReferenceCounted::downgrade(&key),
});
self.calculate_value();
Ok(key)
}
None => Err(ModifiersFullError),
}
}
pub fn add_modifier_with_order(
&mut self,
modifier: StatModifier,
order: i32,
) -> Result<StatModifierHandle, ModifiersFullError> {
self.update_modifiers();
match self.modifiers.iter_mut().find(|m| m.is_none()) {
Some(modifier_option) => {
let key = ReferenceCounted::new(StatModifierHandleTag);
*modifier_option = Some(ModifierMeta {
modifier,
owner_modifier_weak: ReferenceCounted::downgrade(&key),
order,
});
self.calculate_value();
Ok(key)
}
None => Err(ModifiersFullError),
}
}
fn update_modifiers(&mut self) {
let any_modifier_dropped = self
.modifiers
.iter()
.filter_map(|m| m.as_ref())
.any(|m| m.owner_modifier_weak.upgrade().is_none());
if any_modifier_dropped {
self.calculate_value();
}
}
pub fn value(&mut self) -> f32 {
self.update_modifiers();
self.value
}
fn calculate_value(&mut self) {
let mut value = self.base_value;
use std::cmp::Ordering;
self.modifiers.sort_by(|m1_option, m2_option| {
if let Some(m1) = m1_option {
if let Some(m2) = m2_option {
m1.order.cmp(&m2.order)
} else {
Ordering::Less
}
} else {
Ordering::Greater
}
});
#[allow(clippy::manual_flatten)]
for modifier_meta_option in self.modifiers.iter_mut() {
if let Some(modifier_meta) = modifier_meta_option {
match modifier_meta.owner_modifier_weak.upgrade() {
Some(_key) => modifier_meta.modifier.apply(&mut value),
None => *modifier_meta_option = None,
}
}
}
self.value = value;
}
}