feat: outlined planner, started implementation
This commit is contained in:
parent
6e55e5293f
commit
dfe6349c76
95
src/planner.cpp
Normal file
95
src/planner.cpp
Normal file
|
@ -0,0 +1,95 @@
|
|||
#include "planner.hpp"
|
||||
#include "action.hpp"
|
||||
#include "global_world_state.hpp"
|
||||
#include "godot_cpp/templates/pair.hpp"
|
||||
#include "utils/godot_macros.h"
|
||||
#include <godot_cpp/templates/hash_set.hpp>
|
||||
#include <godot_cpp/templates/hashfuncs.hpp>
|
||||
#include <godot_cpp/variant/vector3.hpp>
|
||||
|
||||
namespace godot::goap {
|
||||
void Planner::_bind_methods() {
|
||||
#define CLASSNAME Planner
|
||||
GDPROPERTY_HINTED(actions, Variant::OBJECT, PROPERTY_HINT_ARRAY_TYPE, "Action");
|
||||
}
|
||||
|
||||
void Planner::_ready() {
|
||||
this->global_world_state = GlobalWorldState::get_singleton();
|
||||
}
|
||||
|
||||
void Planner::_process(double delta_time) {
|
||||
this->cached_world_state.clear();
|
||||
}
|
||||
|
||||
Vector<State> Planner::make_plan(Goal *goal) {
|
||||
HashMap<PlannerNode, int, PlannerNodeHasher> open{};
|
||||
open.insert(PlannerNode{{}, goal->goal_state, nullptr}, 0);
|
||||
HashMap<PlannerNode, Ref<Action>, PlannerNodeHasher> from{};
|
||||
HashMap<PlannerNode, float, PlannerNodeHasher> score_to_start{};
|
||||
HashMap<PlannerNode, float, PlannerNodeHasher> heuristic_score{};
|
||||
|
||||
PlannerNode current;
|
||||
while(!open.is_empty()) {
|
||||
int current_weight = 0;
|
||||
for(KeyValue<PlannerNode, int> &kvp : open) {
|
||||
if(kvp.value > current_weight) {
|
||||
current = kvp.key;
|
||||
current_weight = kvp.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Variant Planner::get_world_property(StringName prop_key) {
|
||||
if(world_state_override.has(prop_key))
|
||||
return world_state_override.get(prop_key);
|
||||
if(prop_key.begins_with("g_"))
|
||||
return this->global_world_state->get_world_property(prop_key);
|
||||
if(this->cached_world_state.has(prop_key))
|
||||
return this->cached_world_state[prop_key];
|
||||
if(this->has_method("get_" + prop_key)) {
|
||||
Variant val = this->call(prop_key);
|
||||
this->cached_world_state[prop_key] = val;
|
||||
return val;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Planner::can_do(Ref<Action> action) {
|
||||
for(WorldProperty &prop : action->context_prerequisites) {
|
||||
if(this->get_world_property(prop.key) != prop.value)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector<Ref<Action>> Planner::find_actions_satisfying(WorldState requirements) {
|
||||
Vector<Ref<Action>> found_actions{};
|
||||
for(Ref<Action> &act : this->actions) {
|
||||
for(WorldProperty &prop : requirements) {
|
||||
if(act->effects.has(prop.key)
|
||||
&& act->effects.get(prop.key) == prop.value
|
||||
&& this->can_do(act))
|
||||
found_actions.push_back(act);
|
||||
}
|
||||
}
|
||||
return found_actions;
|
||||
}
|
||||
|
||||
void Planner::set_actions(Array value) {
|
||||
this->actions.clear();
|
||||
for(size_t i{0}; i < value.size(); ++i) {
|
||||
Ref<Action> act = value[i];
|
||||
if(act.is_valid())
|
||||
this->actions.push_back(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Array Planner::get_actions() const {
|
||||
Array array{};
|
||||
for(Ref<Action> const &act : this->actions) {
|
||||
array.push_back(act);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
93
src/planner.hpp
Normal file
93
src/planner.hpp
Normal file
|
@ -0,0 +1,93 @@
|
|||
#ifndef GOAP_PLANNER_HPP
|
||||
#define GOAP_PLANNER_HPP
|
||||
|
||||
#include "action.hpp"
|
||||
#include "godot_cpp/classes/object.hpp"
|
||||
#include "godot_cpp/variant/variant.hpp"
|
||||
#include <godot_cpp/classes/node.hpp>
|
||||
#include <godot_cpp/classes/resource.hpp>
|
||||
#include <godot_cpp/templates/vector.hpp>
|
||||
#include <godot_cpp/variant/string_name.hpp>
|
||||
|
||||
namespace godot {
|
||||
class CharacterActor;
|
||||
|
||||
namespace goap {
|
||||
class GlobalWorldState;
|
||||
|
||||
struct State {
|
||||
static State new_move_to(Vector3 location);
|
||||
static State new_animate(StringName animation);
|
||||
static State new_activate(Node *node);
|
||||
enum Type {
|
||||
STATE_MOVE_TO,
|
||||
STATE_ANIMATE,
|
||||
STATE_ACTIVATE
|
||||
};
|
||||
State::Type type;
|
||||
union {
|
||||
Vector3 move_to;
|
||||
StringName animate;
|
||||
Node *activate;
|
||||
};
|
||||
};
|
||||
|
||||
class Goal : public Resource {
|
||||
GDCLASS(Goal, Resource);
|
||||
static void _bind_methods();
|
||||
public:
|
||||
WorldState goal_state{};
|
||||
};
|
||||
|
||||
class Planner : public Node {
|
||||
GDCLASS(Planner, Node);
|
||||
static void _bind_methods();
|
||||
public:
|
||||
virtual void _ready() override;
|
||||
virtual void _process(double delta_time) override;
|
||||
|
||||
Vector<State> make_plan(Goal *goal);
|
||||
|
||||
Variant get_world_property(StringName prop_key);
|
||||
|
||||
bool can_do(Ref<Action> action);
|
||||
Vector<Ref<Action>> find_actions_satisfying(WorldState requirements);
|
||||
|
||||
void set_actions(Array actions);
|
||||
Array get_actions() const;
|
||||
private:
|
||||
CharacterActor *actor{nullptr};
|
||||
WorldState world_state_override{};
|
||||
WorldState cached_world_state{};
|
||||
GlobalWorldState *global_world_state{nullptr};
|
||||
|
||||
Vector<Ref<Action>> actions{};
|
||||
};
|
||||
|
||||
struct PlannerNode {
|
||||
WorldState state;
|
||||
WorldState open_requirements;
|
||||
Ref<Action> last_edge;
|
||||
|
||||
int heuristic_score(Ref<Action> action) {
|
||||
int score{0};
|
||||
return score;
|
||||
}
|
||||
};
|
||||
|
||||
struct PlannerNodeHasher {
|
||||
static _FORCE_INLINE_ uint32_t hash(godot::goap::PlannerNode const &node) {
|
||||
VariantHasher variant_hasher{};
|
||||
Variant a{1};
|
||||
variant_hasher.hash(a);
|
||||
}
|
||||
};
|
||||
|
||||
static _FORCE_INLINE_ bool operator==(PlannerNode const& lhs, PlannerNode const& rhs) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // !GOAP_PLANNER_HPP
|
Loading…
Reference in a new issue