Skip to content
Snippets Groups Projects
thinker.rs 13.1 KiB
Newer Older
Kat Marchán's avatar
Kat Marchán committed
use std::fs::File;
use std::path::Path;
use std::time::{Duration, Instant};

use serde::Deserialize;
use specs::{
    Component, DenseVecStorage, Entities, Entity, Join, LazyUpdate, Read, ReadStorage, System,
    WriteStorage,
};

use crate::{
    actions::{Action, ActionManager, ActionManagerWrapper, ActionState},
    choices::{Choice, ChoiceBuilder},
    considerations::Utility,
    pickers::Picker,
    stopwatch::Stopwatch,
};

#[derive(Debug, Clone)]
pub struct ActionEnt(pub Entity);

#[derive(Debug, Clone)]
pub struct ConsiderationEnt(pub Entity);

#[derive(Component, Debug)]
pub struct Thinker {
    pub picker: Box<dyn Picker>,
    pub otherwise: Option<ActionEnt>,
    pub choices: Vec<Choice>,
    pub actor: Entity,
    pub current_action: Option<ActionEnt>,
    pub timer: Stopwatch,
}

impl Thinker {
    pub fn load_from<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, ents: &Entities, lazy: &LazyUpdate) -> ActionEnt {
        let action_ent = ActionState::build(Box::new(self), actor, ents, lazy);
        lazy.insert(action_ent.0.clone(), ActiveThinker(true));
        lazy.insert(action_ent.0.clone(), ActionState::Requested);
        action_ent
    }
}

#[typetag::deserialize]
impl Action for builder::Thinker {
    fn build(
        self: Box<Self>,
        actor: Entity,
        action_ent: ActionEnt,
        ents: &Entities,
        lazy: &LazyUpdate,
    ) -> Box<dyn ActionManager> {
        lazy.insert(
            action_ent.0.clone(),
            Thinker {
                picker: self.picker,
                actor,
                choices: self
                    .choices
                    .into_iter()
                    .map(|choice| choice.build(actor.clone(), &ents, &lazy))
                    .collect(),
                otherwise: self
                    .otherwise
                    .map(|builder| ActionState::build(builder, actor.clone(), ents, lazy)),
                current_action: None,
                timer: Stopwatch::new(),
            },
        );
        Box::new(ThinkerManager)
    }
}

#[derive(Debug, Component)]
pub struct ActiveThinker(bool);

#[derive(Debug)]
pub struct ThinkerManager;

impl ActionManager for ThinkerManager {
    fn activate(&self, _: Entity, action_ent: ActionEnt, lazy: &LazyUpdate) {
        lazy.insert(action_ent.0, ActiveThinker(false));
        lazy.insert(action_ent.0, ActionState::Requested);
    }
    fn deactivate(&self, action_ent: ActionEnt, lazy: &LazyUpdate) {
        lazy.remove::<ActiveThinker>(action_ent.0);
    }
}

pub struct ThinkerSystem {
    index: usize,
    max_duration: Duration,
}
impl ThinkerSystem {
    pub fn new(max_duration: Duration) -> Self {
        Self {
            index: 0,
            max_duration,
        }
    }
}

