Game stats that reset every frame, inspired by immediate mode GUI.
gamedev bevy stats
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Moved structs to sperate module.

Also renamed/added comments to make it less confusing.

+83 -73
+11 -73
immediate_stats_macros/src/lib.rs
··· 1 - use proc_macro2::{Ident, Span, TokenStream}; 1 + mod stat_attributes; 2 + 3 + use crate::stat_attributes::{AttrType, Stat}; 4 + use proc_macro2::{Ident, Span}; 2 5 use quote::quote; 3 6 use std::collections::HashMap; 4 7 use syn::{Data, DeriveInput}; 5 8 6 - #[derive(Default)] 7 - struct Stat { 8 - base: Option<Ident>, 9 - bonus: Option<Ident>, 10 - multiplier: Option<Ident>, 11 - } 12 - 13 - impl Stat { 14 - fn set(&mut self, attr_type: AttrType, ident: Ident) { 15 - match attr_type { 16 - AttrType::Base => self.base = Some(ident), 17 - AttrType::Bonus => self.bonus = Some(ident), 18 - AttrType::Multiplier => self.multiplier = Some(ident), 19 - } 20 - } 21 - } 22 - 23 - impl Stat { 24 - /// Create the calculation for stat total. 25 - /// Results in `(self.base + self.bonus) * self.multiplier` 26 - /// with type conversions and missing values removed. 27 - /// # Panics 28 - /// When `self.base` is `None`. 29 - fn total_calculation(&self) -> TokenStream { 30 - let result = self.base.clone().expect("All stats require a base!"); 31 - let mut result = quote! { self.#result }; 32 - 33 - if let Some(bonus) = &self.bonus { 34 - result = quote! { 35 - #result + self.#bonus 36 - } 37 - } 38 - 39 - if let Some(multiplier) = &self.multiplier { 40 - result = quote! { 41 - ((#result) as f32 * self.#multiplier) as i32 42 - } 43 - } 44 - 45 - result 46 - } 47 - } 48 - 49 - enum AttrType { 50 - Base, 51 - Bonus, 52 - Multiplier, 53 - } 54 - 55 - impl TryFrom<&String> for AttrType { 56 - type Error = (); 57 - 58 - fn try_from(value: &String) -> Result<Self, Self::Error> { 59 - match value.as_str() { 60 - "base" => Ok(Self::Base), 61 - "bonus" => Ok(Self::Bonus), 62 - "multiplier" => Ok(Self::Multiplier), 63 - _ => Err(()), 64 - } 65 - } 66 - } 67 - 68 - impl TryFrom<&Ident> for AttrType { 69 - type Error = (); 70 - 71 - fn try_from(value: &Ident) -> Result<Self, Self::Error> { 72 - Self::try_from(&value.to_string()) 73 - } 74 - } 75 - 76 9 #[proc_macro_derive(StatContainer, attributes(base, bonus, multiplier))] 77 10 pub fn stat_container_derive(item: proc_macro::TokenStream) -> proc_macro::TokenStream { 78 11 let tree: DeriveInput = syn::parse(item).expect("A valid TokenStream"); ··· 84 17 match tree.data { 85 18 Data::Struct(s) => { 86 19 for field in s.fields { 87 - let ident = field.ident.unwrap(); 20 + // (health_base, damage_base, etc.) 21 + let field_ident = field 22 + .ident 23 + .expect("Field must have an identifier. Cannot be used on tuples."); 88 24 89 25 for attr in field.attrs { 90 26 let path = attr.meta.path(); 91 27 28 + // (base, bonus, or multiplier) 92 29 let Ok(attr_ident) = path.require_ident() else { 93 30 continue; 94 31 }; ··· 96 33 continue; 97 34 }; 98 35 36 + // (health, damage, etc.) 99 37 let mut stat_ident = String::new(); 100 38 101 39 attr.parse_nested_meta(|meta| { ··· 112 50 } 113 51 }; 114 52 115 - stat.set(attr_type, ident.clone()); 53 + stat.set(attr_type, field_ident.clone()); 116 54 } 117 55 } 118 56 }
+72
immediate_stats_macros/src/stat_attributes.rs
··· 1 + use proc_macro2::{Ident, TokenStream}; 2 + use quote::quote; 3 + 4 + /// Stores the identifiers for the individual stat fields. 5 + #[derive(Default)] 6 + pub(super) struct Stat { 7 + pub base: Option<Ident>, 8 + pub bonus: Option<Ident>, 9 + pub multiplier: Option<Ident>, 10 + } 11 + 12 + impl Stat { 13 + pub(super) fn set(&mut self, attr_type: AttrType, ident: Ident) { 14 + match attr_type { 15 + AttrType::Base => self.base = Some(ident), 16 + AttrType::Bonus => self.bonus = Some(ident), 17 + AttrType::Multiplier => self.multiplier = Some(ident), 18 + } 19 + } 20 + 21 + /// Create the calculation for stat total. 22 + /// Results in `(self.base + self.bonus) * self.multiplier` 23 + /// with type conversions and missing values removed. 24 + /// # Panics 25 + /// When `self.base` is `None`. 26 + pub(super) fn total_calculation(&self) -> TokenStream { 27 + let result = self.base.clone().expect("All stats require a base!"); 28 + let mut result = quote! { self.#result }; 29 + 30 + if let Some(bonus) = &self.bonus { 31 + result = quote! { 32 + #result + self.#bonus 33 + } 34 + } 35 + 36 + if let Some(multiplier) = &self.multiplier { 37 + result = quote! { 38 + ((#result) as f32 * self.#multiplier) as i32 39 + } 40 + } 41 + 42 + result 43 + } 44 + } 45 + 46 + /// The types of stat fields. 47 + pub(super) enum AttrType { 48 + Base, 49 + Bonus, 50 + Multiplier, 51 + } 52 + 53 + impl TryFrom<&String> for AttrType { 54 + type Error = (); 55 + 56 + fn try_from(value: &String) -> Result<Self, Self::Error> { 57 + match value.as_str() { 58 + "base" => Ok(Self::Base), 59 + "bonus" => Ok(Self::Bonus), 60 + "multiplier" => Ok(Self::Multiplier), 61 + _ => Err(()), 62 + } 63 + } 64 + } 65 + 66 + impl TryFrom<&Ident> for AttrType { 67 + type Error = (); 68 + 69 + fn try_from(value: &Ident) -> Result<Self, Self::Error> { 70 + Self::try_from(&value.to_string()) 71 + } 72 + }