refactor: reorganize package structure and decouple framework packages (#338)
* refactor: reorganize package structure and decouple framework packages ## Package Structure Reorganization - Reorganized 55 packages into categorized subdirectories: - packages/framework/ - Generic framework (Laya/Cocos compatible) - packages/engine/ - ESEngine core modules - packages/rendering/ - Rendering modules (WASM dependent) - packages/physics/ - Physics modules - packages/streaming/ - World streaming - packages/network-ext/ - Network extensions - packages/editor/ - Editor framework and plugins - packages/rust/ - Rust WASM engine - packages/tools/ - Build tools and SDK ## Framework Package Decoupling - Decoupled behavior-tree and blueprint packages from ESEngine dependencies - Created abstracted interfaces (IBTAssetManager, IBehaviorTreeAssetContent) - ESEngine-specific code moved to esengine/ subpath exports - Framework packages now usable with Cocos/Laya without ESEngine ## CI Configuration - Updated CI to only type-check and lint framework packages - Added type-check:framework and lint:framework scripts ## Breaking Changes - Package import paths changed due to directory reorganization - ESEngine integrations now use subpath imports (e.g., '@esengine/behavior-tree/esengine') * fix: update es-engine file path after directory reorganization * docs: update README to focus on framework over engine * ci: only build framework packages, remove Rust/WASM dependencies * fix: remove esengine subpath from behavior-tree and blueprint builds ESEngine integration code will only be available in full engine builds. Framework packages are now purely engine-agnostic. * fix: move network-protocols to framework, build both in CI * fix: update workflow paths from packages/core to packages/framework/core * fix: exclude esengine folder from type-check in behavior-tree and blueprint * fix: update network tsconfig references to new paths * fix: add test:ci:framework to only test framework packages in CI * fix: only build core and math npm packages in CI * fix: exclude test files from CodeQL and fix string escaping security issue
This commit is contained in:
497
packages/framework/blueprint/src/triggers/BlueprintTrigger.ts
Normal file
497
packages/framework/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/framework/blueprint/src/triggers/TriggerCondition.ts
Normal file
479
packages/framework/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/framework/blueprint/src/triggers/TriggerDispatcher.ts
Normal file
461
packages/framework/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/framework/blueprint/src/triggers/TriggerTypes.ts
Normal file
400
packages/framework/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/framework/blueprint/src/triggers/index.ts
Normal file
105
packages/framework/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