An experimental, status effects-as-entities system for Bevy.
0
fork

Configure Feed

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

Generic bundle using seperate world.

+71 -40
+1 -9
src/bundle.rs
··· 1 - use crate::EffectMode; 2 1 use bevy_ecs::prelude::*; 3 2 4 3 /// A "bundle" of components/settings used when applying an effect. ··· 14 13 /// ### [`EffectedBy::spawn`](SpawnRelated::spawn) 15 14 #[doc = include_str!("../docs/effected_by_spawn_example.md")] 16 15 #[derive(Default)] 17 - pub struct EffectBundle<B: Bundle> { 18 - /// The name/ID of the effect. Effects with different IDs have no effect on one another. 19 - pub name: Name, 20 - /// Describes the logic used when new effect collides with an existing one. 21 - pub mode: EffectMode, 22 - /// Components that will be added to the effect. This is where the actual effect components get added. 23 - pub bundle: B, 24 - } 16 + pub struct EffectBundle<B: Bundle>(pub B);
+39
src/bundle_inspector.rs
··· 1 + use crate::EffectMode; 2 + use bevy_ecs::prelude::{Bundle, Entity, Name, Resource, World}; 3 + 4 + #[derive(Resource)] 5 + pub(crate) struct BundleInspector { 6 + world: World, 7 + scratch_entity: Entity, 8 + } 9 + 10 + impl Default for BundleInspector { 11 + fn default() -> Self { 12 + let mut world = World::new(); 13 + let scratch_entity = world.spawn_empty().id(); 14 + Self { 15 + world, 16 + scratch_entity, 17 + } 18 + } 19 + } 20 + 21 + impl BundleInspector { 22 + pub fn get_effect_meta<B: Bundle>(&mut self, bundle: B) -> (Option<Name>, EffectMode) { 23 + let e = self.scratch_entity; 24 + self.world.entity_mut(e).insert(bundle); 25 + 26 + let name = self.world.entity(e).get::<Name>().cloned(); 27 + 28 + let mode = self 29 + .world 30 + .entity_mut(e) 31 + .get::<EffectMode>() 32 + .copied() 33 + .unwrap_or_default(); 34 + 35 + self.world.entity_mut(e).clear(); 36 + 37 + (name, mode) 38 + } 39 + }
+28 -31
src/command.rs
··· 1 1 use crate::bundle::EffectBundle; 2 + use crate::bundle_inspector::BundleInspector; 2 3 use crate::registry::{EffectMergeFn, EffectMergeRegistry}; 3 4 use crate::{EffectMode, EffectedBy, Effecting}; 4 5 use bevy_ecs::entity_disabling::Disabled; ··· 17 18 /// The entity to apply the effect to. 18 19 pub target: Entity, 19 20 /// The effect to apply. 20 - pub bundle: EffectBundle<B>, 21 + pub bundle: B, 21 22 } 22 23 23 24 impl<B: Bundle> AddEffectCommand<B> { 24 - fn spawn(self, world: &mut World) -> Entity { 25 - let entity = world.spawn_empty(); 26 - let id = entity.id(); 27 - self.insert(entity); 28 - id 29 - } 30 - 31 - fn insert(self, mut entity: EntityWorldMut) { 32 - entity.insert(( 33 - Effecting(self.target), 34 - self.bundle.name, 35 - self.bundle.mode, 36 - self.bundle.bundle, 37 - )); 25 + fn bundle_full(self) -> (Effecting, B) { 26 + (Effecting(self.target), self.bundle) 38 27 } 39 28 40 29 /// Inserts into the existing entity, and then merges the old effect into it using [`EffectMergeRegistry`]. ··· 70 59 temp 71 60 }; 72 61 73 - self.insert(world.entity_mut(new_effect)); 62 + world.entity_mut(new_effect).insert(self.bundle); 74 63 75 64 // Call merge function on those copied components. 76 65 { ··· 100 89 } 101 90 } 102 91 103 - impl<B: Bundle> Command for AddEffectCommand<B> { 92 + impl<B: Bundle + Clone> Command for AddEffectCommand<B> { 104 93 fn apply(self, world: &mut World) { 105 - if self.bundle.mode == EffectMode::Stack { 106 - self.spawn(world); 94 + let mut inspector = world.get_resource_or_init::<BundleInspector>(); 95 + let (name, mode) = inspector.get_effect_meta(self.bundle.clone()); 96 + 97 + if mode == EffectMode::Stack { 98 + world.spawn(self.bundle_full()); 107 99 return; 108 100 } 109 101 ··· 111 103 .get::<EffectedBy>(self.target) 112 104 .map(|e| e.collection().clone()) 113 105 else { 114 - self.spawn(world); 106 + world.spawn(self.bundle_full()); 115 107 return; 116 108 }; 117 109 ··· 122 114 let other_mode = world.get::<EffectMode>(*entity)?; 123 115 124 116 // Todo Think more about. 125 - if self.bundle.mode != *other_mode { 117 + if mode != *other_mode { 126 118 return None; 127 119 } 128 120 129 - let name = world.get::<Name>(*entity)?; 121 + let other_name = world.get::<Name>(*entity); 130 122 131 - if name == &self.bundle.name { 123 + if name == other_name.cloned() { 132 124 return Some(*entity); 133 125 } 134 126 ··· 136 128 }); 137 129 138 130 let Some(old_entity) = old_entity else { 139 - self.spawn(world); 131 + world.spawn(self.bundle_full()); 140 132 return; 141 133 }; 142 134 143 - match self.bundle.mode { 135 + match mode { 144 136 EffectMode::Stack => unreachable!(), 145 - EffectMode::Insert => self.insert(world.entity_mut(old_entity)), 137 + EffectMode::Insert => { 138 + world.entity_mut(old_entity).insert(self.bundle_full()); 139 + } 146 140 EffectMode::Merge => self.merge(world, old_entity), 147 141 } 148 142 } 149 143 } 150 144 151 145 // Todo This is probably bad practice/has larger performance cost. 152 - impl<B: Bundle> SpawnableList<Effecting> for EffectBundle<B> { 146 + impl<B: Bundle + Clone> SpawnableList<Effecting> for EffectBundle<B> { 153 147 fn spawn(this: MovingPtr<'_, Self>, world: &mut World, target: Entity) { 154 148 let bundle = this.read(); 155 - world.commands().queue(AddEffectCommand { target, bundle }); 149 + world.commands().queue(AddEffectCommand { 150 + target, 151 + bundle: bundle.0, 152 + }); 156 153 } 157 154 158 155 fn size_hint(&self) -> usize { ··· 179 176 /// 180 177 /// # Example 181 178 #[doc = include_str!("../docs/with_effects_example.md")] 182 - pub fn spawn<B: Bundle>(&mut self, bundle: EffectBundle<B>) { 179 + pub fn spawn<B: Bundle + Clone>(&mut self, bundle: B) { 183 180 self.commands.queue(AddEffectCommand { 184 181 target: self.target, 185 182 bundle, ··· 196 193 /// 197 194 /// # Example 198 195 #[doc = include_str!("../docs/with_effect_example.md")] 199 - fn with_effect<B: Bundle>(&mut self, bundle: EffectBundle<B>) -> &mut Self; 196 + fn with_effect<B: Bundle + Clone>(&mut self, bundle: B) -> &mut Self; 200 197 201 198 /// Applies effects to this entity by taking a function that operates on a [`EffectSpawner`]. 202 199 /// ··· 208 205 } 209 206 210 207 impl EffectCommandsExt for EntityCommands<'_> { 211 - fn with_effect<B: Bundle>(&mut self, bundle: EffectBundle<B>) -> &mut Self { 208 + fn with_effect<B: Bundle + Clone>(&mut self, bundle: B) -> &mut Self { 212 209 let target = self.id(); 213 210 self.commands().queue(AddEffectCommand { target, bundle }); 214 211 self
+3
src/lib.rs
··· 1 1 #![doc = include_str!("../README.md")] 2 2 3 3 mod bundle; 4 + mod bundle_inspector; 4 5 mod command; 5 6 mod component; 6 7 mod registry; ··· 11 12 use bevy_reflect::Reflect; 12 13 use bevy_reflect::prelude::ReflectDefault; 13 14 15 + use crate::bundle_inspector::BundleInspector; 14 16 pub use bundle::*; 15 17 pub use command::*; 16 18 pub use component::*; ··· 28 30 .register_type::<Lifetime>() 29 31 .register_type::<Delay>() 30 32 .register_type::<TimerMergeMode>() 33 + .init_resource::<BundleInspector>() 31 34 .init_resource::<EffectMergeRegistry>() 32 35 .add_plugins(TimerPlugin) 33 36 .add_plugins(StackPlugin);