Skip to content

Commit

Permalink
Merge pull request #106 from limbonaut/mapping
Browse files Browse the repository at this point in the history
Mapping variables
  • Loading branch information
limbonaut authored May 18, 2024
2 parents 00396dc + 7ab7a9d commit ff61d55
Show file tree
Hide file tree
Showing 25 changed files with 464 additions and 95 deletions.
101 changes: 91 additions & 10 deletions blackboard/blackboard_plan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

#include "blackboard_plan.h"

#include "../util/limbo_utility.h"

bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) {
String name_str = p_name;

Expand All @@ -25,6 +27,28 @@ bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) {
return true;
}

// * Mapping
if (name_str.begins_with("mapping/")) {
StringName mapped_var_name = name_str.get_slicec('/', 1);
StringName value = p_value;
bool properties_changed = false;
if (value == StringName()) {
if (parent_scope_mapping.has(mapped_var_name)) {
properties_changed = true;
parent_scope_mapping.erase(mapped_var_name);
}
} else {
if (!parent_scope_mapping.has(mapped_var_name)) {
properties_changed = true;
}
parent_scope_mapping[mapped_var_name] = value;
}
if (properties_changed) {
notify_property_list_changed();
}
return true;
}

// * Storage
if (name_str.begins_with("var/")) {
StringName var_name = name_str.get_slicec('/', 1);
Expand Down Expand Up @@ -56,15 +80,30 @@ bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const {

// * Editor
if (var_map.has(p_name)) {
r_ret = var_map[p_name].get_value();
if (has_mapping(p_name)) {
r_ret = "Mapped to " + LimboUtility::get_singleton()->decorate_var(parent_scope_mapping[p_name]);
} else {
r_ret = var_map[p_name].get_value();
}
return true;
}

// * Mapping
if (name_str.begins_with("mapping/")) {
StringName mapped_var_name = name_str.get_slicec('/', 1);
ERR_FAIL_COND_V(mapped_var_name == StringName(), false);
if (parent_scope_mapping.has(mapped_var_name)) {
r_ret = parent_scope_mapping[mapped_var_name];
} else {
r_ret = StringName();
}
return true;
}

// * Storage
if (!name_str.begins_with("var/")) {
return false;
}

StringName var_name = name_str.get_slicec('/', 1);
String what = name_str.get_slicec('/', 2);
ERR_FAIL_COND_V(!var_map.has(var_name), false);
Expand All @@ -90,29 +129,49 @@ void BlackboardPlan::_get_property_list(List<PropertyInfo> *p_list) const {

// * Editor
if (var.get_type() != Variant::NIL && (!is_derived() || !var_name.begins_with("_"))) {
p_list->push_back(PropertyInfo(var.get_type(), var_name, var.get_hint(), var.get_hint_string(), PROPERTY_USAGE_EDITOR));
if (has_mapping(var_name)) {
p_list->push_back(PropertyInfo(Variant::STRING, var_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY));
} else {
p_list->push_back(PropertyInfo(var.get_type(), var_name, var.get_hint(), var.get_hint_string(), PROPERTY_USAGE_EDITOR));
}
}

// * Storage
if (is_derived() && (!var.is_value_changed() || var.get_value() == base->var_map[var_name].get_value())) {
// Don't store variable if it's not modified in a derived plan.
// Variable is considered modified when it's marked as changed and its value is different from the base plan.
continue;
}

// * Storage
p_list->push_back(PropertyInfo(Variant::STRING, "var/" + var_name + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::INT, "var/" + var_name + "/type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(var.get_type(), "var/" + var_name + "/value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::INT, "var/" + var_name + "/hint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::STRING, "var/" + var_name + "/hint_string", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}

// * Mapping
if (is_mapping_enabled()) {
p_list->push_back(PropertyInfo(Variant::NIL, "Mapping", PROPERTY_HINT_NONE, "mapping/", PROPERTY_USAGE_GROUP));
for (const Pair<StringName, BBVariable> &p : var_list) {
// Serialize only non-empty mappings.
PropertyUsageFlags usage = has_mapping(p.first) ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_EDITOR;
p_list->push_back(PropertyInfo(Variant::STRING_NAME, "mapping/" + p.first, PROPERTY_HINT_NONE, "", usage));
}
}
}

bool BlackboardPlan::_property_can_revert(const StringName &p_name) const {
if (String(p_name).begins_with("mapping/")) {
return true;
}
return base.is_valid() && base->var_map.has(p_name);
}

bool BlackboardPlan::_property_get_revert(const StringName &p_name, Variant &r_property) const {
if (String(p_name).begins_with("mapping/")) {
r_property = StringName();
return true;
}
if (base->var_map.has(p_name)) {
r_property = base->var_map[p_name].get_value();
return true;
Expand All @@ -131,6 +190,15 @@ void BlackboardPlan::set_base_plan(const Ref<BlackboardPlan> &p_base) {
notify_property_list_changed();
}

void BlackboardPlan::set_parent_scope_plan_provider(const Callable &p_parent_scope_plan_provider) {
parent_scope_plan_provider = p_parent_scope_plan_provider;
notify_property_list_changed();
}

bool BlackboardPlan::has_mapping(const StringName &p_name) const {
return is_mapping_enabled() && parent_scope_mapping.has(p_name) && parent_scope_mapping[p_name] != StringName();
}

void BlackboardPlan::set_prefetch_nodepath_vars(bool p_enable) {
prefetch_nodepath_vars = p_enable;
emit_changed();
Expand Down Expand Up @@ -214,6 +282,11 @@ void BlackboardPlan::rename_var(const StringName &p_name, const StringName &p_ne
var_map.erase(p_name);
var_map.insert(p_new_name, var);

if (parent_scope_mapping.has(p_name)) {
parent_scope_mapping[p_new_name] = parent_scope_mapping[p_name];
parent_scope_mapping.erase(p_name);
}

notify_property_list_changed();
emit_changed();
}
Expand Down Expand Up @@ -335,12 +408,11 @@ inline void bb_add_var_dup_with_prefetch(const Ref<Blackboard> &p_blackboard, co
}
}

Ref<Blackboard> BlackboardPlan::create_blackboard(Node *p_node) {
Ref<Blackboard> BlackboardPlan::create_blackboard(Node *p_node, const Ref<Blackboard> &p_parent_scope) {
ERR_FAIL_COND_V(p_node == nullptr && prefetch_nodepath_vars, memnew(Blackboard));
Ref<Blackboard> bb = memnew(Blackboard);
for (const Pair<StringName, BBVariable> &p : var_list) {
bb_add_var_dup_with_prefetch(bb, p.first, p.second, prefetch_nodepath_vars, p_node);
}
bb->set_parent(p_parent_scope);
populate_blackboard(bb, true, p_node);
return bb;
}

Expand All @@ -351,6 +423,13 @@ void BlackboardPlan::populate_blackboard(const Ref<Blackboard> &p_blackboard, bo
continue;
}
bb_add_var_dup_with_prefetch(p_blackboard, p.first, p.second, prefetch_nodepath_vars, p_node);
if (parent_scope_mapping.has(p.first)) {
StringName target_var = parent_scope_mapping[p.first];
if (target_var != StringName()) {
ERR_CONTINUE_MSG(p_blackboard->get_parent() == nullptr, vformat("BlackboardPlan: Cannot link variable $%s to parent scope because the parent scope is not set.", p.first));
p_blackboard->link_var(p.first, p_blackboard->get_parent(), target_var);
}
}
}
}

Expand All @@ -362,7 +441,9 @@ void BlackboardPlan::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_base_plan"), &BlackboardPlan::get_base_plan);
ClassDB::bind_method(D_METHOD("is_derived"), &BlackboardPlan::is_derived);
ClassDB::bind_method(D_METHOD("sync_with_base_plan"), &BlackboardPlan::sync_with_base_plan);
ClassDB::bind_method(D_METHOD("create_blackboard", "node"), &BlackboardPlan::create_blackboard);
ClassDB::bind_method(D_METHOD("set_parent_scope_plan_provider", "callable"), &BlackboardPlan::set_parent_scope_plan_provider);
ClassDB::bind_method(D_METHOD("get_parent_scope_plan_provider"), &BlackboardPlan::get_parent_scope_plan_provider);
ClassDB::bind_method(D_METHOD("create_blackboard", "node", "parent_scope"), &BlackboardPlan::create_blackboard, DEFVAL(Ref<Blackboard>()));
ClassDB::bind_method(D_METHOD("populate_blackboard", "blackboard", "overwrite", "node"), &BlackboardPlan::populate_blackboard);

// To avoid cluttering the member namespace, we do not export unnecessary properties in this class.
Expand Down
15 changes: 14 additions & 1 deletion blackboard/blackboard_plan.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ class BlackboardPlan : public Resource {
// and only the values can be different in those variables.
Ref<BlackboardPlan> base;

// Mapping between variables in this plan and their parent scope names.
// Used for linking variables to their parent scope counterparts upon Blackboard creation/population.
HashMap<StringName, StringName> parent_scope_mapping;
// Fetcher function for the parent scope plan. Funtion should return a Ref<BlackboardPlan>.
// Used in the inspector. When set, mapping feature becomes available.
Callable parent_scope_plan_provider;

// If true, NodePath variables will be prefetched, so that the vars will contain node pointers instead (upon BB creation/population).
bool prefetch_nodepath_vars = true;

Expand All @@ -52,6 +59,12 @@ class BlackboardPlan : public Resource {
void set_base_plan(const Ref<BlackboardPlan> &p_base);
Ref<BlackboardPlan> get_base_plan() const { return base; }

void set_parent_scope_plan_provider(const Callable &p_parent_scope_plan_provider);
Callable get_parent_scope_plan_provider() const { return parent_scope_plan_provider; }

bool is_mapping_enabled() const { return parent_scope_plan_provider.is_valid() && (parent_scope_plan_provider.call() != Ref<BlackboardPlan>()); }
bool has_mapping(const StringName &p_name) const;

void set_prefetch_nodepath_vars(bool p_enable);
bool is_prefetching_nodepath_vars() const;

Expand All @@ -72,7 +85,7 @@ class BlackboardPlan : public Resource {
void sync_with_base_plan();
_FORCE_INLINE_ bool is_derived() const { return base.is_valid(); }

Ref<Blackboard> create_blackboard(Node *p_agent);
Ref<Blackboard> create_blackboard(Node *p_agent, const Ref<Blackboard> &p_parent_scope = Ref<Blackboard>());
void populate_blackboard(const Ref<Blackboard> &p_blackboard, bool overwrite, Node *p_node);

BlackboardPlan();
Expand Down
22 changes: 22 additions & 0 deletions bt/behavior_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,13 @@ void BehaviorTree::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) {
}

void BehaviorTree::set_root_task(const Ref<BTTask> &p_value) {
#ifdef TOOLS_ENABLED
_unset_editor_behavior_tree_hint();
#endif // TOOLS_ENABLED
root_task = p_value;
#ifdef TOOLS_ENABLED
_set_editor_behavior_tree_hint();
#endif // TOOLS_ENABLED
emit_changed();
}

Expand Down Expand Up @@ -85,6 +91,22 @@ void BehaviorTree::_plan_changed() {
emit_changed();
}

#ifdef TOOLS_ENABLED

void BehaviorTree::_set_editor_behavior_tree_hint() {
if (root_task.is_valid()) {
root_task->data.behavior_tree_id = this->get_instance_id();
}
}

void BehaviorTree::_unset_editor_behavior_tree_hint() {
if (root_task.is_valid()) {
root_task->data.behavior_tree_id = ObjectID();
}
}

#endif // TOOLS_ENABLED

void BehaviorTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_description", "description"), &BehaviorTree::set_description);
ClassDB::bind_method(D_METHOD("get_description"), &BehaviorTree::get_description);
Expand Down
5 changes: 5 additions & 0 deletions bt/behavior_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ class BehaviorTree : public Resource {

void _plan_changed();

#ifdef TOOLS_ENABLED
void _set_editor_behavior_tree_hint();
void _unset_editor_behavior_tree_hint();
#endif // TOOLS_ENABLED

protected:
static void _bind_methods();

Expand Down
2 changes: 1 addition & 1 deletion bt/bt_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ void BTState::set_behavior_tree(const Ref<BehaviorTree> &p_tree) {
p_tree->connect(LW_NAME(plan_changed), callable_mp(this, &BTState::_update_blackboard_plan));
}
behavior_tree = p_tree;
_update_blackboard_plan();
} else {
behavior_tree = p_tree;
}
_update_blackboard_plan();
}

void BTState::_update_blackboard_plan() {
Expand Down
20 changes: 20 additions & 0 deletions bt/tasks/bt_task.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "../../blackboard/blackboard.h"
#include "../../util/limbo_string_names.h"
#include "../../util/limbo_utility.h"
#include "../behavior_tree.h"
#include "bt_comment.h"

#ifdef LIMBOAI_MODULE
Expand Down Expand Up @@ -376,6 +377,22 @@ void BTTask::print_tree(int p_initial_tabs) {
}
}

#ifdef TOOLS_ENABLED

Ref<BehaviorTree> BTTask::editor_get_behavior_tree() {
BTTask *task = this;
while (task->data.behavior_tree_id.is_null() && task->get_parent().is_valid()) {
task = task->data.parent;
}
return Object::cast_to<BehaviorTree>(ObjectDB::get_instance(task->data.behavior_tree_id));
}

void BTTask::editor_set_behavior_tree(const Ref<BehaviorTree> &p_bt) {
data.behavior_tree_id = p_bt->get_instance_id();
}

#endif // TOOLS_ENABLED

void BTTask::_bind_methods() {
// Public Methods.
ClassDB::bind_method(D_METHOD("is_root"), &BTTask::is_root);
Expand All @@ -397,6 +414,9 @@ void BTTask::_bind_methods() {
ClassDB::bind_method(D_METHOD("print_tree", "initial_tabs"), &BTTask::print_tree, Variant(0));
ClassDB::bind_method(D_METHOD("get_task_name"), &BTTask::get_task_name);
ClassDB::bind_method(D_METHOD("abort"), &BTTask::abort);
#ifdef TOOLS_ENABLED
ClassDB::bind_method(D_METHOD("editor_get_behavior_tree"), &BTTask::editor_get_behavior_tree);
#endif // TOOLS_ENABLED

// Properties, setters and getters.
ClassDB::bind_method(D_METHOD("get_agent"), &BTTask::get_agent);
Expand Down
10 changes: 10 additions & 0 deletions bt/tasks/bt_task.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
using namespace godot;
#endif // LIMBOAI_GDEXTENSION

class BehaviorTree;

/**
* Base class for BTTask.
* Note: In order to properly return Status in the _tick virtual method (GDVIRTUAL1R...)
Expand Down Expand Up @@ -82,6 +84,9 @@ class BTTask : public BT {
Status status = FRESH;
double elapsed = 0.0;
bool display_collapsed = false;
#ifdef TOOLS_ENABLED
ObjectID behavior_tree_id;
#endif
} data;

Array _get_children() const;
Expand Down Expand Up @@ -162,6 +167,11 @@ class BTTask : public BT {

void print_tree(int p_initial_tabs = 0);

#ifdef TOOLS_ENABLED
Ref<BehaviorTree> editor_get_behavior_tree();
void editor_set_behavior_tree(const Ref<BehaviorTree> &p_bt);
#endif

BTTask();
~BTTask();
};
Expand Down
Loading

0 comments on commit ff61d55

Please sign in to comment.