Newer
Older
use std::fs::File;
use std::path::Path;
use std::time::{Duration, Instant};
actions::{self, Action, ActionRunner, ActionRunnerWrapper, ActionState},
pub struct Thinker {
pub picker: Box<dyn Picker>,
pub otherwise: Option<ActionEnt>,
pub choices: Vec<Choice>,
pub current_action: Option<ActionEnt>,
}
impl Thinker {
pub fn load_from_str<S: AsRef<str>>(string: S) -> builder::Thinker {
ron::de::from_str(string.as_ref()).expect("Failed to parse RON")
}
pub fn load_from_path<P: AsRef<Path>>(path: P) -> builder::Thinker {
let f = File::open(&path).expect("Failed to open file");
ron::de::from_reader(f).expect("Failed to read .ron file")
}
}
mod builder {
use super::*;
#[derive(Debug, Deserialize)]
pub struct Thinker {
pub picker: Box<dyn Picker>,
pub otherwise: Option<Box<dyn Action>>,
pub choices: Vec<ChoiceBuilder>,
}
}
impl builder::Thinker {
pub fn build(self, actor: Entity, cmd: &mut Commands) -> ActionEnt {
let action_ent = ActionState::build(Box::new(self), actor, cmd);
cmd.entity(action_ent.0)
.insert(ActiveThinker(true))
.insert(ActionState::Requested);
action_ent
}
}
#[typetag::deserialize]
impl Action for builder::Thinker {
fn build(
self: Box<Self>,
actor: Entity,
action_ent: ActionEnt,
let choices = self
.choices
.into_iter()
.map(|choice| choice.build(actor, cmd))
.collect();
let otherwise = self
.otherwise
.map(|builder| ActionState::build(builder, actor, cmd));
cmd.entity(action_ent.0).insert(Thinker {
picker: self.picker,
choices,
otherwise,
current_action: None,
});
cmd.entity(actor).push_children(&[action_ent.0]);
pub struct ActiveThinker(bool);
#[derive(Debug)]
impl ActionRunner for ThinkerRunner {
fn activate(&self, _: Entity, action_ent: ActionEnt, cmd: &mut Commands) {
cmd.entity(action_ent.0)
.insert(ActiveThinker(false))
.insert(ActionState::Requested);
fn deactivate(&self, action_ent: ActionEnt, cmd: &mut Commands) {
cmd.entity(action_ent.0).remove::<ActiveThinker>();
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, &Parent, &mut Thinker, &ActiveThinker)>,
mut action_states: Query<&mut actions::ActionState>,
builder_wrappers: Query<&ActionRunnerWrapper>,
) {
let start = Instant::now();
for (thinker_ent, Parent(actor), mut thinker, active_thinker) in thinker_q.iter_mut().skip(iterations.index) {
iterations.index += 1;
let thinker_state = action_states
.get_mut(thinker_ent)
.expect("Where is it?")
.clone();
match thinker_state {
ActionState::Init | ActionState::Success | ActionState::Failure => {
if let ActiveThinker(true) = active_thinker {
let mut act_state = action_states.get_mut(thinker_ent).expect("???");
*act_state = ActionState::Requested;
}
ActionState::Cancelled => {
if let Some(current) = &mut thinker.current_action {
let state = action_states.get_mut(current.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.").clone();
match state {
ActionState::Success | ActionState::Failure => {
let mut act_state = action_states.get_mut(thinker_ent).expect("???");
*act_state = state.clone();
let mut state = action_states.get_mut(current.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
*state = ActionState::Init;
thinker.current_action = None;
}
_ => {
let mut state = action_states.get_mut(current.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
*state = ActionState::Cancelled;
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
}
ActionState::Requested | ActionState::Executing => {
if let Some(picked_action_ent) = thinker.picker.pick(&thinker.choices, &utilities) {
// 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,
thinker_ent,
*actor,
&mut thinker,
&picked_action_ent,
&mut action_states,
&builder_wrappers,
);
} 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;
exec_picked_action(
&mut cmd,
thinker_ent,
*actor,
&mut thinker,
&default_action_ent,
&mut action_states,
&builder_wrappers,
);
} 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).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
let factory = builder_wrappers.get(current.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 => {
factory.0.deactivate(*current, &mut cmd);
*state = ActionState::Init;
thinker.current_action = None;
}
_ => {
*state = ActionState::Cancelled;
if iterations.index % 500 == 0 && start.elapsed() > iterations.max_duration {
return;
}
actor: Entity,
thinker: &mut Mut<Thinker>,
builder_wrappers: &Query<&ActionRunnerWrapper>,
) {
// 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(current) = &mut thinker.current_action {
if current.0 != picked_action_ent.0 {
// 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).
let mut curr_action_state = states.get_mut(current.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
// If the action is executing, or was requested, we
// need to cancel it to make sure it stops. The Action
// system will take care of resetting its state as
// needed.
ActionState::Executing | ActionState::Requested => {
*curr_action_state = ActionState::Cancelled;
let mut thinker_state = states.get_mut(thinker_ent).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
*thinker_state = ActionState::Cancelled;
}
ActionState::Init | ActionState::Success | ActionState::Failure => {
let current_action_factory = builder_wrappers.get(current.0).expect("Couldn't find an Action component corresponding to an Action entity. This is definitely a bug.");
let old_state = curr_action_state.clone();
*curr_action_state = ActionState::Init;
*current = *picked_action_ent;
let mut thinker_state = states.get_mut(thinker_ent).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
*thinker_state = old_state;
}
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.
let mut picked_action_state = states.get_mut(picked_action_ent.0).expect("Couldn't find an Action component corresponding to an Action entity. This is definitely a bug.");
if *picked_action_state == ActionState::Init {
let picked_action_factory = builder_wrappers.get(picked_action_ent.0).expect("Couldn't find an Action component corresponding to an Action entity. This is definitely a bug.");
picked_action_factory
.0
.activate(actor, *picked_action_ent, cmd);
*picked_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 picked_action_factory = builder_wrappers.get(picked_action_ent.0).expect("Couldn't find an Action component corresponding to an Action entity. This is definitely a bug.");
let mut picked_action_state = states.get_mut(picked_action_ent.0).expect("Couldn't find an Action component corresponding to an Action entity. This is definitely a bug.");
.activate(actor, *picked_action_ent, cmd);
thinker.current_action = Some(*picked_action_ent);
*picked_action_state = ActionState::Requested;
}
}