1use crate::{
2 archetype::ArchetypeColumnInfo,
3 bundle::{Bundle, BundleColumns},
4 entity::Entity,
5 world::{Relation, World, WorldError},
6 Component, ComponentRef, ComponentRefMut,
7};
8use intuicio_core::{context::Context, function::FunctionHandle, registry::Registry};
9use intuicio_data::{
10 data_stack::DataStackPack,
11 lifetime::Lifetime,
12 managed::{DynamicManaged, DynamicManagedRef},
13};
14use std::collections::HashMap;
15
16pub use intuicio_core::function::Function as ActorMessageFunction;
17
18pub struct ActorChild;
19pub struct ActorParent;
20
21#[derive(Debug, Default, Clone)]
22pub struct ActorMessageListeners(HashMap<String, FunctionHandle>);
23
24#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub struct Actor(Entity);
26
27impl Actor {
28 pub fn spawn(
29 world: &mut World,
30 bundle: impl Bundle + Send + Sync + 'static,
31 ) -> Result<Self, WorldError> {
32 let entity = world.spawn((
33 ActorMessageListeners::default(),
34 Relation::<ActorChild>::default(),
35 Relation::<ActorParent>::default(),
36 ))?;
37 world.insert(entity, bundle)?;
38 Ok(Self(entity))
39 }
40
41 pub fn despawn(self, world: &mut World) -> Result<(), WorldError> {
42 world.despawn(self.0)
43 }
44
45 pub fn insert(
46 self,
47 world: &mut World,
48 bundle: impl Bundle + Send + Sync + 'static,
49 ) -> Result<(), WorldError> {
50 world.insert(self.0, bundle)
51 }
52
53 pub fn remove<T: BundleColumns>(self, world: &mut World) -> Result<(), WorldError> {
54 world.remove::<T>(self.0)
55 }
56
57 pub fn remove_raw(
58 self,
59 world: &mut World,
60 columns: Vec<ArchetypeColumnInfo>,
61 ) -> Result<(), WorldError> {
62 world.remove_raw(self.0, columns)
63 }
64
65 pub fn exists(self, world: &World) -> bool {
66 world.has_entity(self.0)
67 }
68
69 pub fn entity(self) -> Entity {
70 self.0
71 }
72
73 pub fn component<const LOCKING: bool, T: Component>(
74 self,
75 world: &World,
76 ) -> Result<ComponentRef<LOCKING, T>, WorldError> {
77 world.component::<LOCKING, T>(self.0)
78 }
79
80 pub fn component_mut<const LOCKING: bool, T: Component>(
81 self,
82 world: &World,
83 ) -> Result<ComponentRefMut<LOCKING, T>, WorldError> {
84 world.component_mut::<LOCKING, T>(self.0)
85 }
86
87 pub fn add_child<const LOCKING: bool>(
88 self,
89 world: &mut World,
90 other: Self,
91 ) -> Result<(), WorldError> {
92 world.relate::<LOCKING, _>(ActorChild, self.0, other.0)?;
93 world.relate::<LOCKING, _>(ActorParent, other.0, self.0)?;
94 Ok(())
95 }
96
97 pub fn remove_child<const LOCKING: bool>(
98 self,
99 world: &mut World,
100 other: Self,
101 ) -> Result<(), WorldError> {
102 world.unrelate::<LOCKING, ActorChild>(self.0, other.0)?;
103 world.unrelate::<LOCKING, ActorParent>(other.0, self.0)?;
104 Ok(())
105 }
106
107 pub fn children<const LOCKING: bool>(self, world: &World) -> impl Iterator<Item = Self> + '_ {
108 world
109 .relations_outgoing::<LOCKING, ActorChild>(self.0)
110 .map(|(_, _, entity)| Self(entity))
111 }
112
113 pub fn parents<const LOCKING: bool>(self, world: &World) -> impl Iterator<Item = Self> + '_ {
114 world
115 .relations_outgoing::<LOCKING, ActorParent>(self.0)
116 .map(|(_, _, entity)| Self(entity))
117 }
118
119 pub fn register_message_listener<const LOCKING: bool>(
120 self,
121 world: &World,
122 id: impl ToString,
123 function: ActorMessageFunction,
124 ) -> Result<(), WorldError> {
125 let mut listeners = self.component_mut::<LOCKING, ActorMessageListeners>(world)?;
126 listeners.0.insert(id.to_string(), function.into_handle());
127 Ok(())
128 }
129
130 pub fn unregister_message_listener<const LOCKING: bool>(
131 self,
132 world: &World,
133 id: &str,
134 ) -> Result<(), WorldError> {
135 let mut listeners = self.component_mut::<LOCKING, ActorMessageListeners>(world)?;
136 listeners.0.remove(id);
137 Ok(())
138 }
139
140 pub fn message_listener<const LOCKING: bool>(
141 self,
142 world: &World,
143 id: &str,
144 ) -> Result<Option<FunctionHandle>, WorldError> {
145 let listeners = self.component::<LOCKING, ActorMessageListeners>(world)?;
146 Ok(listeners.0.get(id).cloned())
147 }
148
149 pub fn invoke_message<const LOCKING: bool>(
150 self,
151 world: &World,
152 id: &str,
153 context: &mut Context,
154 registry: &Registry,
155 ) -> Result<(), WorldError> {
156 let listeners = self.component::<LOCKING, ActorMessageListeners>(world)?;
157 if let Some(function) = listeners.0.get(id).cloned() {
158 context.stack().push(DynamicManaged::new(self).unwrap());
159 let lifetime = Lifetime::default();
160 let value = DynamicManagedRef::new(world, lifetime.borrow().unwrap());
161 context.stack().push(value);
162 function.invoke(context, registry);
163 }
164 Ok(())
165 }
166
167 pub fn dispatch_message<const LOCKING: bool, O: DataStackPack, I: DataStackPack>(
168 self,
169 world: &World,
170 id: &str,
171 context: &mut Context,
172 registry: &Registry,
173 inputs: I,
174 ) -> Result<Option<O>, WorldError> {
175 let listeners = self.component::<LOCKING, ActorMessageListeners>(world)?;
176 if let Some(function) = listeners.0.get(id).cloned() {
177 inputs.stack_push_reversed(context.stack());
178 context.stack().push(DynamicManaged::new(self).unwrap());
179 let lifetime = Lifetime::default();
180 let value = DynamicManagedRef::new(world, lifetime.borrow().unwrap());
181 context.stack().push(value);
182 function.invoke(context, registry);
183 Ok(Some(O::stack_pop(context.stack())))
184 } else {
185 Ok(None)
186 }
187 }
188
189 pub fn dispatch_message_hierarchy<const LOCKING: bool, I: DataStackPack + Clone>(
190 self,
191 world: &World,
192 id: &str,
193 context: &mut Context,
194 registry: &Registry,
195 inputs: I,
196 ) -> Result<(), WorldError> {
197 self.dispatch_message::<LOCKING, (), I>(world, id, context, registry, inputs.clone())?;
198 for child in self.children::<LOCKING>(world) {
199 child.dispatch_message_hierarchy::<LOCKING, I>(
200 world,
201 id,
202 context,
203 registry,
204 inputs.clone(),
205 )?;
206 }
207 Ok(())
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214 use intuicio_core::prelude::*;
215 use intuicio_derive::intuicio_function;
216
217 fn is_async<T: Send + Sync>() {}
218
219 struct Attack(usize);
220
221 struct Lives(usize);
222
223 #[derive(Debug, Default, Clone)]
224 struct Counter {
225 odd: usize,
226 even: usize,
227 }
228
229 #[intuicio_function(transformer = "DynamicManagedValueTransformer")]
230 fn attack(world: &World, this: Actor, other: Actor) {
231 let this_attack = this.component::<true, Attack>(world).unwrap();
232 let mut other_lives = other.component_mut::<true, Lives>(world).unwrap();
233 other_lives.0 = other_lives.0.saturating_sub(this_attack.0);
234 }
235
236 #[test]
237 fn test_actor() {
238 is_async::<Actor>();
239
240 let registry = Registry::default()
241 .with_basic_types()
242 .with_type(NativeStructBuilder::new_uninitialized::<DynamicManaged>().build())
243 .with_type(NativeStructBuilder::new_uninitialized::<DynamicManagedRef>().build());
244 let mut context = Context::new(10240, 10240);
245 let mut world = World::default();
246
247 let player = Actor::spawn(&mut world, ("player".to_owned(), Attack(2), Lives(1))).unwrap();
248 player
249 .register_message_listener::<true>(&world, "attack", attack::define_function(®istry))
250 .unwrap();
251 assert!(player.exists(&world));
252 assert_eq!(player.component::<true, Attack>(&world).unwrap().0, 2);
253 assert_eq!(player.component::<true, Lives>(&world).unwrap().0, 1);
254
255 let enemy = Actor::spawn(&mut world, ("enemy".to_owned(), Attack(1), Lives(2))).unwrap();
256 assert!(enemy.exists(&world));
257 assert_eq!(enemy.component::<true, Attack>(&world).unwrap().0, 1);
258 assert_eq!(enemy.component::<true, Lives>(&world).unwrap().0, 2);
259
260 player
261 .dispatch_message::<true, (), _>(
262 &world,
263 "attack",
264 &mut context,
265 ®istry,
266 (DynamicManaged::new(enemy).unwrap(),),
267 )
268 .unwrap();
269 assert_eq!(enemy.component::<true, Lives>(&world).unwrap().0, 0);
270 }
271
272 #[test]
273 fn test_actor_singleton() {
274 let mut world = World::default();
275 let resources = Actor::spawn(&mut world, (Counter::default(),)).unwrap();
276
277 for index in 0..5usize {
278 world.spawn((index,)).unwrap();
279 }
280
281 let mut counter = resources.component_mut::<true, Counter>(&world).unwrap();
282 for value in world.query::<true, &usize>() {
283 if *value % 2 == 0 {
284 counter.even += 1;
285 } else {
286 counter.odd += 1;
287 }
288 }
289 assert_eq!(counter.odd, 2);
290 assert_eq!(counter.even, 3);
291 }
292}