all_is_cubes/space/
behaviors.rs

1use all_is_cubes_base::math::{GridAab, GridRotation};
2
3use crate::behavior;
4use crate::inv::EphemeralOpaque;
5use crate::space::Space;
6use crate::universe;
7
8// -------------------------------------------------------------------------------------------------
9
10impl behavior::Host for Space {
11    type Attachment = SpaceBehaviorAttachment;
12}
13
14/// Description of where in a [`Space`] a [`Behavior<Space>`](crate::behavior::Behavior)
15/// exists.
16// ---
17// TODO: This shouldn't directly implement Serialize
18#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
19#[cfg_attr(feature = "save", derive(serde::Serialize, serde::Deserialize))]
20pub struct SpaceBehaviorAttachment {
21    pub(in crate::space) bounds: GridAab,
22    rotation: GridRotation,
23}
24
25impl SpaceBehaviorAttachment {
26    /// Constructs a new [`SpaceBehaviorAttachment`] with no rotation.
27    pub fn new(bounds: GridAab) -> Self {
28        Self {
29            bounds,
30            rotation: GridRotation::IDENTITY,
31        }
32    }
33
34    /// Returns the bounds of this attachment, which specify (without mandating) what
35    /// region the behavior should affect.
36    pub fn bounds(&self) -> GridAab {
37        self.bounds
38    }
39
40    /// Returns the rotation of this attachment, which specifies, if applicable, which
41    /// orientation the behavior should operate in relative to the space.
42    /// The exact meaning of this is up to the behavior.
43    ///
44    /// TODO: explain with an example once we have a good one
45    pub fn rotation(&self) -> GridRotation {
46        self.rotation
47    }
48}
49
50// -------------------------------------------------------------------------------------------------
51
52/// A region of a [`Space`] that does something if [`Tool::Activate`] is used on it.
53///
54/// TODO: This is a placeholder for a better design; it's too specific (external side
55/// effect) and yet also not general enough (we would like buttons to have detailed
56/// reactions to clicking) considering that it's hardcoded in Space.
57///
58/// [`Tool::Activate`]: crate::inv::Tool::Activate
59#[derive(Clone, Debug, Eq, PartialEq)]
60#[expect(clippy::exhaustive_structs)]
61pub struct ActivatableRegion {
62    /// The function to call when this region is activated.
63    pub effect: EphemeralOpaque<dyn Fn() + Send + Sync>,
64}
65
66impl ActivatableRegion {
67    /// Activate this region, calling the embedded function.
68    pub fn activate(&self) {
69        if let Some(f) = self.effect.try_ref() {
70            f();
71        }
72    }
73}
74
75impl behavior::Behavior<Space> for ActivatableRegion {
76    fn step(
77        &self,
78        _context: &behavior::Context<'_, '_, Space>,
79    ) -> (universe::UniverseTransaction, behavior::Then) {
80        // TODO: Give a way for this to be deleted automatically when
81        // its effect is gone
82        (
83            universe::UniverseTransaction::default(),
84            behavior::Then::Step,
85        )
86    }
87    fn persistence(&self) -> Option<behavior::Persistence> {
88        // Not useful to serialize since `EphemeralOpaque` can't be.
89        None
90    }
91}
92
93impl universe::VisitHandles for ActivatableRegion {
94    fn visit_handles(&self, _: &mut dyn universe::HandleVisitor) {
95        // Our only interesting member is an EphemeralOpaque — which is opaque.
96    }
97}