Teleport node works

This commit is contained in:
2026-04-25 21:55:21 +03:00
parent 2cff982473
commit 75ba39895f
4 changed files with 169 additions and 3 deletions

View File

@@ -23,6 +23,11 @@
* "checkValue" - Leaf check: blackboard comparison (name=key, params="op val")
* "blackboardDump" - Leaf: dumps entire blackboard to log
* "delay" - Leaf: waits for N seconds (params=seconds as float)
* "teleportToChild" - Leaf: teleports character to a named child entity
* of the Smart Object being interacted with.
* name = child entity name to teleport to.
* The character is positioned at the child's absolute
* world transform (position + orientation).
*/
struct BehaviorTreeNode {
Ogre::String type = "task";
@@ -63,7 +68,7 @@ struct BehaviorTreeNode {
type == "isAnimationEnded" || type == "setBit" ||
type == "checkBit" || type == "setValue" ||
type == "checkValue" || type == "blackboardDump" ||
type == "delay";
type == "delay" || type == "teleportToChild";
}
};

View File

@@ -1,8 +1,13 @@
#include "BehaviorTreeSystem.hpp"
#include "AnimationTreeSystem.hpp"
#include "SmartObjectSystem.hpp"
#include "../components/BehaviorTree.hpp"
#include "../components/ActionDatabase.hpp"
#include "../components/GoapBlackboard.hpp"
#include "../components/SmartObject.hpp"
#include "../components/Transform.hpp"
#include "../components/EntityName.hpp"
#include "../components/Relationship.hpp"
#include <OgreLogManager.h>
#include <iostream>
#include <cstdlib>
@@ -453,6 +458,140 @@ BehaviorTreeSystem::evaluateNode(const BehaviorTreeNode &node, flecs::entity e,
return Status::success;
}
/* --- Teleport to Smart Object child node --- */
if (node.type == "teleportToChild") {
if (isNewlyActive(state, &node)) {
Ogre::String childName = node.name;
if (childName.empty()) {
std::cout << "[BT] teleportToChild: no child "
"name specified"
<< std::endl;
return Status::failure;
}
// Get the SmartObjectSystem to find the current
// smart object target for this character
SmartObjectSystem *soSystem =
SmartObjectSystem::getInstance();
if (!soSystem) {
std::cout << "[BT] teleportToChild: "
"SmartObjectSystem not available"
<< std::endl;
return Status::failure;
}
// Find the smart object entity that this character
// is currently interacting with. We query the
// SmartObjectSystem's internal state by looking
// for the nearest smart object with a matching
// action in the character's blackboard.
flecs::entity smartObject = flecs::entity::null();
// Walk all SmartObjectComponent entities to find
// the one this character is executing
m_world.query<SmartObjectComponent, TransformComponent>()
.each([&](flecs::entity so,
SmartObjectComponent &soComp,
TransformComponent &) {
(void)soComp;
// Check if this character has an
// ActionDebug with a matching action
// name from this smart object
if (e.has<ActionDebug>()) {
auto &debug =
e.get<ActionDebug>();
for (const auto &an :
soComp.actionNames) {
if (an ==
debug.currentActionName) {
smartObject =
so;
return;
}
}
}
});
if (!smartObject.is_alive()) {
std::cout << "[BT] teleportToChild: no smart "
"object found for character "
<< e.id() << std::endl;
return Status::failure;
}
// Find the child entity by name under the smart
// object using Flecs' built-in child_of relationship
flecs::entity childEntity = flecs::entity::null();
smartObject.children([&](flecs::entity child) {
if (childEntity.is_alive())
return;
if (child.has<EntityNameComponent>()) {
auto &nameComp =
child.get<EntityNameComponent>();
if (nameComp.name == childName) {
childEntity = child;
}
}
});
if (!childEntity.is_alive()) {
std::cout << "[BT] teleportToChild: child '"
<< childName
<< "' not found under smart object "
<< smartObject.id() << std::endl;
return Status::failure;
}
// Get the child's absolute world transform
Ogre::Vector3 targetPos = Ogre::Vector3::ZERO;
Ogre::Quaternion targetRot = Ogre::Quaternion::IDENTITY;
if (childEntity.has<TransformComponent>()) {
auto &childTrans =
childEntity.get<TransformComponent>();
if (childTrans.node) {
targetPos =
childTrans.node
->_getDerivedPosition();
targetRot =
childTrans.node
->_getDerivedOrientation();
} else {
targetPos = childTrans.position;
targetRot = childTrans.rotation;
}
}
// Teleport the character to the target position
if (e.has<TransformComponent>()) {
auto &charTrans =
e.get_mut<TransformComponent>();
if (charTrans.node) {
charTrans.node->setPosition(targetPos);
charTrans.node->setOrientation(
targetRot);
}
charTrans.position = targetPos;
charTrans.rotation = targetRot;
charTrans.markChanged();
std::cout << "[BT] teleportToChild: teleported "
<< "character " << e.id()
<< " to child '" << childName
<< "' at (" << targetPos.x << ", "
<< targetPos.y << ", " << targetPos.z
<< ")" << std::endl;
} else {
std::cout << "[BT] teleportToChild: character "
<< e.id()
<< " has no TransformComponent"
<< std::endl;
return Status::failure;
}
}
return Status::success;
}
return Status::success;
}

