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 calculation method generation.

+66 -6
+51 -4
immediate_stats_macros/src/lib.rs
··· 1 - use proc_macro::TokenStream; 2 - use proc_macro2::Ident; 1 + use proc_macro2::{Ident, Span, TokenStream}; 3 2 use quote::quote; 4 3 use std::collections::HashMap; 5 4 use syn::{Data, DeriveInput}; ··· 21 20 } 22 21 } 23 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 + 24 49 enum AttrType { 25 50 Base, 26 51 Bonus, ··· 49 74 } 50 75 51 76 #[proc_macro_derive(StatContainer, attributes(base, bonus, multiplier))] 52 - pub fn stat_container_derive(item: TokenStream) -> TokenStream { 77 + pub fn stat_container_derive(item: proc_macro::TokenStream) -> proc_macro::TokenStream { 53 78 let tree: DeriveInput = syn::parse(item).expect("A valid TokenStream"); 54 79 55 80 let struct_name = &tree.ident; ··· 71 96 continue; 72 97 }; 73 98 74 - let stat_ident = path.segments.first().unwrap().ident.to_string(); 99 + let mut stat_ident = String::new(); 100 + 101 + attr.parse_nested_meta(|meta| { 102 + stat_ident = meta.path.get_ident().unwrap().to_string(); 103 + Ok(()) 104 + }) 105 + .unwrap(); 106 + 75 107 let stat = match stats.get_mut(&stat_ident) { 76 108 Some(s) => s, 77 109 None => { ··· 91 123 let bonuses = stats.values().filter_map(|x| x.bonus.clone()); 92 124 let multipliers = stats.values().filter_map(|x| x.multiplier.clone()); 93 125 126 + let methods = stats.iter().map(|(name, stat)| { 127 + let name = Ident::new(name, Span::call_site()); 128 + let calculation = stat.total_calculation(); 129 + 130 + quote! { 131 + pub fn #name(&self) -> i32 { 132 + #calculation 133 + } 134 + } 135 + }); 136 + 94 137 quote! { 95 138 impl StatContainer for #struct_name { 96 139 fn reset_modifiers(&mut self) { 97 140 #(self.#bonuses = 0;)* 98 141 #(self.#multipliers = 1.0;)* 99 142 } 143 + } 144 + 145 + impl #struct_name { 146 + #(#methods)* 100 147 } 101 148 } 102 149 .into()
+15 -2
src/lib.rs
··· 19 19 } 20 20 21 21 #[test] 22 - fn default() { 22 + fn reset() { 23 23 for base in 0..10 { 24 24 let mut h = Health { 25 25 health_base: base, ··· 35 35 health_base: base, 36 36 health_bonus: 0, 37 37 health_multiplier: 1.0, 38 - } 38 + }, 39 39 ); 40 40 } 41 + } 42 + 43 + #[test] 44 + fn calculate() { 45 + assert_eq!( 46 + Health { 47 + health_base: 10, 48 + health_bonus: 4, 49 + health_multiplier: 1.5, 50 + } 51 + .health(), 52 + 21, 53 + ); 41 54 } 42 55 }