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.

at dev 114 lines 3.8 kB view raw
1//! This example shows using [Immediate Stats](https://github.com/AlephCubed/immediate_stats) 2//! to add a decaying movement speed buff, using Bevy Auto Plugin 3//! (there is a second version of this example which just uses normal Bevy). 4//! This means that the strength of the buff decreases throughout its duration. 5//! 6//! This uses [`EffectMode::Merge`], which prevents having multiple of the effect applied at the 7//! same time (no 10x speed multiplier for you). 8 9use bevy::prelude::*; 10use bevy_alchemy::*; 11use bevy_auto_plugin::prelude::{AutoPlugin, auto_plugin_build_hook, auto_system}; 12use immediate_stats::*; 13 14fn main() { 15 App::new() 16 .add_plugins((DefaultPlugins, AlchemyPlugin, ImmediateStatsPlugin)) 17 .add_plugins(DecayingSpeedPlugin) 18 .run(); 19} 20 21#[derive(AutoPlugin)] 22#[auto_plugin(impl_plugin_trait)] 23struct DecayingSpeedPlugin; 24 25/// Tracks an entities current movement speed. 26#[derive(Component, StatContainer)] 27#[auto_plugin_build_hook(plugin = DecayingSpeedPlugin, hook = ResetComponentHook)] 28struct MovementSpeed(Stat); 29 30/// Applies a speed boost, which decreases throughout its duration. 31#[derive(Component, Default, Clone)] 32struct DecayingSpeed { 33 start_speed_boost: Modifier, 34} 35 36/// Spawn a target on startup. 37#[auto_system(plugin = DecayingSpeedPlugin, schedule = Startup)] 38fn init_scene(mut commands: Commands) { 39 commands.spawn((Name::new("Target"), MovementSpeed(Stat::new(100)))); 40 commands.spawn(( 41 Node { 42 margin: UiRect::all(Val::Px(10.0)), 43 ..default() 44 }, 45 Text::default(), 46 )); 47 commands.spawn(Camera2d); 48} 49 50/// When space is pressed, apply decaying speed to the target. 51#[auto_system(plugin = DecayingSpeedPlugin, schedule = Update)] 52fn on_space_pressed( 53 mut commands: Commands, 54 keyboard_input: Res<ButtonInput<KeyCode>>, 55 target: Single<Entity, With<MovementSpeed>>, 56) { 57 if !keyboard_input.just_pressed(KeyCode::Space) { 58 return; 59 } 60 61 commands.entity(*target).with_effect(( 62 EffectMode::Insert, // Block having multiple of effect stacked on a single target. 63 Lifetime::from_seconds(2.0), // The duration of the effect. 64 DecayingSpeed { 65 start_speed_boost: Modifier { 66 bonus: 10, 67 multiplier: 2.0, 68 }, 69 }, 70 )); 71} 72 73/// Applies the effect to the target. Because of how Immediate Stats works, this needs to run every frame. 74#[auto_system(plugin = DecayingSpeedPlugin, schedule = Update)] 75fn apply_speed_boost( 76 effects: Query<(&Effecting, &Lifetime, &DecayingSpeed)>, 77 mut targets: Query<&mut MovementSpeed>, 78) { 79 for (target, lifetime, effect) in effects { 80 // Skip if the target doesn't have movement speed. 81 let Ok(mut speed) = targets.get_mut(target.0) else { 82 continue; 83 }; 84 85 // Otherwise, apply the buff, scaled by the remaining time. 86 speed.0.apply_scaled( 87 effect.start_speed_boost, 88 lifetime.timer.fraction_remaining(), 89 ); 90 } 91} 92 93/// Updates the UI to match the world state. 94#[auto_system(plugin = DecayingSpeedPlugin, schedule = PostUpdate)] 95fn update_ui( 96 mut ui: Single<&mut Text>, 97 target: Single<&MovementSpeed>, 98 effects: Query<(Entity, &Lifetime, &DecayingSpeed)>, 99) { 100 ui.0 = "Press Space to apply decaying movement speed\n\n".to_string(); 101 102 ui.0 += &format!("Speed: {:.1} ({:.1})\n\n", target.0.total(), target.0); 103 104 for (entity, lifetime, speed) in &effects { 105 ui.0 += &format!( 106 "{} - {:.1}s ({:.1})\n", 107 entity, 108 lifetime.timer.remaining_secs(), 109 speed 110 .start_speed_boost 111 .scaled(lifetime.timer.fraction_remaining()) 112 ); 113 } 114}