1use std::marker::PhantomData;
2
3use crate::basis_capabilities::TnuaBasisWithGround;
4use crate::ghost_overrides::TnuaGhostOverwrites;
5use crate::sensor_sets::TnuaSensors;
6use crate::{
7 TnuaActionInitiationDirective, TnuaActionLifecycleDirective, TnuaActionLifecycleStatus, math::*,
8};
9use bevy::ecs::schedule::{InternedScheduleLabel, ScheduleLabel};
10use bevy::prelude::*;
11use bevy::time::Stopwatch;
12use bevy_tnua_physics_integration_layer::data_for_backends::TnuaGhostSensor;
13#[cfg(feature = "serialize")]
14use serde::{Deserialize, Serialize};
15
16use crate::basis_action_traits::{
17 TnuaActionContext, TnuaActionDiscriminant, TnuaActionState, TnuaBasis, TnuaBasisAccess,
18 TnuaScheme, TnuaSchemeConfig, TnuaUpdateInActionStateResult,
19};
20use crate::{
21 TnuaBasisContext, TnuaMotor, TnuaPipelineSystems, TnuaProximitySensor, TnuaRigidBodyTracker,
22 TnuaSystems, TnuaToggle, TnuaUserControlsSystems,
23};
24
25pub struct TnuaControllerPlugin<S: TnuaScheme> {
26 schedule: InternedScheduleLabel,
27 _phantom: PhantomData<S>,
28}
29
30impl<S: TnuaScheme> TnuaControllerPlugin<S> {
42 pub fn new(schedule: impl ScheduleLabel) -> Self {
43 Self {
44 schedule: schedule.intern(),
45 _phantom: PhantomData,
46 }
47 }
48}
49
50impl<S: TnuaScheme> Plugin for TnuaControllerPlugin<S> {
51 fn build(&self, app: &mut App) {
52 app.init_asset::<S::Config>();
53 app.configure_sets(
54 self.schedule,
55 (
56 TnuaPipelineSystems::Sensors,
57 TnuaUserControlsSystems,
58 TnuaPipelineSystems::Logic,
59 TnuaPipelineSystems::Motors,
60 )
61 .chain()
62 .in_set(TnuaSystems),
63 );
64 app.add_systems(
65 self.schedule,
66 (apply_ghost_overwrites::<S>, apply_controller_system::<S>)
67 .chain()
68 .in_set(TnuaPipelineSystems::Logic),
69 );
70 }
71}
72
73#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
74struct ContenderAction<S: TnuaScheme> {
75 action: S,
76 being_fed_for: Stopwatch,
77}
78
79#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
80#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
81enum FedStatus {
82 #[default]
83 Not,
84 Lingering,
85 Fresh,
86 Trigger,
87 Interrupt,
88 Held,
89}
90
91impl FedStatus {
92 fn considered_fed(&self) -> bool {
93 match self {
94 FedStatus::Not => false,
95 FedStatus::Lingering => true,
96 FedStatus::Fresh => true,
97 FedStatus::Trigger => true,
98 FedStatus::Interrupt => true,
99 FedStatus::Held => true,
100 }
101 }
102}
103
104#[derive(Default, Debug)]
105#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
106struct FedEntry {
107 status: FedStatus,
108 rescheduled_in: Option<Timer>,
109}
110
111#[derive(Component)]
136#[require(TnuaMotor, TnuaRigidBodyTracker)]
137#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
138pub struct TnuaController<S: TnuaScheme> {
139 pub basis: S::Basis,
141 #[cfg_attr(
142 feature = "serialize",
143 serde(bound(
144 serialize = "<S::Basis as TnuaBasis>::Memory: Serialize",
145 deserialize = "<S::Basis as TnuaBasis>::Memory: Deserialize<'de>",
146 ))
147 )]
148 pub basis_memory: <S::Basis as TnuaBasis>::Memory,
149 pub basis_config: Option<<S::Basis as TnuaBasis>::Config>,
150 #[cfg_attr(feature = "serialize", serde(skip))]
151 pub sensors_entities:
152 <<S::Basis as TnuaBasis>::Sensors<'static> as TnuaSensors<'static>>::Entities,
153 #[cfg_attr(feature = "serialize", serde(skip))]
154 pub config: Handle<S::Config>,
155 actions_being_fed: Vec<FedEntry>,
157 contender_action: Option<ContenderAction<S>>,
158 #[cfg_attr(feature = "serialize", serde(skip))]
159 action_flow_status: TnuaActionFlowStatus<S::ActionDiscriminant>,
160 up_direction: Option<Dir3>,
161 action_feeding_initiated: bool,
162 #[cfg_attr(
163 feature = "serialize",
164 serde(bound(
165 serialize = "S::ActionState: Serialize",
166 deserialize = "S::ActionState: Deserialize<'de>",
167 ))
168 )]
169 pub current_action: Option<S::ActionState>,
170}
171
172#[derive(Debug, Clone, Default)]
174pub enum TnuaActionFlowStatus<D: TnuaActionDiscriminant> {
175 #[default]
177 NoAction,
178
179 ActionStarted(D),
181
182 ActionOngoing(D),
184
185 ActionEnded(D),
189
190 Cancelled { old: D, new: D },
192}
193
194impl<D: TnuaActionDiscriminant> TnuaActionFlowStatus<D> {
195 pub fn ongoing(&self) -> Option<D> {
199 match self {
200 TnuaActionFlowStatus::NoAction | TnuaActionFlowStatus::ActionEnded(_) => None,
201 TnuaActionFlowStatus::ActionStarted(discriminant)
202 | TnuaActionFlowStatus::ActionOngoing(discriminant)
203 | TnuaActionFlowStatus::Cancelled {
204 old: _,
205 new: discriminant,
206 } => Some(*discriminant),
207 }
208 }
209
210 pub fn just_starting(&self) -> Option<D> {
215 match self {
216 TnuaActionFlowStatus::NoAction
217 | TnuaActionFlowStatus::ActionOngoing(_)
218 | TnuaActionFlowStatus::ActionEnded(_) => None,
219 TnuaActionFlowStatus::ActionStarted(discriminant)
220 | TnuaActionFlowStatus::Cancelled {
221 old: _,
222 new: discriminant,
223 } => Some(*discriminant),
224 }
225 }
226}
227
228impl<S: TnuaScheme> TnuaController<S> {
229 pub fn new(config: Handle<S::Config>) -> Self {
230 Self {
231 basis: Default::default(),
232 basis_memory: Default::default(),
233 basis_config: None,
234 sensors_entities: Default::default(),
235 config,
236 actions_being_fed: (0..S::NUM_VARIANTS).map(|_| Default::default()).collect(),
237 contender_action: None,
238 action_flow_status: TnuaActionFlowStatus::NoAction,
239 up_direction: None,
240 action_feeding_initiated: false,
241 current_action: None,
242 }
243 }
244
245 pub fn basis_access(
246 &'_ self,
247 ) -> Result<TnuaBasisAccess<'_, S::Basis>, TnuaControllerHasNotPulledConfiguration> {
248 Ok(TnuaBasisAccess {
249 input: &self.basis,
250 config: self
251 .basis_config
252 .as_ref()
253 .ok_or(TnuaControllerHasNotPulledConfiguration)?,
254 memory: &self.basis_memory,
255 })
256 }
257
258 pub fn initiate_action_feeding(&mut self) {
259 self.action_feeding_initiated = true;
260 }
261
262 pub fn action(&mut self, action: S) {
264 assert!(
265 self.action_feeding_initiated,
266 "Feeding action without invoking `initiate_action_feeding()`"
267 );
268 let fed_entry = &mut self.actions_being_fed[action.variant_idx()];
269
270 match fed_entry.status {
271 FedStatus::Lingering
272 | FedStatus::Fresh
273 | FedStatus::Trigger
274 | FedStatus::Interrupt
275 | FedStatus::Held => {
276 fed_entry.status = FedStatus::Fresh;
277 if let Some(current_action) = self.current_action.as_mut() {
278 match action.update_in_action_state(current_action) {
279 TnuaUpdateInActionStateResult::Success => {
280 }
282 TnuaUpdateInActionStateResult::WrongVariant(_) => {
283 }
286 }
287 } else if self.contender_action.is_none()
288 && fed_entry
289 .rescheduled_in
290 .as_ref()
291 .is_some_and(|timer| timer.is_finished())
292 {
293 self.contender_action = Some(ContenderAction {
296 action,
297 being_fed_for: Stopwatch::new(),
298 });
299 } else {
300 }
302 }
303 FedStatus::Not => {
304 *fed_entry = FedEntry {
305 status: FedStatus::Fresh,
306 rescheduled_in: None,
307 };
308 if let Some(contender_action) = self.contender_action.as_mut()
309 && action.discriminant() == contender_action.action.discriminant()
310 {
311 contender_action.action = action;
312 } else if let Some(contender_action) = self.contender_action.as_ref()
313 && self.actions_being_fed[contender_action.action.discriminant().variant_idx()]
314 .status
315 == FedStatus::Interrupt
316 {
317 } else {
319 self.contender_action = Some(ContenderAction {
320 action,
321 being_fed_for: Stopwatch::new(),
322 });
323 }
324 }
325 }
326 }
327
328 pub fn action_trigger(&mut self, action: S) {
329 let fed_entry = &mut self.actions_being_fed[action.variant_idx()];
330
331 match fed_entry.status {
332 FedStatus::Lingering
333 | FedStatus::Fresh
334 | FedStatus::Trigger
335 | FedStatus::Interrupt
336 | FedStatus::Held => {
337 }
339 FedStatus::Not => {
340 *fed_entry = FedEntry {
341 status: FedStatus::Trigger,
342 rescheduled_in: None,
343 };
344 if let Some(contender_action) = self.contender_action.as_mut()
345 && action.discriminant() == contender_action.action.discriminant()
346 {
347 contender_action.action = action;
348 } else if let Some(contender_action) = self.contender_action.as_ref()
349 && self.actions_being_fed[contender_action.action.discriminant().variant_idx()]
350 .status
351 == FedStatus::Interrupt
352 {
353 } else {
355 self.contender_action = Some(ContenderAction {
356 action,
357 being_fed_for: Stopwatch::new(),
358 });
359 }
360 }
361 }
362 }
363
364 pub fn action_interrupt(&mut self, action: S) {
365 self.actions_being_fed[action.variant_idx()] = FedEntry {
368 status: FedStatus::Interrupt,
369 rescheduled_in: None,
370 };
371
372 let action = if let Some(current_action) = self.current_action.as_mut() {
373 match action.update_in_action_state(current_action) {
374 TnuaUpdateInActionStateResult::Success => {
375 return;
376 }
377 TnuaUpdateInActionStateResult::WrongVariant(action) => {
378 action
380 }
381 }
382 } else {
383 action
384 };
385 self.contender_action = Some(ContenderAction {
387 action,
388 being_fed_for: Stopwatch::new(),
389 });
390 }
391
392 pub fn action_start(&mut self, action: S) {
393 let fed_entry = &mut self.actions_being_fed[action.variant_idx()];
394
395 match fed_entry.status {
396 FedStatus::Lingering | FedStatus::Fresh | FedStatus::Trigger | FedStatus::Interrupt => {
397 }
399 FedStatus::Held => {
400 let action = if let Some(current_action) = self.current_action.as_mut() {
402 match action.update_in_action_state(current_action) {
403 TnuaUpdateInActionStateResult::Success => {
404 return;
406 }
407 TnuaUpdateInActionStateResult::WrongVariant(action) => action,
408 }
409 } else {
410 action
411 };
412 if let Some(contender_action) = self.contender_action.as_mut()
413 && action.discriminant() == contender_action.action.discriminant()
414 {
415 contender_action.action = action;
417 }
418 }
419 FedStatus::Not => {
420 *fed_entry = FedEntry {
421 status: FedStatus::Held,
422 rescheduled_in: None,
423 };
424 if let Some(contender_action) = self.contender_action.as_mut()
425 && action.discriminant() == contender_action.action.discriminant()
426 {
427 contender_action.action = action;
428 } else if let Some(contender_action) = self.contender_action.as_ref()
429 && self.actions_being_fed[contender_action.action.discriminant().variant_idx()]
430 .status
431 == FedStatus::Interrupt
432 {
433 } else {
435 self.contender_action = Some(ContenderAction {
436 action,
437 being_fed_for: Stopwatch::new(),
438 });
439 }
440 }
441 }
442 }
443
444 pub fn action_end(&mut self, action: S::ActionDiscriminant) {
445 self.actions_being_fed[action.variant_idx()] = Default::default();
447 }
448
449 pub fn prolong_action(&mut self) {
454 if let Some(current_action) = self.action_discriminant() {
455 self.actions_being_fed[current_action.variant_idx()].status = FedStatus::Fresh;
456 }
457 }
458
459 pub fn action_discriminant(&self) -> Option<S::ActionDiscriminant> {
461 Some(self.current_action.as_ref()?.discriminant())
462 }
463
464 pub fn action_flow_status(&self) -> &TnuaActionFlowStatus<S::ActionDiscriminant> {
478 &self.action_flow_status
479 }
480
481 pub fn up_direction(&self) -> Option<Dir3> {
489 self.up_direction
490 }
491}
492
493impl<S: TnuaScheme> TnuaController<S>
494where
495 S::Basis: TnuaBasisWithGround,
496{
497 pub fn is_airborne(&self) -> Result<bool, TnuaControllerHasNotPulledConfiguration> {
502 Ok(S::Basis::is_airborne(&self.basis_access()?))
503 }
504}
505
506#[derive(thiserror::Error, Debug)]
507#[error("The Tnua controller did not pull the configuration asset yet")]
508pub struct TnuaControllerHasNotPulledConfiguration;
509
510fn apply_ghost_overwrites<S: TnuaScheme>(
511 mut query: Query<(&TnuaController<S>, &mut TnuaGhostOverwrites<S>)>,
512 mut proximity_sensors_query: Query<(&mut TnuaProximitySensor, &TnuaGhostSensor)>,
513) {
514 for (controller, mut ghost_overwrites) in query.iter_mut() {
515 for (ghost_overwrite, sensor_entity) in S::Basis::ghost_sensor_overwrites(
516 ghost_overwrites.as_mut().as_mut(),
517 &controller.sensors_entities,
518 ) {
519 let Ok((mut proximity_sensor, ghost_sensor)) =
520 proximity_sensors_query.get_mut(sensor_entity)
521 else {
522 continue;
523 };
524 if let Some(ghost_output) = ghost_overwrite.find_in(&ghost_sensor.0) {
525 proximity_sensor.output = Some(ghost_output.clone());
526 } else {
527 ghost_overwrite.clear();
528 }
529 }
530 }
531}
532
533#[allow(clippy::type_complexity)]
534fn apply_controller_system<S: TnuaScheme>(
535 time: Res<Time>,
536 mut query: Query<(
537 Entity,
538 &mut TnuaController<S>,
539 &TnuaRigidBodyTracker,
540 &mut TnuaMotor,
541 Option<&TnuaToggle>,
542 Has<TnuaGhostOverwrites<S>>,
543 )>,
544 proximity_sensors_query: Query<(&TnuaProximitySensor, Has<TnuaGhostSensor>)>,
545 config_assets: Res<Assets<S::Config>>,
546 mut commands: Commands,
547) {
548 let frame_duration = time.delta().as_secs_f64() as Float;
549 if frame_duration == 0.0 {
550 return;
551 }
552 for (
553 controller_entity,
554 mut controller,
555 tracker,
556 mut motor,
557 tnua_toggle,
558 has_ghost_overwrites,
559 ) in query.iter_mut()
560 {
561 match tnua_toggle.copied().unwrap_or_default() {
562 TnuaToggle::Disabled => continue,
563 TnuaToggle::SenseOnly => {}
564 TnuaToggle::Enabled => {}
565 }
566 let controller = controller.as_mut();
567
568 let Some(config) = config_assets.get(&controller.config) else {
569 continue;
570 };
571 controller.basis_config = Some({
572 let mut basis_config = config.basis_config().clone();
573 if let Some(current_action) = controller.current_action.as_ref() {
574 current_action.modify_basis_config(&mut basis_config);
575 }
576 basis_config
577 });
578 let basis_config = controller
579 .basis_config
580 .as_ref()
581 .expect("We just set it to Some");
582
583 let up_direction = Dir3::new(-tracker.gravity.f32()).ok();
584 controller.up_direction = up_direction;
585 let up_direction = up_direction.unwrap_or(Dir3::Y);
587
588 let Some(sensors) = S::Basis::get_or_create_sensors(
589 up_direction,
590 basis_config,
591 &controller.basis_memory,
592 &mut controller.sensors_entities,
593 &proximity_sensors_query,
594 controller_entity,
595 &mut commands,
596 has_ghost_overwrites,
597 ) else {
598 continue;
599 };
600
601 match controller.action_flow_status {
602 TnuaActionFlowStatus::NoAction | TnuaActionFlowStatus::ActionOngoing(_) => {}
603 TnuaActionFlowStatus::ActionEnded(_) => {
604 controller.action_flow_status = TnuaActionFlowStatus::NoAction;
605 }
606 TnuaActionFlowStatus::ActionStarted(discriminant)
607 | TnuaActionFlowStatus::Cancelled {
608 old: _,
609 new: discriminant,
610 } => {
611 controller.action_flow_status = TnuaActionFlowStatus::ActionOngoing(discriminant);
612 }
613 }
614
615 controller.basis.apply(
616 basis_config,
617 &mut controller.basis_memory,
618 &sensors,
619 TnuaBasisContext {
620 frame_duration,
621 tracker,
622 up_direction,
623 },
624 &mut motor,
625 );
626
627 if controller.action_feeding_initiated {
628 controller.action_feeding_initiated = false;
629 for fed_entry in controller.actions_being_fed.iter_mut() {
630 match fed_entry.status {
631 FedStatus::Not | FedStatus::Held => {}
632 FedStatus::Lingering => {
633 *fed_entry = Default::default();
634 }
635 FedStatus::Fresh | FedStatus::Trigger | FedStatus::Interrupt => {
636 fed_entry.status = FedStatus::Lingering;
637 if let Some(rescheduled_in) = &mut fed_entry.rescheduled_in {
638 rescheduled_in.tick(time.delta());
639 }
640 }
641 }
642 }
643 }
644
645 let has_valid_contender =
646 if let Some(contender_action) = controller.contender_action.as_mut() {
647 if controller.actions_being_fed[contender_action.action.variant_idx()]
648 .status
649 .considered_fed()
650 {
651 let initiation_decision = contender_action.action.initiation_decision(
652 config,
653 &sensors,
654 TnuaActionContext {
655 frame_duration,
656 tracker,
657 up_direction,
658 basis: &TnuaBasisAccess {
659 input: &controller.basis,
660 config: basis_config,
661 memory: &controller.basis_memory,
662 },
663 },
664 &contender_action.being_fed_for,
665 );
666 contender_action.being_fed_for.tick(time.delta());
667 match initiation_decision {
668 TnuaActionInitiationDirective::Reject => {
669 controller.contender_action = None;
670 false
671 }
672 TnuaActionInitiationDirective::Delay => false,
673 TnuaActionInitiationDirective::Allow => true,
674 }
675 } else {
676 controller.contender_action = None;
677 false
678 }
679 } else {
680 false
681 };
682
683 if let Some(action_state) = controller.current_action.as_mut() {
684 let lifecycle_status = if has_valid_contender {
685 TnuaActionLifecycleStatus::CancelledInto
686 } else if controller.actions_being_fed[action_state.variant_idx()]
687 .status
688 .considered_fed()
689 {
690 TnuaActionLifecycleStatus::StillFed
691 } else {
692 TnuaActionLifecycleStatus::NoLongerFed
693 };
694
695 let directive = action_state.interface_mut().apply(
696 &sensors,
697 TnuaActionContext {
698 frame_duration,
699 tracker,
700 basis: &TnuaBasisAccess {
701 input: &controller.basis,
702 config: basis_config,
703 memory: &controller.basis_memory,
704 },
705 up_direction,
706 },
707 lifecycle_status,
708 motor.as_mut(),
709 );
710 action_state.interface_mut().influence_basis(
711 TnuaBasisContext {
712 frame_duration,
713 tracker,
714 up_direction,
715 },
716 &controller.basis,
717 basis_config,
718 &mut controller.basis_memory,
719 );
720 match directive {
721 TnuaActionLifecycleDirective::StillActive => {
722 if !lifecycle_status.is_active()
723 && let TnuaActionFlowStatus::ActionOngoing(action_discriminant) =
724 controller.action_flow_status
725 {
726 controller.action_flow_status =
727 TnuaActionFlowStatus::ActionEnded(action_discriminant);
728 }
729 }
730 TnuaActionLifecycleDirective::Finished
731 | TnuaActionLifecycleDirective::Reschedule { .. } => {
732 if let TnuaActionLifecycleDirective::Reschedule { after_seconds } = directive {
733 controller.actions_being_fed[action_state.variant_idx()].rescheduled_in =
734 Some(Timer::from_seconds(after_seconds.f32(), TimerMode::Once));
735 }
736 controller.current_action = if has_valid_contender {
737 let contender_action = controller.contender_action.take().expect(
738 "has_valid_contender can only be true if contender_action is Some",
739 );
740 let mut contender_action_state =
741 contender_action.action.into_action_state_variant(config);
742
743 controller.actions_being_fed[contender_action_state.variant_idx()]
744 .rescheduled_in = None;
745
746 let contender_directive = contender_action_state.interface_mut().apply(
747 &sensors,
748 TnuaActionContext {
749 frame_duration,
750 tracker,
751 basis: &TnuaBasisAccess {
752 input: &controller.basis,
753 config: basis_config,
754 memory: &controller.basis_memory,
755 },
756 up_direction,
757 },
758 TnuaActionLifecycleStatus::CancelledFrom,
759 motor.as_mut(),
760 );
761 contender_action_state.interface_mut().influence_basis(
762 TnuaBasisContext {
763 frame_duration,
764 tracker,
765 up_direction,
766 },
767 &controller.basis,
768 basis_config,
769 &mut controller.basis_memory,
770 );
771 match contender_directive {
772 TnuaActionLifecycleDirective::StillActive => {
773 controller.action_flow_status =
774 if let TnuaActionFlowStatus::ActionOngoing(discriminant) =
775 controller.action_flow_status
776 {
777 TnuaActionFlowStatus::Cancelled {
778 old: discriminant,
779 new: contender_action_state.discriminant(),
780 }
781 } else {
782 TnuaActionFlowStatus::ActionStarted(
783 contender_action_state.discriminant(),
784 )
785 };
786 Some(contender_action_state)
787 }
788 TnuaActionLifecycleDirective::Finished
789 | TnuaActionLifecycleDirective::Reschedule { after_seconds: _ } => {
790 if let TnuaActionLifecycleDirective::Reschedule { after_seconds } =
791 contender_directive
792 {
793 controller.actions_being_fed
794 [contender_action_state.variant_idx()]
795 .rescheduled_in = Some(Timer::from_seconds(
796 after_seconds.f32(),
797 TimerMode::Once,
798 ));
799 }
800 if let TnuaActionFlowStatus::ActionOngoing(discriminant) =
801 controller.action_flow_status
802 {
803 controller.action_flow_status =
804 TnuaActionFlowStatus::ActionEnded(discriminant);
805 }
806 None
807 }
808 }
809 } else {
810 controller.action_flow_status =
811 TnuaActionFlowStatus::ActionEnded(action_state.discriminant());
812 None
813 };
814 }
815 }
816 } else if has_valid_contender {
817 let contender_action = controller
818 .contender_action
819 .take()
820 .expect("has_valid_contender can only be true if contender_action is Some");
821 let mut contender_action_state =
822 contender_action.action.into_action_state_variant(config);
823
824 contender_action_state.interface_mut().apply(
825 &sensors,
826 TnuaActionContext {
827 frame_duration,
828 tracker,
829 basis: &TnuaBasisAccess {
830 input: &controller.basis,
831 config: basis_config,
832 memory: &controller.basis_memory,
833 },
834 up_direction,
835 },
836 TnuaActionLifecycleStatus::Initiated,
837 motor.as_mut(),
838 );
839 contender_action_state.interface_mut().influence_basis(
840 TnuaBasisContext {
841 frame_duration,
842 tracker,
843 up_direction,
844 },
845 &controller.basis,
846 basis_config,
847 &mut controller.basis_memory,
848 );
849 controller.action_flow_status =
850 TnuaActionFlowStatus::ActionStarted(contender_action_state.discriminant());
851 controller.current_action = Some(contender_action_state);
852 }
853
854 for fed_entry in controller.actions_being_fed.iter_mut() {
855 match fed_entry.status {
856 FedStatus::Not | FedStatus::Lingering | FedStatus::Fresh | FedStatus::Held => {}
857 FedStatus::Trigger | FedStatus::Interrupt => {
858 fed_entry.status = FedStatus::Not;
859 }
860 }
861 }
862 }
863}