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