bevy_tnua/control_helpers/
blip_reuse_avoidance.rs

1use bevy::platform::collections::HashMap;
2use bevy::prelude::*;
3use bevy_tnua_physics_integration_layer::obstacle_radar::TnuaObstacleRadar;
4
5use crate::TnuaScheme;
6use crate::controller::TnuaActionFlowStatus;
7use crate::prelude::TnuaController;
8
9/// Helper for keeping track on entities the character was just interacting with, so that it won't
10/// immediately interact with again after the action is finished.
11///
12/// For example - this can be use to avoid climbing again on a ladder immediately after dropping
13/// from it.
14#[derive(Component)]
15pub struct TnuaBlipReuseAvoidance<S: TnuaScheme> {
16    current_entity: Option<Entity>,
17    entities_to_avoid: HashMap<Entity, S::ActionDiscriminant>,
18}
19
20impl<S: TnuaScheme> Default for TnuaBlipReuseAvoidance<S> {
21    fn default() -> Self {
22        Self {
23            current_entity: None,
24            entities_to_avoid: Default::default(),
25        }
26    }
27}
28
29/// Must be implemented by control schemes that want to use [`TnuaBlipReuseAvoidance`] or
30pub trait TnuaHasTargetEntity: TnuaScheme {
31    /// The entity used by the given action.
32    ///
33    /// Note that entities are not part of the actions themselves - they are part of the payloads.
34    /// It's up to user code to define them in the control scheme for the relevant actions and to
35    /// pass then when feeding these actions.
36    fn target_entity(action_state: &Self::ActionState) -> Option<Entity>;
37}
38
39impl<S> TnuaBlipReuseAvoidance<S>
40where
41    S: TnuaScheme + TnuaHasTargetEntity,
42{
43    /// Call this every frame.
44    pub fn update(&mut self, controller: &TnuaController<S>, radar: &TnuaObstacleRadar) {
45        let current_entity = controller
46            .current_action
47            .as_ref()
48            .and_then(S::target_entity);
49
50        if current_entity != self.current_entity
51            && let Some(old_entity) = self.current_entity.as_ref()
52            && let TnuaActionFlowStatus::ActionEnded(action_discriminant)
53            | TnuaActionFlowStatus::Cancelled {
54                old: action_discriminant,
55                new: _,
56            } = controller.action_flow_status()
57        {
58            self.entities_to_avoid
59                .insert(*old_entity, *action_discriminant);
60        }
61
62        self.entities_to_avoid
63            .retain(|entity, _| radar.has_blip(*entity));
64
65        self.current_entity = current_entity;
66    }
67
68    /// Returns true the entity was already interacted with and the character did not move away
69    /// from it yet.
70    pub fn should_avoid(&self, entity: Entity) -> bool {
71        self.entities_to_avoid.contains_key(&entity)
72    }
73}