use bevy::platform::collections::HashMap;
use evalexpr::{ContextWithMutableVariables, Value, IterateVariablesContext, Context};
use super::prelude::*;
use dashmap::DashMap;
#[derive(PartialEq, Debug, Clone, Default)]
pub enum ModType {
#[default]
Add,
Mul,
}
#[derive(Debug, Clone)]
pub(crate) enum StatType {
Flat(Flat),
Modifiable(Modifiable),
Complex(Complex),
Tagged(Tagged),
}
impl Stat for StatType {
fn new(path: &StatPath) -> Self {
let stat_type_name = Konfig::get_stat_type(path.name);
match stat_type_name.as_str() {
"Flat" => StatType::Flat(Flat::new(path)),
"Modifiable" => StatType::Modifiable(Modifiable::new(path)),
"Complex" => StatType::Complex(Complex::new(path)),
"Tagged" => StatType::Tagged(Tagged::new(path)),
_ => panic!("Invalid stat type: {}", stat_type_name),
}
}
fn initialize(&self, path: &StatPath, stats: &mut Stats) {
match self {
StatType::Flat(flat) => flat.initialize(path, stats),
StatType::Modifiable(modifiable) => modifiable.initialize(path, stats),
StatType::Complex(complex) => complex.initialize(path, stats),
StatType::Tagged(tagged) => tagged.initialize(path, stats),
}
}
fn add_modifier(&mut self, path: &StatPath, modifier: ModifierType) {
match self {
StatType::Flat(flat) => flat.add_modifier(path, modifier),
StatType::Modifiable(modifiable) => modifiable.add_modifier(path, modifier),
StatType::Complex(complex) => complex.add_modifier(path, modifier),
StatType::Tagged(tagged) => tagged.add_modifier(path, modifier),
}
}
fn remove_modifier(&mut self, path: &StatPath, modifier: &ModifierType) {
match self {
StatType::Flat(flat) => flat.remove_modifier(path, modifier),
StatType::Modifiable(modifiable) => modifiable.remove_modifier(path, modifier),
StatType::Complex(complex) => complex.remove_modifier(path, modifier),
StatType::Tagged(tagged) => tagged.remove_modifier(path, modifier),
}
}
fn evaluate(&self, path: &StatPath, stats: &Stats) -> f32 {
match self {
StatType::Flat(flat) => flat.0,
StatType::Modifiable(modifiable) => modifiable.evaluate(path, stats),
StatType::Complex(complex) => complex.evaluate(path, stats),
StatType::Tagged(tagged) => tagged.evaluate(path, stats),
}
}
fn clear_internal_cache(&mut self, path: &StatPath) -> Vec<String> {
match self {
StatType::Flat(_) => Vec::new(),
StatType::Modifiable(_) => Vec::new(),
StatType::Complex(_) => Vec::new(),
StatType::Tagged(tagged) => tagged.clear_internal_cache(path),
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct Flat(f32);
impl Stat for Flat {
fn new(_path: &StatPath) -> Self { Self(0.0) }
fn add_modifier(&mut self, _path: &StatPath, modifier: ModifierType) {
if let ModifierType::Literal(value) = modifier {
self.0 += value;
}
}
fn remove_modifier(&mut self, _path: &StatPath, modifier: &ModifierType) {
if let ModifierType::Literal(value) = modifier {
self.0 -= value;
}
}
fn set(&mut self, _path: &StatPath, value: f32) { self.0 = value; }
fn evaluate(&self, _path: &StatPath, _stats: &Stats) -> f32 { self.0 }
}
#[derive(Debug, Clone)]
pub(crate) struct Modifiable {
pub(crate) relationship: ModType,
pub(crate) base: f32,
pub(crate) mods: Vec<Expression>,
}
impl Stat for Modifiable {
fn new(path: &StatPath) -> Self {
let relationship = Konfig::get_relationship_type(path.name);
let base = if relationship == ModType::Mul { 1.0 } else { 0.0 };
Self { relationship, base, mods: Vec::new() }
}
fn add_modifier(&mut self, _path: &StatPath, modifier: ModifierType) {
match modifier {
ModifierType::Literal(vals) => {
match self.relationship {
ModType::Add => self.base += vals,
ModType::Mul => {
let multiplier = vals + 1.0;
if self.base == 0.0 && self.mods.is_empty() {
self.base = multiplier;
} else {
self.base *= multiplier;
}
}
}
}
ModifierType::Expression(expression) => self.mods.push(expression.clone()),
}
}
fn remove_modifier(&mut self, _path: &StatPath, modifier: &ModifierType) {
match modifier {
ModifierType::Literal(vals) => {
match self.relationship {
ModType::Add => self.base -= vals,
ModType::Mul => {
let multiplier = vals + 1.0;
self.base /= multiplier;
}
}
}
ModifierType::Expression(expression) => {
if let Some(pos) = self.mods.iter().position(|e| e == expression) {
self.mods.remove(pos);
}
}
}
}
fn evaluate(&self, _path: &StatPath, stats: &Stats) -> f32 {
let computed: Vec<f32> = self.mods.iter()
.map(|expr| {
stats.evaluate_expression(&expr.definition, None).unwrap_or(0.0)
})
.collect();
let result = match self.relationship {
ModType::Add => self.base + computed.iter().sum::<f32>(),
ModType::Mul => {
let multiplier = if computed.is_empty() {
1.0
} else {
computed.iter().map(|v| v + 1.0).product::<f32>()
};
self.base * multiplier
},
};
result
}
}
#[derive(Debug, Clone)]
pub(crate) struct Complex {
pub(crate) total: Expression,
pub(crate) modifier_steps: HashMap<String, Modifiable>,
}
impl Stat for Complex {
fn new(path: &StatPath) -> Self {
let total_expression_str = Konfig::get_total_expression(path.name);
let compiled_expression = Expression::new(&total_expression_str).unwrap_or_else(|e| panic!("Failed to compile total_expression for {}: {} - Error: {}", path.name, total_expression_str, e));
let mut modifier_steps = HashMap::new();
for part in compiled_expression.compiled.iter_identifiers() {
let part_path = &StatPath::parse(part);
let step = Modifiable::new(part_path);
modifier_steps.insert(part.to_string(), step);
}
Self {
total: compiled_expression,
modifier_steps,
}
}
fn initialize(&self, path: &StatPath, stats: &mut Stats) {
for part in self.total.compiled.iter_identifiers() {
let part_path = format!("{}.{}", path.name, part);
stats.add_dependent(&part_path, DependentType::LocalStat(path.name.to_string()));
}
}
fn add_modifier(&mut self, path: &StatPath, modifier: ModifierType) {
let Some(part_key) = path.part else { return };
let part = self.modifier_steps.get_mut(part_key).unwrap();
part.add_modifier(path, modifier);
}
fn remove_modifier(&mut self, path: &StatPath, modifier: &ModifierType) {
let Some(part_key) = path.part else { return };
let part = self.modifier_steps.get_mut(part_key).unwrap();
part.remove_modifier(path, modifier);
}
fn evaluate(&self, path: &StatPath, stats: &Stats) -> f32 {
if let Some(part_key) = path.part {
let Some(part) = self.modifier_steps.get(part_key) else { return 0.0 };
let part_total = part.evaluate(path, stats);
stats.set_cached(path.full_path, part_total);
return part_total;
} else {
let mut expression_context = evalexpr::HashMapContext::new();
for (part_name, _modifiable_part_definition) in &self.modifier_steps {
let part_full_path_str = format!("{}.{}", path.name, part_name);
let part_value = stats.evaluate_by_string(&part_full_path_str);
expression_context.set_value(part_name.clone(), evalexpr::Value::Float(part_value as f64))
.map_err(|e| StatError::Internal{details: format!("Failed to set part '{}' in Complex eval context: {}", part_name, e)})
.unwrap();
}
let main_cache_context = stats.get_context();
for (var_key, var_val) in main_cache_context.iter_variables() {
if expression_context.get_value(&var_key).is_none() {
expression_context.set_value(var_key.clone().into(), var_val.clone())
.map_err(|e| StatError::Internal{details: format!("Failed to merge var '{}' in Complex eval context: {}", var_key, e)})
.unwrap();
}
}
let total_expr_str = self.total.definition.as_str();
let total = self.total.compiled
.eval_with_context(&expression_context)
.map_err(|e| StatError::ExpressionError { expression: total_expr_str.to_string(), details: e.to_string() })
.unwrap()
.as_number()
.unwrap_or(0.0) as f32;
stats.set_cached(&path.full_path, total);
return total;
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct Tagged {
pub(crate) total: Expression,
pub(crate) modifier_steps: HashMap<String, TaggedEntry>,
pub(crate) query_tracker: DashMap<(String, u32), ()>, }
#[derive(Debug, Clone)]
pub(crate) struct TaggedEntry(pub HashMap<u32, Modifiable>);
impl TaggedEntry {
fn new() -> Self {
Self(HashMap::new())
}
}
impl Tagged {
fn evaluate_part(&self, part: &str, tag: u32, stats: &Stats) -> f32 {
let Some(tagged_entry) = self.modifier_steps.get(part) else {
return 0.0;
};
let mod_type = Konfig::get_relationship_type(part);
let mut relevant_mod_values = vec![0.0];
for (mod_tag_key, modifiable_stat_for_tag) in &tagged_entry.0 {
let modifier_applies = if tag == 0 {
true } else if *mod_tag_key == u32::MAX {
false } else {
(tag & mod_tag_key) == tag
};
if modifier_applies {
let mod_value = modifiable_stat_for_tag.evaluate(&StatPath::parse(""), stats);
relevant_mod_values.push(mod_value);
}
}
let final_value = match mod_type {
ModType::Add => relevant_mod_values.iter().sum(),
ModType::Mul => {
relevant_mod_values.iter().map(|v| v + 1.0).product()
},
};
final_value
}
}
impl Stat for Tagged {
fn new(path: &StatPath) -> Self {
let total_expression_str = Konfig::get_total_expression(path.name);
let compiled_expression = Expression::new(&total_expression_str).unwrap_or_else(|e| panic!("Failed to compile total_expression for {}: {} - Error: {}", path.name, total_expression_str, e));
let mut modifier_steps = HashMap::new();
for part in compiled_expression.compiled.iter_identifiers() {
let step = TaggedEntry::new();
modifier_steps.insert(part.to_string(), step);
}
Self {
total: compiled_expression,
modifier_steps,
query_tracker: DashMap::new(),
}
}
fn add_modifier(&mut self, path: &StatPath, modifier: ModifierType) {
let Some(tag) = path.tag else { return };
let Some(part) = path.part else { return };
let step_map = self.modifier_steps.entry(part.to_string())
.or_insert(TaggedEntry(HashMap::new()));
let step = step_map.0.entry(tag).or_insert(Modifiable::new(path));
step.add_modifier(path, modifier);
}
fn remove_modifier(&mut self, path: &StatPath, modifier: &ModifierType) {
let Some(tag) = path.tag else { return };
let Some(part) = path.part else { return };
if let Some(step_map) = self.modifier_steps.get_mut(part) {
if let Some(step) = step_map.0.get_mut(&tag) {
step.remove_modifier(path, modifier);
}
}
}
fn evaluate(&self, path: &StatPath, stats: &Stats) -> f32 {
if let (Some(part_name), Some(tag_val)) = (&path.part, path.tag) {
let query_key = (part_name.to_string(), tag_val);
self.query_tracker.insert(query_key, ());
let value = self.evaluate_part(part_name, tag_val, stats);
return value;
}
else if path.part.is_none() && path.tag.is_some() {
let tag_val = path.tag.unwrap();
let mut context = stats.cached_stats.context().clone();
for (part_name_in_total_expr, _step_definition) in &self.modifier_steps {
let part_value = self.evaluate_part(part_name_in_total_expr, tag_val, stats);
context.set_value(part_name_in_total_expr.to_string(), Value::Float(part_value as f64)).unwrap();
}
let total_val = self.total.evaluate(&context);
stats.set_cached(&path.full_path, total_val);
return total_val;
}
0.0
}
fn clear_internal_cache(&mut self, path: &StatPath) -> Vec<String> {
let mut paths_to_invalidate = Vec::new();
if let Some(tag) = path.tag {
self.query_tracker.retain(|(part, query_tag_from_key), _| {
let should_invalidate = if *query_tag_from_key == 0 {
true } else if tag == u32::MAX {
false } else {
(*query_tag_from_key & tag) == *query_tag_from_key
};
if should_invalidate {
let full_path = format!("{}.{}.{}", path.name, part, query_tag_from_key);
paths_to_invalidate.push(full_path);
}
!should_invalidate });
} else {
self.query_tracker.clear();
}
paths_to_invalidate
}
}