use bevy::ecs::query::QueryFilter;
use bevy::prelude::*;
use crate::attributes_mut::AttributesMut;
use crate::node::ReduceFn;
use crate::tags::TagMask;
pub trait AttributeBuilder: Send + Sync {
fn apply(&self, entity: Entity, attributes: &mut AttributesMut);
fn clone_box(&self) -> Box<dyn AttributeBuilder>;
fn fmt_debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
}
impl Clone for Box<dyn AttributeBuilder> {
fn clone(&self) -> Self {
self.clone_box()
}
}
impl std::fmt::Debug for Box<dyn AttributeBuilder> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_debug(f)
}
}
#[derive(Clone, Debug)]
pub struct ComplexAttribute {
pub name: String,
pub parts: Vec<(String, ReduceFn)>,
pub expression: String,
}
impl ComplexAttribute {
pub fn new(name: &str, parts: &[(&str, ReduceFn)], expression: &str) -> Self {
Self {
name: name.to_string(),
parts: parts.iter().map(|(n, r)| (n.to_string(), r.clone())).collect(),
expression: expression.to_string(),
}
}
}
impl AttributeBuilder for ComplexAttribute {
fn apply(&self, entity: Entity, attributes: &mut AttributesMut) {
let parts: Vec<(&str, ReduceFn)> = self.parts
.iter()
.map(|(n, r)| (n.as_str(), r.clone()))
.collect();
let _ = attributes.complex_attribute(entity, &self.name, &parts, &self.expression);
}
fn clone_box(&self) -> Box<dyn AttributeBuilder> {
Box::new(self.clone())
}
fn fmt_debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
}
}
#[derive(Clone, Debug)]
pub enum ModifierValue {
Literal(f32),
ExprSource(String),
}
impl From<f32> for ModifierValue {
fn from(val: f32) -> Self {
ModifierValue::Literal(val)
}
}
impl From<&str> for ModifierValue {
fn from(s: &str) -> Self {
ModifierValue::ExprSource(s.to_string())
}
}
impl From<String> for ModifierValue {
fn from(s: String) -> Self {
ModifierValue::ExprSource(s)
}
}
#[derive(Clone, Debug)]
pub struct ModifierEntry {
pub attribute: String,
pub value: ModifierValue,
pub tag: TagMask,
}
#[derive(Clone, Debug, Default)]
pub struct ModifierSet {
pub(crate) entries: Vec<ModifierEntry>,
pub(crate) builders: Vec<Box<dyn AttributeBuilder>>,
}
impl ModifierSet {
pub fn new() -> Self {
Self::default()
}
pub fn entries(&self) -> &[ModifierEntry] {
&self.entries
}
pub fn add(&mut self, attribute: &str, value: impl Into<ModifierValue>) {
self.entries.push(ModifierEntry {
attribute: attribute.to_string(),
value: value.into(),
tag: TagMask::NONE,
});
}
pub fn add_tagged(&mut self, attribute: &str, value: impl Into<ModifierValue>, tag: TagMask) {
self.entries.push(ModifierEntry {
attribute: attribute.to_string(),
value: value.into(),
tag,
});
}
pub fn add_expr(&mut self, attribute: &str, expr_source: &str) {
self.add(
attribute,
ModifierValue::ExprSource(expr_source.to_string()),
);
}
pub fn add_expr_tagged(&mut self, attribute: &str, expr_source: &str, tag: TagMask) {
self.add_tagged(
attribute,
ModifierValue::ExprSource(expr_source.to_string()),
tag,
);
}
pub fn add_builder(&mut self, builder: impl AttributeBuilder + 'static) {
self.builders.push(Box::new(builder));
}
pub fn apply_builders(&self, entity: Entity, attributes: &mut AttributesMut) {
for builder in &self.builders {
builder.apply(entity, attributes);
}
}
pub fn apply<F: QueryFilter>(&self, entity: Entity, attributes: &mut AttributesMut<'_, '_, F>) {
for entry in &self.entries {
match &entry.value {
ModifierValue::Literal(val) => {
attributes.add_modifier_tagged(entity, &entry.attribute, *val, entry.tag);
}
ModifierValue::ExprSource(src) => {
if entry.tag.is_empty() {
let _ = attributes.add_expr_modifier(entity, &entry.attribute, src);
} else {
let _ = attributes.add_expr_modifier_tagged(
entity,
&entry.attribute,
src,
entry.tag,
);
}
}
}
}
}
pub fn try_apply<F: QueryFilter>(
&self,
entity: Entity,
attributes: &mut AttributesMut<'_, '_, F>,
) -> Result<(), crate::expr::CompileError> {
for entry in &self.entries {
match &entry.value {
ModifierValue::Literal(val) => {
attributes.add_modifier_tagged(entity, &entry.attribute, *val, entry.tag);
}
ModifierValue::ExprSource(src) => {
if entry.tag.is_empty() {
attributes.add_expr_modifier(entity, &entry.attribute, src)?;
} else {
attributes.add_expr_modifier_tagged(
entity,
&entry.attribute,
src,
entry.tag,
)?;
}
}
}
}
Ok(())
}
pub fn apply_all(&self, entity: Entity, attributes: &mut AttributesMut) {
self.apply_builders(entity, attributes);
self.apply(entity, attributes);
}
pub fn remove<F: QueryFilter>(
&self,
entity: Entity,
attributes: &mut AttributesMut<'_, '_, F>,
) {
for entry in &self.entries {
match &entry.value {
ModifierValue::Literal(val) => {
let modifier = crate::modifier::Modifier::Flat(*val);
attributes.remove_modifier_tagged(
entity,
&entry.attribute,
&modifier,
entry.tag,
);
}
ModifierValue::ExprSource(src) => {
if let Ok(expr) =
crate::expr::Expr::compile(src, Some(attributes.tag_resolver()))
{
let modifier = crate::modifier::Modifier::Expr(expr);
attributes.remove_modifier_tagged(
entity,
&entry.attribute,
&modifier,
entry.tag,
);
}
}
}
}
}
pub fn try_remove<F: QueryFilter>(
&self,
entity: Entity,
attributes: &mut AttributesMut<'_, '_, F>,
) -> Result<(), crate::expr::CompileError> {
for entry in &self.entries {
match &entry.value {
ModifierValue::Literal(val) => {
let modifier = crate::modifier::Modifier::Flat(*val);
attributes.remove_modifier_tagged(
entity,
&entry.attribute,
&modifier,
entry.tag,
);
}
ModifierValue::ExprSource(src) => {
let expr = crate::expr::Expr::compile(src, Some(attributes.tag_resolver()))?;
let modifier = crate::modifier::Modifier::Expr(expr);
attributes.remove_modifier_tagged(
entity,
&entry.attribute,
&modifier,
entry.tag,
);
}
}
}
Ok(())
}
pub fn combine(&mut self, other: &ModifierSet) {
self.entries.extend(other.entries.iter().cloned());
self.builders.extend(other.builders.iter().map(|b| b.clone_box()));
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty() && self.builders.is_empty()
}
}
#[derive(Component, Clone, Debug, Default)]
#[require(crate::prelude::Attributes)]
pub struct AttributeInitializer(pub ModifierSet);
impl AttributeInitializer {
pub fn new(set: ModifierSet) -> Self {
Self(set)
}
}
pub(crate) fn apply_initial_attributes(
trigger: On<Add, AttributeInitializer>,
initial_query: Query<&AttributeInitializer>,
mut attributes: AttributesMut,
mut commands: Commands,
) {
let entity = trigger.entity;
if let Ok(initial) = initial_query.get(entity) {
initial.0.apply_builders(entity, &mut attributes);
initial.0.apply(entity, &mut attributes);
}
commands.entity(entity).remove::<AttributeInitializer>();
}