diff --git a/public/icons/status000.svg b/public/icons/status000.svg
new file mode 100644
index 0000000..4188e96
--- /dev/null
+++ b/public/icons/status000.svg
@@ -0,0 +1,40 @@
+
+
diff --git a/public/icons/status001.svg b/public/icons/status001.svg
new file mode 100644
index 0000000..9d0e44e
--- /dev/null
+++ b/public/icons/status001.svg
@@ -0,0 +1,50 @@
+
+
diff --git a/public/icons/status010.svg b/public/icons/status010.svg
new file mode 100644
index 0000000..cf6f4b7
--- /dev/null
+++ b/public/icons/status010.svg
@@ -0,0 +1,50 @@
+
+
diff --git a/public/icons/status011.svg b/public/icons/status011.svg
new file mode 100644
index 0000000..01f6e65
--- /dev/null
+++ b/public/icons/status011.svg
@@ -0,0 +1,60 @@
+
+
diff --git a/public/icons/status100.svg b/public/icons/status100.svg
new file mode 100644
index 0000000..2e55f91
--- /dev/null
+++ b/public/icons/status100.svg
@@ -0,0 +1,50 @@
+
+
diff --git a/public/icons/status101.svg b/public/icons/status101.svg
new file mode 100644
index 0000000..895a34a
--- /dev/null
+++ b/public/icons/status101.svg
@@ -0,0 +1,60 @@
+
+
diff --git a/public/icons/status110.svg b/public/icons/status110.svg
new file mode 100644
index 0000000..0a35fb0
--- /dev/null
+++ b/public/icons/status110.svg
@@ -0,0 +1,60 @@
+
+
diff --git a/public/icons/status111.svg b/public/icons/status111.svg
new file mode 100644
index 0000000..2184d91
--- /dev/null
+++ b/public/icons/status111.svg
@@ -0,0 +1,70 @@
+
+
diff --git a/src/components/register-node.ts b/src/components/register-node.ts
index bcfc817..605272d 100644
--- a/src/components/register-node.ts
+++ b/src/components/register-node.ts
@@ -201,6 +201,19 @@ G6.registerNode(
name: "node-icon",
});
+ // status
+ const status = ((cfg.status || 0) as number).toString(2).padStart(3, "0");
+ addShape("image", {
+ attrs: {
+ x: 204,
+ y: 3,
+ height: 18,
+ width: 18,
+ img: `./icons/status${status}.svg`,
+ },
+ name: "status-icon",
+ });
+
// name text
addShape("text", {
attrs: {
@@ -219,7 +232,7 @@ G6.registerNode(
if (cfg.debug) {
addShape("image", {
attrs: {
- x: 200,
+ x: 192,
y: 4,
height: 16,
width: 16,
@@ -232,7 +245,7 @@ G6.registerNode(
if (cfg.disabled) {
addShape("image", {
attrs: {
- x: 200 - (cfg.debug ? 18 : 0),
+ x: 200 - (cfg.debug ? 25 : 8),
y: 4,
height: 16,
width: 16,
diff --git a/src/misc/b3type.ts b/src/misc/b3type.ts
index a06af44..24ef9e5 100644
--- a/src/misc/b3type.ts
+++ b/src/misc/b3type.ts
@@ -15,6 +15,9 @@ export interface NodeArg {
default?: unknown;
options?: NodeArgOption[];
}
+
+type Status = "success" | "running" | "failure";
+
export interface NodeDef {
name: string;
type: NodeType;
@@ -25,6 +28,7 @@ export interface NodeDef {
doc?: string;
color?: string;
icon?: string;
+ status?: Exclude<`${Status}` | `!${Status}` | `?${Status}` | `&${Status}`, "!running">[];
}
export interface NodeModel {
@@ -65,6 +69,7 @@ export interface TreeGraphData extends G6TreeGraphData {
highlightInput?: boolean;
highlightOutput?: boolean;
highlightGray?: boolean;
+ status?: number;
}
export const unknownNodeDef: NodeDef = {
diff --git a/src/misc/b3util.ts b/src/misc/b3util.ts
index 0b31edd..e0cb0eb 100644
--- a/src/misc/b3util.ts
+++ b/src/misc/b3util.ts
@@ -207,16 +207,72 @@ export const createNode = (data: TreeGraphData, includeChildren: boolean = true)
return node;
};
+const toStatusFlag = (data: TreeGraphData) => {
+ let status = 0;
+ data.def.status?.forEach((s) => {
+ switch (s) {
+ case "success":
+ status |= 1 << 2;
+ break;
+ case "failure":
+ status |= 1 << 1;
+ break;
+ case "running":
+ status |= 1 << 0;
+ break;
+ }
+ });
+ return status;
+};
+
+const appendStatusFlag = (data: TreeGraphData, childStatus: number) => {
+ const childSuccess = (childStatus >> 2) & 1;
+ const childFailure = (childStatus >> 1) & 1;
+ const childRunning = (childStatus >> 0) & 1;
+ let status = data.status!;
+ if (data.def.status?.length) {
+ data.def.status?.forEach((s) => {
+ switch (s) {
+ case "!success":
+ status |= childFailure << 2;
+ break;
+ case "!failure":
+ status |= childSuccess << 1;
+ break;
+ case "?success":
+ status |= childSuccess << 2;
+ break;
+ case "?failure":
+ status |= childFailure << 1;
+ break;
+ case "?running":
+ status |= childRunning << 0;
+ break;
+ }
+ });
+ data.status = status;
+ } else {
+ data.status = status | childStatus;
+ }
+};
+
export const refreshTreeDataId = (data: TreeGraphData, id?: number) => {
if (!id) {
id = 1;
}
+ const status = toStatusFlag(data);
data.id = (id++).toString();
+ data.status = status;
if (data.children) {
+ let childStatus = 0;
data.children.forEach((child) => {
child.parent = data.id;
id = refreshTreeDataId(child, id);
+ if (child.status && !child.disabled) {
+ childStatus |= child.status;
+ }
});
+ appendStatusFlag(data, childStatus);
}
return id;
};
diff --git a/src/misc/template.ts b/src/misc/template.ts
index d9dd306..d83a40a 100644
--- a/src/misc/template.ts
+++ b/src/misc/template.ts
@@ -4,12 +4,14 @@ export const zhNodeDef = () => {
{
name: "AlwaysFail",
type: "Decorator",
+ status: ["failure", "?running"],
desc: "始终返回失败",
doc: "+ 只能有一个子节点,多个仅执行第一个\n+ 不管子节点是否成功都返回`失败`\n",
},
{
name: "AlwaysSuccess",
type: "Decorator",
+ status: ["success", "?running"],
desc: "始终返回成功",
doc: "+ 只能有一个子节点,多个仅执行第一个\n+ 不管子节点是否成功都返回`成功`\n",
},
@@ -17,6 +19,7 @@ export const zhNodeDef = () => {
name: "Assert",
type: "Decorator",
desc: "断言",
+ status: ["success"],
args: [
{
name: "message",
@@ -29,6 +32,7 @@ export const zhNodeDef = () => {
{
name: "Calculate",
type: "Action",
+ status: ["success"],
desc: "简单的数值公式计算",
args: [
{
@@ -43,6 +47,7 @@ export const zhNodeDef = () => {
{
name: "Check",
type: "Condition",
+ status: ["success", "failure"],
desc: "检查True或False",
args: [
{
@@ -56,12 +61,14 @@ export const zhNodeDef = () => {
{
name: "Clear",
type: "Action",
+ status: ["success"],
desc: "清除变量",
output: ["清除的变量名"],
},
{
name: "Concat",
type: "Action",
+ status: ["success", "failure"],
desc: "将两个输入合并为一个数组,并返回新数组",
input: ["数组1", "数组2"],
output: ["新数组"],
@@ -70,6 +77,7 @@ export const zhNodeDef = () => {
{
name: "Filter",
type: "Action",
+ status: ["success", "failure"],
desc: "返回满足条件的元素",
input: ["输入数组"],
output: ["变量", "新数组"],
@@ -78,6 +86,7 @@ export const zhNodeDef = () => {
{
name: "ForEach",
type: "Action",
+ status: ["success", "running", "failure"],
desc: "遍历数组",
input: ["数组"],
output: ["变量"],
@@ -86,6 +95,7 @@ export const zhNodeDef = () => {
{
name: "Includes",
type: "Condition",
+ status: ["success", "failure"],
desc: "判断元素是否在数组中",
input: ["数组", "元素"],
doc: "+ 若输入的元素不合法,返回`失败`\n+ 只有数组包含元素时返回`成功`,否则返回`失败`\n",
@@ -93,6 +103,7 @@ export const zhNodeDef = () => {
{
name: "Index",
type: "Action",
+ status: ["success", "failure"],
desc: "索引输入的数组或对象",
args: [
{
@@ -108,18 +119,21 @@ export const zhNodeDef = () => {
{
name: "Inverter",
type: "Decorator",
+ status: ["!success", "!failure", "?running"],
desc: "反转子节点运行结果",
doc: "+ 只能有一个子节点,多个仅执行第一个\n+ 当子节点返回`成功`时返回`失败`\n+ 当子节点返回`失败`时返回`成功`\n",
},
{
name: "IsNull",
type: "Condition",
+ status: ["success", "failure"],
desc: "判断变量是否不存在",
input: ["判断的变量"],
},
{
name: "IsStatus",
type: "Condition",
+ status: ["success", "failure"],
desc: "检查子节点状态",
args: [
{
@@ -147,6 +161,7 @@ export const zhNodeDef = () => {
{
name: "Let",
type: "Action",
+ status: ["success"],
desc: "定义新的变量名",
input: ["变量名"],
output: ["新变量名"],
@@ -154,6 +169,7 @@ export const zhNodeDef = () => {
{
name: "Listen",
type: "Decorator",
+ status: ["success"],
desc: "侦听事件",
input: ["目标对象?"],
output: ["事件参数?", "事件目标?"],
@@ -191,6 +207,7 @@ export const zhNodeDef = () => {
{
name: "Log",
type: "Action",
+ status: ["success"],
desc: "打印日志",
args: [
{
@@ -227,6 +244,7 @@ export const zhNodeDef = () => {
{
name: "Loop",
type: "Action",
+ status: ["success", "running", "failure"],
desc: "循环执行",
input: ["循环次数?"],
args: [
@@ -241,30 +259,35 @@ export const zhNodeDef = () => {
{
name: "NotNull",
type: "Condition",
+ status: ["success", "failure"],
desc: "判断变量是否存在",
input: ["判断的变量"],
},
{
name: "Now",
type: "Action",
+ status: ["success"],
desc: "获取当前时间",
output: ["当前时间"],
},
{
name: "Once",
type: "Decorator",
+ status: ["success", "failure", "?running"],
desc: "只执行一次",
doc: "+ 只能有一个子节点,多个仅执行第一个\n+ 第一次执行完全部子节点时返回`成功`,之后永远返回`失败`",
},
{
name: "Parallel",
type: "Composite",
+ status: ["success", "?running"],
desc: "并行执行",
doc: "+ 并行执行所有子节点\n+ 当有子节点返回`运行中`时,返回`运行中`状态\n+ 执行完所有子节点后,返回`成功`",
},
{
name: "Push",
type: "Action",
+ status: ["success", "failure"],
desc: "向数组中添加元素",
input: ["数组", "元素"],
doc: "+ 当变量`数组`不是数组类型时返回`失败`\n+ 其余返回`成功`\n",
@@ -272,6 +295,7 @@ export const zhNodeDef = () => {
{
name: "Random",
type: "Action",
+ status: ["success"],
desc: "返回一个随机数",
input: ["最小值?", "最大值?"],
args: [
@@ -296,6 +320,7 @@ export const zhNodeDef = () => {
{
name: "RandomIndex",
type: "Action",
+ status: ["success", "failure"],
desc: "随机返回输入的其中一个!",
input: ["输入目标"],
output: ["随机目标"],
@@ -304,6 +329,7 @@ export const zhNodeDef = () => {
{
name: "RepeatUntilFailure",
type: "Decorator",
+ status: ["!success", "failure", "?running"],
desc: "一直尝试直到子节点返回失败",
input: ["最大循环次数?"],
args: [
@@ -318,6 +344,7 @@ export const zhNodeDef = () => {
{
name: "RepeatUntilSuccess",
type: "Decorator",
+ status: ["?success", "failure", "?running"],
desc: "一直尝试直到子节点返回成功",
input: ["最大循环次数?"],
args: [
@@ -333,11 +360,13 @@ export const zhNodeDef = () => {
name: "Selector",
type: "Composite",
desc: "选择执行",
+ status: ["?success", "?failure", "?running"],
doc: "+ 一直往下执行,直到有子节点返回`成功`则返回`成功`\n+ 若全部节点返回`失败`则返回`失败`",
},
{
name: "Sequence",
type: "Composite",
+ status: ["?success", "?failure", "?running"],
desc: "顺序执行",
doc: "+ 一直往下执行,只有当所有子节点都返回`成功`, 才返回`成功`\n+ 若子节点返回`失败`,则直接返回`失败`状态\n+ 其余情况返回`运行中`状态\n",
},
@@ -345,6 +374,7 @@ export const zhNodeDef = () => {
name: "Timeout",
type: "Decorator",
desc: "超时",
+ status: ["?success", "?running", "failure"],
args: [
{
name: "time",
@@ -357,6 +387,7 @@ export const zhNodeDef = () => {
{
name: "Wait",
type: "Action",
+ status: ["success", "running"],
desc: "等待",
input: ["等待时间?"],
args: [