Skip to main content

mittens_engine/engine/ecs/component/
raycast.rs

1use crate::engine::ecs::ComponentId;
2use crate::engine::ecs::component::Component;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum RayCastMode {
6    Continuous,
7    EventDriven,
8}
9
10/// Ray casting request/behavior.
11///
12/// Semantics:
13/// - Attach this anywhere (commonly under a camera rig transform).
14/// - The RayCastSystem resolves the actual ray source from surrounding topology.
15/// - `EventDriven` means the raycaster casts only when explicitly requested, except for
16///   desktop cursor-through-camera pointers which currently auto-cast from desktop mouse input.
17#[derive(Debug, Clone, Copy)]
18pub struct RayCastComponent {
19    pub mode: RayCastMode,
20
21    /// Max ray distance in world units.
22    pub max_distance: f32,
23
24    /// Incremented by `IntentValue::RequestRaycast` to request a cast on this frame.
25    ///
26    /// This is intentionally not serialized; it is a transient runtime signal.
27    pub cast_requests: u32,
28
29    component: Option<ComponentId>,
30}
31
32impl RayCastComponent {
33    pub fn new(mode: RayCastMode) -> Self {
34        Self {
35            mode,
36            max_distance: 200.0,
37            cast_requests: 0,
38            component: None,
39        }
40    }
41
42    pub fn continuous() -> Self {
43        Self::new(RayCastMode::Continuous)
44    }
45
46    pub fn event_driven() -> Self {
47        Self::new(RayCastMode::EventDriven)
48    }
49
50    pub fn with_max_distance(mut self, max_distance: f32) -> Self {
51        self.max_distance = max_distance;
52        self
53    }
54}
55
56impl Default for RayCastComponent {
57    fn default() -> Self {
58        Self::event_driven()
59    }
60}
61
62impl Component for RayCastComponent {
63    fn name(&self) -> &'static str {
64        "raycast"
65    }
66
67    fn set_id(&mut self, component: ComponentId) {
68        self.component = Some(component);
69    }
70
71    fn as_any(&self) -> &dyn std::any::Any {
72        self
73    }
74
75    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
76        self
77    }
78
79    fn init(&mut self, emit: &mut dyn crate::engine::ecs::SignalEmitter, component: ComponentId) {
80        self.component = Some(component);
81        emit.push_intent_now(
82            component,
83            crate::engine::ecs::IntentValue::RegisterRaycast {
84                component_ids: vec![component],
85            },
86        );
87    }
88
89    fn cleanup(
90        &mut self,
91        emit: &mut dyn crate::engine::ecs::SignalEmitter,
92        component: ComponentId,
93    ) {
94        emit.push_intent_now(
95            component,
96            crate::engine::ecs::IntentValue::RemoveRaycast {
97                component_ids: vec![component],
98            },
99        );
100    }
101
102    fn to_mms_ast(
103        &self,
104        _world: &crate::engine::ecs::World,
105    ) -> crate::scripting::ast::ComponentExpression {
106        use crate::engine::ecs::component::ce_helpers::*;
107        let ctor = match self.mode {
108            RayCastMode::Continuous => "continuous",
109            RayCastMode::EventDriven => "event_driven",
110        };
111        ce_call("Raycast", ctor, vec![])
112            .with_call("max_distance", vec![num(self.max_distance as f64)])
113    }
114}