Newer
Older
/*!
Thinkers are the "brain" of an entity. You attach Scorers to it, and the Thinker picks the right Action to run based on the resulting Scores.
*/
use bevy::{
prelude::*,
utils::{Duration, Instant},
};
actions::{self, ActionBuilder, ActionBuilderWrapper, ActionState},
choices::{Choice, ChoiceBuilder},
pickers::Picker,
/**
Wrapper for Actor entities. In terms of Scorers, Thinkers, and Actions, this is the [`Entity`] actually _performing_ the action, rather than the entity a Scorer/Thinker/Action is attached to. Generally, you will use this entity when writing Queries for Action and Scorer systems.
*/
#[derive(Debug, Clone, Component, Copy)]
#[derive(Debug, Clone, Component, Copy)]
pub(crate) struct ActionEnt(pub Entity);
#[derive(Debug, Clone, Component, Copy)]
pub(crate) struct ScorerEnt(pub Entity);
/**
The "brains" behind this whole operation. A `Thinker` is what glues together `Actions` and `Scorers` and shapes larger, intelligent-seeming systems.
Note: Thinkers are also Actions, so anywhere you can pass in an Action (or [`ActionBuilder`]), you can pass in a Thinker (or [`ThinkerBuilder`]).
### Example
```no_run
pub fn init_entities(mut cmd: Commands) {
cmd.spawn()
.insert(Thirst::new(70.0, 2.0))
.insert(Hunger::new(50.0, 3.0))
.insert(
Thinker::build()
.picker(FirstToScore::new(80.0))
.when(Thirsty::build(), Drink::build())
.when(Hungry::build(), Eat::build())
.otherwise(Meander::build()),
);
}
```
*/
picker: Arc<dyn Picker>,
otherwise: Option<ActionBuilderWrapper>,
choices: Vec<Choice>,
current_action: Option<(ActionEnt, ActionBuilderWrapper)>,
/**
Make a new [`ThinkerBuilder`]. This is what you'll actually use to configure Thinker behavior.
*/
pub fn build() -> ThinkerBuilder {
ThinkerBuilder::new()
/**
This is what you actually use to configure Thinker behavior. It's a plain old [`ActionBuilder`], as well.
*/
picker: Option<Arc<dyn Picker>>,
otherwise: Option<ActionBuilderWrapper>,
choices: Vec<ChoiceBuilder>,
impl ThinkerBuilder {
pub(crate) fn new() -> Self {
Self {
picker: None,
otherwise: None,
choices: Vec::new(),
}
/**
Define a [`Picker`](crate::pickers::Picker) for this Thinker.
*/
pub fn picker(mut self, picker: impl Picker + 'static) -> Self {
self.picker = Some(Arc::new(picker));
self
}
/**
Define an [`ActionBuilder`](crate::actions::ActionBuilder) and [`ScorerBuilder`](crate::scorers::ScorerBuilder) pair.
*/
scorer: impl ScorerBuilder + 'static,
action: impl ActionBuilder + 'static,
) -> Self {
self.choices
.push(ChoiceBuilder::new(Arc::new(scorer), Arc::new(action)));
/**
Default `Action` to execute if the `Picker` did not pick any of the given choices.
*/
pub fn otherwise(mut self, otherwise: impl ActionBuilder + 'static) -> Self {
self.otherwise = Some(ActionBuilderWrapper::new(Arc::new(otherwise)));
self
impl ActionBuilder for ThinkerBuilder {
fn build(&self, cmd: &mut Commands, action_ent: Entity, actor: Entity) {
.map(|choice| choice.build(cmd, actor, action_ent))
cmd.entity(action_ent)
.insert(Thinker {
// TODO: reasonable default?...
picker: self
.picker
.clone()
.expect("ThinkerBuilder must have a Picker"),
choices,
otherwise: self.otherwise.clone(),
current_action: None,
})
pub fn thinker_component_attach_system(
mut cmd: Commands,
q: Query<(Entity, &ThinkerBuilder), Without<HasThinker>>,
for (entity, thinker_builder) in q.iter() {
let thinker = thinker_builder.attach(&mut cmd, entity);
cmd.entity(entity).insert(HasThinker(thinker));
}
}
pub fn thinker_component_detach_system(
mut cmd: Commands,
q: Query<(Entity, &HasThinker), Without<ThinkerBuilder>>,
) {
for (actor, HasThinker(thinker)) in q.iter() {
cmd.entity(*thinker).despawn_recursive();
cmd.entity(actor).remove::<HasThinker>();
}
}
pub fn actor_gone_cleanup(
mut cmd: Commands,
actors: Query<&ThinkerBuilder>,
q: Query<(Entity, &Actor)>,
) {
for (child, Actor(actor)) in q.iter() {
if actors.get(*actor).is_err() {
// Actor is gone. Let's clean up.
cmd.entity(child).despawn_recursive();
}
}
}
pub struct HasThinker(Entity);
pub fn new(max_duration: Duration) -> Self {
Self {
index: 0,
max_duration,
}
}
}
impl Default for ThinkerIterations {
fn default() -> Self {
Self::new(Duration::from_millis(10))
}
}
pub fn thinker_system(
mut cmd: Commands,
mut iterations: Local<ThinkerIterations>,
mut thinker_q: Query<(Entity, &Actor, &mut Thinker)>,
mut action_states: Query<&mut actions::ActionState>,
) {
let start = Instant::now();
for (thinker_ent, Actor(actor), mut thinker) in thinker_q.iter_mut().skip(iterations.index) {
let thinker_state = action_states
.get_mut(thinker_ent)
.expect("Where is it?")
.clone();
match thinker_state {
ActionState::Init => {
let mut act_state = action_states.get_mut(thinker_ent).expect("???");
*act_state = ActionState::Requested;
ActionState::Requested => {
let mut act_state = action_states.get_mut(thinker_ent).expect("???");
*act_state = ActionState::Executing;
}
ActionState::Success | ActionState::Failure => {}
ActionState::Cancelled => {
if let Some(current) = &mut thinker.current_action {
let state = action_states.get_mut(current.0.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.").clone();
match state {
ActionState::Success | ActionState::Failure => {
cmd.entity(current.0 .0).despawn_recursive();
thinker.current_action = None;
}
_ => {
let mut state = action_states.get_mut(current.0.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
} else {
let mut act_state = action_states.get_mut(thinker_ent).expect("???");
*act_state = ActionState::Success;
ActionState::Executing => {
if let Some(choice) = thinker.picker.pick(&thinker.choices, &scores) {
// Think about what action we're supposed to be taking. We do this
// every tick, because we might change our mind.
// ...and then execute it (details below).
exec_picked_action(
&mut cmd,
*actor,
&mut thinker,
&mut action_states,
);
} else if let Some(default_action_ent) = &thinker.otherwise {
// Otherwise, let's just execute the default one! (if it's there)
let default_action_ent = default_action_ent.clone();
exec_picked_action(
&mut cmd,
*actor,
&mut thinker,
&default_action_ent,
&mut action_states,
);
} else if let Some(current) = &mut thinker.current_action {
// If we didn't pick anything, and there's no default action,
// we need to see if there's any action currently executing,
// and cancel it. We also use this opportunity to clean up
// stale action components so they don't slow down joins.
let mut state = action_states.get_mut(current.0.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
match *state {
actions::ActionState::Init
| actions::ActionState::Success
| actions::ActionState::Failure => {
cmd.entity(current.0 .0).despawn_recursive();
thinker.current_action = None;
}
_ => {
*state = ActionState::Cancelled;
if iterations.index % 500 == 0 && start.elapsed() > iterations.max_duration {
return;
}
cmd: &mut Commands,
actor: Entity,
thinker: &mut Mut<Thinker>,
) {
// If we do find one, then we need to grab the corresponding
// component for it. The "action" that `picker.pick()` returns
// is just a newtype for an Entity.
//
// Now we check the current action. We need to check if we picked the same one as the previous tick.
//
// TODO: I don't know where the right place to put this is
// (maybe not in this logic), but we do need some kind of
// oscillation protection so we're not just bouncing back and
// forth between the same couple of actions.
if let Some((action_ent, ActionBuilderWrapper(current_id, _))) = &mut thinker.current_action {
let mut curr_action_state = states.get_mut(action_ent.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
if !Arc::ptr_eq(current_id, &picked_action.0)
|| matches!(*curr_action_state, ActionState::Success)
|| matches!(*curr_action_state, ActionState::Failure)
{
// So we've picked a different action than we were
// currently executing. Just like before, we grab the
// actual Action component (and we assume it exists).
// If the action is executing, or was requested, we
ActionState::Executing | ActionState::Requested => {
*curr_action_state = ActionState::Cancelled;
}
ActionState::Init | ActionState::Success | ActionState::Failure => {
// Despawn the action itself.
cmd.entity(action_ent.0).despawn_recursive();
thinker.current_action = Some((
ActionEnt(picked_action.1.attach(cmd, actor)),
picked_action.clone(),
));
}
ActionState::Cancelled => {}
};
} else {
// Otherwise, it turns out we want to keep executing
// the same action. Just in case, we go ahead and set
// it as Requested if for some reason it had finished
// but the Action System hasn't gotten around to
// cleaning it up.
if *curr_action_state == ActionState::Init {
*curr_action_state = ActionState::Requested;
}
}
} else {
// This branch arm is called when there's no
// current_action in the thinker. The logic here is pretty
// straightforward -- we set the action, Request it, and
// that's it.
let new_action = picked_action.1.attach(cmd, actor);
thinker.current_action = Some((ActionEnt(new_action), picked_action.clone()));