Teleport node works
This commit is contained in:
@@ -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";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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)))
|
||||
|
||||
Reference in New Issue
Block a user