@eva/plugin-trigger 2.1.0-beta.1 → 2.1.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,211 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var eva_js = require('@eva/eva.js');
6
+ var pluginSignalBus = require('@eva/plugin-signal-bus');
7
+
8
+ /******************************************************************************
9
+ Copyright (c) Microsoft Corporation.
10
+
11
+ Permission to use, copy, modify, and/or distribute this software for any
12
+ purpose with or without fee is hereby granted.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
15
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
16
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
17
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
18
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
19
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20
+ PERFORMANCE OF THIS SOFTWARE.
21
+ ***************************************************************************** */
22
+
23
+ function __decorate(decorators, target, key, desc) {
24
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
25
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
26
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
27
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
28
+ }
29
+
30
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
31
+ var e = new Error(message);
32
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
33
+ };
34
+
35
+ /**
36
+ * Trigger 组件 — 把信号映射成 DSL 动作。
37
+ *
38
+ * DSL 用法:
39
+ * ```json
40
+ * {
41
+ * "type": "Trigger",
42
+ * "props": {
43
+ * "rules": [
44
+ * { "on": "input:fire:press",
45
+ * "do": [
46
+ * { "type": "emit", "signal": "rocket:spawn" },
47
+ * { "type": "incStore", "key": "shotsFired" }
48
+ * ] },
49
+ * { "on": "rocket:hit",
50
+ * "guard": "ctx.allowedScene === true",
51
+ * "do": [
52
+ * { "type": "incStore", "key": "score" },
53
+ * { "type": "emit", "signal": "monster:hurt" }
54
+ * ] }
55
+ * ]
56
+ * }
57
+ * }
58
+ * ```
59
+ *
60
+ * 这是 plugin-state-machine 的"无状态弟弟":只配 input → output,不维护 state。
61
+ * 适合写 80% 的"按钮按下→记分"业务,大幅减少自定义 Component 数量。
62
+ */
63
+ exports.Trigger = class Trigger extends eva_js.Component {
64
+ constructor() {
65
+ super(...arguments);
66
+ this.rules = [];
67
+ this.subs = [];
68
+ this.ctx = {};
69
+ }
70
+ init(params) {
71
+ var _a, _b;
72
+ if (!params)
73
+ return;
74
+ this.rules = (_a = params.rules) !== null && _a !== void 0 ? _a : [];
75
+ this.ctx = (_b = params.context) !== null && _b !== void 0 ? _b : {};
76
+ }
77
+ awake() {
78
+ const bus = pluginSignalBus.getSignalBus();
79
+ for (const rule of this.rules) {
80
+ const h = bus.on(rule.on, (payload) => {
81
+ if (rule.guard && !this.evalGuard(rule.guard, payload))
82
+ return;
83
+ for (const a of rule.do)
84
+ this.exec(a, payload);
85
+ });
86
+ this.subs.push(h);
87
+ }
88
+ }
89
+ exec(action, payload) {
90
+ var _a, _b, _c, _d;
91
+ try {
92
+ switch (action.type) {
93
+ case 'emit':
94
+ pluginSignalBus.getSignalBus().emit(action.signal, (_a = action.payload) !== null && _a !== void 0 ? _a : payload);
95
+ break;
96
+ case 'setStore':
97
+ if (typeof mx !== 'undefined' && ((_b = mx === null || mx === void 0 ? void 0 : mx.store) === null || _b === void 0 ? void 0 : _b.update)) {
98
+ mx.store.update(action.key, () => action.value);
99
+ }
100
+ break;
101
+ case 'incStore':
102
+ if (typeof mx !== 'undefined' && ((_c = mx === null || mx === void 0 ? void 0 : mx.store) === null || _c === void 0 ? void 0 : _c.update)) {
103
+ mx.store.update(action.key, (v) => { var _a; return (v !== null && v !== void 0 ? v : 0) + ((_a = action.delta) !== null && _a !== void 0 ? _a : 1); });
104
+ }
105
+ break;
106
+ case 'log':
107
+ // eslint-disable-next-line no-console
108
+ console.log('[trigger]', action.message, payload);
109
+ break;
110
+ case 'callMethod':
111
+ this.callMethod(action.entity, action.component, action.method, (_d = action.args) !== null && _d !== void 0 ? _d : [], action.ref);
112
+ break;
113
+ }
114
+ }
115
+ catch (err) {
116
+ // eslint-disable-next-line no-console
117
+ console.warn('[plugin-trigger] action failed', action, err);
118
+ }
119
+ }
120
+ /**
121
+ * 按 (entity, componentName[, ref]) 查找并调用方法。
122
+ *
123
+ * ADR-0024B:加 `ref` 字段后修复 alert-chase.json 等模板"同 entity 多
124
+ * BehaviorScript / Trigger 静默 dedup"的 hidden broken state。匹配规则:
125
+ * - 无 ref:按 `(entity, componentName)` 取首个命中(legacy 行为)
126
+ * - 有 ref:按 `(entity, componentName, ref)` 三元组定位,匹配 instance.ref /
127
+ * instance.name / constructor.ref 字段(优先 instance.ref,与
128
+ * ADR-0021 BehaviorScript first-class 一致)
129
+ */
130
+ callMethod(entity, compName, method, args, ref) {
131
+ var _a, _b, _c, _d, _e, _f, _g;
132
+ const game = (_b = (_a = this.gameObject) === null || _a === void 0 ? void 0 : _a.scene) === null || _b === void 0 ? void 0 : _b.game;
133
+ if (!game)
134
+ return;
135
+ const stack = [...((_d = (_c = game.scene) === null || _c === void 0 ? void 0 : _c.gameObjects) !== null && _d !== void 0 ? _d : [])];
136
+ while (stack.length) {
137
+ const go = stack.pop();
138
+ if (!go)
139
+ continue;
140
+ if (go.name === entity) {
141
+ const comps = (_e = go.components) !== null && _e !== void 0 ? _e : [];
142
+ const c = this.findComponentByRef(comps, compName, ref);
143
+ if (c && typeof c[method] === 'function') {
144
+ c[method](...args);
145
+ }
146
+ return;
147
+ }
148
+ if ((_g = (_f = go.transform) === null || _f === void 0 ? void 0 : _f.children) === null || _g === void 0 ? void 0 : _g.length) {
149
+ for (const ch of go.transform.children)
150
+ stack.push(ch.gameObject);
151
+ }
152
+ }
153
+ }
154
+ /**
155
+ * 从 comps 列表里取出符合 (componentName, ref) 的 Component 实例。
156
+ *
157
+ * `ref` 可选 - 未传时取首个 componentName 匹配的实例(legacy);传入时按
158
+ * 三元组定位,匹配优先级 instance.ref → instance.name → constructor.ref。
159
+ */
160
+ findComponentByRef(comps, componentName, ref) {
161
+ if (!ref) {
162
+ return comps.find((c) => { var _a; return ((_a = c === null || c === void 0 ? void 0 : c.constructor) === null || _a === void 0 ? void 0 : _a.componentName) === componentName; });
163
+ }
164
+ return comps.find((c) => {
165
+ var _a, _b;
166
+ if (((_a = c === null || c === void 0 ? void 0 : c.constructor) === null || _a === void 0 ? void 0 : _a.componentName) !== componentName)
167
+ return false;
168
+ // 优先 instance.ref(ADR-0021 BehaviorScript first-class)
169
+ if (typeof c.ref === 'string' && c.ref === ref)
170
+ return true;
171
+ // fallback:instance.name(自定义 Component 习惯字段)
172
+ if (typeof c.name === 'string' && c.name === ref)
173
+ return true;
174
+ // fallback:constructor.ref(静态标签)
175
+ if (((_b = c === null || c === void 0 ? void 0 : c.constructor) === null || _b === void 0 ? void 0 : _b.ref) === ref)
176
+ return true;
177
+ return false;
178
+ });
179
+ }
180
+ evalGuard(guard, payload) {
181
+ try {
182
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func
183
+ const fn = new Function('payload', 'ctx', `return (${guard});`);
184
+ return Boolean(fn(payload, this.ctx));
185
+ }
186
+ catch (err) {
187
+ // eslint-disable-next-line no-console
188
+ console.warn(`[plugin-trigger] bad guard "${guard}":`, err);
189
+ return false;
190
+ }
191
+ }
192
+ onDestroy() {
193
+ for (const h of this.subs)
194
+ h.dispose();
195
+ this.subs = [];
196
+ }
197
+ };
198
+ exports.Trigger.componentName = 'Trigger';
199
+ exports.Trigger = __decorate([
200
+ eva_js.decorators.componentObserver({})
201
+ ], exports.Trigger);
202
+
203
+ class TriggerSystem extends eva_js.System {
204
+ constructor() {
205
+ super(...arguments);
206
+ this.name = 'Trigger';
207
+ }
208
+ }
209
+ TriggerSystem.systemName = 'Trigger';
210
+
211
+ exports.TriggerSystem = TriggerSystem;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@eva/eva.js"),o=require("@eva/plugin-signal-bus");"function"==typeof SuppressedError&&SuppressedError,exports.Trigger=class extends e.Component{constructor(){super(...arguments),this.rules=[],this.subs=[],this.ctx={}}init(e){var o,t;e&&(this.rules=null!==(o=e.rules)&&void 0!==o?o:[],this.ctx=null!==(t=e.context)&&void 0!==t?t:{})}awake(){const e=o.getSignalBus();for(const o of this.rules){const t=e.on(o.on,e=>{if(!o.guard||this.evalGuard(o.guard,e))for(const t of o.do)this.exec(t,e)});this.subs.push(t)}}exec(e,t){var r,n,s,i;try{switch(e.type){case"emit":o.getSignalBus().emit(e.signal,null!==(r=e.payload)&&void 0!==r?r:t);break;case"setStore":"undefined"!=typeof mx&&(null===(n=null===mx||void 0===mx?void 0:mx.store)||void 0===n?void 0:n.update)&&mx.store.update(e.key,()=>e.value);break;case"incStore":"undefined"!=typeof mx&&(null===(s=null===mx||void 0===mx?void 0:mx.store)||void 0===s?void 0:s.update)&&mx.store.update(e.key,o=>{var t;return(null!=o?o:0)+(null!==(t=e.delta)&&void 0!==t?t:1)});break;case"log":console.log("[trigger]",e.message,t);break;case"callMethod":this.callMethod(e.entity,e.component,e.method,null!==(i=e.args)&&void 0!==i?i:[],e.ref)}}catch(o){console.warn("[plugin-trigger] action failed",e,o)}}callMethod(e,o,t,r,n){var s,i,l,u,c,a,d;const v=null===(i=null===(s=this.gameObject)||void 0===s?void 0:s.scene)||void 0===i?void 0:i.game;if(!v)return;const g=[...null!==(u=null===(l=v.scene)||void 0===l?void 0:l.gameObjects)&&void 0!==u?u:[]];for(;g.length;){const s=g.pop();if(s){if(s.name===e){const e=null!==(c=s.components)&&void 0!==c?c:[],i=this.findComponentByRef(e,o,n);return void(i&&"function"==typeof i[t]&&i[t](...r))}if(null===(d=null===(a=s.transform)||void 0===a?void 0:a.children)||void 0===d?void 0:d.length)for(const e of s.transform.children)g.push(e.gameObject)}}}findComponentByRef(e,o,t){return t?e.find(e=>{var r,n;return(null===(r=null==e?void 0:e.constructor)||void 0===r?void 0:r.componentName)===o&&("string"==typeof e.ref&&e.ref===t||("string"==typeof e.name&&e.name===t||(null===(n=null==e?void 0:e.constructor)||void 0===n?void 0:n.ref)===t))}):e.find(e=>{var t;return(null===(t=null==e?void 0:e.constructor)||void 0===t?void 0:t.componentName)===o})}evalGuard(e,o){try{const t=new Function("payload","ctx",`return (${e});`);return Boolean(t(o,this.ctx))}catch(o){return console.warn(`[plugin-trigger] bad guard "${e}":`,o),!1}}onDestroy(){for(const e of this.subs)e.dispose();this.subs=[]}},exports.Trigger.componentName="Trigger",exports.Trigger=function(e,o,t,r){var n,s=arguments.length,i=s<3?o:null===r?r=Object.getOwnPropertyDescriptor(o,t):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)i=Reflect.decorate(e,o,t,r);else for(var l=e.length-1;l>=0;l--)(n=e[l])&&(i=(s<3?n(i):s>3?n(o,t,i):n(o,t))||i);return s>3&&i&&Object.defineProperty(o,t,i),i}([e.decorators.componentObserver({})],exports.Trigger);class t extends e.System{constructor(){super(...arguments),this.name="Trigger"}}t.systemName="Trigger",exports.TriggerSystem=t;
@@ -0,0 +1,118 @@
1
+ import { Component } from '@eva/eva.js';
2
+ import { System } from '@eva/eva.js';
3
+
4
+ /**
5
+ * Trigger 组件 — 把信号映射成 DSL 动作。
6
+ *
7
+ * DSL 用法:
8
+ * ```json
9
+ * {
10
+ * "type": "Trigger",
11
+ * "props": {
12
+ * "rules": [
13
+ * { "on": "input:fire:press",
14
+ * "do": [
15
+ * { "type": "emit", "signal": "rocket:spawn" },
16
+ * { "type": "incStore", "key": "shotsFired" }
17
+ * ] },
18
+ * { "on": "rocket:hit",
19
+ * "guard": "ctx.allowedScene === true",
20
+ * "do": [
21
+ * { "type": "incStore", "key": "score" },
22
+ * { "type": "emit", "signal": "monster:hurt" }
23
+ * ] }
24
+ * ]
25
+ * }
26
+ * }
27
+ * ```
28
+ *
29
+ * 这是 plugin-state-machine 的"无状态弟弟":只配 input → output,不维护 state。
30
+ * 适合写 80% 的"按钮按下→记分"业务,大幅减少自定义 Component 数量。
31
+ */
32
+ export declare class Trigger extends Component<TriggerParams> {
33
+ static componentName: string;
34
+ private rules;
35
+ private subs;
36
+ ctx: Record<string, any>;
37
+ init(params?: TriggerParams): void;
38
+ awake(): void;
39
+ private exec;
40
+ /**
41
+ * 按 (entity, componentName[, ref]) 查找并调用方法。
42
+ *
43
+ * ADR-0024B:加 `ref` 字段后修复 alert-chase.json 等模板"同 entity 多
44
+ * BehaviorScript / Trigger 静默 dedup"的 hidden broken state。匹配规则:
45
+ * - 无 ref:按 `(entity, componentName)` 取首个命中(legacy 行为)
46
+ * - 有 ref:按 `(entity, componentName, ref)` 三元组定位,匹配 instance.ref /
47
+ * instance.name / constructor.ref 字段(优先 instance.ref,与
48
+ * ADR-0021 BehaviorScript first-class 一致)
49
+ */
50
+ private callMethod;
51
+ /**
52
+ * 从 comps 列表里取出符合 (componentName, ref) 的 Component 实例。
53
+ *
54
+ * `ref` 可选 - 未传时取首个 componentName 匹配的实例(legacy);传入时按
55
+ * 三元组定位,匹配优先级 instance.ref → instance.name → constructor.ref。
56
+ */
57
+ private findComponentByRef;
58
+ private evalGuard;
59
+ onDestroy(): void;
60
+ }
61
+
62
+ /** 单条 action,DSL 描述时只填 type + 字段 */
63
+ export declare type TriggerAction = {
64
+ type: 'emit';
65
+ signal: string;
66
+ payload?: any;
67
+ } | {
68
+ type: 'setStore';
69
+ key: string;
70
+ value: any;
71
+ } | {
72
+ type: 'incStore';
73
+ key: string;
74
+ delta?: number;
75
+ } | {
76
+ type: 'log';
77
+ message: string;
78
+ } | {
79
+ type: 'callMethod';
80
+ entity: string;
81
+ component: string;
82
+ method: string;
83
+ args?: any[];
84
+ /**
85
+ * 同 entity 上同 componentName 多实例时的实例区分符(ADR-0024B / ADR-0015)。
86
+ *
87
+ * 默认按 `(entity, componentName)` 二元组定位 — 同名多实例时取首个命中。
88
+ * 设 `ref` 后按 `(entity, componentName, ref)` 三元组定位,匹配
89
+ * `BehaviorScript` 的 ADR-0021 `ref` 字段或自定义 Component 的 `name` /
90
+ * `static ref` 字段。
91
+ *
92
+ * 修复 alert-chase.json 模板"同 entity 多 BehaviorScript 静默 dedup"的
93
+ * hidden broken state。
94
+ */
95
+ ref?: string;
96
+ };
97
+
98
+ export declare interface TriggerParams {
99
+ rules: TriggerRule[];
100
+ /** ctx 变量,可在 guard 中读 */
101
+ context?: Record<string, any>;
102
+ }
103
+
104
+ export declare interface TriggerRule {
105
+ /** 监听的信号名 */
106
+ on: string;
107
+ /** 命中后执行的动作列表 */
108
+ do: TriggerAction[];
109
+ /** 可选:JS 表达式;以 (payload, ctx) 为变量,假则跳过 */
110
+ guard?: string;
111
+ }
112
+
113
+ export declare class TriggerSystem extends System {
114
+ static systemName: string;
115
+ readonly name = "Trigger";
116
+ }
117
+
118
+ export { }
@@ -0,0 +1,207 @@
1
+ import { Component, decorators, System } from '@eva/eva.js';
2
+ import { getSignalBus } from '@eva/plugin-signal-bus';
3
+
4
+ /******************************************************************************
5
+ Copyright (c) Microsoft Corporation.
6
+
7
+ Permission to use, copy, modify, and/or distribute this software for any
8
+ purpose with or without fee is hereby granted.
9
+
10
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
11
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
13
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
15
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16
+ PERFORMANCE OF THIS SOFTWARE.
17
+ ***************************************************************************** */
18
+
19
+ function __decorate(decorators, target, key, desc) {
20
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
21
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
22
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
23
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
24
+ }
25
+
26
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
27
+ var e = new Error(message);
28
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
29
+ };
30
+
31
+ /**
32
+ * Trigger 组件 — 把信号映射成 DSL 动作。
33
+ *
34
+ * DSL 用法:
35
+ * ```json
36
+ * {
37
+ * "type": "Trigger",
38
+ * "props": {
39
+ * "rules": [
40
+ * { "on": "input:fire:press",
41
+ * "do": [
42
+ * { "type": "emit", "signal": "rocket:spawn" },
43
+ * { "type": "incStore", "key": "shotsFired" }
44
+ * ] },
45
+ * { "on": "rocket:hit",
46
+ * "guard": "ctx.allowedScene === true",
47
+ * "do": [
48
+ * { "type": "incStore", "key": "score" },
49
+ * { "type": "emit", "signal": "monster:hurt" }
50
+ * ] }
51
+ * ]
52
+ * }
53
+ * }
54
+ * ```
55
+ *
56
+ * 这是 plugin-state-machine 的"无状态弟弟":只配 input → output,不维护 state。
57
+ * 适合写 80% 的"按钮按下→记分"业务,大幅减少自定义 Component 数量。
58
+ */
59
+ let Trigger = class Trigger extends Component {
60
+ constructor() {
61
+ super(...arguments);
62
+ this.rules = [];
63
+ this.subs = [];
64
+ this.ctx = {};
65
+ }
66
+ init(params) {
67
+ var _a, _b;
68
+ if (!params)
69
+ return;
70
+ this.rules = (_a = params.rules) !== null && _a !== void 0 ? _a : [];
71
+ this.ctx = (_b = params.context) !== null && _b !== void 0 ? _b : {};
72
+ }
73
+ awake() {
74
+ const bus = getSignalBus();
75
+ for (const rule of this.rules) {
76
+ const h = bus.on(rule.on, (payload) => {
77
+ if (rule.guard && !this.evalGuard(rule.guard, payload))
78
+ return;
79
+ for (const a of rule.do)
80
+ this.exec(a, payload);
81
+ });
82
+ this.subs.push(h);
83
+ }
84
+ }
85
+ exec(action, payload) {
86
+ var _a, _b, _c, _d;
87
+ try {
88
+ switch (action.type) {
89
+ case 'emit':
90
+ getSignalBus().emit(action.signal, (_a = action.payload) !== null && _a !== void 0 ? _a : payload);
91
+ break;
92
+ case 'setStore':
93
+ if (typeof mx !== 'undefined' && ((_b = mx === null || mx === void 0 ? void 0 : mx.store) === null || _b === void 0 ? void 0 : _b.update)) {
94
+ mx.store.update(action.key, () => action.value);
95
+ }
96
+ break;
97
+ case 'incStore':
98
+ if (typeof mx !== 'undefined' && ((_c = mx === null || mx === void 0 ? void 0 : mx.store) === null || _c === void 0 ? void 0 : _c.update)) {
99
+ mx.store.update(action.key, (v) => { var _a; return (v !== null && v !== void 0 ? v : 0) + ((_a = action.delta) !== null && _a !== void 0 ? _a : 1); });
100
+ }
101
+ break;
102
+ case 'log':
103
+ // eslint-disable-next-line no-console
104
+ console.log('[trigger]', action.message, payload);
105
+ break;
106
+ case 'callMethod':
107
+ this.callMethod(action.entity, action.component, action.method, (_d = action.args) !== null && _d !== void 0 ? _d : [], action.ref);
108
+ break;
109
+ }
110
+ }
111
+ catch (err) {
112
+ // eslint-disable-next-line no-console
113
+ console.warn('[plugin-trigger] action failed', action, err);
114
+ }
115
+ }
116
+ /**
117
+ * 按 (entity, componentName[, ref]) 查找并调用方法。
118
+ *
119
+ * ADR-0024B:加 `ref` 字段后修复 alert-chase.json 等模板"同 entity 多
120
+ * BehaviorScript / Trigger 静默 dedup"的 hidden broken state。匹配规则:
121
+ * - 无 ref:按 `(entity, componentName)` 取首个命中(legacy 行为)
122
+ * - 有 ref:按 `(entity, componentName, ref)` 三元组定位,匹配 instance.ref /
123
+ * instance.name / constructor.ref 字段(优先 instance.ref,与
124
+ * ADR-0021 BehaviorScript first-class 一致)
125
+ */
126
+ callMethod(entity, compName, method, args, ref) {
127
+ var _a, _b, _c, _d, _e, _f, _g;
128
+ const game = (_b = (_a = this.gameObject) === null || _a === void 0 ? void 0 : _a.scene) === null || _b === void 0 ? void 0 : _b.game;
129
+ if (!game)
130
+ return;
131
+ const stack = [...((_d = (_c = game.scene) === null || _c === void 0 ? void 0 : _c.gameObjects) !== null && _d !== void 0 ? _d : [])];
132
+ while (stack.length) {
133
+ const go = stack.pop();
134
+ if (!go)
135
+ continue;
136
+ if (go.name === entity) {
137
+ const comps = (_e = go.components) !== null && _e !== void 0 ? _e : [];
138
+ const c = this.findComponentByRef(comps, compName, ref);
139
+ if (c && typeof c[method] === 'function') {
140
+ c[method](...args);
141
+ }
142
+ return;
143
+ }
144
+ if ((_g = (_f = go.transform) === null || _f === void 0 ? void 0 : _f.children) === null || _g === void 0 ? void 0 : _g.length) {
145
+ for (const ch of go.transform.children)
146
+ stack.push(ch.gameObject);
147
+ }
148
+ }
149
+ }
150
+ /**
151
+ * 从 comps 列表里取出符合 (componentName, ref) 的 Component 实例。
152
+ *
153
+ * `ref` 可选 - 未传时取首个 componentName 匹配的实例(legacy);传入时按
154
+ * 三元组定位,匹配优先级 instance.ref → instance.name → constructor.ref。
155
+ */
156
+ findComponentByRef(comps, componentName, ref) {
157
+ if (!ref) {
158
+ return comps.find((c) => { var _a; return ((_a = c === null || c === void 0 ? void 0 : c.constructor) === null || _a === void 0 ? void 0 : _a.componentName) === componentName; });
159
+ }
160
+ return comps.find((c) => {
161
+ var _a, _b;
162
+ if (((_a = c === null || c === void 0 ? void 0 : c.constructor) === null || _a === void 0 ? void 0 : _a.componentName) !== componentName)
163
+ return false;
164
+ // 优先 instance.ref(ADR-0021 BehaviorScript first-class)
165
+ if (typeof c.ref === 'string' && c.ref === ref)
166
+ return true;
167
+ // fallback:instance.name(自定义 Component 习惯字段)
168
+ if (typeof c.name === 'string' && c.name === ref)
169
+ return true;
170
+ // fallback:constructor.ref(静态标签)
171
+ if (((_b = c === null || c === void 0 ? void 0 : c.constructor) === null || _b === void 0 ? void 0 : _b.ref) === ref)
172
+ return true;
173
+ return false;
174
+ });
175
+ }
176
+ evalGuard(guard, payload) {
177
+ try {
178
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func
179
+ const fn = new Function('payload', 'ctx', `return (${guard});`);
180
+ return Boolean(fn(payload, this.ctx));
181
+ }
182
+ catch (err) {
183
+ // eslint-disable-next-line no-console
184
+ console.warn(`[plugin-trigger] bad guard "${guard}":`, err);
185
+ return false;
186
+ }
187
+ }
188
+ onDestroy() {
189
+ for (const h of this.subs)
190
+ h.dispose();
191
+ this.subs = [];
192
+ }
193
+ };
194
+ Trigger.componentName = 'Trigger';
195
+ Trigger = __decorate([
196
+ decorators.componentObserver({})
197
+ ], Trigger);
198
+
199
+ class TriggerSystem extends System {
200
+ constructor() {
201
+ super(...arguments);
202
+ this.name = 'Trigger';
203
+ }
204
+ }
205
+ TriggerSystem.systemName = 'Trigger';
206
+
207
+ export { Trigger, TriggerSystem };
package/index.js ADDED
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ if (process.env.NODE_ENV === 'production') {
4
+ module.exports = require('./dist/plugin-trigger.cjs.prod.js');
5
+ } else {
6
+ module.exports = require('./dist/plugin-trigger.cjs.js');
7
+ }
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@eva/plugin-trigger",
3
- "version": "2.1.0-beta.1",
3
+ "version": "2.1.0-beta.11",
4
4
  "description": "Trigger — 信号 → DSL 动作的执行器,DSL 配置 \"on signal\" + \"do actions\",emit/setStore/transitionState/playSound 等。",
