diff --git a/README.md b/README.md index efd6620e315c65cc92477a9a0c210b6674aa44a2..90b5ab24246a6ea3572f8c8442e6d92caf8fb584 100644 --- a/README.md +++ b/README.md @@ -104,16 +104,16 @@ fn drink_action_system( ### Thinker Definition -Finally, you can use it when define the `Thinker`: +Finally, you can use it when define the `Thinker`, which you can attach as a +regular Component: ```rust -let actor = cmd.spawn().insert(Thirst::new(70.0, 2.0)).id(); - -let thinker = Thinker::build() - .picker(FirstToScore { threshold: 80.0 }) - .when(Thirsty::build(), Drink::build()) - .attach(&mut cmd, actor); -cmd.entity(actor).push_children(&[thinker]); +cmd.spawn().insert(Thirst::new(70.0, 2.0)).insert( + Thinker::build() + .picker(FirstToScore { threshold: 80.0 }) + .when(Thirsty::build(), Drink::build()) + .component(), +); ``` ## License diff --git a/examples/thirst.rs b/examples/thirst.rs index 062eeca9a6b5a272c30d06291c7f9b53eb7b2213..0f3b5d792a4789dd17913c212e62fc2b34936572 100644 --- a/examples/thirst.rs +++ b/examples/thirst.rs @@ -150,18 +150,15 @@ pub fn thirsty_scorer_system( // to have AI behavior should have one *or more* Thinkers attached to it. pub fn init_entities(mut cmd: Commands) { // Create the entity and throw the Thirst component in there. Nothing special here. - let actor = cmd.spawn().insert(Thirst::new(70.0, 2.0)).id(); - - // And finally, we put all the pieces together! - let thinker = Thinker::build() - .picker(FirstToScore { threshold: 80.0 }) - // Note that what we pass in are _builders_, not components! - .when(Thirsty::build(), Drink::build()) - // .attach will do all the necessary work of attaching this component - // and hooking it up to the AI system. - .attach(&mut cmd, actor); - // TODO: this is a footgun and a pita. Please ignore. - cmd.entity(actor).push_children(&[thinker]); + cmd.spawn().insert(Thirst::new(70.0, 2.0)).insert( + // Thinker::build().component() will return a regular component you + // can attach normally! + Thinker::build() + .picker(FirstToScore { threshold: 80.0 }) + // Note that what we pass in are _builders_, not components! + .when(Thirsty::build(), Drink::build()) + .component(), + ); } fn main() { diff --git a/src/lib.rs b/src/lib.rs index c7b7de97ec0f8313e8cfe8dce18c2232022a28a9..d184f7a6bfdcc75264fceac0b981e12e2f2fbb59 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,8 @@ pub struct BigBrainPlugin; impl Plugin for BigBrainPlugin { fn build(&self, app: &mut AppBuilder) { app.add_system(thinker_system.system()); + app.add_system(thinker_component_attach_system.system()); + app.add_system(thinker_component_detach_system.system()); app.add_system(steps_system.system()); app.add_system(fixed_score_system.system()); app.add_system(all_or_nothing_system.system()); diff --git a/src/thinker.rs b/src/thinker.rs index a9ec2df1053859c1e551962b398b910cf5f87fac..e00918bcd0e70e6d52908381ed675cb599f2e5b1 100644 --- a/src/thinker.rs +++ b/src/thinker.rs @@ -51,24 +51,29 @@ impl ThinkerBuilder { } } - pub fn picker(&mut self, picker: impl Picker + 'static) -> &mut Self { + pub fn picker(mut self, picker: impl Picker + 'static) -> Self { self.picker = Some(Arc::new(picker)); self } pub fn when( - &mut self, + mut self, scorer: impl ScorerBuilder + 'static, action: impl ActionBuilder + 'static, - ) -> &mut Self { - self.choices.push(ChoiceBuilder::new(Arc::new(scorer), Arc::new(action))); + ) -> Self { + self.choices + .push(ChoiceBuilder::new(Arc::new(scorer), Arc::new(action))); self } - pub fn otherwise(&mut self, otherwise: impl ActionBuilder + 'static) -> &mut Self { + pub fn otherwise(mut self, otherwise: impl ActionBuilder + 'static) -> Self { self.otherwise = Some(ActionBuilderWrapper::new(Arc::new(otherwise))); self } + + pub fn component(self) -> ThinkerComponent { + ThinkerComponent(self) + } } impl ActionBuilder for ThinkerBuilder { @@ -95,6 +100,34 @@ impl ActionBuilder for ThinkerBuilder { } } +#[derive(Debug)] +pub struct ThinkerComponent(ThinkerBuilder); + +pub fn thinker_component_attach_system( + mut cmd: Commands, + q: Query<(Entity, &ThinkerComponent), Without<HasThinker>>, +) { + for (entity, thinker_comp) in q.iter() { + let thinker = thinker_comp.0.attach(&mut cmd, entity); + cmd.entity(entity) + .insert(HasThinker(thinker)) + .push_children(&[thinker]); + } +} + +pub fn thinker_component_detach_system( + mut cmd: Commands, + q: Query<(Entity, &HasThinker), Without<ThinkerComponent>>, +) { + for (actor, HasThinker(thinker)) in q.iter() { + cmd.entity(*thinker).despawn_recursive(); + cmd.entity(actor).remove::<HasThinker>(); + } +} + +#[derive(Debug)] +pub struct HasThinker(Entity); + #[derive(Debug)] pub struct ActiveThinker(bool); @@ -192,7 +225,7 @@ pub fn thinker_system( actions::ActionState::Init | actions::ActionState::Success | actions::ActionState::Failure => { - cmd.entity(current.0.0).despawn_recursive(); + cmd.entity(current.0 .0).despawn_recursive(); thinker.current_action = None; } _ => {