#ifndef GOAP_PLANNER_HPP #define GOAP_PLANNER_HPP #include "action.hpp" #include "actor_world_state.hpp" #include "goal.hpp" #include #include #include #include namespace gd = godot; namespace goap { typedef gd::Vector Plan; /*! Wrapper for WorldState to be used to represent nodes in the A* implementation. * Mainly required to let a HashMap of HashMaps work. */ struct WorldStateNode { WorldStateNode() = default; //! Root node constructor WorldStateNode(WorldState const &goal, ActorWorldState *context); //! Path node constructor WorldStateNode(WorldStateNode const &last_state, Action const *last_action); ~WorldStateNode() = default; int requirements_unmet() const; WorldState state{}; WorldState open_requirements{}; Action const *last_action{nullptr}; ActorWorldState *context; }; struct WorldStateNodeHasher { static uint32_t hash(WorldStateNode const &state); }; extern bool operator==(WorldStateNode const &lhs, WorldStateNode const &rhs); extern bool operator!=(WorldStateNode const &lhs, WorldStateNode const &rhs); extern bool operator<(WorldStateNode const &lhs, WorldStateNode const &rhs); extern bool operator<=(WorldStateNode const &lhs, WorldStateNode const &rhs); extern bool operator>(WorldStateNode const &lhs, WorldStateNode const &rhs); extern bool operator>=(WorldStateNode const &lhs, WorldStateNode const &rhs); typedef gd::HashMap NodeMap; typedef gd::HashMap NodeScoreMap; /*! Uses A* to compute `Action` sequences for an actor to achieve `Goal`s. * * Set up available actions from the editor and call plan_for_goal with a desired goal. * Requires a sibling node inheriting from ActorWorldState called "ActorWorldState". */ class Planner : public gd::Node { GDCLASS(Planner, gd::Node); static void _bind_methods(); public: virtual void _enter_tree() override; //! Compute a plan to achieve the passed `Goal`. Plan plan_for_goal(gd::Ref goal); void set_actions(gd::Array array); gd::Array get_actions() const; void set_test_goal(gd::Ref goal); gd::Ref get_test_goal() const; private: //! \returns A vector of all actions that satisfy any of the requirements in `unsatisfied` gd::Vector get_neighbours(WorldStateNode const &from) const; //! \returns True if the passed action contributes to any of the open requirements in `node`. bool does_action_contribute(Action const *action, WorldStateNode const &node) const; //! \returns A plan starting with `start_node` traced backwards through the `from` map. Plan unroll_plan(WorldStateNode const &start_node, NodeMap const &from); private: gd::Ref test_goal{}; ActorWorldState *world_state{nullptr}; gd::Vector actions; }; } #endif // !GOAP_PLANNER_HPP