impl<'a> System<'a> for ThinkerSystem {
    type SystemData = (
        Entities<'a>,
        WriteStorage<'a, Thinker>,
        ReadStorage<'a, ActiveThinker>,
        ReadStorage<'a, Utility>,
        WriteStorage<'a, ActionState>,
        ReadStorage<'a, ActionManagerWrapper>,
        Read<'a, LazyUpdate>,
    );
    fn run(
        &mut self,
        (ents, mut thinkers, active_thinkers, utilities, mut action_states, builder_wrappers, lazy): Self::SystemData,
    ) {
        let start = Instant::now();
        for (thinker, thinker_ent, active_thinker) in (&mut thinkers, &ents, &active_thinkers)
            .join()
            .skip(self.index)
        {
            self.index += 1;

            let thinker_state = action_states
                .get(thinker_ent.clone())
                .expect("Where is it?")
                .clone();
            match thinker_state {
                ActionState::Init | ActionState::Success | ActionState::Failure => {
                    if let ActiveThinker(true) = active_thinker {
                        let act_state = action_states.get_mut(thinker_ent.clone()).expect("???");
                        *act_state = ActionState::Requested;
                    }
                }
                ActionState::Cancelled => {
                    if let Some(current) = &mut thinker.current_action {
                        let state = action_states.get(current.0.clone()).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.").clone();
                        match state {
                            ActionState::Success | ActionState::Failure => {
                                let act_state =
                                    action_states.get_mut(thinker_ent.clone()).expect("???");
                                *act_state = state.clone();
                                let state = action_states.get_mut(current.0.clone()).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
                                *state = ActionState::Init;
                                thinker.current_action = None;
                            }
                            _ => {
                                let state = action_states.get_mut(current.0.clone()).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)
                    {
                        // 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(
                            thinker_ent,
                            thinker,
                            &picked_action_ent,
                            &mut action_states,
                            &builder_wrappers,
                            &lazy,
                        );
                    } 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(
                            thinker_ent,
                            thinker,
                            &default_action_ent,
                            &mut action_states,
                            &builder_wrappers,
                            &lazy,
                        );
                    } 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 state = action_states.get_mut(current.0.clone()).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
                        let factory = builder_wrappers.get(current.0.clone()).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
                        match state {
                            ActionState::Init | ActionState::Success | ActionState::Failure => {
                                factory.0.deactivate(current.clone(), &lazy);
                                *state = ActionState::Init;
                                thinker.current_action = None;
                            }
                            _ => {
                                *state = ActionState::Cancelled;
                            }
                        }
                    }
                }
            }
            thinker.timer.reset();
            thinker.timer.start();
            if self.index % 500 == 0 && start.elapsed() > self.max_duration {
                return;
            }
        }
        self.index = 0;
    }
}

fn exec_picked_action(
    thinker_ent: Entity,
    thinker: &mut Thinker,
    picked_action_ent: &ActionEnt,
    states: &mut WriteStorage<ActionState>,
    builder_wrappers: &ReadStorage<ActionManagerWrapper>,
    lazy: &Read<LazyUpdate>,
) {
    // 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 curr_action_state = states.get_mut(current.0.clone()).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.
            match curr_action_state {
                ActionState::Executing | ActionState::Requested => {
                    *curr_action_state = ActionState::Cancelled;
                    let thinker_state = states.get_mut(thinker_ent.clone()).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.clone()).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.clone(), &lazy);
                    let old_state = curr_action_state.clone();
                    *curr_action_state = ActionState::Init;
                    *current = picked_action_ent.clone();
                    let thinker_state = states.get_mut(thinker_ent.clone()).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 picked_action_state = states.get_mut(picked_action_ent.0.clone()).expect("Couldn't find an Action component corresponding to an Action entity. This is definitely a bug.");
            match picked_action_state {
                ActionState::Init => {
                    let picked_action_factory = builder_wrappers.get(picked_action_ent.0.clone()).expect("Couldn't find an Action component corresponding to an Action entity. This is definitely a bug.");
                    picked_action_factory.0.activate(
                        thinker.actor.clone(),
                        picked_action_ent.clone(),
                        lazy,
                    );
                    *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.clone()).expect("Couldn't find an Action component corresponding to an Action entity. This is definitely a bug.");
        let picked_action_state = states.get_mut(picked_action_ent.0.clone()).expect("Couldn't find an Action component corresponding to an Action entity. This is definitely a bug.");
        picked_action_factory
            .0
            .activate(thinker.actor.clone(), picked_action_ent.clone(), lazy);
        thinker.current_action = Some(picked_action_ent.clone());
        *picked_action_state = ActionState::Requested;
    }
}