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 97 lines 2.9 kB view raw
1//! A simple damage-over-time effect. 2//! 3//! Each application of the effect is its own entity, meaning an entity can be poisoned multiple times. 4//! This can be changed by using a different [`EffectMode`](bevy_alchemy::EffectMode). 5//! The `poison_falloff` example shows a different way to handle effect stacking. 6 7use bevy::prelude::*; 8use bevy_alchemy::{AlchemyPlugin, Delay, EffectCommandsExt, EffectTimer, Effecting, Lifetime}; 9 10fn main() { 11 App::new() 12 .add_plugins((DefaultPlugins, AlchemyPlugin)) 13 .add_systems(Startup, init_scene) 14 .add_systems(Update, (on_space_pressed, deal_poison_damage)) 15 .add_systems(PostUpdate, update_ui) 16 .run(); 17} 18 19#[derive(Component)] 20struct Health(i32); 21 22/// Deals damage over time to the target entity. 23#[derive(Component, Default, Clone)] 24struct Poison { 25 damage: i32, 26} 27 28/// Spawn a target on startup. 29fn init_scene(mut commands: Commands) { 30 commands.spawn((Name::new("Target"), Health(100))); 31 commands.spawn(( 32 Node { 33 margin: UiRect::all(Val::Px(10.0)), 34 ..default() 35 }, 36 Text::default(), 37 )); 38 commands.spawn(Camera2d); 39} 40 41/// When space is pressed, apply poison to the target. 42fn on_space_pressed( 43 mut commands: Commands, 44 keyboard_input: Res<ButtonInput<KeyCode>>, 45 target: Single<Entity, With<Health>>, 46) { 47 if !keyboard_input.just_pressed(KeyCode::Space) { 48 return; 49 } 50 51 commands.entity(*target).with_effect(( 52 Lifetime::from_seconds(3.0), // The duration of the effect. 53 Delay::from_seconds(1.0) // The time between damage ticks. 54 .trigger_immediately(), // Make damage tick immediately when the effect is applied. 55 Poison { damage: 1 }, // The amount of damage to apply per tick. 56 )); 57} 58 59/// Runs every frame and deals the poison damage. 60fn deal_poison_damage( 61 effects: Query<(&Effecting, &Delay, &Poison)>, 62 mut targets: Query<&mut Health>, 63) { 64 for (target, delay, poison) in effects { 65 // We wait until the delay finishes to apply the damage. 66 if !delay.timer.is_finished() { 67 continue; 68 } 69 70 // Skip if the target doesn't have health. 71 let Ok(mut health) = targets.get_mut(target.0) else { 72 continue; 73 }; 74 75 // Otherwise, deal the damage. 76 health.0 -= poison.damage; 77 } 78} 79 80fn update_ui( 81 mut ui: Single<&mut Text>, 82 target: Single<&Health>, 83 effects: Query<(Entity, &Lifetime, &Delay), With<Poison>>, 84) { 85 ui.0 = "Press Space to apply poison\n\n".to_string(); 86 87 ui.0 += &format!("Health: {}\n\n", target.0); 88 89 for (entity, lifetime, delay) in &effects { 90 ui.0 += &format!( 91 "{} - {:.1}s (tick in {:.1}s)\n", 92 entity, 93 lifetime.timer.remaining_secs(), 94 delay.timer.remaining_secs() 95 ); 96 } 97}