1use std::collections::HashMap;
2
3use bevy_ecs::prelude::*;
4pub use chunkedge_generated::attributes::{EntityAttribute, EntityAttributeOperation};
5use chunkedge_protocol::packets::play::update_attributes_s2c::*;
6use chunkedge_protocol::{Ident, VarInt};
7use indexmap::IndexMap;
8
9#[derive(Component, Clone, PartialEq, Debug)]
11pub struct EntityAttributeInstance {
12 attribute: EntityAttribute,
14 base_value: f64,
16 add_modifiers: IndexMap<String, f64>,
18 multiply_base_modifiers: IndexMap<String, f64>,
20 multiply_total_modifiers: IndexMap<String, f64>,
22}
23
24impl EntityAttributeInstance {
25 pub fn new(attribute: EntityAttribute) -> Self {
27 Self {
28 attribute,
29 base_value: attribute.default_value(),
30 add_modifiers: IndexMap::new(),
31 multiply_base_modifiers: IndexMap::new(),
32 multiply_total_modifiers: IndexMap::new(),
33 }
34 }
35
36 pub fn new_with_value(attribute: EntityAttribute, base_value: f64) -> Self {
38 Self {
39 attribute,
40 base_value,
41 add_modifiers: IndexMap::new(),
42 multiply_base_modifiers: IndexMap::new(),
43 multiply_total_modifiers: IndexMap::new(),
44 }
45 }
46
47 pub fn attribute(&self) -> EntityAttribute {
49 self.attribute
50 }
51
52 pub fn base_value(&self) -> f64 {
54 self.base_value
55 }
56
57 pub fn compute_value(&self) -> f64 {
59 let mut value = self.base_value;
60
61 for (_, modifier) in &self.add_modifiers {
63 value += modifier;
64 }
65
66 let v = value;
67
68 for (_, modifier) in &self.multiply_base_modifiers {
70 value += v * modifier;
71 }
72
73 for (_, modifier) in &self.multiply_total_modifiers {
75 value += value * modifier;
76 }
77
78 value.clamp(self.attribute.min_value(), self.attribute.max_value())
79 }
80
81 pub fn with_add_modifier(&mut self, id: &str, modifier: f64) -> &mut Self {
87 self.add_modifiers.insert(id.to_owned(), modifier);
88 self
89 }
90
91 pub fn with_multiply_base_modifier(&mut self, id: &str, modifier: f64) -> &mut Self {
97 self.multiply_base_modifiers.insert(id.to_owned(), modifier);
98 self
99 }
100
101 pub fn with_multiply_total_modifier(&mut self, id: &str, modifier: f64) -> &mut Self {
107 self.multiply_total_modifiers
108 .insert(id.to_owned(), modifier);
109 self
110 }
111
112 pub fn with_modifier(
118 &mut self,
119 id: &str,
120 modifier: f64,
121 operation: EntityAttributeOperation,
122 ) -> &mut Self {
123 match operation {
124 EntityAttributeOperation::Add => self.with_add_modifier(id, modifier),
125 EntityAttributeOperation::MultiplyBase => {
126 self.with_multiply_base_modifier(id, modifier)
127 }
128 EntityAttributeOperation::MultiplyTotal => {
129 self.with_multiply_total_modifier(id, modifier)
130 }
131 }
132 }
133
134 pub fn remove_modifier(&mut self, id: &String) {
136 self.add_modifiers.swap_remove(id);
137 self.multiply_base_modifiers.swap_remove(id);
138 self.multiply_total_modifiers.swap_remove(id);
139 }
140
141 pub fn clear_modifiers(&mut self) {
143 self.add_modifiers.clear();
144 self.multiply_base_modifiers.clear();
145 self.multiply_total_modifiers.clear();
146 }
147
148 pub fn has_modifier(&self, id: &String) -> bool {
150 self.add_modifiers.contains_key(id)
151 || self.multiply_base_modifiers.contains_key(id)
152 || self.multiply_total_modifiers.contains_key(id)
153 }
154
155 pub(crate) fn to_property(&self) -> TrackedEntityProperty {
158 TrackedEntityProperty {
159 id: self.attribute.get_id().into(),
160 value: self.base_value(),
161 modifiers: self
162 .add_modifiers
163 .iter()
164 .map(|(id, &amount)| TrackedAttributeModifier {
165 id: id.clone(),
166 amount,
167 operation: 0,
168 })
169 .chain(self.multiply_base_modifiers.iter().map(|(id, &amount)| {
170 TrackedAttributeModifier {
171 id: id.clone(),
172 amount,
173 operation: 1,
174 }
175 }))
176 .chain(self.multiply_total_modifiers.iter().map(|(id, &amount)| {
177 TrackedAttributeModifier {
178 id: id.clone(),
179 amount,
180 operation: 2,
181 }
182 }))
183 .collect(),
184 }
185 }
186}
187
188#[derive(Component, Clone, PartialEq, Debug, Default)]
190pub struct EntityAttributes {
191 attributes: HashMap<EntityAttribute, EntityAttributeInstance>,
192 recently_changed: Vec<EntityAttribute>,
193}
194
195impl EntityAttributes {
196 pub(crate) fn take_recently_changed(&mut self) -> Vec<EntityAttribute> {
198 std::mem::take(&mut self.recently_changed)
199 }
200
201 pub(crate) fn mark_recently_changed(&mut self, attribute: EntityAttribute) {
203 if attribute.tracked() && !self.recently_changed.contains(&attribute) {
204 self.recently_changed.push(attribute);
205 }
206 }
207}
208
209impl EntityAttributes {
210 pub fn new() -> Self {
212 Self {
213 attributes: HashMap::new(),
214 recently_changed: Vec::new(),
215 }
216 }
217
218 pub fn get(&self, attribute: EntityAttribute) -> Option<&EntityAttributeInstance> {
220 self.attributes.get(&attribute)
221 }
222
223 pub fn get_base_value(&self, attribute: EntityAttribute) -> Option<f64> {
227 self.get(attribute).map(|instance| instance.base_value())
228 }
229
230 pub fn get_compute_value(&self, attribute: EntityAttribute) -> Option<f64> {
234 self.get(attribute).map(|instance| instance.compute_value())
235 }
236
237 pub fn has_attribute(&self, attribute: EntityAttribute) -> bool {
239 self.attributes.contains_key(&attribute)
240 }
241
242 pub fn create_attribute(&mut self, attribute: EntityAttribute) {
244 self.mark_recently_changed(attribute);
245 self.attributes
246 .entry(attribute)
247 .or_insert_with(|| EntityAttributeInstance::new(attribute));
248 }
249
250 pub(crate) fn with_attribute_and_value(
258 mut self,
259 attribute: EntityAttribute,
260 base_value: f64,
261 ) -> Self {
262 self.attributes
263 .entry(attribute)
264 .or_insert_with(|| EntityAttributeInstance::new_with_value(attribute, base_value))
265 .base_value = base_value;
266 self
267 }
268
269 pub fn set_base_value(&mut self, attribute: EntityAttribute, value: f64) {
271 self.mark_recently_changed(attribute);
272 self.attributes
273 .entry(attribute)
274 .or_insert_with(|| EntityAttributeInstance::new(attribute))
275 .base_value = value;
276 }
277
278 pub fn set_add_modifier(&mut self, attribute: EntityAttribute, id: &str, modifier: f64) {
280 self.mark_recently_changed(attribute);
281 self.attributes
282 .entry(attribute)
283 .or_insert_with(|| EntityAttributeInstance::new(attribute))
284 .with_add_modifier(id, modifier);
285 }
286
287 pub fn set_multiply_base_modifier(
289 &mut self,
290 attribute: EntityAttribute,
291 id: &str,
292 modifier: f64,
293 ) {
294 self.mark_recently_changed(attribute);
295 self.attributes
296 .entry(attribute)
297 .or_insert_with(|| EntityAttributeInstance::new(attribute))
298 .with_multiply_base_modifier(id, modifier);
299 }
300
301 pub fn set_multiply_total_modifier(
303 &mut self,
304 attribute: EntityAttribute,
305 id: &str,
306 modifier: f64,
307 ) {
308 self.mark_recently_changed(attribute);
309 self.attributes
310 .entry(attribute)
311 .or_insert_with(|| EntityAttributeInstance::new(attribute))
312 .with_multiply_total_modifier(id, modifier);
313 }
314
315 pub fn set_modifier<S: Into<String>>(
317 &mut self,
318 attribute: EntityAttribute,
319 id: S,
320 modifier: f64,
321 operation: EntityAttributeOperation,
322 ) {
323 self.mark_recently_changed(attribute);
324 self.attributes
325 .entry(attribute)
326 .or_insert_with(|| EntityAttributeInstance::new(attribute))
327 .with_modifier(&id.into(), modifier, operation);
328 }
329
330 pub fn remove_modifier<S: Into<String>>(&mut self, attribute: EntityAttribute, id: S) {
332 self.mark_recently_changed(attribute);
333 if let Some(instance) = self.attributes.get_mut(&attribute) {
334 instance.remove_modifier(&id.into());
335 }
336 }
337
338 pub fn clear_modifiers(&mut self, attribute: EntityAttribute) {
340 self.mark_recently_changed(attribute);
341 if let Some(instance) = self.attributes.get_mut(&attribute) {
342 instance.clear_modifiers();
343 }
344 }
345
346 pub fn has_modifier<S: Into<String>>(&self, attribute: EntityAttribute, id: S) -> bool {
348 self.attributes
349 .get(&attribute)
350 .is_some_and(|inst| inst.has_modifier(&id.into()))
351 }
352
353 pub fn to_properties(&self) -> Vec<AttributeProperty<'_>> {
357 self.attributes
358 .iter()
359 .filter(|(_, instance)| instance.attribute().tracked())
360 .map(|(_, instance)| instance.to_property().to_property())
361 .collect()
362 }
363}
364
365#[derive(Component, Clone, Debug, Default)]
367pub struct TrackedEntityAttributes {
368 modified: IndexMap<EntityAttribute, TrackedEntityProperty>,
370}
371
372#[derive(Clone, Debug)]
373pub(crate) struct TrackedEntityProperty {
374 id: i32,
375 value: f64,
376 modifiers: Vec<TrackedAttributeModifier>,
377}
378
379#[derive(Clone, Debug)]
380pub(crate) struct TrackedAttributeModifier {
381 id: String,
382 amount: f64,
383 operation: u8,
384}
385
386impl TrackedEntityProperty {
387 fn to_property(&self) -> AttributeProperty<'static> {
389 AttributeProperty {
390 id: VarInt(self.id),
391 value: self.value,
392 modifiers: self
393 .modifiers
394 .iter()
395 .map(|modifier| AttributeModifier {
396 id: Ident::new(modifier.id.clone()).unwrap(),
397 amount: modifier.amount,
398 operation: modifier.operation,
399 })
400 .collect(),
401 }
402 }
403}
404
405impl TrackedEntityAttributes {
406 pub fn new() -> Self {
408 Self {
409 modified: IndexMap::new(),
410 }
411 }
412
413 pub fn mark_modified(&mut self, attributes: &EntityAttributes, attribute: EntityAttribute) {
415 if let Some(instance) = attributes.get(attribute) {
416 self.modified.insert(attribute, instance.to_property());
417 }
418 }
419
420 pub fn get_properties(&self) -> Vec<AttributeProperty<'static>> {
422 self.modified
423 .iter()
424 .map(|(_, property)| property.to_property())
425 .collect()
426 }
427
428 pub fn clear(&mut self) {
430 self.modified.clear();
431 }
432}
433
434#[cfg(test)]
435mod tests {
436 use super::*;
437
438 #[test]
439 fn test_compute_value() {
440 let add_id = "my_attr".to_owned();
441 let mut attributes = EntityAttributes::new();
442 attributes.set_base_value(EntityAttribute::MaxHealth, 20.0);
443 attributes.set_add_modifier(EntityAttribute::MaxHealth, &add_id, 10.0);
444 attributes.set_multiply_base_modifier(EntityAttribute::MaxHealth, "1", 0.2);
445 attributes.set_multiply_base_modifier(EntityAttribute::MaxHealth, "2", 0.2);
446 attributes.set_multiply_total_modifier(EntityAttribute::MaxHealth, "3", 0.5);
447
448 assert_eq!(
449 attributes.get_compute_value(EntityAttribute::MaxHealth),
450 Some(63.0) );
452
453 attributes.remove_modifier(EntityAttribute::MaxHealth, &add_id);
454
455 assert_eq!(
456 attributes.get_compute_value(EntityAttribute::MaxHealth),
457 Some(42.0) );
459 }
460}