valence_server/
status_effect.rs
1use bevy_app::prelude::*;
2use bevy_ecs::prelude::*;
3use bevy_ecs::query::QueryData;
4use bevy_ecs::system::SystemState;
5use valence_entity::active_status_effects::{ActiveStatusEffect, ActiveStatusEffects};
6use valence_entity::entity::Flags;
7use valence_entity::living::{PotionSwirlsAmbient, PotionSwirlsColor};
8use valence_protocol::packets::play::{
9 entity_status_effect_s2c, EntityStatusEffectS2c, RemoveEntityStatusEffectS2c,
10};
11use valence_protocol::status_effects::StatusEffect;
12use valence_protocol::{VarInt, WritePacket};
13
14use crate::client::Client;
15use crate::EventLoopPostUpdate;
16
17#[derive(Event, Clone, PartialEq, Eq, Debug)]
20pub struct StatusEffectAdded {
21 pub entity: Entity,
22 pub status_effect: StatusEffect,
23}
24
25#[derive(Event, Clone, PartialEq, Eq, Debug)]
27pub struct StatusEffectRemoved {
28 pub entity: Entity,
29 pub status_effect: ActiveStatusEffect,
30}
31
32pub struct StatusEffectPlugin;
33
34impl Plugin for StatusEffectPlugin {
35 fn build(&self, app: &mut App) {
36 app.add_event::<StatusEffectAdded>()
37 .add_event::<StatusEffectRemoved>()
38 .add_systems(
39 EventLoopPostUpdate,
40 (
41 add_status_effects,
42 update_active_status_effects,
43 add_status_effects,
44 ),
45 );
46 }
47}
48
49fn update_active_status_effects(
50 world: &mut World,
51 state: &mut SystemState<Query<&mut ActiveStatusEffects>>,
52) {
53 let mut query = state.get_mut(world);
54 for mut active_status_effects in &mut query {
55 active_status_effects.increment_active_ticks();
56 }
57}
58
59fn create_packet(effect: &ActiveStatusEffect) -> EntityStatusEffectS2c {
60 EntityStatusEffectS2c {
61 entity_id: VarInt(0), effect_id: VarInt(i32::from(effect.status_effect().to_raw())),
63 amplifier: effect.amplifier(),
64 duration: VarInt(effect.remaining_duration().unwrap_or(-1)),
65 flags: entity_status_effect_s2c::Flags::new()
66 .with_is_ambient(effect.ambient())
67 .with_show_particles(effect.show_particles())
68 .with_show_icon(effect.show_icon()),
69 factor_codec: None,
70 }
71}
72
73#[derive(QueryData)]
74#[query_data(mutable)]
75struct StatusEffectQuery {
76 entity: Entity,
77 active_effects: &'static mut ActiveStatusEffects,
78 client: Option<&'static mut Client>,
79 entity_flags: Option<&'static mut Flags>,
80 swirl_color: Option<&'static mut PotionSwirlsColor>,
81 swirl_ambient: Option<&'static mut PotionSwirlsAmbient>,
82}
83
84fn add_status_effects(
85 mut query: Query<StatusEffectQuery>,
86 mut add_events: EventWriter<StatusEffectAdded>,
87 mut remove_events: EventWriter<StatusEffectRemoved>,
88) {
89 for mut query in &mut query {
90 let updated = query.active_effects.apply_changes();
91
92 if updated.is_empty() {
93 continue;
94 }
95
96 set_swirl(
97 &query.active_effects,
98 &mut query.swirl_color,
99 &mut query.swirl_ambient,
100 );
101
102 for (status_effect, prev) in updated {
103 if query.active_effects.has_effect(status_effect) {
104 add_events.send(StatusEffectAdded {
105 entity: query.entity,
106 status_effect,
107 });
108 } else if let Some(prev) = prev {
109 remove_events.send(StatusEffectRemoved {
110 entity: query.entity,
111 status_effect: prev,
112 });
113 } else {
114 panic!("status effect was removed but was never added");
116 }
117
118 update_status_effect(&mut query, status_effect);
119 }
120 }
121}
122
123fn update_status_effect(query: &mut StatusEffectQueryItem, status_effect: StatusEffect) {
124 let current_effect = query.active_effects.get_current_effect(status_effect);
125
126 if let Some(ref mut client) = query.client {
127 if let Some(updated_effect) = current_effect {
128 client.write_packet(&create_packet(updated_effect));
129 } else {
130 client.write_packet(&RemoveEntityStatusEffectS2c {
131 entity_id: VarInt(0),
132 effect_id: VarInt(i32::from(status_effect.to_raw())),
133 });
134 }
135 }
136}
137
138fn set_swirl(
139 active_status_effects: &ActiveStatusEffects,
140 swirl_color: &mut Option<Mut<'_, PotionSwirlsColor>>,
141 swirl_ambient: &mut Option<Mut<'_, PotionSwirlsAmbient>>,
142) {
143 if let Some(ref mut swirl_ambient) = swirl_ambient {
144 swirl_ambient.0 = active_status_effects
145 .get_current_effects()
146 .iter()
147 .any(|effect| effect.ambient());
148 }
149
150 if let Some(ref mut swirl_color) = swirl_color {
151 swirl_color.0 = get_color(active_status_effects);
152 }
153}
154
155fn get_color(effects: &ActiveStatusEffects) -> i32 {
159 if effects.no_effects() {
160 return 0;
164 }
165
166 let effects = effects.get_current_effects();
167 let mut f = 0.0;
168 let mut g = 0.0;
169 let mut h = 0.0;
170 let mut j = 0.0;
171
172 for status_effect_instance in effects {
173 if !status_effect_instance.show_particles() {
174 continue;
175 }
176
177 let k = status_effect_instance.status_effect().color();
178 let l = f32::from(status_effect_instance.amplifier() + 1);
179 f += (l * ((k >> 16) & 0xff) as f32) / 255.0;
180 g += (l * ((k >> 8) & 0xff) as f32) / 255.0;
181 h += (l * ((k) & 0xff) as f32) / 255.0;
182 j += l;
183 }
184
185 if j == 0.0 {
186 return 0;
187 }
188
189 f = f / j * 255.0;
190 g = g / j * 255.0;
191 h = h / j * 255.0;
192
193 ((f as i32) << 16) | ((g as i32) << 8) | (h as i32)
194}