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.

Format

+35 -26
+3
Cargo.toml
··· 8 8 [features] 9 9 bevy = ["bevy_ecs", "bevy_app"] 10 10 11 + [workspace] 12 + members = ["immediate_stats_macros"] 13 + 11 14 [dependencies] 12 15 bevy_app = { version = "0.15.3", default-features = false, optional = true } 13 16 bevy_ecs = { version = "0.15", default-features = false, optional = true }
+26 -20
immediate_stats_macros/src/lib.rs
··· 1 - use proc_macro::{TokenStream}; 1 + use proc_macro::TokenStream; 2 + use proc_macro2::Ident; 2 3 use quote::quote; 3 4 use std::collections::HashMap; 4 - use proc_macro2::Ident; 5 5 use syn::{Data, DeriveInput}; 6 6 7 7 #[derive(Default)] ··· 14 14 impl Stat { 15 15 fn set(&mut self, attr_type: AttrType, ident: Ident) { 16 16 match attr_type { 17 - AttrType::Base => self.base = Some(ident), 17 + AttrType::Base => self.base = Some(ident), 18 18 AttrType::Bonus => self.bonus = Some(ident), 19 19 AttrType::Multiplier => self.multiplier = Some(ident), 20 20 } ··· 22 22 } 23 23 24 24 enum AttrType { 25 - Base, Bonus, Multiplier, 25 + Base, 26 + Bonus, 27 + Multiplier, 26 28 } 27 29 28 30 impl TryFrom<&String> for AttrType { 29 31 type Error = (); 30 - 32 + 31 33 fn try_from(value: &String) -> Result<Self, Self::Error> { 32 34 match value.as_str() { 33 35 "base" => Ok(Self::Base), ··· 48 50 49 51 #[proc_macro_derive(StatContainer, attributes(base, bonus, multiplier))] 50 52 pub fn stat_container_derive(item: TokenStream) -> TokenStream { 51 - let tree: DeriveInput = syn::parse(item) 52 - .expect("A valid TokenStream"); 53 + let tree: DeriveInput = syn::parse(item).expect("A valid TokenStream"); 53 54 54 55 let struct_name = &tree.ident; 55 56 56 57 let mut stats: HashMap<String, Stat> = HashMap::new(); 57 - 58 + 58 59 match tree.data { 59 - Data::Struct(s) => { 60 + Data::Struct(s) => { 60 61 for field in s.fields { 61 62 let ident = field.ident.unwrap(); 62 - 63 + 63 64 for attr in field.attrs { 64 65 let path = attr.meta.path(); 65 - 66 - let Ok(attr_ident) = path.require_ident() else { continue }; 67 - let Ok(attr_type) = AttrType::try_from(attr_ident) else { continue }; 68 - 66 + 67 + let Ok(attr_ident) = path.require_ident() else { 68 + continue; 69 + }; 70 + let Ok(attr_type) = AttrType::try_from(attr_ident) else { 71 + continue; 72 + }; 73 + 69 74 let stat_ident = path.segments.first().unwrap().ident.to_string(); 70 75 let stat = match stats.get_mut(&stat_ident) { 71 76 Some(s) => s, 72 77 None => { 73 78 stats.insert(stat_ident.clone(), Stat::default()); 74 79 stats.get_mut(&stat_ident).unwrap() 75 - }, 80 + } 76 81 }; 77 - 82 + 78 83 stat.set(attr_type, ident.clone()); 79 84 } 80 85 } 81 - }, 86 + } 82 87 Data::Enum(_) => todo!(), 83 88 Data::Union(_) => unimplemented!(), 84 89 } 85 - 90 + 86 91 let bonuses = stats.values().filter_map(|x| x.bonus.clone()); 87 92 let multipliers = stats.values().filter_map(|x| x.multiplier.clone()); 88 93 ··· 93 98 #(self.#multipliers = 1.0;)* 94 99 } 95 100 } 96 - }.into() 97 - } 101 + } 102 + .into() 103 + }
+6 -6
src/lib.rs
··· 5 5 6 6 #[cfg(test)] 7 7 mod tests { 8 - use immediate_stats_macros::StatContainer; 9 8 use super::*; 10 - 9 + use immediate_stats_macros::StatContainer; 10 + 11 11 #[derive(StatContainer, PartialEq, Debug)] 12 12 struct Health { 13 13 #[base(health)] ··· 17 17 #[multiplier(health)] 18 18 health_multiplier: f32, 19 19 } 20 - 20 + 21 21 #[test] 22 22 fn default() { 23 23 let mut h = Health { ··· 25 25 health_bonus: 3, 26 26 health_multiplier: 1.5, 27 27 }; 28 - 28 + 29 29 h.reset_modifiers(); 30 - 30 + 31 31 assert_eq!( 32 32 h, 33 33 Health { ··· 37 37 } 38 38 ); 39 39 } 40 - } 40 + }