bevy_tnua/control_helpers/
crouch_enforcer.rs1use std::any::Any;
2
3use bevy::ecs::schedule::{InternedScheduleLabel, ScheduleLabel};
4use bevy::ecs::system::EntityCommands;
5use bevy::prelude::*;
6use bevy_tnua_physics_integration_layer::math::{AdjustPrecision, Float, Quaternion, Vector3};
7
8use crate::controller::TnuaController;
9use crate::subservient_sensors::TnuaSubservientSensor;
10use crate::{TnuaAction, TnuaPipelineStages, TnuaProximitySensor};
11
12pub struct TnuaCrouchEnforcerPlugin {
13 schedule: InternedScheduleLabel,
14}
15
16impl TnuaCrouchEnforcerPlugin {
21 pub fn new(schedule: impl ScheduleLabel) -> Self {
22 Self {
23 schedule: schedule.intern(),
24 }
25 }
26}
27
28impl Plugin for TnuaCrouchEnforcerPlugin {
29 fn build(&self, app: &mut App) {
30 app.add_systems(
31 self.schedule,
32 update_crouch_enforcer.in_set(TnuaPipelineStages::SubservientSensors),
33 );
34 }
35}
36
37#[derive(Component)]
62pub struct TnuaCrouchEnforcer {
63 sensor_entity: Option<Entity>,
64 offset: Vector3,
65 modify_sensor: Box<dyn Send + Sync + Fn(&mut EntityCommands)>,
66 enforced_action: Option<(Box<dyn DynamicCrouchEnforcedAction>, bool)>,
67 currently_enforcing: bool,
68}
69
70impl TnuaCrouchEnforcer {
71 pub fn new(
83 offset: Vector3,
84 modify_sensor: impl 'static + Send + Sync + Fn(&mut EntityCommands),
85 ) -> Self {
86 Self {
87 sensor_entity: None,
88 offset,
89 modify_sensor: Box::new(modify_sensor),
90 enforced_action: None,
91 currently_enforcing: false,
92 }
93 }
94
95 pub fn enforcing<A: TnuaCrouchEnforcedAction>(&mut self, mut crouch_action: A) -> A {
96 if let Some((enforced_action, fed_this_frame)) = self.enforced_action.as_mut() {
97 if enforced_action.overwrite(&crouch_action).is_ok() {
98 *fed_this_frame = true;
99 if self.currently_enforcing {
100 crouch_action.prevent_cancellation();
101 }
102 return crouch_action;
103 }
104 }
105 self.enforced_action = Some((
106 Box::new(BoxableCrouchEnforcedAction(crouch_action.clone())),
107 true,
108 ));
109 if self.currently_enforcing {
110 crouch_action.prevent_cancellation();
111 }
112 crouch_action
113 }
114}
115
116pub trait TnuaCrouchEnforcedAction: TnuaAction + Clone {
118 fn range_to_cast_up(&self, state: &Self::State) -> Float;
121
122 fn prevent_cancellation(&mut self);
124}
125
126trait DynamicCrouchEnforcedAction: Send + Sync {
127 fn overwrite(&mut self, value: &dyn Any) -> Result<(), ()>;
128 fn feed_to_controller(&mut self, controller: &mut TnuaController);
129 fn range_to_cast_up(&self, controller: &TnuaController) -> Option<Float>;
130}
131
132struct BoxableCrouchEnforcedAction<A: TnuaCrouchEnforcedAction>(A);
133
134impl<A: TnuaCrouchEnforcedAction> DynamicCrouchEnforcedAction for BoxableCrouchEnforcedAction<A> {
135 fn overwrite(&mut self, value: &dyn Any) -> Result<(), ()> {
136 if let Some(concrete) = value.downcast_ref::<A>() {
137 self.0 = concrete.clone();
138 Ok(())
139 } else {
140 Err(())
141 }
142 }
143
144 fn feed_to_controller(&mut self, controller: &mut TnuaController) {
145 let mut action = self.0.clone();
146 action.prevent_cancellation();
147 controller.action(action);
148 }
149
150 fn range_to_cast_up(&self, controller: &TnuaController) -> Option<Float> {
151 if let Some((action, state)) = controller.concrete_action::<A>() {
152 Some(action.range_to_cast_up(state))
153 } else {
154 None
155 }
156 }
157}
158
159fn update_crouch_enforcer(
160 mut query: Query<(Entity, &mut TnuaController, &mut TnuaCrouchEnforcer)>,
161 mut sensors_query: Query<(&mut TnuaProximitySensor, Has<TnuaSubservientSensor>)>,
162 mut commands: Commands,
163) {
164 for (owner_entity, mut controller, mut crouch_enforcer) in query.iter_mut() {
165 struct SetSensor {
166 cast_direction: Dir3,
167 cast_range: Float,
168 }
169 let set_sensor: Option<SetSensor>;
170 if let Some((enforced_action, fed_this_frame)) = crouch_enforcer.enforced_action.as_mut() {
171 if *fed_this_frame {
172 set_sensor = enforced_action
173 .range_to_cast_up(controller.as_mut())
174 .and_then(|cast_range| {
175 let (main_sensor, _) = sensors_query.get(owner_entity).ok()?;
176 Some(SetSensor {
177 cast_direction: -main_sensor.cast_direction,
178 cast_range,
179 })
180 });
181 *fed_this_frame = false;
182 } else {
183 set_sensor = None;
184 crouch_enforcer.enforced_action = None;
185 }
186 } else {
187 set_sensor = None;
188 }
189
190 if let Some(SetSensor {
191 cast_direction,
192 cast_range,
193 }) = set_sensor
194 {
195 if let Some((mut subservient_sensor, true)) = crouch_enforcer
196 .sensor_entity
197 .and_then(|entity| sensors_query.get_mut(entity).ok())
198 {
199 subservient_sensor.cast_shape_rotation = Quaternion::from_rotation_arc(
201 Vector3::Y,
202 subservient_sensor.cast_direction.adjust_precision(),
203 );
204 subservient_sensor.cast_origin = subservient_sensor
205 .cast_shape_rotation
206 .mul_vec3(crouch_enforcer.offset);
207 subservient_sensor.cast_direction = cast_direction;
208 subservient_sensor.cast_range = cast_range;
209 } else {
210 let mut cmd = commands.spawn((
211 Transform::default(),
212 TnuaSubservientSensor { owner_entity },
213 TnuaProximitySensor {
214 cast_origin: crouch_enforcer.offset,
215 cast_direction,
216 cast_range,
217 ..Default::default()
218 },
219 ));
220 cmd.insert(ChildOf(owner_entity));
221 (crouch_enforcer.modify_sensor)(&mut cmd);
222 let sensor_entity = cmd.id();
223 crouch_enforcer.sensor_entity = Some(sensor_entity);
224 }
225 } else if let Some((mut subservient_sensor, true)) = crouch_enforcer
226 .sensor_entity
227 .and_then(|entity| sensors_query.get_mut(entity).ok())
228 {
229 subservient_sensor.cast_range = 0.0;
231 }
232 if let Some((enforced_action, fed_this_frame)) =
233 crouch_enforcer.sensor_entity.and_then(|entity| {
234 let Ok((sensor, true)) = sensors_query.get_mut(entity) else {
235 return None;
236 };
237 if sensor.output.is_some() {
238 crouch_enforcer.enforced_action.as_mut()
239 } else {
240 None
241 }
242 })
243 {
244 enforced_action.feed_to_controller(controller.as_mut());
245 *fed_this_frame = true;
246 crouch_enforcer.currently_enforcing = true;
247 } else {
248 crouch_enforcer.currently_enforcing = false;
249 }
250 }
251}