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.

Added support for alternate attribute syntax.

Needs some cleanup.

+93 -9
+48
immediate_stats/tests/bevy_butler.rs
··· 58 58 Some(Health(Stat::new(100))).as_ref() 59 59 ); 60 60 } 61 + 62 + #[derive(Resource, Component, StatContainer, Default, PartialEq, Debug)] 63 + #[add_component(plugin(MyPlugin))] 64 + #[add_resource(plugin(MyPlugin))] 65 + struct AlternateSyntax(Stat); 66 + 67 + #[test] 68 + fn reset_component_auto_alternate_syntax() { 69 + let mut app = App::new(); 70 + 71 + app.add_plugins(MyPlugin); 72 + 73 + let entity = app 74 + .world_mut() 75 + .spawn(AlternateSyntax(Stat { 76 + base: 100, 77 + bonus: 50, 78 + multiplier: 2.0, 79 + })) 80 + .id(); 81 + 82 + app.update(); 83 + 84 + assert_eq!( 85 + app.world().get::<AlternateSyntax>(entity), 86 + Some(AlternateSyntax(Stat::new(100))).as_ref() 87 + ); 88 + } 89 + 90 + #[test] 91 + fn reset_resource_auto_alternate_syntax() { 92 + let mut app = App::new(); 93 + 94 + app.add_plugins(MyPlugin); 95 + 96 + app.insert_resource(AlternateSyntax(Stat { 97 + base: 100, 98 + bonus: 50, 99 + multiplier: 2.0, 100 + })); 101 + 102 + app.update(); 103 + 104 + assert_eq!( 105 + app.world().get_resource::<AlternateSyntax>(), 106 + Some(AlternateSyntax(Stat::new(100))).as_ref() 107 + ); 108 + }
+45 -9
immediate_stats_macros/src/bevy/butler.rs
··· 1 - use darling::FromMeta; 1 + use darling::ast::NestedMeta; 2 + use darling::{Error, FromMeta}; 2 3 use proc_macro::{self, TokenStream}; 3 4 use proc_macro2::Ident; 4 5 use quote::{ToTokens, format_ident, quote}; 5 - use syn::{DeriveInput, Path, parse_macro_input}; 6 + use syn::{DeriveInput, Expr, Meta, Path, parse_macro_input}; 6 7 8 + // Todo Fix error handling and add documentation. 7 9 pub fn register_systems(input: TokenStream) -> TokenStream { 8 10 let input = parse_macro_input!(input as DeriveInput); 9 11 let struct_name = &input.ident; ··· 23 25 butler_attributes.into_token_stream().into() 24 26 } 25 27 26 - #[derive(FromMeta)] 27 - pub struct PluginPath { 28 - pub plugin: Path, 29 - } 30 - 31 28 pub struct ButlerAttributes<'a> { 32 29 ident: &'a Ident, 33 30 component_plugin: Option<PluginPath>, ··· 48 45 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { 49 46 if let Some(plugin_path) = &self.component_plugin { 50 47 let ident = &self.ident; 51 - let plugin = &plugin_path.plugin; 48 + let plugin = &plugin_path.0; 52 49 let use_as = format_ident!("__{ident}_component"); 53 50 54 51 tokens.extend(quote! { ··· 59 56 60 57 if let Some(plugin_path) = &self.resource_plugin { 61 58 let ident = &self.ident; 62 - let plugin = &plugin_path.plugin; 59 + let plugin = &plugin_path.0; 63 60 let use_as = format_ident!("__{ident}_resource"); 64 61 65 62 tokens.extend(quote! { ··· 69 66 } 70 67 } 71 68 } 69 + 70 + /// Represents a `plugin(PATH)` or `plugin = PATH` attribute meta. 71 + pub struct PluginPath(pub Path); 72 + 73 + impl FromMeta for PluginPath { 74 + fn from_list(items: &[NestedMeta]) -> darling::Result<Self> { 75 + for item in items { 76 + return match item { 77 + NestedMeta::Meta(meta) => match meta { 78 + Meta::Path(_) => Err(Error::custom("Expected a value for `plugin`")), 79 + Meta::List(list) => { 80 + if list.path.require_ident()? != "plugin" { 81 + continue; 82 + } 83 + 84 + let mut path = None; 85 + 86 + list.parse_nested_meta(|value_meta| { 87 + path = Some(value_meta.path); 88 + Ok(()) 89 + })?; 90 + 91 + match path { 92 + None => Err(Error::custom("Expected `plugin` attribute")), 93 + Some(path) => Ok(PluginPath(path)), 94 + } 95 + } 96 + Meta::NameValue(name_value) => match &name_value.value { 97 + Expr::Path(p) => Ok(PluginPath(p.path.clone())), 98 + _ => Err(Error::custom("Expected a path to a butler plugin")), 99 + }, 100 + }, 101 + NestedMeta::Lit(_) => Err(Error::custom("Expected `plugin` attribute")), 102 + }; 103 + } 104 + 105 + Err(Error::custom("Expected `plugin` attribute")) 106 + } 107 + }