2022-03-21 09:27:37 +00:00
|
|
|
import { ComAttackable } from "../ECS/components/ComAttackable";
|
2022-03-22 07:35:41 +00:00
|
|
|
import { ComBeAttacked } from "../ECS/components/ComBeAttacked";
|
2022-03-21 09:27:37 +00:00
|
|
|
import { ComCocosNode } from "../ECS/components/ComCocosNode";
|
|
|
|
import { ComMonitor } from "../ECS/components/ComMonitor";
|
|
|
|
import { ComMovable } from "../ECS/components/ComMovable";
|
|
|
|
import { ComRoleConfig } from "../ECS/components/ComRoleConfig";
|
|
|
|
import { ComTransform } from "../ECS/components/ComTransform";
|
|
|
|
import { ECSWorld } from "../ECS/lib/ECSWorld";
|
|
|
|
import { SysBehaviorTree } from "../ECS/systems/SysBehaviorTree";
|
|
|
|
import { EventAttack, EventHPChange, EventHurt } from "../Struct/NodeEvent";
|
|
|
|
export namespace BT {
|
|
|
|
|
|
|
|
export class BlackBoard {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
export class ExecuteContext {
|
|
|
|
executor: SysBehaviorTree;
|
|
|
|
world: ECSWorld;
|
|
|
|
bb: BlackBoard;
|
|
|
|
|
|
|
|
entity: number;
|
|
|
|
dt: number;
|
|
|
|
public init(executor: SysBehaviorTree, world: ECSWorld) {
|
|
|
|
this.executor = executor;
|
|
|
|
this.world = world;
|
|
|
|
}
|
|
|
|
|
|
|
|
public set(entity: number, dt: number, bb: BlackBoard) {
|
|
|
|
this.entity = entity;
|
|
|
|
this.dt = dt;
|
|
|
|
this.bb = bb;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** 节点状态 */
|
|
|
|
export enum NodeState {
|
|
|
|
Executing, // 执行中
|
|
|
|
Success, // 成功
|
|
|
|
Fail // 失败
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 节点类型 */
|
|
|
|
export enum NodeType {
|
|
|
|
// 组合节点
|
2022-03-22 07:35:41 +00:00
|
|
|
LockedSequence, // 锁状态顺序节点
|
|
|
|
Sequence, //
|
2022-03-21 09:27:37 +00:00
|
|
|
Selector, // 选择节点
|
|
|
|
RandomSelector, // 随机选择节点
|
|
|
|
Parallel, // 并行节点
|
|
|
|
|
|
|
|
// 修饰节点
|
|
|
|
Inverter, // 逆变节点
|
|
|
|
Success, // 成功节点
|
|
|
|
Fail, // 失败节点
|
|
|
|
Repeater, // 重复节点
|
|
|
|
RetryTillSuccess, // 重复直到成功
|
|
|
|
|
|
|
|
// 叶子结点
|
|
|
|
Wait, // 等待
|
|
|
|
Action,
|
|
|
|
WalkToPos,
|
|
|
|
WalkToRandomPos, //
|
|
|
|
WalkToTarget,
|
|
|
|
Monitor, // 监视
|
|
|
|
Attack,
|
|
|
|
|
2022-03-22 07:35:41 +00:00
|
|
|
EnoughAttr, // 足够的属性
|
|
|
|
WillBeAttacked, // 即将收到攻击
|
|
|
|
Avoid, // 闪避
|
|
|
|
RunAway, // 逃跑
|
|
|
|
InAttacking
|
|
|
|
|
2022-03-21 09:27:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export class NodeBase {
|
|
|
|
public type: NodeType;
|
|
|
|
public state: NodeState = NodeState.Success;
|
|
|
|
|
|
|
|
constructor(type: NodeType) {
|
|
|
|
this.type = type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** 组合节点 */
|
|
|
|
class CombineNode extends NodeBase {
|
|
|
|
public children: NodeBase[] = [];
|
|
|
|
|
|
|
|
public constructor(type: NodeType, children: NodeBase[]) {
|
|
|
|
super(type);
|
|
|
|
this.children = children;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class SequenceNode extends CombineNode {
|
|
|
|
public currIdx = 0;
|
|
|
|
public ignoreFailure = false;
|
|
|
|
|
|
|
|
constructor(children: NodeBase[], ignoreFailture = false) {
|
|
|
|
super(NodeType.Sequence, children);
|
|
|
|
this.ignoreFailure = ignoreFailture;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-22 07:35:41 +00:00
|
|
|
/** 依次执行子节点, 遇到执行失败的则退出并返回失败, 全部执行成功则返回成功 */
|
|
|
|
export class LockedSequenceNode extends CombineNode {
|
|
|
|
public currIdx = 0;
|
|
|
|
public ignoreFailure = false;
|
|
|
|
|
|
|
|
constructor(children: NodeBase[], ignoreFailture = false) {
|
|
|
|
super(NodeType.LockedSequence, children);
|
|
|
|
this.ignoreFailure = ignoreFailture;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-21 09:27:37 +00:00
|
|
|
/** 依次执行子节点, 遇到执行成功的则退出并返回成功, 全部执行失败则返回失败 */
|
|
|
|
export class SelectorNode extends CombineNode {
|
|
|
|
public currIdx:number = -1;
|
|
|
|
|
|
|
|
constructor(children: NodeBase[], ) {
|
|
|
|
super(NodeType.Selector, children);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** 根据权重随机选择执行某个子节点 */
|
|
|
|
export class RandomSelectorNode extends CombineNode {
|
|
|
|
public weights: number[]; // 权重
|
|
|
|
public currIdx = -1; // 选中的节点
|
|
|
|
constructor(children: NodeBase[], weigets?: number[]) {
|
|
|
|
super(NodeType.RandomSelector, children);
|
|
|
|
this.weights = weigets ? weigets : new Array(children.length).fill(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** 并行执行所有子节点, 全部执行完毕后返回 */
|
|
|
|
export class ParallelNode extends CombineNode {
|
|
|
|
public ignoreFailture = true;
|
|
|
|
constructor(children: NodeBase[], ignoreFailture: boolean) {
|
|
|
|
super(NodeType.Parallel, children);
|
|
|
|
this.ignoreFailture = ignoreFailture;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** 修饰节点 */
|
|
|
|
class DecoratorNode extends NodeBase {
|
|
|
|
public child: NodeBase = null;
|
|
|
|
public constructor(type: NodeType, child: NodeBase) {
|
|
|
|
super(type);
|
|
|
|
this.child = child;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** 返回子节点执行结果的取反值 */
|
|
|
|
export class InverterNode extends DecoratorNode {
|
|
|
|
constructor(child: NodeBase) {
|
|
|
|
super(NodeType.Inverter, child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** 子节点执行完毕后, 必定返回成功 */
|
|
|
|
export class SuccessNode extends DecoratorNode {
|
|
|
|
constructor(child: NodeBase) {
|
|
|
|
super(NodeType.Success, child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** 子节点执行完毕后, 必定返回失败 */
|
|
|
|
export class FailNode extends DecoratorNode {
|
|
|
|
constructor(child: NodeBase) {
|
|
|
|
super(NodeType.Fail, child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** 子节点执行重复repeatCount次后返回成功 */
|
|
|
|
export class RepeaterNode extends DecoratorNode {
|
|
|
|
public repeatCount = 1;
|
|
|
|
public currRepeatCount = 0;
|
|
|
|
public mustSuccess = false; // 子节点必须支持成功才增加重复次数
|
|
|
|
constructor(child: NodeBase, repeatCount: number, mustSuccess = false) {
|
|
|
|
super(NodeType.Repeater, child);
|
|
|
|
this.repeatCount = repeatCount;
|
|
|
|
this.mustSuccess = mustSuccess;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** 子节点重复执行直到返回成功 */
|
|
|
|
export class RetryTillSuccess extends DecoratorNode {
|
|
|
|
timeout: number; // 超时时间
|
|
|
|
countDown: number; // 剩余时间
|
|
|
|
constructor(child: NodeBase, timeout:number) {
|
|
|
|
super(NodeType.RetryTillSuccess, child);
|
|
|
|
this.timeout = timeout;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 叶子结点 */
|
|
|
|
export class WaitNode extends NodeBase {
|
|
|
|
public waitSeconds:number;
|
|
|
|
public countDown:number;
|
|
|
|
|
|
|
|
constructor(seconds:number) {
|
|
|
|
super(NodeType.Wait);
|
|
|
|
this.waitSeconds = seconds;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** 移动到目标位置后 返回成功 */
|
|
|
|
export class WalkToPosNode extends NodeBase {
|
|
|
|
public speed:number;
|
|
|
|
public targetPos: cc.Vec2;
|
|
|
|
constructor(speed: number, pos: cc.Vec2) {
|
|
|
|
super(NodeType.WalkToPos);
|
|
|
|
this.speed = speed;
|
|
|
|
this.targetPos = pos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class WalkToRandomPosNode extends NodeBase {
|
|
|
|
public speed: number;
|
|
|
|
public size: cc.Size;
|
|
|
|
constructor(speed: number, size: cc.Size) {
|
|
|
|
super(NodeType.WalkToRandomPos);
|
|
|
|
this.speed = speed;
|
|
|
|
this.size = size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class WalkToTargetNode extends NodeBase {
|
|
|
|
public speed: number;
|
|
|
|
constructor(speed: number) {
|
|
|
|
super(NodeType.WalkToTarget);
|
|
|
|
this.speed = speed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class MonitorNode extends NodeBase {
|
|
|
|
constructor() {
|
|
|
|
super(NodeType.Monitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class AttackNode extends NodeBase {
|
2022-03-22 07:35:41 +00:00
|
|
|
constructor() {
|
2022-03-21 09:27:37 +00:00
|
|
|
super(NodeType.Attack);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class EnoughAttrNode extends NodeBase {
|
|
|
|
public com: {prototype: any};
|
|
|
|
public attr: string;
|
|
|
|
public value: number;
|
|
|
|
constructor(com: {prototype: any}, attr: string, value: number) {
|
|
|
|
super(NodeType.EnoughAttr);
|
|
|
|
this.com = com;
|
|
|
|
this.attr = attr;
|
|
|
|
this.value = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-22 07:35:41 +00:00
|
|
|
export class WillBeAttackedNode extends NodeBase {
|
|
|
|
constructor() {
|
|
|
|
super(NodeType.WillBeAttacked);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class AvoidNode extends NodeBase {
|
|
|
|
public speed: number;
|
|
|
|
constructor(speed: number) {
|
|
|
|
super(NodeType.Avoid);
|
|
|
|
this.speed = speed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class RunAwayNode extends NodeBase {
|
|
|
|
constructor() {
|
|
|
|
super(NodeType.RunAway);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class InAttackingNode extends NodeBase {
|
|
|
|
constructor() {
|
|
|
|
super(NodeType.InAttacking);
|
2022-03-21 09:27:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class NodeHandler {
|
|
|
|
onEnter:(node: NodeBase, context: ExecuteContext) => void;
|
|
|
|
onUpdate:(node: NodeBase, context: ExecuteContext) => void;
|
|
|
|
}
|
|
|
|
|
|
|
|
export const NodeHandlers : NodeHandler[] = [];
|
|
|
|
|
|
|
|
/** Sequence node */
|
|
|
|
NodeHandlers[NodeType.Sequence] = {
|
|
|
|
onEnter(node: SequenceNode, context: ExecuteContext) : void {
|
|
|
|
node.currIdx = 0;
|
|
|
|
context.executor.onEnterBTNode(node.children[node.currIdx], context);
|
|
|
|
node.state = NodeState.Executing;
|
|
|
|
},
|
|
|
|
onUpdate(node: SequenceNode, context: ExecuteContext) : void {
|
2022-03-22 07:35:41 +00:00
|
|
|
|
|
|
|
if(node.currIdx < 0 || node.currIdx >= node.children.length) {
|
|
|
|
// 越界了, 不应该发生, 直接认为是失败了
|
|
|
|
node.state = NodeState.Fail;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 检查前置条件是否满足
|
|
|
|
for(let i=0; i<node.currIdx; i++) {
|
|
|
|
context.executor.updateBTNode(node.children[i], context);
|
|
|
|
if(node.children[i].state !== NodeState.Success) return;
|
|
|
|
}
|
|
|
|
|
|
|
|
context.executor.updateBTNode(node.children[node.currIdx], context);
|
|
|
|
let state = node.children[node.currIdx].state;
|
|
|
|
if(state == NodeState.Executing) return;
|
|
|
|
|
|
|
|
if(state === NodeState.Fail && !node.ignoreFailure) {
|
|
|
|
node.state = NodeState.Fail;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(state === NodeState.Success && node.currIdx == node.children.length-1) {
|
|
|
|
node.state = NodeState.Success;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
context.executor.onEnterBTNode(node.children[++node.currIdx], context);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** LockedSequence node */
|
|
|
|
NodeHandlers[NodeType.LockedSequence] = {
|
|
|
|
onEnter(node: LockedSequenceNode, context: ExecuteContext) : void {
|
|
|
|
node.currIdx = 0;
|
|
|
|
context.executor.onEnterBTNode(node.children[node.currIdx], context);
|
|
|
|
node.state = NodeState.Executing;
|
|
|
|
},
|
|
|
|
onUpdate(node: LockedSequenceNode, context: ExecuteContext) : void {
|
2022-03-21 09:27:37 +00:00
|
|
|
if(node.state !== NodeState.Executing) return ;
|
|
|
|
if(node.currIdx < 0 || node.currIdx >= node.children.length) {
|
|
|
|
// 越界了, 不应该发生, 直接认为是失败了
|
|
|
|
node.state = NodeState.Fail;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
context.executor.updateBTNode(node.children[node.currIdx], context);
|
|
|
|
let state = node.children[node.currIdx].state;
|
|
|
|
if(state == NodeState.Executing) return;
|
|
|
|
|
|
|
|
if(state === NodeState.Fail && !node.ignoreFailure) {
|
|
|
|
node.state = NodeState.Fail;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(state === NodeState.Success && node.currIdx == node.children.length-1) {
|
|
|
|
node.state = NodeState.Success;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
context.executor.onEnterBTNode(node.children[++node.currIdx], context);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Selector node */
|
|
|
|
NodeHandlers[NodeType.Selector] = {
|
|
|
|
onEnter(node: SelectorNode, context: ExecuteContext) : void {
|
|
|
|
node.currIdx = 0;
|
|
|
|
context.executor.onEnterBTNode(node.children[node.currIdx], context);
|
|
|
|
node.state = NodeState.Executing;
|
|
|
|
},
|
|
|
|
onUpdate(node: SelectorNode, context: ExecuteContext) : void {
|
|
|
|
if(node.state !== NodeState.Executing) return ;
|
|
|
|
if(node.currIdx < 0 || node.currIdx >= node.children.length) {
|
|
|
|
// 越界了, 认为是失败了
|
|
|
|
node.state = NodeState.Fail;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
context.executor.updateBTNode(node.children[node.currIdx], context);
|
|
|
|
let state = node.children[node.currIdx].state;
|
|
|
|
if(state == NodeState.Executing) return;
|
|
|
|
|
|
|
|
// 执行到最后一个都失败了, 那边selector失败了
|
|
|
|
if(state === NodeState.Fail && node.currIdx == node.children.length-1) {
|
|
|
|
node.state = NodeState.Fail;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(state == NodeState.Success) {
|
|
|
|
node.state = NodeState.Success;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
context.executor.onEnterBTNode(node.children[++node.currIdx], context);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Selector node */
|
|
|
|
NodeHandlers[NodeType.RandomSelector] = {
|
|
|
|
onEnter(node: RandomSelectorNode, context: ExecuteContext) : void {
|
|
|
|
// 根据权重随机获取idx
|
|
|
|
let totalWeight = 0;
|
|
|
|
for(const weight of node.weights) {
|
|
|
|
totalWeight += weight;
|
|
|
|
}
|
|
|
|
let randomWeight = Math.random() * totalWeight;
|
|
|
|
for(let i=0; i<node.weights.length; i++) {
|
|
|
|
randomWeight -= node.weights[i];
|
|
|
|
if(randomWeight <= 0) {
|
|
|
|
node.currIdx = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
context.executor.onEnterBTNode(node.children[node.currIdx], context);
|
|
|
|
node.state = NodeState.Executing;
|
|
|
|
},
|
|
|
|
onUpdate(node: RandomSelectorNode, context: ExecuteContext) : void {
|
|
|
|
if(node.state !== NodeState.Executing) return ;
|
|
|
|
let n = node.children[node.currIdx];
|
|
|
|
context.executor.updateBTNode(n, context);
|
|
|
|
node.state = n.state;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Parallel node */
|
|
|
|
NodeHandlers[NodeType.Parallel] = {
|
|
|
|
onEnter(node: ParallelNode, context: ExecuteContext) : void {
|
|
|
|
for(const n of node.children) {
|
|
|
|
context.executor.onEnterBTNode(n, context);
|
|
|
|
}
|
|
|
|
node.state = NodeState.Executing;
|
|
|
|
},
|
|
|
|
onUpdate(node: ParallelNode, context: ExecuteContext) : void {
|
|
|
|
if(node.state !== NodeState.Executing) return ;
|
|
|
|
let end = true;
|
|
|
|
for(const child of node.children) {
|
|
|
|
context.executor.updateBTNode(child, context);
|
|
|
|
if(child.state === NodeState.Executing) {
|
|
|
|
end = false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(child.state == NodeState.Fail) {
|
|
|
|
node.state = NodeState.Fail;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(end) {
|
|
|
|
node.state = NodeState.Success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Inverter node */
|
|
|
|
NodeHandlers[NodeType.Inverter] = {
|
|
|
|
onEnter(node: InverterNode, context: ExecuteContext) : void {
|
|
|
|
context.executor.onEnterBTNode(node.child, context);
|
|
|
|
node.state = NodeState.Executing;
|
|
|
|
},
|
|
|
|
onUpdate(node: InverterNode, context: ExecuteContext) : void {
|
|
|
|
context.executor.updateBTNode(node.child, context);
|
|
|
|
if(node.child.state === NodeState.Executing) return ;
|
|
|
|
if(node.child.state == NodeState.Success) node.state = NodeState.Fail;
|
|
|
|
if(node.child.state == NodeState.Fail) node.state = NodeState.Success;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Success node */
|
|
|
|
NodeHandlers[NodeType.Success] = {
|
|
|
|
onEnter(node: SuccessNode, context: ExecuteContext) : void {
|
|
|
|
context.executor.onEnterBTNode(node.child, context);
|
|
|
|
node.state = NodeState.Executing;
|
|
|
|
},
|
|
|
|
onUpdate(node: SuccessNode, context: ExecuteContext) : void {
|
|
|
|
if(node.state !== NodeState.Executing) return ;
|
|
|
|
context.executor.updateBTNode(node.child, context);
|
|
|
|
if(node.child.state === NodeState.Executing) return ;
|
|
|
|
node.state = NodeState.Success;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Fail node */
|
|
|
|
NodeHandlers[NodeType.Fail] = {
|
|
|
|
onEnter(node: FailNode, context: ExecuteContext) : void {
|
|
|
|
context.executor.onEnterBTNode(node.child, context);
|
|
|
|
node.state = NodeState.Executing;
|
|
|
|
},
|
|
|
|
onUpdate(node: FailNode, context: ExecuteContext) : void {
|
|
|
|
if(node.state !== NodeState.Executing) return ;
|
|
|
|
context.executor.updateBTNode(node.child, context);
|
|
|
|
if(node.child.state === NodeState.Executing) return ;
|
|
|
|
node.state = NodeState.Fail;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Repeater node */
|
|
|
|
NodeHandlers[NodeType.Repeater] = {
|
|
|
|
onEnter(node: RepeaterNode, context: ExecuteContext) : void {
|
|
|
|
node.currRepeatCount = 0;
|
|
|
|
context.executor.onEnterBTNode(node.child, context);
|
|
|
|
node.state = NodeState.Executing;
|
|
|
|
},
|
|
|
|
onUpdate(node: RepeaterNode, context: ExecuteContext) : void {
|
|
|
|
if(node.state !== NodeState.Executing) return ;
|
|
|
|
context.executor.updateBTNode(node.child, context);
|
|
|
|
if(node.child.state === NodeState.Executing) return ;
|
|
|
|
if(!node.mustSuccess || node.child.state == NodeState.Success) node.currRepeatCount ++;
|
|
|
|
if(node.currRepeatCount >= node.repeatCount) {
|
|
|
|
node.state = NodeState.Success;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
context.executor.onEnterBTNode(node.child, context);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/** RetryTillSuccess node */
|
|
|
|
NodeHandlers[NodeType.RetryTillSuccess] = {
|
|
|
|
onEnter(node: RetryTillSuccess, context: ExecuteContext) : void {
|
|
|
|
node.countDown = node.timeout;
|
|
|
|
context.executor.onEnterBTNode(node.child, context);
|
|
|
|
node.state = NodeState.Executing;
|
|
|
|
},
|
|
|
|
|
|
|
|
onUpdate(node: RetryTillSuccess, context: ExecuteContext) : void {
|
|
|
|
if(node.state !== NodeState.Executing) return ;
|
|
|
|
node.countDown -= context.dt;
|
|
|
|
|
|
|
|
context.executor.updateBTNode(node.child, context);
|
|
|
|
if(node.child.state === NodeState.Executing) return ;
|
|
|
|
|
|
|
|
if(node.child.state == NodeState.Success) {
|
|
|
|
node.state = NodeState.Success;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(node.countDown > 0) {
|
|
|
|
context.executor.onEnterBTNode(node.child, context);
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
node.state = NodeState.Fail;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/** Wait node */
|
|
|
|
NodeHandlers[NodeType.Wait] = {
|
|
|
|
onEnter(node: WaitNode, context: ExecuteContext) : void {
|
|
|
|
node.countDown = node.waitSeconds;
|
|
|
|
node.state = NodeState.Executing;
|
|
|
|
},
|
|
|
|
onUpdate(node: WaitNode, context: ExecuteContext) : void {
|
|
|
|
if(node.state !== NodeState.Executing) return ;
|
|
|
|
node.countDown -= context.dt;
|
|
|
|
if(node.countDown <= 0) {
|
|
|
|
node.state = NodeState.Success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Wait node */
|
|
|
|
NodeHandlers[NodeType.WalkToPos] = {
|
|
|
|
onEnter(node: WalkToPosNode, context: ExecuteContext) : void {
|
|
|
|
let comTrans = context.world.getComponent(context.entity, ComTransform);
|
|
|
|
let comMovable = context.world.getComponent(context.entity, ComMovable);
|
|
|
|
comMovable.pointIdx = 0;
|
|
|
|
comMovable.points.length = 0;
|
|
|
|
comMovable.points.push(cc.v2(comTrans.x, comTrans.y), node.targetPos);
|
|
|
|
comMovable.speed = node.speed;
|
|
|
|
comMovable.speedDirty = true;
|
2022-03-22 07:35:41 +00:00
|
|
|
comMovable.keepDir = false;
|
2022-03-21 09:27:37 +00:00
|
|
|
node.state = NodeState.Executing;
|
|
|
|
comMovable.running = false;
|
|
|
|
},
|
|
|
|
onUpdate(node: WalkToPosNode, context: ExecuteContext) : void {
|
|
|
|
if(node.state !== NodeState.Executing) return ;
|
|
|
|
let comMovable = context.world.getComponent(context.entity, ComMovable);
|
|
|
|
if(comMovable.points.length == 0 || comMovable.pointIdx < 0 || comMovable.pointIdx >= comMovable.points.length) {
|
|
|
|
node.state = BT.NodeState.Success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** WalkToRandomPos node */
|
|
|
|
NodeHandlers[NodeType.WalkToRandomPos] = {
|
|
|
|
onEnter(node: WalkToRandomPosNode, context: ExecuteContext) : void {
|
|
|
|
let comTrans = context.world.getComponent(context.entity, ComTransform);
|
|
|
|
let comMovable = context.world.getComponent(context.entity, ComMovable);
|
|
|
|
comMovable.pointIdx = 0;
|
|
|
|
comMovable.points.length = 0;
|
|
|
|
let targetX = node.size.width * Math.random() - node.size.width/2;
|
2022-10-27 06:40:15 +00:00
|
|
|
let targetY = node.size.height * Math.random() - node.size.height/2 - 150;
|
2022-03-21 09:27:37 +00:00
|
|
|
comMovable.points.push(cc.v2(comTrans.x, comTrans.y), cc.v2(targetX, targetY));
|
|
|
|
comMovable.speed = node.speed;
|
|
|
|
comMovable.speedDirty = true;
|
2022-03-22 07:35:41 +00:00
|
|
|
comMovable.keepDir = false;
|
2022-03-21 09:27:37 +00:00
|
|
|
node.state = NodeState.Executing;
|
|
|
|
comMovable.running = false;
|
|
|
|
},
|
|
|
|
onUpdate(node: WalkToPosNode, context: ExecuteContext) : void {
|
|
|
|
if(node.state !== NodeState.Executing) return ;
|
|
|
|
let comMovable = context.world.getComponent(context.entity, ComMovable);
|
|
|
|
if(comMovable.points.length == 0 || comMovable.pointIdx < 0 || comMovable.pointIdx >= comMovable.points.length) {
|
|
|
|
node.state = BT.NodeState.Success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** WalkToTarget node */
|
|
|
|
NodeHandlers[NodeType.WalkToTarget] = {
|
|
|
|
onEnter(node: WalkToTargetNode, context: ExecuteContext) : void {
|
|
|
|
let comTrans = context.world.getComponent(context.entity, ComTransform);
|
|
|
|
let comMovable = context.world.getComponent(context.entity, ComMovable);
|
|
|
|
let comMonitor = context.world.getComponent(context.entity, ComMonitor);
|
|
|
|
if(comMonitor.others.length <= 0) return ;
|
|
|
|
let target = context.world.getComponent(comMonitor.others[0], ComTransform);
|
2022-03-22 07:35:41 +00:00
|
|
|
let xOffdet = Math.sign(comTrans.x - target.x) * 80;
|
2022-03-21 09:27:37 +00:00
|
|
|
comMovable.pointIdx = 0;
|
|
|
|
comMovable.points.length = 0;
|
|
|
|
comMovable.points.push(cc.v2(comTrans.x, comTrans.y), cc.v2(target.x + xOffdet, target.y));
|
|
|
|
comMovable.speed = node.speed;
|
|
|
|
comMovable.speedDirty = true;
|
2022-03-22 07:35:41 +00:00
|
|
|
comMovable.keepDir = false;
|
2022-03-21 09:27:37 +00:00
|
|
|
node.state = NodeState.Executing;
|
|
|
|
comMovable.running = false;
|
|
|
|
|
|
|
|
},
|
|
|
|
onUpdate(node: WalkToTargetNode, context: ExecuteContext) : void {
|
|
|
|
if(node.state !== NodeState.Executing) return ;
|
|
|
|
let comTrans = context.world.getComponent(context.entity, ComTransform);
|
|
|
|
let comMonitor = context.world.getComponent(context.entity, ComMonitor);
|
|
|
|
let comMovable = context.world.getComponent(context.entity, ComMovable);
|
2022-05-15 03:30:36 +00:00
|
|
|
if(comMonitor.others.length <=0){
|
|
|
|
node.state = BT.NodeState.Fail;
|
|
|
|
return ;
|
|
|
|
}
|
2022-03-21 09:27:37 +00:00
|
|
|
if(comMovable.points.length == 0 || comMovable.pointIdx < 0 || comMovable.pointIdx >= comMovable.points.length) {
|
2022-03-22 07:35:41 +00:00
|
|
|
let target = context.world.getComponent(comMonitor.others[0], ComTransform);
|
|
|
|
|
|
|
|
comTrans.dir.x = Math.abs(comTrans.dir.x) * -Math.sign(comTrans.x - target.x)
|
2022-03-21 09:27:37 +00:00
|
|
|
node.state = BT.NodeState.Success;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
if(comMonitor.others.length <= 0) {
|
|
|
|
node.state = BT.NodeState.Fail;
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
let target = context.world.getComponent(comMonitor.others[0], ComTransform);
|
2022-03-22 07:35:41 +00:00
|
|
|
let xOffdet = Math.sign(comTrans.x - target.x) * 80;
|
2022-03-21 09:27:37 +00:00
|
|
|
comMovable.points[1].x = target.x + xOffdet;
|
|
|
|
comMovable.points[1].y = target.y;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Monitor node */
|
|
|
|
NodeHandlers[NodeType.Monitor] = {
|
|
|
|
onEnter(node: MonitorNode, context: ExecuteContext) : void {
|
|
|
|
let comMonitor = context.world.getComponent(context.entity, ComMonitor);
|
|
|
|
if(!comMonitor) return ;
|
|
|
|
node.state = NodeState.Executing;
|
|
|
|
},
|
|
|
|
onUpdate(node: MonitorNode, context: ExecuteContext) : void {
|
|
|
|
let comMonitor = context.world.getComponent(context.entity, ComMonitor);
|
|
|
|
node.state = comMonitor.others.length > 0 ? BT.NodeState.Success : BT.NodeState.Fail;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Monitor node */
|
|
|
|
NodeHandlers[NodeType.Attack] = {
|
|
|
|
onEnter(node: AttackNode, context: ExecuteContext) : void {
|
|
|
|
node.state = NodeState.Executing;
|
|
|
|
|
|
|
|
let comCocosNode = context.world.getComponent(context.entity, ComCocosNode);
|
|
|
|
if(!comCocosNode.loaded) return ;
|
|
|
|
|
|
|
|
comCocosNode.events.push(new (EventAttack));
|
|
|
|
let comAttackable = context.world.getComponent(context.entity, ComAttackable);
|
|
|
|
comAttackable.countDown = comAttackable.duration;
|
|
|
|
comAttackable.dirty = true;
|
2022-03-22 07:35:41 +00:00
|
|
|
comAttackable.debugInfo = null;
|
|
|
|
|
|
|
|
|
|
|
|
comAttackable.willHurtFrame = 0.8;
|
|
|
|
comAttackable.willHurtFrameCompleted = false;
|
|
|
|
|
2022-03-21 09:27:37 +00:00
|
|
|
comAttackable.hurtFrame = 0.5;
|
2022-03-22 07:35:41 +00:00
|
|
|
comAttackable.hurtFrameCompleted = false;
|
|
|
|
|
|
|
|
|
2022-03-21 09:27:37 +00:00
|
|
|
},
|
|
|
|
onUpdate(node: AttackNode, context: ExecuteContext) : void {
|
|
|
|
if(node.state !== NodeState.Executing) return ;
|
|
|
|
let comAttackable = context.world.getComponent(context.entity, ComAttackable);
|
|
|
|
|
|
|
|
if(comAttackable.countDown <= 0) {
|
|
|
|
node.state = NodeState.Success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** EnoughAttr node */
|
|
|
|
NodeHandlers[NodeType.EnoughAttr] = {
|
|
|
|
onEnter(node: EnoughAttrNode, context: ExecuteContext) : void {
|
|
|
|
let com = context.world.getComponent(context.entity, node.com);
|
|
|
|
if(!com) return ;
|
|
|
|
node.state = NodeState.Executing;
|
|
|
|
},
|
|
|
|
onUpdate(node: EnoughAttrNode, context: ExecuteContext) : void {
|
|
|
|
let com = context.world.getComponent(context.entity, node.com);
|
|
|
|
if(!com) return ;
|
|
|
|
node.state = com[node.attr] >= node.value ? NodeState.Success : NodeState.Fail;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-03-22 07:35:41 +00:00
|
|
|
/** WillBeAttacked node */
|
|
|
|
NodeHandlers[NodeType.WillBeAttacked] = {
|
|
|
|
onEnter(node: WillBeAttackedNode, context: ExecuteContext) : void {
|
|
|
|
let comBeAttacked = context.world.getComponent(context.entity, ComBeAttacked);
|
|
|
|
|
|
|
|
if(!comBeAttacked) return ;
|
2022-03-21 09:27:37 +00:00
|
|
|
node.state = NodeState.Executing;
|
|
|
|
},
|
2022-03-22 07:35:41 +00:00
|
|
|
onUpdate(node: WillBeAttackedNode, context: ExecuteContext) : void {
|
|
|
|
let comBeAttacked = context.world.getComponent(context.entity, ComBeAttacked);
|
|
|
|
let comMonitor = context.world.getComponent(context.entity, ComMonitor);
|
|
|
|
let monitor = comMonitor.others && comMonitor.others.indexOf(comBeAttacked.attacker) !== -1;
|
|
|
|
if(!comBeAttacked) return ;
|
|
|
|
|
|
|
|
node.state = (monitor && comBeAttacked.attacker !== -1 && Math.random() > 0.8) ? NodeState.Success : NodeState.Fail;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Avoid node */
|
|
|
|
NodeHandlers[NodeType.Avoid] = {
|
|
|
|
onEnter(node: AvoidNode, context: ExecuteContext) : void {
|
|
|
|
let comTrans = context.world.getComponent(context.entity, ComTransform);
|
|
|
|
let comMovable = context.world.getComponent(context.entity, ComMovable);
|
|
|
|
comMovable.pointIdx = 0;
|
|
|
|
comMovable.points.length = 0;
|
|
|
|
|
|
|
|
let selfPoint = cc.v2(comTrans.x, comTrans.y);
|
|
|
|
comMovable.points.push(selfPoint, selfPoint.sub(cc.v2(50 * Math.sign(comTrans.dir.x), 0)));
|
|
|
|
comMovable.speed = node.speed;
|
|
|
|
comMovable.speedDirty = true;
|
|
|
|
comMovable.running = false;
|
|
|
|
comMovable.keepDir = true;
|
|
|
|
|
|
|
|
|
|
|
|
node.state = NodeState.Executing;
|
|
|
|
},
|
|
|
|
onUpdate(node: AvoidNode, context: ExecuteContext) : void {
|
|
|
|
if(node.state !== NodeState.Executing) return ;
|
|
|
|
let comMovable = context.world.getComponent(context.entity, ComMovable);
|
|
|
|
if(!comMovable) {
|
|
|
|
node.state = BT.NodeState.Fail;
|
|
|
|
return ;
|
2022-03-21 09:27:37 +00:00
|
|
|
}
|
2022-03-22 07:35:41 +00:00
|
|
|
|
|
|
|
if(comMovable.points.length == 0 || comMovable.pointIdx < 0 || comMovable.pointIdx >= comMovable.points.length) {
|
|
|
|
node.state = BT.NodeState.Success;
|
|
|
|
}
|
2022-03-21 09:27:37 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-03-22 07:35:41 +00:00
|
|
|
/** NotInAttack node */
|
|
|
|
NodeHandlers[NodeType.InAttacking] = {
|
|
|
|
onEnter(node: InAttackingNode, context: ExecuteContext) : void {
|
|
|
|
let comAttackable = context.world.getComponent(context.entity, ComAttackable);
|
|
|
|
if(!comAttackable) return ;
|
|
|
|
node.state = NodeState.Executing;
|
|
|
|
},
|
|
|
|
onUpdate(node: InAttackingNode, context: ExecuteContext) : void {
|
|
|
|
let comAttackable = context.world.getComponent(context.entity, ComAttackable);
|
|
|
|
if(!comAttackable) return ;
|
|
|
|
|
|
|
|
node.state = (comAttackable.countDown > 0 && comAttackable.countDown < comAttackable.duration-0.2) ? NodeState.Success : NodeState.Fail;
|
|
|
|
}
|
|
|
|
};
|
2022-03-21 09:27:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|