81 lines
3 KiB
C++
81 lines
3 KiB
C++
#ifndef GOAP_PLANNER_HPP
|
|
#define GOAP_PLANNER_HPP
|
|
|
|
#include "action.hpp"
|
|
#include "actor_world_state.hpp"
|
|
#include "goal.hpp"
|
|
#include <cstdint>
|
|
#include <godot_cpp/classes/node.hpp>
|
|
#include <godot_cpp/templates/vector.hpp>
|
|
#include <godot_cpp/variant/packed_int32_array.hpp>
|
|
|
|
namespace gd = godot;
|
|
|
|
namespace goap {
|
|
typedef gd::Vector<Action const *> 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<WorldStateNode, WorldStateNode, WorldStateNodeHasher> NodeMap;
|
|
typedef gd::HashMap<WorldStateNode, float, WorldStateNodeHasher> 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> goal);
|
|
|
|
void set_actions(gd::Array array);
|
|
gd::Array get_actions() const;
|
|
void set_test_goal(gd::Ref<Goal> goal);
|
|
gd::Ref<Goal> get_test_goal() const;
|
|
private:
|
|
//! \returns A vector of all actions that satisfy any of the requirements in `unsatisfied`
|
|
gd::Vector<Action const *> 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<Goal> test_goal{};
|
|
ActorWorldState *world_state{nullptr};
|
|
gd::Vector<Action const *> actions;
|
|
};
|
|
}
|
|
|
|
#endif // !GOAP_PLANNER_HPP
|