use std::fs::File;
use std::path::Path;
use std::time::{Duration, Instant};
use bevy::prelude::*;
use serde::Deserialize;
use crate::{
actions::{self, Action, ActionManager, ActionManagerWrapper, ActionState},
choices::{Choice, ChoiceBuilder},
considerations::Utility,
pickers::Picker,
};
#[derive(Debug, Clone, Copy)]
pub struct ActionEnt(pub Entity);
#[derive(Debug, Clone, Copy)]
pub struct ConsiderationEnt(pub Entity);
#[derive(Debug)]
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,
cmd: &mut Commands,
) -> Box<dyn ActionManager> {
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]);
Box::new(ThinkerManager)
}
}
#[derive(Debug)]
pub struct ActiveThinker(bool);
#[derive(Debug)]
pub struct ThinkerManager;
impl ActionManager for ThinkerManager {
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 struct ThinkerIterations {
index: usize,
max_duration: Duration,
}
impl ThinkerIterations {
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)>,
utilities: Query<&Utility>,
mut action_states: Query<&mut actions::ActionState>,
builder_wrappers: Query<&ActionManagerWrapper>,
) {
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;
}
}
}
}
ActionState::Requested | ActionState::Executing => {
if let Some(picked_action_ent) = thinker.picker.pick(&thinker.choices, &utilities) {
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 {
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 {
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;
}
}
iterations.index = 0;
}
fn exec_picked_action(
cmd: &mut Commands,
thinker_ent: Entity,
actor: Entity,
thinker: &mut Mut<Thinker>,
picked_action_ent: &ActionEnt,
states: &mut Query<&mut ActionState>,
builder_wrappers: &Query<&ActionManagerWrapper>,
) {
if let Some(current) = &mut thinker.current_action {
if current.0 != picked_action_ent.0 {
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.");
match *curr_action_state {
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.");
current_action_factory
.0
.deactivate(*picked_action_ent, cmd);
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 {
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 {
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.");
picked_action_factory
.0
.activate(actor, *picked_action_ent, cmd);
thinker.current_action = Some(*picked_action_ent);
*picked_action_state = ActionState::Requested;
}
}