use std::collections::HashMap;
use bevy::prelude::*;
use bevy_ecs::system::SystemParam;
use crate::{prelude::*, stat_effect::InstantStatEffectInstance};
#[derive(Component, Default, Reflect)]
pub struct StatContext {
pub sources: HashMap<String, Entity>,
}
impl StatContext {
pub fn insert(&mut self, context: &str, entity: Entity) {
self.sources.insert(context.to_string(), entity);
}
pub fn trigger_change_detection(&mut self) {}
}
#[derive(Debug)]
pub enum StatContextRefs<'a> {
Definitions(&'a StatCollection),
SubContext(Box<HashMap<&'a str, StatContextRefs<'a>>>),
}
impl<'a> StatContextRefs<'a> {
pub fn build(
entity: Entity,
defs_query: &'a Query<'_, '_, &mut StatCollection>,
ctx_query: &'a Query<'_, '_, &StatContext>,
) -> StatContextRefs<'a> {
let mut context_map = HashMap::new();
if let Ok(defs) = defs_query.get(entity) {
context_map.insert("self", StatContextRefs::Definitions(defs));
}
if let Ok(stat_context) = ctx_query.get(entity) {
for (key, child_entity) in &stat_context.sources {
if *child_entity == entity {
continue;
}
let child_src = Self::build(*child_entity, defs_query, ctx_query);
context_map.insert(key, child_src);
}
}
StatContextRefs::SubContext(Box::new(context_map))
}
pub fn get(&self, var: &str) -> Result<f32, StatError> {
let parts: Vec<&str> = var.split('.').collect();
self.get_parts(&parts)
}
fn get_parts(&self, parts: &[&str]) -> Result<f32, StatError> {
if parts.is_empty() {
return Err(StatError::NotFound("Empty stat identifier".to_string()));
}
match self {
StatContextRefs::Definitions(defs) => {
if parts.len() == 1 {
defs.get_str(parts[0], self)
} else {
let joined = parts.join(".");
defs.get_str(&joined, self)
}
}
StatContextRefs::SubContext(context_map) => {
let head = parts[0];
let tail = &parts[1..];
if let Some(subcontext) = context_map.get(head) {
return subcontext.get_parts(tail);
}
if let Some(StatContextRefs::Definitions(defs)) = context_map.get("self") {
let stat_name = parts.join(".");
return defs.get_str(&stat_name, self);
}
Err(StatError::NotFound(format!("Context '{}' not found", head)))
}
}
}
}
#[derive(SystemParam)]
pub struct StatAccessor<'w, 's> {
definitions: Query<'w, 's, &'static mut StatCollection>,
contexts: Query<'w, 's, &'static StatContext>,
}
impl StatAccessor<'_, '_> {
pub fn build(&self, entity: Entity) -> StatContextRefs {
StatContextRefs::build(entity, &self.definitions, &self.contexts)
}
pub fn build_with_target(&self, entity: Entity, target: Entity) -> StatContextRefs {
let target_context = StatContextRefs::build(entity, &self.definitions, &self.contexts);
let mut value = StatContextRefs::build(target, &self.definitions, &self.contexts);
if let StatContextRefs::SubContext(ref mut hash_map) = value {
hash_map.insert("target", target_context);
}
value
}
pub fn apply_effect(&mut self, origin: Entity, target: Entity, stat_effect: &StatEffect) {
let effect_instance = {
let stat_context = self.build(origin);
stat_effect.build_instant(&stat_context)
};
self.apply_instant_effect(target, &effect_instance);
}
pub fn apply_instant_effect(&mut self, entity: Entity, effect: &InstantStatEffectInstance) {
let Ok(mut stats) = self.definitions.get_mut(entity) else {
return;
};
for (stat, value) in effect.effects.iter() {
let _ = stats.add(stat, *value);
}
}
}