5
- "main": "lib/index.ts",
6
- "module": "lib/index.ts",
7
- "types": "lib/index.ts",
5
+ "main": "index.js",
6
+ "module": "dist/plugin-trigger.esm.js",
7
+ "types": "dist/plugin-trigger.d.ts",
8
8
  "files": [
9
- "lib"
9
+ "index.js",
10
+ "dist"
10
11
  ],
11
12
  "keywords": [
12
13
  "eva.js",
@@ -16,7 +17,7 @@
16
17
  ],
17
18
  "license": "MIT",
18
19
  "dependencies": {
19
- "@eva/eva.js": "2.1.0-beta.1",
20
- "@eva/plugin-signal-bus": "2.1.0-beta.1"
20
+ "@eva/eva.js": "2.1.0-beta.11",
21
+ "@eva/plugin-signal-bus": "2.1.0-beta.11"
21
22
  }
22
23
  }
package/lib/Trigger.ts DELETED
@@ -1,127 +0,0 @@
1
- import { Component, decorators } from '@eva/eva.js';
2
- import { getSignalBus, SignalHandle } from '@eva/plugin-signal-bus';
3
- import type { TriggerParams, TriggerRule, TriggerAction } from './types';
4
-
5
- declare const mx: any;
6
-
7
- /**
8
- * Trigger 组件 — 把信号映射成 DSL 动作。
9
- *
10
- * DSL 用法:
11
- * ```json
12
- * {
13
- * "type": "Trigger",
14
- * "props": {
15
- * "rules": [
16
- * { "on": "input:fire:press",
17
- * "do": [
18
- * { "type": "emit", "signal": "rocket:spawn" },
19
- * { "type": "incStore", "key": "shotsFired" }
20
- * ] },
21
- * { "on": "rocket:hit",
22
- * "guard": "ctx.allowedScene === true",
23
- * "do": [
24
- * { "type": "incStore", "key": "score" },
25
- * { "type": "emit", "signal": "monster:hurt" }
26
- * ] }
27
- * ]
28
- * }
29
- * }
30
- * ```
31
- *
32
- * 这是 plugin-state-machine 的"无状态弟弟":只配 input → output,不维护 state。
33
- * 适合写 80% 的"按钮按下→记分"业务,大幅减少自定义 Component 数量。
34
- */
35
- @decorators.componentObserver({})
36
- export class Trigger extends Component<TriggerParams> {
37
- static componentName = 'Trigger';
38
-
39
- private rules: TriggerRule[] = [];
40
- private subs: SignalHandle[] = [];
41
- ctx: Record<string, any> = {};
42
-
43
- init(params?: TriggerParams) {
44
- if (!params) return;
45
- this.rules = params.rules ?? [];
46
- this.ctx = params.context ?? {};
47
- }
48
-
49
- awake() {
50
- const bus = getSignalBus();
51
- for (const rule of this.rules) {
52
- const h = bus.on(rule.on, (payload: any) => {
53
- if (rule.guard && !this.evalGuard(rule.guard, payload)) return;
54
- for (const a of rule.do) this.exec(a, payload);
55
- });
56
- this.subs.push(h);
57
- }
58
- }
59
-
60
- private exec(action: TriggerAction, payload: any) {
61
- try {
62
- switch (action.type) {
63
- case 'emit':
64
- getSignalBus().emit(action.signal, action.payload ?? payload);
65
- break;
66
- case 'setStore':
67
- if (typeof mx !== 'undefined' && mx?.store?.update) {
68
- mx.store.update(action.key, () => action.value);
69
- }
70
- break;
71
- case 'incStore':
72
- if (typeof mx !== 'undefined' && mx?.store?.update) {
73
- mx.store.update(action.key, (v: number) => (v ?? 0) + (action.delta ?? 1));
74
- }
75
- break;
76
- case 'log':
77
- // eslint-disable-next-line no-console
78
- console.log('[trigger]', action.message, payload);
79
- break;
80
- case 'callMethod':
81
- this.callMethod(action.entity, action.component, action.method, action.args ?? []);
82
- break;
83
- }
84
- } catch (err) {
85
- // eslint-disable-next-line no-console
86
- console.warn('[plugin-trigger] action failed', action, err);
87
- }
88
- }
89
-
90
- private callMethod(entity: string, compName: string, method: string, args: any[]) {
91
- const game: any = (this as any).gameObject?.scene?.game;
92
- if (!game) return;
93
- const stack: any[] = [...(game.scene?.gameObjects ?? [])];
94
- while (stack.length) {
95
- const go = stack.pop();
96
- if (!go) continue;
97
- if (go.name === entity) {
98
- const comps: any[] = go.components ?? [];
99
- const c = comps.find((c) => c?.constructor?.componentName === compName);
100
- if (c && typeof c[method] === 'function') {
101
- c[method](...args);
102
- }
103
- return;
104
- }
105
- if (go.transform?.children?.length) {
106
- for (const ch of go.transform.children) stack.push(ch.gameObject);
107
- }
108
- }
109
- }
110
-
111
- private evalGuard(guard: string, payload: any): boolean {
112
- try {
113
- // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func
114
- const fn = new Function('payload', 'ctx', `return (${guard});`);
115
- return Boolean(fn(payload, this.ctx));
116
- } catch (err) {
117
- // eslint-disable-next-line no-console
118
- console.warn(`[plugin-trigger] bad guard "${guard}":`, err);
119
- return false;
120
- }
121
- }
122
-
123
- onDestroy() {
124
- for (const h of this.subs) h.dispose();
125
- this.subs = [];
126
- }
127
- }
@@ -1,6 +0,0 @@
1
- import { System } from '@eva/eva.js';
2
-
3
- export class TriggerSystem extends System {
4
- static systemName = 'Trigger';
5
- readonly name = 'Trigger';
6
- }
package/lib/index.ts DELETED
@@ -1,3 +0,0 @@
1
- export { Trigger } from './Trigger';
2
- export { TriggerSystem } from './TriggerSystem';
3
- export type { TriggerParams, TriggerRule, TriggerAction } from './types';
package/lib/types.ts DELETED
@@ -1,22 +0,0 @@
1
- /** 单条 action,DSL 描述时只填 type + 字段 */
2
- export type TriggerAction =
3
- | { type: 'emit'; signal: string; payload?: any }
4
- | { type: 'setStore'; key: string; value: any }
5
- | { type: 'incStore'; key: string; delta?: number }
6
- | { type: 'log'; message: string }
7
- | { type: 'callMethod'; entity: string; component: string; method: string; args?: any[] };
8
-
9
- export interface TriggerRule {
10
- /** 监听的信号名 */
11
- on: string;
12
- /** 命中后执行的动作列表 */
13
- do: TriggerAction[];
14
- /** 可选:JS 表达式;以 (payload, ctx) 为变量,假则跳过 */
15
- guard?: string;
16
- }
17
-
18
- export interface TriggerParams {
19
- rules: TriggerRule[];
20
- /** ctx 变量,可在 guard 中读 */
21
- context?: Record<string, any>;
22
- }