载入中...
搜索中...
未找到
BehaviorGraph.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <functional>
5#include <set>
6
8namespace {
9EditorDiagnostic diagnostic(const char* rule, std::string message) {
11 RuleId(rule), DiagnosticSeverity::Error,
12 std::move(message));
13}
14} // namespace
15
17 const GraphPinRecord& to) const {
19 if (from.direction != GraphPinDirection::Output || to.direction != GraphPinDirection::Input)
20 result.diagnostics.push_back(diagnostic("editor.behavior.pin-direction",
21 "Behavior edges must connect an output to an input"));
22 else if (from.node == to.node)
23 result.diagnostics.push_back(
24 diagnostic("editor.behavior.self-edge", "Behavior nodes cannot connect to themselves"));
25 else if (from.type != "flow" || to.type != "flow")
26 result.diagnostics.push_back(
27 diagnostic("editor.behavior.pin-type", "Behavior control edges require flow pins"));
28 result.allowed = result.diagnostics.empty();
29 return result;
30}
31
33 BehaviorCompileResult result; result.documentRevision = graph.revision;
34 if (graph.domain != domain()) {
35 result.diagnostics.push_back(
36 diagnostic("editor.behavior.domain", "Graph domain must be behavior")); return result;
37 }
38 std::map<GraphNodeId, const GraphNodeRecord*> nodes;
39 std::map<GraphPinId, GraphNodeId> pins;
40 std::vector<GraphNodeId> roots;
41 static const std::set<std::string> supported{"root", "sequence", "selector", "condition", "action"};
42 for (const auto& node : graph.nodes) {
43 if (!nodes.emplace(node.id, &node).second)
44 result.diagnostics.push_back(
45 diagnostic("editor.behavior.duplicate-node", "Behavior node ids must be unique"));
46 if (!supported.contains(node.type))
47 result.diagnostics.push_back(diagnostic("editor.behavior.unsupported-node",
48 "Unsupported behavior node type: " + node.type));
49 if (node.type == "root") roots.push_back(node.id);
50 for (const auto& pin : node.pins) pins.emplace(pin.id, node.id);
51 }
52 if (roots.size() != 1)
53 result.diagnostics.push_back(
54 diagnostic("editor.behavior.root-count", "Behavior graph requires exactly one root"));
55 std::map<GraphNodeId, std::vector<GraphNodeId>> children;
56 std::map<GraphNodeId, int> parents;
57 for (const auto& edge : graph.edges) {
58 const auto from = pins.find(edge.from), to = pins.find(edge.to);
59 if (from == pins.end() || to == pins.end()) {
60 result.diagnostics.push_back(diagnostic("editor.behavior.dangling-edge",
61 "Behavior edge references a missing pin")); continue;
62 }
63 children[from->second].push_back(to->second); ++parents[to->second];
64 }
65 for (const auto& [node, count] : parents) if (count > 1)
66 result.diagnostics.push_back(diagnostic("editor.behavior.multiple-parents",
67 "Behavior node " + node.value() + " has multiple parents"));
68 if (!roots.empty() && parents[roots.front()] != 0)
69 result.diagnostics.push_back(
70 diagnostic("editor.behavior.root-parent", "Behavior root cannot have a parent"));
71 std::set<GraphNodeId> visiting, visited;
72 std::function<void(const GraphNodeId&)> visit = [&](const GraphNodeId& id) {
73 if (visiting.contains(id)) {
74 result.diagnostics.push_back(
75 diagnostic("editor.behavior.cycle", "Behavior graph contains a cycle")); return;
76 }
77 if (visited.contains(id)) return;
78 visiting.insert(id);
79 for (const auto& child : children[id]) visit(child);
80 visiting.erase(id); visited.insert(id);
81 };
82 if (!roots.empty()) visit(roots.front());
83 for (const auto& [id, node] : nodes) {
84 if (!visited.contains(id)) result.diagnostics.push_back(diagnostic(
85 "editor.behavior.unreachable-node",
86 "Behavior node " + id.value() + " is unreachable from root"));
87 const std::size_t count = children[id].size();
88 if ((node->type == "root" && count != 1) ||
89 ((node->type == "condition" || node->type == "action") && count != 0) ||
90 ((node->type == "sequence" || node->type == "selector") && count == 0))
91 result.diagnostics.push_back(diagnostic(
92 "editor.behavior.child-count",
93 "Behavior node has an invalid number of children: " + id.value()));
94 }
95 if (std::any_of(result.diagnostics.begin(), result.diagnostics.end(), [](const EditorDiagnostic& diagnostic) {
96 return diagnostic.severity() == DiagnosticSeverity::Error;
97 })) return result;
98 result.root = roots.front();
99 std::function<void(const GraphNodeId&)> emit = [&](const GraphNodeId& id) {
100 const auto* node = nodes.at(id);
101 result.instructions.push_back({id, node->type, node->properties, children[id]});
102 for (const auto& child : children[id]) emit(child);
103 };
104 emit(result.root); result.status = EditorStatus::Applied; return result;
105}
106
107} // namespace eve::npc_ai_editing
double value
std::string from
std::string message
HexCoordinates to
Cell the unit walks towards on this segment.
Definition HexUnits.cpp:64
std::vector< BvhNode > nodes
std::map< std::string, std::vector< std::string > > graph
Definition Package.cpp:59
std::string id
Definition PlayHost.cpp:108
const RoadNode * node
const RoadEdge * edge
std::uint32_t count
int children
Definition TreeMesh.cpp:295
std::set< std::string > visiting
A structured explanation of a failed, degraded, or noteworthy result.
Definition Diagnostic.h:94
BehaviorCompileResult compile(const GraphDocumentData &graph) const
Validate and compile root/sequence/selector/condition/action nodes.
GraphConnectionDecision canConnect(const GraphPinRecord &from, const GraphPinRecord &to) const override
Can connect.
std::string domain() const override
Domain.
Diagnostic ruleDiagnostic(eve::DiagnosticCode code, RuleId rule, DiagnosticSeverity severity, std::string message)
Build a common diagnostic carrying an open editing rule identity.
Result of domain-specific connection validation.
std::vector< Diagnostic > diagnostics
Immutable graph document value sent to a domain compiler.
Stable typed pin owned by a graph node.
Immutable compiled behavior graph with source revision.
std::vector< BehaviorInstruction > instructions
std::vector< EditorDiagnostic > diagnostics