feat(blueprint): 添加蓝图触发器系统 (#323)
* feat(blueprint): 添加蓝图触发器系统 - TriggerTypes: 定义触发器类型和上下文接口 - tick/input/collision/message/timer/stateEnter/stateExit/custom - 各类型的上下文接口和工厂函数 - TriggerCondition: 触发条件系统 - ITriggerCondition 接口 - 复合条件 (CompositeCondition, NotCondition) - 通用条件 (AlwaysTrue, AlwaysFalse, TriggerType, EntityId) - 特定类型条件 (InputAction, MessageName, StateName, TimerId, CollisionEntity, CustomEvent) - ConditionBuilder 链式 API - BlueprintTrigger: 触发器核心实现 - IBlueprintTrigger 接口 - BlueprintTrigger 实现类 - TriggerRegistry 触发器注册表 - 各类型触发器工厂函数 - TriggerDispatcher: 触发器调度系统 - ITriggerDispatcher 调度器接口 - TriggerDispatcher 实现 - EntityTriggerManager 实体触发器管理器 * feat(blueprint): 添加触发器相关事件节点 - EventInput: 输入事件节点 (action/value/pressed/released) - EventCollisionEnter/Exit: 碰撞事件节点 - EventMessage: 消息事件节点 - EventTimer: 定时器事件节点 - EventStateEnter/Exit: 状态机事件节点
This commit is contained in:
@@ -9,6 +9,9 @@ export * from './types';
|
||||
// Runtime
|
||||
export * from './runtime';
|
||||
|
||||
// Triggers
|
||||
export * from './triggers';
|
||||
|
||||
// Nodes (import to register)
|
||||
import './nodes';
|
||||
|
||||
|
||||
118
packages/blueprint/src/nodes/events/EventCollision.ts
Normal file
118
packages/blueprint/src/nodes/events/EventCollision.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* @zh 碰撞事件节点 - 碰撞发生时触发
|
||||
* @en Event Collision Node - Triggered on collision events
|
||||
*/
|
||||
|
||||
import { BlueprintNodeTemplate, BlueprintNode } from '../../types/nodes';
|
||||
import { ExecutionResult } from '../../runtime/ExecutionContext';
|
||||
import { INodeExecutor, RegisterNode } from '../../runtime/NodeRegistry';
|
||||
|
||||
/**
|
||||
* @zh EventCollisionEnter 节点模板
|
||||
* @en EventCollisionEnter node template
|
||||
*/
|
||||
export const EventCollisionEnterTemplate: BlueprintNodeTemplate = {
|
||||
type: 'EventCollisionEnter',
|
||||
title: 'Event Collision Enter',
|
||||
category: 'event',
|
||||
color: '#CC0000',
|
||||
description: 'Triggered when collision starts / 碰撞开始时触发',
|
||||
keywords: ['collision', 'enter', 'hit', 'overlap', 'event'],
|
||||
menuPath: ['Event', 'Collision', 'Enter'],
|
||||
inputs: [],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
type: 'exec',
|
||||
displayName: ''
|
||||
},
|
||||
{
|
||||
name: 'otherEntityId',
|
||||
type: 'string',
|
||||
displayName: 'Other Entity'
|
||||
},
|
||||
{
|
||||
name: 'pointX',
|
||||
type: 'float',
|
||||
displayName: 'Point X'
|
||||
},
|
||||
{
|
||||
name: 'pointY',
|
||||
type: 'float',
|
||||
displayName: 'Point Y'
|
||||
},
|
||||
{
|
||||
name: 'normalX',
|
||||
type: 'float',
|
||||
displayName: 'Normal X'
|
||||
},
|
||||
{
|
||||
name: 'normalY',
|
||||
type: 'float',
|
||||
displayName: 'Normal Y'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh EventCollisionEnter 节点执行器
|
||||
* @en EventCollisionEnter node executor
|
||||
*/
|
||||
@RegisterNode(EventCollisionEnterTemplate)
|
||||
export class EventCollisionEnterExecutor implements INodeExecutor {
|
||||
execute(_node: BlueprintNode): ExecutionResult {
|
||||
return {
|
||||
nextExec: 'exec',
|
||||
outputs: {
|
||||
otherEntityId: '',
|
||||
pointX: 0,
|
||||
pointY: 0,
|
||||
normalX: 0,
|
||||
normalY: 0
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh EventCollisionExit 节点模板
|
||||
* @en EventCollisionExit node template
|
||||
*/
|
||||
export const EventCollisionExitTemplate: BlueprintNodeTemplate = {
|
||||
type: 'EventCollisionExit',
|
||||
title: 'Event Collision Exit',
|
||||
category: 'event',
|
||||
color: '#CC0000',
|
||||
description: 'Triggered when collision ends / 碰撞结束时触发',
|
||||
keywords: ['collision', 'exit', 'end', 'separate', 'event'],
|
||||
menuPath: ['Event', 'Collision', 'Exit'],
|
||||
inputs: [],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
type: 'exec',
|
||||
displayName: ''
|
||||
},
|
||||
{
|
||||
name: 'otherEntityId',
|
||||
type: 'string',
|
||||
displayName: 'Other Entity'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh EventCollisionExit 节点执行器
|
||||
* @en EventCollisionExit node executor
|
||||
*/
|
||||
@RegisterNode(EventCollisionExitTemplate)
|
||||
export class EventCollisionExitExecutor implements INodeExecutor {
|
||||
execute(_node: BlueprintNode): ExecutionResult {
|
||||
return {
|
||||
nextExec: 'exec',
|
||||
outputs: {
|
||||
otherEntityId: ''
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
79
packages/blueprint/src/nodes/events/EventInput.ts
Normal file
79
packages/blueprint/src/nodes/events/EventInput.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* @zh 输入事件节点 - 输入触发时触发
|
||||
* @en Event Input Node - Triggered on input events
|
||||
*/
|
||||
|
||||
import { BlueprintNodeTemplate, BlueprintNode } from '../../types/nodes';
|
||||
import { ExecutionContext, ExecutionResult } from '../../runtime/ExecutionContext';
|
||||
import { INodeExecutor, RegisterNode } from '../../runtime/NodeRegistry';
|
||||
|
||||
/**
|
||||
* @zh EventInput 节点模板
|
||||
* @en EventInput node template
|
||||
*/
|
||||
export const EventInputTemplate: BlueprintNodeTemplate = {
|
||||
type: 'EventInput',
|
||||
title: 'Event Input',
|
||||
category: 'event',
|
||||
color: '#CC0000',
|
||||
description: 'Triggered when input action occurs / 输入动作发生时触发',
|
||||
keywords: ['input', 'key', 'button', 'action', 'event'],
|
||||
menuPath: ['Event', 'Input'],
|
||||
inputs: [
|
||||
{
|
||||
name: 'action',
|
||||
type: 'string',
|
||||
displayName: 'Action',
|
||||
defaultValue: ''
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
type: 'exec',
|
||||
displayName: ''
|
||||
},
|
||||
{
|
||||
name: 'action',
|
||||
type: 'string',
|
||||
displayName: 'Action'
|
||||
},
|
||||
{
|
||||
name: 'value',
|
||||
type: 'float',
|
||||
displayName: 'Value'
|
||||
},
|
||||
{
|
||||
name: 'pressed',
|
||||
type: 'bool',
|
||||
displayName: 'Pressed'
|
||||
},
|
||||
{
|
||||
name: 'released',
|
||||
type: 'bool',
|
||||
displayName: 'Released'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh EventInput 节点执行器
|
||||
* @en EventInput node executor
|
||||
*
|
||||
* @zh 注意:事件节点的输出由 VM 在触发时通过 setOutputs 设置
|
||||
* @en Note: Event node outputs are set by VM via setOutputs when triggered
|
||||
*/
|
||||
@RegisterNode(EventInputTemplate)
|
||||
export class EventInputExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, _context: ExecutionContext): ExecutionResult {
|
||||
return {
|
||||
nextExec: 'exec',
|
||||
outputs: {
|
||||
action: node.data?.action ?? '',
|
||||
value: 0,
|
||||
pressed: false,
|
||||
released: false
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
70
packages/blueprint/src/nodes/events/EventMessage.ts
Normal file
70
packages/blueprint/src/nodes/events/EventMessage.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* @zh 消息事件节点 - 接收消息时触发
|
||||
* @en Event Message Node - Triggered when message is received
|
||||
*/
|
||||
|
||||
import { BlueprintNodeTemplate, BlueprintNode } from '../../types/nodes';
|
||||
import { ExecutionResult } from '../../runtime/ExecutionContext';
|
||||
import { INodeExecutor, RegisterNode } from '../../runtime/NodeRegistry';
|
||||
|
||||
/**
|
||||
* @zh EventMessage 节点模板
|
||||
* @en EventMessage node template
|
||||
*/
|
||||
export const EventMessageTemplate: BlueprintNodeTemplate = {
|
||||
type: 'EventMessage',
|
||||
title: 'Event Message',
|
||||
category: 'event',
|
||||
color: '#CC0000',
|
||||
description: 'Triggered when a message is received / 接收到消息时触发',
|
||||
keywords: ['message', 'receive', 'broadcast', 'event', 'signal'],
|
||||
menuPath: ['Event', 'Message'],
|
||||
inputs: [
|
||||
{
|
||||
name: 'messageName',
|
||||
type: 'string',
|
||||
displayName: 'Message Name',
|
||||
defaultValue: ''
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
type: 'exec',
|
||||
displayName: ''
|
||||
},
|
||||
{
|
||||
name: 'messageName',
|
||||
type: 'string',
|
||||
displayName: 'Message'
|
||||
},
|
||||
{
|
||||
name: 'senderId',
|
||||
type: 'string',
|
||||
displayName: 'Sender ID'
|
||||
},
|
||||
{
|
||||
name: 'payload',
|
||||
type: 'any',
|
||||
displayName: 'Payload'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh EventMessage 节点执行器
|
||||
* @en EventMessage node executor
|
||||
*/
|
||||
@RegisterNode(EventMessageTemplate)
|
||||
export class EventMessageExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode): ExecutionResult {
|
||||
return {
|
||||
nextExec: 'exec',
|
||||
outputs: {
|
||||
messageName: node.data?.messageName ?? '',
|
||||
senderId: '',
|
||||
payload: null
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
132
packages/blueprint/src/nodes/events/EventState.ts
Normal file
132
packages/blueprint/src/nodes/events/EventState.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* @zh 状态事件节点 - 状态机状态变化时触发
|
||||
* @en Event State Node - Triggered on state machine state changes
|
||||
*/
|
||||
|
||||
import { BlueprintNodeTemplate, BlueprintNode } from '../../types/nodes';
|
||||
import { ExecutionResult } from '../../runtime/ExecutionContext';
|
||||
import { INodeExecutor, RegisterNode } from '../../runtime/NodeRegistry';
|
||||
|
||||
/**
|
||||
* @zh EventStateEnter 节点模板
|
||||
* @en EventStateEnter node template
|
||||
*/
|
||||
export const EventStateEnterTemplate: BlueprintNodeTemplate = {
|
||||
type: 'EventStateEnter',
|
||||
title: 'Event State Enter',
|
||||
category: 'event',
|
||||
color: '#CC0000',
|
||||
description: 'Triggered when entering a state / 进入状态时触发',
|
||||
keywords: ['state', 'enter', 'fsm', 'machine', 'event'],
|
||||
menuPath: ['Event', 'State', 'Enter'],
|
||||
inputs: [
|
||||
{
|
||||
name: 'stateName',
|
||||
type: 'string',
|
||||
displayName: 'State Name',
|
||||
defaultValue: ''
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
type: 'exec',
|
||||
displayName: ''
|
||||
},
|
||||
{
|
||||
name: 'stateMachineId',
|
||||
type: 'string',
|
||||
displayName: 'State Machine'
|
||||
},
|
||||
{
|
||||
name: 'currentState',
|
||||
type: 'string',
|
||||
displayName: 'Current State'
|
||||
},
|
||||
{
|
||||
name: 'previousState',
|
||||
type: 'string',
|
||||
displayName: 'Previous State'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh EventStateEnter 节点执行器
|
||||
* @en EventStateEnter node executor
|
||||
*/
|
||||
@RegisterNode(EventStateEnterTemplate)
|
||||
export class EventStateEnterExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode): ExecutionResult {
|
||||
return {
|
||||
nextExec: 'exec',
|
||||
outputs: {
|
||||
stateMachineId: '',
|
||||
currentState: node.data?.stateName ?? '',
|
||||
previousState: ''
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh EventStateExit 节点模板
|
||||
* @en EventStateExit node template
|
||||
*/
|
||||
export const EventStateExitTemplate: BlueprintNodeTemplate = {
|
||||
type: 'EventStateExit',
|
||||
title: 'Event State Exit',
|
||||
category: 'event',
|
||||
color: '#CC0000',
|
||||
description: 'Triggered when exiting a state / 退出状态时触发',
|
||||
keywords: ['state', 'exit', 'leave', 'fsm', 'machine', 'event'],
|
||||
menuPath: ['Event', 'State', 'Exit'],
|
||||
inputs: [
|
||||
{
|
||||
name: 'stateName',
|
||||
type: 'string',
|
||||
displayName: 'State Name',
|
||||
defaultValue: ''
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
type: 'exec',
|
||||
displayName: ''
|
||||
},
|
||||
{
|
||||
name: 'stateMachineId',
|
||||
type: 'string',
|
||||
displayName: 'State Machine'
|
||||
},
|
||||
{
|
||||
name: 'currentState',
|
||||
type: 'string',
|
||||
displayName: 'Current State'
|
||||
},
|
||||
{
|
||||
name: 'previousState',
|
||||
type: 'string',
|
||||
displayName: 'Previous State'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh EventStateExit 节点执行器
|
||||
* @en EventStateExit node executor
|
||||
*/
|
||||
@RegisterNode(EventStateExitTemplate)
|
||||
export class EventStateExitExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode): ExecutionResult {
|
||||
return {
|
||||
nextExec: 'exec',
|
||||
outputs: {
|
||||
stateMachineId: '',
|
||||
currentState: '',
|
||||
previousState: node.data?.stateName ?? ''
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
70
packages/blueprint/src/nodes/events/EventTimer.ts
Normal file
70
packages/blueprint/src/nodes/events/EventTimer.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* @zh 定时器事件节点 - 定时器触发时调用
|
||||
* @en Event Timer Node - Triggered when timer fires
|
||||
*/
|
||||
|
||||
import { BlueprintNodeTemplate, BlueprintNode } from '../../types/nodes';
|
||||
import { ExecutionResult } from '../../runtime/ExecutionContext';
|
||||
import { INodeExecutor, RegisterNode } from '../../runtime/NodeRegistry';
|
||||
|
||||
/**
|
||||
* @zh EventTimer 节点模板
|
||||
* @en EventTimer node template
|
||||
*/
|
||||
export const EventTimerTemplate: BlueprintNodeTemplate = {
|
||||
type: 'EventTimer',
|
||||
title: 'Event Timer',
|
||||
category: 'event',
|
||||
color: '#CC0000',
|
||||
description: 'Triggered when a timer fires / 定时器触发时执行',
|
||||
keywords: ['timer', 'delay', 'schedule', 'event', 'interval'],
|
||||
menuPath: ['Event', 'Timer'],
|
||||
inputs: [
|
||||
{
|
||||
name: 'timerId',
|
||||
type: 'string',
|
||||
displayName: 'Timer ID',
|
||||
defaultValue: ''
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
type: 'exec',
|
||||
displayName: ''
|
||||
},
|
||||
{
|
||||
name: 'timerId',
|
||||
type: 'string',
|
||||
displayName: 'Timer ID'
|
||||
},
|
||||
{
|
||||
name: 'isRepeating',
|
||||
type: 'bool',
|
||||
displayName: 'Is Repeating'
|
||||
},
|
||||
{
|
||||
name: 'timesFired',
|
||||
type: 'int',
|
||||
displayName: 'Times Fired'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh EventTimer 节点执行器
|
||||
* @en EventTimer node executor
|
||||
*/
|
||||
@RegisterNode(EventTimerTemplate)
|
||||
export class EventTimerExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode): ExecutionResult {
|
||||
return {
|
||||
nextExec: 'exec',
|
||||
outputs: {
|
||||
timerId: node.data?.timerId ?? '',
|
||||
isRepeating: false,
|
||||
timesFired: 0
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,16 @@
|
||||
/**
|
||||
* Event Nodes - Entry points for blueprint execution
|
||||
* 事件节点 - 蓝图执行的入口点
|
||||
* @zh 事件节点 - 蓝图执行的入口点
|
||||
* @en Event Nodes - Entry points for blueprint execution
|
||||
*/
|
||||
|
||||
// 生命周期事件 | Lifecycle events
|
||||
export * from './EventBeginPlay';
|
||||
export * from './EventTick';
|
||||
export * from './EventEndPlay';
|
||||
|
||||
// 触发器事件 | Trigger events
|
||||
export * from './EventInput';
|
||||
export * from './EventCollision';
|
||||
export * from './EventMessage';
|
||||
export * from './EventTimer';
|
||||
export * from './EventState';
|
||||
|
||||
497
packages/blueprint/src/triggers/BlueprintTrigger.ts
Normal file
497
packages/blueprint/src/triggers/BlueprintTrigger.ts
Normal file
@@ -0,0 +1,497 @@
|
||||
/**
|
||||
* @zh 蓝图触发器
|
||||
* @en Blueprint Trigger
|
||||
*
|
||||
* @zh 定义触发器的核心实现
|
||||
* @en Defines core trigger implementation
|
||||
*/
|
||||
|
||||
import type { TriggerType, ITriggerContext } from './TriggerTypes';
|
||||
import type { ITriggerCondition } from './TriggerCondition';
|
||||
import { AlwaysTrueCondition } from './TriggerCondition';
|
||||
|
||||
// =============================================================================
|
||||
// 触发器接口 | Trigger Interface
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 触发器回调函数类型
|
||||
* @en Trigger callback function type
|
||||
*/
|
||||
export type TriggerCallback = (context: ITriggerContext) => void;
|
||||
|
||||
/**
|
||||
* @zh 蓝图触发器接口
|
||||
* @en Blueprint trigger interface
|
||||
*/
|
||||
export interface IBlueprintTrigger {
|
||||
/**
|
||||
* @zh 触发器唯一标识
|
||||
* @en Trigger unique identifier
|
||||
*/
|
||||
readonly id: string;
|
||||
|
||||
/**
|
||||
* @zh 触发器类型
|
||||
* @en Trigger type
|
||||
*/
|
||||
readonly type: TriggerType;
|
||||
|
||||
/**
|
||||
* @zh 触发器条件
|
||||
* @en Trigger conditions
|
||||
*/
|
||||
readonly condition: ITriggerCondition;
|
||||
|
||||
/**
|
||||
* @zh 是否启用
|
||||
* @en Is enabled
|
||||
*/
|
||||
enabled: boolean;
|
||||
|
||||
/**
|
||||
* @zh 优先级(越高越先执行)
|
||||
* @en Priority (higher executes first)
|
||||
*/
|
||||
readonly priority: number;
|
||||
|
||||
/**
|
||||
* @zh 检查是否应该触发
|
||||
* @en Check if should fire
|
||||
*/
|
||||
shouldFire(context: ITriggerContext): boolean;
|
||||
|
||||
/**
|
||||
* @zh 执行触发器
|
||||
* @en Execute trigger
|
||||
*/
|
||||
fire(context: ITriggerContext): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 触发器配置
|
||||
* @en Trigger configuration
|
||||
*/
|
||||
export interface TriggerConfig {
|
||||
/**
|
||||
* @zh 触发器 ID
|
||||
* @en Trigger ID
|
||||
*/
|
||||
id?: string;
|
||||
|
||||
/**
|
||||
* @zh 触发器类型
|
||||
* @en Trigger type
|
||||
*/
|
||||
type: TriggerType;
|
||||
|
||||
/**
|
||||
* @zh 触发条件
|
||||
* @en Trigger condition
|
||||
*/
|
||||
condition?: ITriggerCondition;
|
||||
|
||||
/**
|
||||
* @zh 是否启用
|
||||
* @en Is enabled
|
||||
*/
|
||||
enabled?: boolean;
|
||||
|
||||
/**
|
||||
* @zh 优先级
|
||||
* @en Priority
|
||||
*/
|
||||
priority?: number;
|
||||
|
||||
/**
|
||||
* @zh 回调函数
|
||||
* @en Callback function
|
||||
*/
|
||||
callback?: TriggerCallback;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 触发器实现 | Trigger Implementation
|
||||
// =============================================================================
|
||||
|
||||
let _triggerId = 0;
|
||||
|
||||
/**
|
||||
* @zh 生成唯一触发器 ID
|
||||
* @en Generate unique trigger ID
|
||||
*/
|
||||
function generateTriggerId(): string {
|
||||
return `trigger_${++_triggerId}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 蓝图触发器实现
|
||||
* @en Blueprint trigger implementation
|
||||
*/
|
||||
export class BlueprintTrigger implements IBlueprintTrigger {
|
||||
readonly id: string;
|
||||
readonly type: TriggerType;
|
||||
readonly condition: ITriggerCondition;
|
||||
readonly priority: number;
|
||||
enabled: boolean;
|
||||
|
||||
private readonly _callback?: TriggerCallback;
|
||||
private readonly _callbacks: Set<TriggerCallback> = new Set();
|
||||
|
||||
constructor(config: TriggerConfig) {
|
||||
this.id = config.id ?? generateTriggerId();
|
||||
this.type = config.type;
|
||||
this.condition = config.condition ?? new AlwaysTrueCondition();
|
||||
this.priority = config.priority ?? 0;
|
||||
this.enabled = config.enabled ?? true;
|
||||
this._callback = config.callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 检查是否应该触发
|
||||
* @en Check if should fire
|
||||
*/
|
||||
shouldFire(context: ITriggerContext): boolean {
|
||||
if (!this.enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (context.type !== this.type && this.type !== 'custom') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.condition.evaluate(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 执行触发器
|
||||
* @en Execute trigger
|
||||
*/
|
||||
fire(context: ITriggerContext): void {
|
||||
if (this._callback) {
|
||||
this._callback(context);
|
||||
}
|
||||
|
||||
for (const callback of this._callbacks) {
|
||||
callback(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 添加回调
|
||||
* @en Add callback
|
||||
*/
|
||||
addCallback(callback: TriggerCallback): void {
|
||||
this._callbacks.add(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 移除回调
|
||||
* @en Remove callback
|
||||
*/
|
||||
removeCallback(callback: TriggerCallback): void {
|
||||
this._callbacks.delete(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 清除所有回调
|
||||
* @en Clear all callbacks
|
||||
*/
|
||||
clearCallbacks(): void {
|
||||
this._callbacks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 触发器注册表 | Trigger Registry
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 触发器注册表接口
|
||||
* @en Trigger registry interface
|
||||
*/
|
||||
export interface ITriggerRegistry {
|
||||
/**
|
||||
* @zh 注册触发器
|
||||
* @en Register trigger
|
||||
*/
|
||||
register(trigger: IBlueprintTrigger): void;
|
||||
|
||||
/**
|
||||
* @zh 注销触发器
|
||||
* @en Unregister trigger
|
||||
*/
|
||||
unregister(triggerId: string): boolean;
|
||||
|
||||
/**
|
||||
* @zh 获取触发器
|
||||
* @en Get trigger
|
||||
*/
|
||||
get(triggerId: string): IBlueprintTrigger | undefined;
|
||||
|
||||
/**
|
||||
* @zh 获取所有触发器
|
||||
* @en Get all triggers
|
||||
*/
|
||||
getAll(): IBlueprintTrigger[];
|
||||
|
||||
/**
|
||||
* @zh 按类型获取触发器
|
||||
* @en Get triggers by type
|
||||
*/
|
||||
getByType(type: TriggerType): IBlueprintTrigger[];
|
||||
|
||||
/**
|
||||
* @zh 清除所有触发器
|
||||
* @en Clear all triggers
|
||||
*/
|
||||
clear(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 触发器注册表实现
|
||||
* @en Trigger registry implementation
|
||||
*/
|
||||
export class TriggerRegistry implements ITriggerRegistry {
|
||||
private readonly _triggers: Map<string, IBlueprintTrigger> = new Map();
|
||||
private readonly _triggersByType: Map<TriggerType, Set<string>> = new Map();
|
||||
|
||||
/**
|
||||
* @zh 注册触发器
|
||||
* @en Register trigger
|
||||
*/
|
||||
register(trigger: IBlueprintTrigger): void {
|
||||
if (this._triggers.has(trigger.id)) {
|
||||
console.warn(`Trigger ${trigger.id} already registered, overwriting`);
|
||||
}
|
||||
|
||||
this._triggers.set(trigger.id, trigger);
|
||||
|
||||
if (!this._triggersByType.has(trigger.type)) {
|
||||
this._triggersByType.set(trigger.type, new Set());
|
||||
}
|
||||
this._triggersByType.get(trigger.type)!.add(trigger.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 注销触发器
|
||||
* @en Unregister trigger
|
||||
*/
|
||||
unregister(triggerId: string): boolean {
|
||||
const trigger = this._triggers.get(triggerId);
|
||||
if (!trigger) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this._triggers.delete(triggerId);
|
||||
|
||||
const typeSet = this._triggersByType.get(trigger.type);
|
||||
if (typeSet) {
|
||||
typeSet.delete(triggerId);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取触发器
|
||||
* @en Get trigger
|
||||
*/
|
||||
get(triggerId: string): IBlueprintTrigger | undefined {
|
||||
return this._triggers.get(triggerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取所有触发器
|
||||
* @en Get all triggers
|
||||
*/
|
||||
getAll(): IBlueprintTrigger[] {
|
||||
return Array.from(this._triggers.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 按类型获取触发器
|
||||
* @en Get triggers by type
|
||||
*/
|
||||
getByType(type: TriggerType): IBlueprintTrigger[] {
|
||||
const typeSet = this._triggersByType.get(type);
|
||||
if (!typeSet) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const triggers: IBlueprintTrigger[] = [];
|
||||
for (const id of typeSet) {
|
||||
const trigger = this._triggers.get(id);
|
||||
if (trigger) {
|
||||
triggers.push(trigger);
|
||||
}
|
||||
}
|
||||
|
||||
return triggers.sort((a, b) => b.priority - a.priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 清除所有触发器
|
||||
* @en Clear all triggers
|
||||
*/
|
||||
clear(): void {
|
||||
this._triggers.clear();
|
||||
this._triggersByType.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取触发器数量
|
||||
* @en Get trigger count
|
||||
*/
|
||||
get count(): number {
|
||||
return this._triggers.size;
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 工厂函数 | Factory Functions
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 创建触发器
|
||||
* @en Create trigger
|
||||
*/
|
||||
export function createTrigger(config: TriggerConfig): BlueprintTrigger {
|
||||
return new BlueprintTrigger(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建 Tick 触发器
|
||||
* @en Create tick trigger
|
||||
*/
|
||||
export function createTickTrigger(
|
||||
callback?: TriggerCallback,
|
||||
options?: { id?: string; condition?: ITriggerCondition; priority?: number }
|
||||
): BlueprintTrigger {
|
||||
return new BlueprintTrigger({
|
||||
id: options?.id,
|
||||
type: 'tick',
|
||||
condition: options?.condition,
|
||||
priority: options?.priority,
|
||||
callback
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建输入触发器
|
||||
* @en Create input trigger
|
||||
*/
|
||||
export function createInputTrigger(
|
||||
callback?: TriggerCallback,
|
||||
options?: { id?: string; condition?: ITriggerCondition; priority?: number }
|
||||
): BlueprintTrigger {
|
||||
return new BlueprintTrigger({
|
||||
id: options?.id,
|
||||
type: 'input',
|
||||
condition: options?.condition,
|
||||
priority: options?.priority,
|
||||
callback
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建碰撞触发器
|
||||
* @en Create collision trigger
|
||||
*/
|
||||
export function createCollisionTrigger(
|
||||
callback?: TriggerCallback,
|
||||
options?: { id?: string; condition?: ITriggerCondition; priority?: number }
|
||||
): BlueprintTrigger {
|
||||
return new BlueprintTrigger({
|
||||
id: options?.id,
|
||||
type: 'collision',
|
||||
condition: options?.condition,
|
||||
priority: options?.priority,
|
||||
callback
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建消息触发器
|
||||
* @en Create message trigger
|
||||
*/
|
||||
export function createMessageTrigger(
|
||||
callback?: TriggerCallback,
|
||||
options?: { id?: string; condition?: ITriggerCondition; priority?: number }
|
||||
): BlueprintTrigger {
|
||||
return new BlueprintTrigger({
|
||||
id: options?.id,
|
||||
type: 'message',
|
||||
condition: options?.condition,
|
||||
priority: options?.priority,
|
||||
callback
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建定时器触发器
|
||||
* @en Create timer trigger
|
||||
*/
|
||||
export function createTimerTrigger(
|
||||
callback?: TriggerCallback,
|
||||
options?: { id?: string; condition?: ITriggerCondition; priority?: number }
|
||||
): BlueprintTrigger {
|
||||
return new BlueprintTrigger({
|
||||
id: options?.id,
|
||||
type: 'timer',
|
||||
condition: options?.condition,
|
||||
priority: options?.priority,
|
||||
callback
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建状态进入触发器
|
||||
* @en Create state enter trigger
|
||||
*/
|
||||
export function createStateEnterTrigger(
|
||||
callback?: TriggerCallback,
|
||||
options?: { id?: string; condition?: ITriggerCondition; priority?: number }
|
||||
): BlueprintTrigger {
|
||||
return new BlueprintTrigger({
|
||||
id: options?.id,
|
||||
type: 'stateEnter',
|
||||
condition: options?.condition,
|
||||
priority: options?.priority,
|
||||
callback
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建状态退出触发器
|
||||
* @en Create state exit trigger
|
||||
*/
|
||||
export function createStateExitTrigger(
|
||||
callback?: TriggerCallback,
|
||||
options?: { id?: string; condition?: ITriggerCondition; priority?: number }
|
||||
): BlueprintTrigger {
|
||||
return new BlueprintTrigger({
|
||||
id: options?.id,
|
||||
type: 'stateExit',
|
||||
condition: options?.condition,
|
||||
priority: options?.priority,
|
||||
callback
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建自定义触发器
|
||||
* @en Create custom trigger
|
||||
*/
|
||||
export function createCustomTrigger(
|
||||
callback?: TriggerCallback,
|
||||
options?: { id?: string; condition?: ITriggerCondition; priority?: number }
|
||||
): BlueprintTrigger {
|
||||
return new BlueprintTrigger({
|
||||
id: options?.id,
|
||||
type: 'custom',
|
||||
condition: options?.condition,
|
||||
priority: options?.priority,
|
||||
callback
|
||||
});
|
||||
}
|
||||
479
packages/blueprint/src/triggers/TriggerCondition.ts
Normal file
479
packages/blueprint/src/triggers/TriggerCondition.ts
Normal file
@@ -0,0 +1,479 @@
|
||||
/**
|
||||
* @zh 触发器条件系统
|
||||
* @en Trigger Condition System
|
||||
*
|
||||
* @zh 提供触发器触发前的条件检查能力
|
||||
* @en Provides condition checking before trigger fires
|
||||
*/
|
||||
|
||||
import type {
|
||||
ITriggerContext,
|
||||
TriggerType,
|
||||
IInputTriggerContext,
|
||||
IMessageTriggerContext,
|
||||
IStateTriggerContext,
|
||||
ITimerTriggerContext,
|
||||
ICollisionTriggerContext,
|
||||
ICustomTriggerContext
|
||||
} from './TriggerTypes';
|
||||
|
||||
// =============================================================================
|
||||
// 条件接口 | Condition Interface
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 触发器条件接口
|
||||
* @en Trigger condition interface
|
||||
*/
|
||||
export interface ITriggerCondition {
|
||||
/**
|
||||
* @zh 条件类型标识
|
||||
* @en Condition type identifier
|
||||
*/
|
||||
readonly type: string;
|
||||
|
||||
/**
|
||||
* @zh 评估条件是否满足
|
||||
* @en Evaluate if condition is met
|
||||
*
|
||||
* @param context - @zh 触发器上下文 @en Trigger context
|
||||
* @returns @zh 条件是否满足 @en Whether condition is met
|
||||
*/
|
||||
evaluate(context: ITriggerContext): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 条件组合逻辑
|
||||
* @en Condition combination logic
|
||||
*/
|
||||
export type ConditionLogic = 'and' | 'or';
|
||||
|
||||
// =============================================================================
|
||||
// 复合条件 | Composite Conditions
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 复合条件 - 组合多个条件
|
||||
* @en Composite condition - combines multiple conditions
|
||||
*/
|
||||
export class CompositeCondition implements ITriggerCondition {
|
||||
readonly type = 'composite';
|
||||
|
||||
constructor(
|
||||
private readonly _conditions: ITriggerCondition[],
|
||||
private readonly _logic: ConditionLogic = 'and'
|
||||
) {}
|
||||
|
||||
evaluate(context: ITriggerContext): boolean {
|
||||
if (this._conditions.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this._logic === 'and') {
|
||||
return this._conditions.every(c => c.evaluate(context));
|
||||
} else {
|
||||
return this._conditions.some(c => c.evaluate(context));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 非条件 - 取反
|
||||
* @en Not condition - negates
|
||||
*/
|
||||
export class NotCondition implements ITriggerCondition {
|
||||
readonly type = 'not';
|
||||
|
||||
constructor(private readonly _condition: ITriggerCondition) {}
|
||||
|
||||
evaluate(context: ITriggerContext): boolean {
|
||||
return !this._condition.evaluate(context);
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 通用条件 | Generic Conditions
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 始终为真的条件
|
||||
* @en Always true condition
|
||||
*/
|
||||
export class AlwaysTrueCondition implements ITriggerCondition {
|
||||
readonly type = 'alwaysTrue';
|
||||
|
||||
evaluate(_context: ITriggerContext): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 始终为假的条件
|
||||
* @en Always false condition
|
||||
*/
|
||||
export class AlwaysFalseCondition implements ITriggerCondition {
|
||||
readonly type = 'alwaysFalse';
|
||||
|
||||
evaluate(_context: ITriggerContext): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 触发器类型条件
|
||||
* @en Trigger type condition
|
||||
*/
|
||||
export class TriggerTypeCondition implements ITriggerCondition {
|
||||
readonly type = 'triggerType';
|
||||
|
||||
constructor(private readonly _allowedTypes: TriggerType[]) {}
|
||||
|
||||
evaluate(context: ITriggerContext): boolean {
|
||||
return this._allowedTypes.includes(context.type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 实体 ID 条件
|
||||
* @en Entity ID condition
|
||||
*/
|
||||
export class EntityIdCondition implements ITriggerCondition {
|
||||
readonly type = 'entityId';
|
||||
|
||||
constructor(
|
||||
private readonly _entityId: string,
|
||||
private readonly _checkSource: boolean = true
|
||||
) {}
|
||||
|
||||
evaluate(context: ITriggerContext): boolean {
|
||||
if (this._checkSource) {
|
||||
return context.sourceEntityId === this._entityId;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 自定义函数条件
|
||||
* @en Custom function condition
|
||||
*/
|
||||
export class FunctionCondition implements ITriggerCondition {
|
||||
readonly type = 'function';
|
||||
|
||||
constructor(
|
||||
private readonly _predicate: (context: ITriggerContext) => boolean
|
||||
) {}
|
||||
|
||||
evaluate(context: ITriggerContext): boolean {
|
||||
return this._predicate(context);
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 特定类型条件 | Type-Specific Conditions
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 输入动作条件
|
||||
* @en Input action condition
|
||||
*/
|
||||
export class InputActionCondition implements ITriggerCondition {
|
||||
readonly type = 'inputAction';
|
||||
|
||||
constructor(
|
||||
private readonly _action: string,
|
||||
private readonly _checkPressed?: boolean,
|
||||
private readonly _checkReleased?: boolean
|
||||
) {}
|
||||
|
||||
evaluate(context: ITriggerContext): boolean {
|
||||
if (context.type !== 'input') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const inputContext = context as unknown as IInputTriggerContext;
|
||||
|
||||
if (inputContext.action !== this._action) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._checkPressed !== undefined && inputContext.pressed !== this._checkPressed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._checkReleased !== undefined && inputContext.released !== this._checkReleased) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 消息名称条件
|
||||
* @en Message name condition
|
||||
*/
|
||||
export class MessageNameCondition implements ITriggerCondition {
|
||||
readonly type = 'messageName';
|
||||
|
||||
constructor(private readonly _messageName: string) {}
|
||||
|
||||
evaluate(context: ITriggerContext): boolean {
|
||||
if (context.type !== 'message') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const messageContext = context as unknown as IMessageTriggerContext;
|
||||
return messageContext.messageName === this._messageName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 状态名称条件
|
||||
* @en State name condition
|
||||
*/
|
||||
export class StateNameCondition implements ITriggerCondition {
|
||||
readonly type = 'stateName';
|
||||
|
||||
constructor(
|
||||
private readonly _stateName: string,
|
||||
private readonly _checkCurrent: boolean = true
|
||||
) {}
|
||||
|
||||
evaluate(context: ITriggerContext): boolean {
|
||||
if (context.type !== 'stateEnter' && context.type !== 'stateExit') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const stateContext = context as unknown as IStateTriggerContext;
|
||||
|
||||
if (this._checkCurrent) {
|
||||
return stateContext.currentState === this._stateName;
|
||||
} else {
|
||||
return stateContext.previousState === this._stateName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 定时器 ID 条件
|
||||
* @en Timer ID condition
|
||||
*/
|
||||
export class TimerIdCondition implements ITriggerCondition {
|
||||
readonly type = 'timerId';
|
||||
|
||||
constructor(private readonly _timerId: string) {}
|
||||
|
||||
evaluate(context: ITriggerContext): boolean {
|
||||
if (context.type !== 'timer') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const timerContext = context as unknown as ITimerTriggerContext;
|
||||
return timerContext.timerId === this._timerId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 碰撞实体条件
|
||||
* @en Collision entity condition
|
||||
*/
|
||||
export class CollisionEntityCondition implements ITriggerCondition {
|
||||
readonly type = 'collisionEntity';
|
||||
|
||||
constructor(
|
||||
private readonly _otherEntityId?: string,
|
||||
private readonly _checkEnter?: boolean,
|
||||
private readonly _checkExit?: boolean
|
||||
) {}
|
||||
|
||||
evaluate(context: ITriggerContext): boolean {
|
||||
if (context.type !== 'collision') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const collisionContext = context as unknown as ICollisionTriggerContext;
|
||||
|
||||
if (this._otherEntityId !== undefined && collisionContext.otherEntityId !== this._otherEntityId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._checkEnter !== undefined && collisionContext.isEnter !== this._checkEnter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._checkExit !== undefined && collisionContext.isExit !== this._checkExit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 自定义事件名称条件
|
||||
* @en Custom event name condition
|
||||
*/
|
||||
export class CustomEventCondition implements ITriggerCondition {
|
||||
readonly type = 'customEvent';
|
||||
|
||||
constructor(private readonly _eventName: string) {}
|
||||
|
||||
evaluate(context: ITriggerContext): boolean {
|
||||
if (context.type !== 'custom') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const customContext = context as unknown as ICustomTriggerContext;
|
||||
return customContext.eventName === this._eventName;
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 条件构建器 | Condition Builder
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 条件构建器 - 链式 API
|
||||
* @en Condition builder - fluent API
|
||||
*/
|
||||
export class ConditionBuilder {
|
||||
private _conditions: ITriggerCondition[] = [];
|
||||
private _logic: ConditionLogic = 'and';
|
||||
|
||||
/**
|
||||
* @zh 设置组合逻辑为 AND
|
||||
* @en Set combination logic to AND
|
||||
*/
|
||||
and(): this {
|
||||
this._logic = 'and';
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 设置组合逻辑为 OR
|
||||
* @en Set combination logic to OR
|
||||
*/
|
||||
or(): this {
|
||||
this._logic = 'or';
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 添加触发器类型条件
|
||||
* @en Add trigger type condition
|
||||
*/
|
||||
ofType(...types: TriggerType[]): this {
|
||||
this._conditions.push(new TriggerTypeCondition(types));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 添加实体 ID 条件
|
||||
* @en Add entity ID condition
|
||||
*/
|
||||
fromEntity(entityId: string): this {
|
||||
this._conditions.push(new EntityIdCondition(entityId));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 添加输入动作条件
|
||||
* @en Add input action condition
|
||||
*/
|
||||
onInput(action: string, options?: { pressed?: boolean; released?: boolean }): this {
|
||||
this._conditions.push(new InputActionCondition(action, options?.pressed, options?.released));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 添加消息条件
|
||||
* @en Add message condition
|
||||
*/
|
||||
onMessage(messageName: string): this {
|
||||
this._conditions.push(new MessageNameCondition(messageName));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 添加状态条件
|
||||
* @en Add state condition
|
||||
*/
|
||||
onState(stateName: string, checkCurrent: boolean = true): this {
|
||||
this._conditions.push(new StateNameCondition(stateName, checkCurrent));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 添加定时器条件
|
||||
* @en Add timer condition
|
||||
*/
|
||||
onTimer(timerId: string): this {
|
||||
this._conditions.push(new TimerIdCondition(timerId));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 添加碰撞条件
|
||||
* @en Add collision condition
|
||||
*/
|
||||
onCollision(options?: { entityId?: string; isEnter?: boolean; isExit?: boolean }): this {
|
||||
this._conditions.push(new CollisionEntityCondition(
|
||||
options?.entityId,
|
||||
options?.isEnter,
|
||||
options?.isExit
|
||||
));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 添加自定义事件条件
|
||||
* @en Add custom event condition
|
||||
*/
|
||||
onCustomEvent(eventName: string): this {
|
||||
this._conditions.push(new CustomEventCondition(eventName));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 添加自定义函数条件
|
||||
* @en Add custom function condition
|
||||
*/
|
||||
where(predicate: (context: ITriggerContext) => boolean): this {
|
||||
this._conditions.push(new FunctionCondition(predicate));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 添加取反条件
|
||||
* @en Add negated condition
|
||||
*/
|
||||
not(condition: ITriggerCondition): this {
|
||||
this._conditions.push(new NotCondition(condition));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 构建条件
|
||||
* @en Build condition
|
||||
*/
|
||||
build(): ITriggerCondition {
|
||||
if (this._conditions.length === 0) {
|
||||
return new AlwaysTrueCondition();
|
||||
}
|
||||
|
||||
if (this._conditions.length === 1) {
|
||||
return this._conditions[0];
|
||||
}
|
||||
|
||||
return new CompositeCondition(this._conditions, this._logic);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建条件构建器
|
||||
* @en Create condition builder
|
||||
*/
|
||||
export function condition(): ConditionBuilder {
|
||||
return new ConditionBuilder();
|
||||
}
|
||||
461
packages/blueprint/src/triggers/TriggerDispatcher.ts
Normal file
461
packages/blueprint/src/triggers/TriggerDispatcher.ts
Normal file
@@ -0,0 +1,461 @@
|
||||
/**
|
||||
* @zh 触发器调度器
|
||||
* @en Trigger Dispatcher
|
||||
*
|
||||
* @zh 负责分发触发器事件到订阅者
|
||||
* @en Responsible for dispatching trigger events to subscribers
|
||||
*/
|
||||
|
||||
import type { TriggerType, ITriggerContext } from './TriggerTypes';
|
||||
import type { IBlueprintTrigger, ITriggerRegistry, TriggerCallback } from './BlueprintTrigger';
|
||||
import { TriggerRegistry } from './BlueprintTrigger';
|
||||
|
||||
// =============================================================================
|
||||
// 调度器接口 | Dispatcher Interface
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 触发结果
|
||||
* @en Trigger result
|
||||
*/
|
||||
export interface TriggerResult {
|
||||
/**
|
||||
* @zh 触发器 ID
|
||||
* @en Trigger ID
|
||||
*/
|
||||
triggerId: string;
|
||||
|
||||
/**
|
||||
* @zh 是否成功
|
||||
* @en Is successful
|
||||
*/
|
||||
success: boolean;
|
||||
|
||||
/**
|
||||
* @zh 错误信息
|
||||
* @en Error message
|
||||
*/
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 调度结果
|
||||
* @en Dispatch result
|
||||
*/
|
||||
export interface DispatchResult {
|
||||
/**
|
||||
* @zh 上下文
|
||||
* @en Context
|
||||
*/
|
||||
context: ITriggerContext;
|
||||
|
||||
/**
|
||||
* @zh 触发的触发器数量
|
||||
* @en Number of triggers fired
|
||||
*/
|
||||
triggeredCount: number;
|
||||
|
||||
/**
|
||||
* @zh 各触发器结果
|
||||
* @en Results of each trigger
|
||||
*/
|
||||
results: TriggerResult[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 触发器调度器接口
|
||||
* @en Trigger dispatcher interface
|
||||
*/
|
||||
export interface ITriggerDispatcher {
|
||||
/**
|
||||
* @zh 调度触发器
|
||||
* @en Dispatch trigger
|
||||
*/
|
||||
dispatch(context: ITriggerContext): DispatchResult;
|
||||
|
||||
/**
|
||||
* @zh 异步调度触发器
|
||||
* @en Async dispatch trigger
|
||||
*/
|
||||
dispatchAsync(context: ITriggerContext): Promise<DispatchResult>;
|
||||
|
||||
/**
|
||||
* @zh 订阅触发器类型
|
||||
* @en Subscribe to trigger type
|
||||
*/
|
||||
subscribe(type: TriggerType, callback: TriggerCallback): () => void;
|
||||
|
||||
/**
|
||||
* @zh 取消订阅
|
||||
* @en Unsubscribe
|
||||
*/
|
||||
unsubscribe(type: TriggerType, callback: TriggerCallback): void;
|
||||
|
||||
/**
|
||||
* @zh 获取注册表
|
||||
* @en Get registry
|
||||
*/
|
||||
readonly registry: ITriggerRegistry;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 调度器实现 | Dispatcher Implementation
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 触发器调度器实现
|
||||
* @en Trigger dispatcher implementation
|
||||
*/
|
||||
export class TriggerDispatcher implements ITriggerDispatcher {
|
||||
private readonly _registry: ITriggerRegistry;
|
||||
private readonly _typeSubscribers: Map<TriggerType, Set<TriggerCallback>> = new Map();
|
||||
private readonly _globalSubscribers: Set<TriggerCallback> = new Set();
|
||||
private _isDispatching: boolean = false;
|
||||
private _pendingContexts: ITriggerContext[] = [];
|
||||
|
||||
constructor(registry?: ITriggerRegistry) {
|
||||
this._registry = registry ?? new TriggerRegistry();
|
||||
}
|
||||
|
||||
get registry(): ITriggerRegistry {
|
||||
return this._registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 调度触发器
|
||||
* @en Dispatch trigger
|
||||
*/
|
||||
dispatch(context: ITriggerContext): DispatchResult {
|
||||
if (this._isDispatching) {
|
||||
this._pendingContexts.push(context);
|
||||
return {
|
||||
context,
|
||||
triggeredCount: 0,
|
||||
results: []
|
||||
};
|
||||
}
|
||||
|
||||
this._isDispatching = true;
|
||||
|
||||
try {
|
||||
const result = this._doDispatch(context);
|
||||
|
||||
while (this._pendingContexts.length > 0) {
|
||||
const pendingContext = this._pendingContexts.shift()!;
|
||||
this._doDispatch(pendingContext);
|
||||
}
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
this._isDispatching = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 执行调度
|
||||
* @en Do dispatch
|
||||
*/
|
||||
private _doDispatch(context: ITriggerContext): DispatchResult {
|
||||
const results: TriggerResult[] = [];
|
||||
let triggeredCount = 0;
|
||||
|
||||
const triggers = this._registry.getByType(context.type);
|
||||
|
||||
for (const trigger of triggers) {
|
||||
if (trigger.shouldFire(context)) {
|
||||
try {
|
||||
trigger.fire(context);
|
||||
triggeredCount++;
|
||||
results.push({
|
||||
triggerId: trigger.id,
|
||||
success: true
|
||||
});
|
||||
} catch (error) {
|
||||
results.push({
|
||||
triggerId: trigger.id,
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._notifySubscribers(context);
|
||||
|
||||
return {
|
||||
context,
|
||||
triggeredCount,
|
||||
results
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 通知订阅者
|
||||
* @en Notify subscribers
|
||||
*/
|
||||
private _notifySubscribers(context: ITriggerContext): void {
|
||||
const typeSubscribers = this._typeSubscribers.get(context.type);
|
||||
if (typeSubscribers) {
|
||||
for (const callback of typeSubscribers) {
|
||||
try {
|
||||
callback(context);
|
||||
} catch (error) {
|
||||
console.error(`Trigger subscriber error: ${error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const callback of this._globalSubscribers) {
|
||||
try {
|
||||
callback(context);
|
||||
} catch (error) {
|
||||
console.error(`Global trigger subscriber error: ${error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 异步调度触发器
|
||||
* @en Async dispatch trigger
|
||||
*/
|
||||
async dispatchAsync(context: ITriggerContext): Promise<DispatchResult> {
|
||||
return new Promise((resolve) => {
|
||||
queueMicrotask(() => {
|
||||
resolve(this.dispatch(context));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 订阅触发器类型
|
||||
* @en Subscribe to trigger type
|
||||
*/
|
||||
subscribe(type: TriggerType, callback: TriggerCallback): () => void {
|
||||
if (!this._typeSubscribers.has(type)) {
|
||||
this._typeSubscribers.set(type, new Set());
|
||||
}
|
||||
|
||||
this._typeSubscribers.get(type)!.add(callback);
|
||||
|
||||
return () => this.unsubscribe(type, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 取消订阅
|
||||
* @en Unsubscribe
|
||||
*/
|
||||
unsubscribe(type: TriggerType, callback: TriggerCallback): void {
|
||||
const subscribers = this._typeSubscribers.get(type);
|
||||
if (subscribers) {
|
||||
subscribers.delete(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 订阅所有触发器
|
||||
* @en Subscribe to all triggers
|
||||
*/
|
||||
subscribeAll(callback: TriggerCallback): () => void {
|
||||
this._globalSubscribers.add(callback);
|
||||
return () => this.unsubscribeAll(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 取消订阅所有
|
||||
* @en Unsubscribe from all
|
||||
*/
|
||||
unsubscribeAll(callback: TriggerCallback): void {
|
||||
this._globalSubscribers.delete(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 清除所有订阅
|
||||
* @en Clear all subscriptions
|
||||
*/
|
||||
clearSubscriptions(): void {
|
||||
this._typeSubscribers.clear();
|
||||
this._globalSubscribers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 实体触发器管理器 | Entity Trigger Manager
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 实体触发器管理器接口
|
||||
* @en Entity trigger manager interface
|
||||
*/
|
||||
export interface IEntityTriggerManager {
|
||||
/**
|
||||
* @zh 为实体注册触发器
|
||||
* @en Register trigger for entity
|
||||
*/
|
||||
registerForEntity(entityId: string, trigger: IBlueprintTrigger): void;
|
||||
|
||||
/**
|
||||
* @zh 注销实体的触发器
|
||||
* @en Unregister trigger from entity
|
||||
*/
|
||||
unregisterFromEntity(entityId: string, triggerId: string): boolean;
|
||||
|
||||
/**
|
||||
* @zh 获取实体的所有触发器
|
||||
* @en Get all triggers for entity
|
||||
*/
|
||||
getEntityTriggers(entityId: string): IBlueprintTrigger[];
|
||||
|
||||
/**
|
||||
* @zh 清除实体的所有触发器
|
||||
* @en Clear all triggers for entity
|
||||
*/
|
||||
clearEntityTriggers(entityId: string): void;
|
||||
|
||||
/**
|
||||
* @zh 调度器
|
||||
* @en Dispatcher
|
||||
*/
|
||||
readonly dispatcher: ITriggerDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 实体触发器管理器实现
|
||||
* @en Entity trigger manager implementation
|
||||
*/
|
||||
export class EntityTriggerManager implements IEntityTriggerManager {
|
||||
private readonly _dispatcher: ITriggerDispatcher;
|
||||
private readonly _entityTriggers: Map<string, Set<string>> = new Map();
|
||||
|
||||
constructor(dispatcher?: ITriggerDispatcher) {
|
||||
this._dispatcher = dispatcher ?? new TriggerDispatcher();
|
||||
}
|
||||
|
||||
get dispatcher(): ITriggerDispatcher {
|
||||
return this._dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 为实体注册触发器
|
||||
* @en Register trigger for entity
|
||||
*/
|
||||
registerForEntity(entityId: string, trigger: IBlueprintTrigger): void {
|
||||
this._dispatcher.registry.register(trigger);
|
||||
|
||||
if (!this._entityTriggers.has(entityId)) {
|
||||
this._entityTriggers.set(entityId, new Set());
|
||||
}
|
||||
|
||||
this._entityTriggers.get(entityId)!.add(trigger.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 注销实体的触发器
|
||||
* @en Unregister trigger from entity
|
||||
*/
|
||||
unregisterFromEntity(entityId: string, triggerId: string): boolean {
|
||||
const entitySet = this._entityTriggers.get(entityId);
|
||||
if (!entitySet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!entitySet.has(triggerId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
entitySet.delete(triggerId);
|
||||
return this._dispatcher.registry.unregister(triggerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取实体的所有触发器
|
||||
* @en Get all triggers for entity
|
||||
*/
|
||||
getEntityTriggers(entityId: string): IBlueprintTrigger[] {
|
||||
const entitySet = this._entityTriggers.get(entityId);
|
||||
if (!entitySet) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const triggers: IBlueprintTrigger[] = [];
|
||||
for (const triggerId of entitySet) {
|
||||
const trigger = this._dispatcher.registry.get(triggerId);
|
||||
if (trigger) {
|
||||
triggers.push(trigger);
|
||||
}
|
||||
}
|
||||
|
||||
return triggers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 清除实体的所有触发器
|
||||
* @en Clear all triggers for entity
|
||||
*/
|
||||
clearEntityTriggers(entityId: string): void {
|
||||
const entitySet = this._entityTriggers.get(entityId);
|
||||
if (!entitySet) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const triggerId of entitySet) {
|
||||
this._dispatcher.registry.unregister(triggerId);
|
||||
}
|
||||
|
||||
this._entityTriggers.delete(entityId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 调度触发器到实体
|
||||
* @en Dispatch trigger to entity
|
||||
*/
|
||||
dispatchToEntity(entityId: string, context: ITriggerContext): DispatchResult {
|
||||
const entityTriggers = this.getEntityTriggers(entityId);
|
||||
const results: TriggerResult[] = [];
|
||||
let triggeredCount = 0;
|
||||
|
||||
for (const trigger of entityTriggers) {
|
||||
if (trigger.shouldFire(context)) {
|
||||
try {
|
||||
trigger.fire(context);
|
||||
triggeredCount++;
|
||||
results.push({
|
||||
triggerId: trigger.id,
|
||||
success: true
|
||||
});
|
||||
} catch (error) {
|
||||
results.push({
|
||||
triggerId: trigger.id,
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
context,
|
||||
triggeredCount,
|
||||
results
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 工厂函数 | Factory Functions
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 创建触发器调度器
|
||||
* @en Create trigger dispatcher
|
||||
*/
|
||||
export function createTriggerDispatcher(registry?: ITriggerRegistry): TriggerDispatcher {
|
||||
return new TriggerDispatcher(registry);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建实体触发器管理器
|
||||
* @en Create entity trigger manager
|
||||
*/
|
||||
export function createEntityTriggerManager(dispatcher?: ITriggerDispatcher): EntityTriggerManager {
|
||||
return new EntityTriggerManager(dispatcher);
|
||||
}
|
||||
400
packages/blueprint/src/triggers/TriggerTypes.ts
Normal file
400
packages/blueprint/src/triggers/TriggerTypes.ts
Normal file
@@ -0,0 +1,400 @@
|
||||
/**
|
||||
* @zh 蓝图触发器类型定义
|
||||
* @en Blueprint Trigger Type Definitions
|
||||
*
|
||||
* @zh 定义触发器的核心类型和接口
|
||||
* @en Defines core types and interfaces for triggers
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// 触发器类型 | Trigger Types
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 触发器类型枚举
|
||||
* @en Trigger type enumeration
|
||||
*/
|
||||
export type TriggerType =
|
||||
| 'tick' // 每帧触发 | Every frame
|
||||
| 'input' // 输入事件 | Input event
|
||||
| 'collision' // 碰撞事件 | Collision event
|
||||
| 'message' // 消息事件 | Message event
|
||||
| 'timer' // 定时器事件 | Timer event
|
||||
| 'stateEnter' // 状态进入 | State enter
|
||||
| 'stateExit' // 状态退出 | State exit
|
||||
| 'custom'; // 自定义事件 | Custom event
|
||||
|
||||
/**
|
||||
* @zh 触发器类型常量
|
||||
* @en Trigger type constants
|
||||
*/
|
||||
export const TriggerTypes = {
|
||||
TICK: 'tick' as const,
|
||||
INPUT: 'input' as const,
|
||||
COLLISION: 'collision' as const,
|
||||
MESSAGE: 'message' as const,
|
||||
TIMER: 'timer' as const,
|
||||
STATE_ENTER: 'stateEnter' as const,
|
||||
STATE_EXIT: 'stateExit' as const,
|
||||
CUSTOM: 'custom' as const
|
||||
} as const;
|
||||
|
||||
// =============================================================================
|
||||
// 触发器上下文 | Trigger Context
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 触发器上下文基础接口
|
||||
* @en Trigger context base interface
|
||||
*/
|
||||
export interface ITriggerContext {
|
||||
/**
|
||||
* @zh 触发器类型
|
||||
* @en Trigger type
|
||||
*/
|
||||
readonly type: TriggerType;
|
||||
|
||||
/**
|
||||
* @zh 触发时间戳
|
||||
* @en Trigger timestamp
|
||||
*/
|
||||
readonly timestamp: number;
|
||||
|
||||
/**
|
||||
* @zh 触发源实体 ID
|
||||
* @en Source entity ID
|
||||
*/
|
||||
readonly sourceEntityId?: string;
|
||||
|
||||
/**
|
||||
* @zh 附加数据
|
||||
* @en Additional data
|
||||
*/
|
||||
readonly data?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh Tick 触发器上下文
|
||||
* @en Tick trigger context
|
||||
*/
|
||||
export interface ITickTriggerContext extends ITriggerContext {
|
||||
readonly type: 'tick';
|
||||
/**
|
||||
* @zh 增量时间(秒)
|
||||
* @en Delta time (seconds)
|
||||
*/
|
||||
readonly deltaTime: number;
|
||||
/**
|
||||
* @zh 帧计数
|
||||
* @en Frame count
|
||||
*/
|
||||
readonly frameCount: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 输入触发器上下文
|
||||
* @en Input trigger context
|
||||
*/
|
||||
export interface IInputTriggerContext extends ITriggerContext {
|
||||
readonly type: 'input';
|
||||
/**
|
||||
* @zh 输入动作名称
|
||||
* @en Input action name
|
||||
*/
|
||||
readonly action: string;
|
||||
/**
|
||||
* @zh 输入值
|
||||
* @en Input value
|
||||
*/
|
||||
readonly value: number | boolean;
|
||||
/**
|
||||
* @zh 是否刚按下
|
||||
* @en Is just pressed
|
||||
*/
|
||||
readonly pressed?: boolean;
|
||||
/**
|
||||
* @zh 是否刚释放
|
||||
* @en Is just released
|
||||
*/
|
||||
readonly released?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 碰撞触发器上下文
|
||||
* @en Collision trigger context
|
||||
*/
|
||||
export interface ICollisionTriggerContext extends ITriggerContext {
|
||||
readonly type: 'collision';
|
||||
/**
|
||||
* @zh 碰撞的另一个实体 ID
|
||||
* @en Other entity ID in collision
|
||||
*/
|
||||
readonly otherEntityId: string;
|
||||
/**
|
||||
* @zh 碰撞点
|
||||
* @en Collision point
|
||||
*/
|
||||
readonly point?: { x: number; y: number };
|
||||
/**
|
||||
* @zh 碰撞法线
|
||||
* @en Collision normal
|
||||
*/
|
||||
readonly normal?: { x: number; y: number };
|
||||
/**
|
||||
* @zh 是否开始碰撞
|
||||
* @en Is collision start
|
||||
*/
|
||||
readonly isEnter: boolean;
|
||||
/**
|
||||
* @zh 是否结束碰撞
|
||||
* @en Is collision end
|
||||
*/
|
||||
readonly isExit: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 消息触发器上下文
|
||||
* @en Message trigger context
|
||||
*/
|
||||
export interface IMessageTriggerContext extends ITriggerContext {
|
||||
readonly type: 'message';
|
||||
/**
|
||||
* @zh 消息名称
|
||||
* @en Message name
|
||||
*/
|
||||
readonly messageName: string;
|
||||
/**
|
||||
* @zh 发送者 ID
|
||||
* @en Sender ID
|
||||
*/
|
||||
readonly senderId?: string;
|
||||
/**
|
||||
* @zh 消息负载
|
||||
* @en Message payload
|
||||
*/
|
||||
readonly payload?: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 定时器触发器上下文
|
||||
* @en Timer trigger context
|
||||
*/
|
||||
export interface ITimerTriggerContext extends ITriggerContext {
|
||||
readonly type: 'timer';
|
||||
/**
|
||||
* @zh 定时器 ID
|
||||
* @en Timer ID
|
||||
*/
|
||||
readonly timerId: string;
|
||||
/**
|
||||
* @zh 是否循环触发
|
||||
* @en Is repeating
|
||||
*/
|
||||
readonly isRepeating: boolean;
|
||||
/**
|
||||
* @zh 已触发次数
|
||||
* @en Times fired
|
||||
*/
|
||||
readonly timesFired: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 状态触发器上下文
|
||||
* @en State trigger context
|
||||
*/
|
||||
export interface IStateTriggerContext extends ITriggerContext {
|
||||
readonly type: 'stateEnter' | 'stateExit';
|
||||
/**
|
||||
* @zh 状态机 ID
|
||||
* @en State machine ID
|
||||
*/
|
||||
readonly stateMachineId: string;
|
||||
/**
|
||||
* @zh 当前状态
|
||||
* @en Current state
|
||||
*/
|
||||
readonly currentState: string;
|
||||
/**
|
||||
* @zh 之前状态
|
||||
* @en Previous state
|
||||
*/
|
||||
readonly previousState?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 自定义触发器上下文
|
||||
* @en Custom trigger context
|
||||
*/
|
||||
export interface ICustomTriggerContext extends ITriggerContext {
|
||||
readonly type: 'custom';
|
||||
/**
|
||||
* @zh 事件名称
|
||||
* @en Event name
|
||||
*/
|
||||
readonly eventName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 所有触发器上下文的联合类型
|
||||
* @en Union type of all trigger contexts
|
||||
*/
|
||||
export type TriggerContext =
|
||||
| ITickTriggerContext
|
||||
| IInputTriggerContext
|
||||
| ICollisionTriggerContext
|
||||
| IMessageTriggerContext
|
||||
| ITimerTriggerContext
|
||||
| IStateTriggerContext
|
||||
| ICustomTriggerContext;
|
||||
|
||||
// =============================================================================
|
||||
// 工厂函数 | Factory Functions
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 创建 Tick 触发器上下文
|
||||
* @en Create tick trigger context
|
||||
*/
|
||||
export function createTickContext(
|
||||
deltaTime: number,
|
||||
frameCount: number,
|
||||
sourceEntityId?: string
|
||||
): ITickTriggerContext {
|
||||
return {
|
||||
type: 'tick',
|
||||
timestamp: Date.now(),
|
||||
deltaTime,
|
||||
frameCount,
|
||||
sourceEntityId
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建输入触发器上下文
|
||||
* @en Create input trigger context
|
||||
*/
|
||||
export function createInputContext(
|
||||
action: string,
|
||||
value: number | boolean,
|
||||
options?: {
|
||||
pressed?: boolean;
|
||||
released?: boolean;
|
||||
sourceEntityId?: string;
|
||||
}
|
||||
): IInputTriggerContext {
|
||||
return {
|
||||
type: 'input',
|
||||
timestamp: Date.now(),
|
||||
action,
|
||||
value,
|
||||
pressed: options?.pressed,
|
||||
released: options?.released,
|
||||
sourceEntityId: options?.sourceEntityId
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建碰撞触发器上下文
|
||||
* @en Create collision trigger context
|
||||
*/
|
||||
export function createCollisionContext(
|
||||
otherEntityId: string,
|
||||
isEnter: boolean,
|
||||
options?: {
|
||||
point?: { x: number; y: number };
|
||||
normal?: { x: number; y: number };
|
||||
sourceEntityId?: string;
|
||||
}
|
||||
): ICollisionTriggerContext {
|
||||
return {
|
||||
type: 'collision',
|
||||
timestamp: Date.now(),
|
||||
otherEntityId,
|
||||
isEnter,
|
||||
isExit: !isEnter,
|
||||
point: options?.point,
|
||||
normal: options?.normal,
|
||||
sourceEntityId: options?.sourceEntityId
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建消息触发器上下文
|
||||
* @en Create message trigger context
|
||||
*/
|
||||
export function createMessageContext(
|
||||
messageName: string,
|
||||
payload?: unknown,
|
||||
options?: {
|
||||
senderId?: string;
|
||||
sourceEntityId?: string;
|
||||
}
|
||||
): IMessageTriggerContext {
|
||||
return {
|
||||
type: 'message',
|
||||
timestamp: Date.now(),
|
||||
messageName,
|
||||
payload,
|
||||
senderId: options?.senderId,
|
||||
sourceEntityId: options?.sourceEntityId
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建定时器触发器上下文
|
||||
* @en Create timer trigger context
|
||||
*/
|
||||
export function createTimerContext(
|
||||
timerId: string,
|
||||
isRepeating: boolean,
|
||||
timesFired: number,
|
||||
sourceEntityId?: string
|
||||
): ITimerTriggerContext {
|
||||
return {
|
||||
type: 'timer',
|
||||
timestamp: Date.now(),
|
||||
timerId,
|
||||
isRepeating,
|
||||
timesFired,
|
||||
sourceEntityId
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建状态触发器上下文
|
||||
* @en Create state trigger context
|
||||
*/
|
||||
export function createStateContext(
|
||||
type: 'stateEnter' | 'stateExit',
|
||||
stateMachineId: string,
|
||||
currentState: string,
|
||||
previousState?: string,
|
||||
sourceEntityId?: string
|
||||
): IStateTriggerContext {
|
||||
return {
|
||||
type,
|
||||
timestamp: Date.now(),
|
||||
stateMachineId,
|
||||
currentState,
|
||||
previousState,
|
||||
sourceEntityId
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建自定义触发器上下文
|
||||
* @en Create custom trigger context
|
||||
*/
|
||||
export function createCustomContext(
|
||||
eventName: string,
|
||||
data?: Record<string, unknown>,
|
||||
sourceEntityId?: string
|
||||
): ICustomTriggerContext {
|
||||
return {
|
||||
type: 'custom',
|
||||
timestamp: Date.now(),
|
||||
eventName,
|
||||
data,
|
||||
sourceEntityId
|
||||
};
|
||||
}
|
||||
105
packages/blueprint/src/triggers/index.ts
Normal file
105
packages/blueprint/src/triggers/index.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* @zh 蓝图触发器模块
|
||||
* @en Blueprint Triggers Module
|
||||
*
|
||||
* @zh 提供蓝图触发器系统的所有导出
|
||||
* @en Provides all exports for the blueprint trigger system
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// 触发器类型 | Trigger Types
|
||||
// =============================================================================
|
||||
|
||||
export type {
|
||||
TriggerType,
|
||||
ITriggerContext,
|
||||
ITickTriggerContext,
|
||||
IInputTriggerContext,
|
||||
ICollisionTriggerContext,
|
||||
IMessageTriggerContext,
|
||||
ITimerTriggerContext,
|
||||
IStateTriggerContext,
|
||||
ICustomTriggerContext,
|
||||
TriggerContext
|
||||
} from './TriggerTypes';
|
||||
|
||||
export {
|
||||
TriggerTypes,
|
||||
createTickContext,
|
||||
createInputContext,
|
||||
createCollisionContext,
|
||||
createMessageContext,
|
||||
createTimerContext,
|
||||
createStateContext,
|
||||
createCustomContext
|
||||
} from './TriggerTypes';
|
||||
|
||||
// =============================================================================
|
||||
// 触发器条件 | Trigger Conditions
|
||||
// =============================================================================
|
||||
|
||||
export type {
|
||||
ITriggerCondition,
|
||||
ConditionLogic
|
||||
} from './TriggerCondition';
|
||||
|
||||
export {
|
||||
CompositeCondition,
|
||||
NotCondition,
|
||||
AlwaysTrueCondition,
|
||||
AlwaysFalseCondition,
|
||||
TriggerTypeCondition,
|
||||
EntityIdCondition,
|
||||
FunctionCondition,
|
||||
InputActionCondition,
|
||||
MessageNameCondition,
|
||||
StateNameCondition,
|
||||
TimerIdCondition,
|
||||
CollisionEntityCondition,
|
||||
CustomEventCondition,
|
||||
ConditionBuilder,
|
||||
condition
|
||||
} from './TriggerCondition';
|
||||
|
||||
// =============================================================================
|
||||
// 蓝图触发器 | Blueprint Trigger
|
||||
// =============================================================================
|
||||
|
||||
export type {
|
||||
TriggerCallback,
|
||||
IBlueprintTrigger,
|
||||
TriggerConfig,
|
||||
ITriggerRegistry
|
||||
} from './BlueprintTrigger';
|
||||
|
||||
export {
|
||||
BlueprintTrigger,
|
||||
TriggerRegistry,
|
||||
createTrigger,
|
||||
createTickTrigger,
|
||||
createInputTrigger,
|
||||
createCollisionTrigger,
|
||||
createMessageTrigger,
|
||||
createTimerTrigger,
|
||||
createStateEnterTrigger,
|
||||
createStateExitTrigger,
|
||||
createCustomTrigger
|
||||
} from './BlueprintTrigger';
|
||||
|
||||
// =============================================================================
|
||||
// 触发器调度器 | Trigger Dispatcher
|
||||
// =============================================================================
|
||||
|
||||
export type {
|
||||
TriggerResult,
|
||||
DispatchResult,
|
||||
ITriggerDispatcher,
|
||||
IEntityTriggerManager
|
||||
} from './TriggerDispatcher';
|
||||
|
||||
export {
|
||||
TriggerDispatcher,
|
||||
EntityTriggerManager,
|
||||
createTriggerDispatcher,
|
||||
createEntityTriggerManager
|
||||
} from './TriggerDispatcher';
|
||||
Reference in New Issue
Block a user