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}