View File

@@ -21,6 +21,8 @@ static ImU32 nodeTypeColorU32(const Ogre::String &type)
return IM_COL32(0xDD, 0x88, 0xFF, 0xFF);
if (type == "delay")
return IM_COL32(0xFF, 0xD7, 0x00, 0xFF);
if (type == "teleportToChild")
return IM_COL32(0xFF, 0x69, 0xB4, 0xFF);
return IM_COL32(0xFF, 0xFF, 0xFF, 0xFF);
}
@@ -192,6 +194,8 @@ void BehaviorTreeEditor::renderTree(BehaviorTreeNode &node,
queueAddChild(&node, "blackboardDump");
if (ImGui::MenuItem("Add Delay"))
queueAddChild(&node, "delay");
if (ImGui::MenuItem("Add Teleport To Child"))
queueAddChild(&node, "teleportToChild");
}
if (parent) {
size_t idx = findChildIndex(*parent, &node);
@@ -239,6 +243,8 @@ void BehaviorTreeEditor::renderTree(BehaviorTreeNode &node,
queueAddChild(&node, "blackboardDump");
if (ImGui::MenuItem("Delay"))
queueAddChild(&node, "delay");
if (ImGui::MenuItem("Teleport To Child"))
queueAddChild(&node, "teleportToChild");
ImGui::EndPopup();
}
}
@@ -289,7 +295,8 @@ void BehaviorTreeEditor::renderProperties(BehaviorTreeNode *node)
"setValue",
"checkValue",
"blackboardDump",
"delay" };
"delay",
"teleportToChild" };
int typeIdx = 0;
for (int i = 0; i < IM_ARRAYSIZE(types); i++) {
if (node->type == types[i]) {
@@ -330,6 +337,9 @@ void BehaviorTreeEditor::renderProperties(BehaviorTreeNode *node)
} else if (node->type == "blackboardDump") {
label = "Label";
hint = "Optional label prefix";
} else if (node->type == "teleportToChild") {
label = "Child Name";
hint = "Name of child entity under Smart Object to teleport to";
}
if (ImGui::InputText(label, buf, sizeof(buf)))

View File

@@ -90,6 +90,8 @@ static ImVec4 typeColorVec(const char *type)
return ImVec4(0.27f, 1.0f, 0.67f, 1.0f);
if (!strcmp(type, "delay"))
return ImVec4(1.0f, 0.84f, 0.0f, 1.0f);
if (!strcmp(type, "teleportToChild"))
return ImVec4(1.0f, 0.41f, 0.71f, 1.0f);
return ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
}
@@ -133,6 +135,12 @@ void InlineBehaviorTreeEditor::renderNode(BehaviorTreeNode &node,
.childIndex = node.children.size(),
.addType = "delay" });
}
if (ImGui::MenuItem("Add Teleport To Child")) {
queueOp(EditOp{ .type = EditOp::Add,
.parent = &node,
.childIndex = node.children.size(),
.addType = "teleportToChild" });
}
if (parent && ImGui::MenuItem("Remove")) {
for (size_t i = 0; i < parent->children.size(); i++) {
if (&parent->children[i] == &node) {
@@ -245,7 +253,8 @@ void InlineBehaviorTreeEditor::renderProps(BehaviorTreeNode *node)
"setValue",
"checkValue",
"blackboardDump",
"delay" };
"delay",
"teleportToChild" };
int current = 0;
for (int i = 0; i < IM_ARRAYSIZE(types); i++) {
if (node->type == types[i]) {
@@ -287,6 +296,9 @@ void InlineBehaviorTreeEditor::renderProps(BehaviorTreeNode *node)
} else if (node->type == "blackboardDump") {
label = "Label";
hint = "Optional label prefix";
} else if (node->type == "teleportToChild") {
label = "Child Name";
hint = "Name of child entity under Smart Object to teleport to";
}
if (ImGui::InputText(label, buf, sizeof(buf)))