@combos-fun/plugin-development-tool 0.0.48 → 0.0.50
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.
- package/README.md +11 -1
- package/agent-skill.md +40 -24
- package/combos-plugin.json +3 -4
- package/dist/plugin-development-tool.cjs.js +664 -386
- package/dist/plugin-development-tool.cjs.js.map +1 -1
- package/dist/plugin-development-tool.cjs.prod.js +1 -1
- package/dist/plugin-development-tool.d.ts +46 -55
- package/dist/plugin-development-tool.esm.js +665 -388
- package/dist/plugin-development-tool.esm.js.map +1 -1
- package/dist/vite.cjs +540 -0
- package/dist/vite.cjs.map +1 -0
- package/dist/vite.d.ts +43 -0
- package/dist/vite.mjs +527 -0
- package/dist/vite.mjs.map +1 -0
- package/package.json +11 -8
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
var tslib = require('tslib');
|
|
4
4
|
var engine = require('@combos-fun/engine');
|
|
5
|
-
var pluginRenderer = require('@combos-fun/plugin-renderer');
|
|
6
|
-
var pluginRendererGraphics = require('@combos-fun/plugin-renderer-graphics');
|
|
7
|
-
var pluginRendererEvent = require('@combos-fun/plugin-renderer-event');
|
|
8
5
|
var inspectorDecorator = require('@combos-fun/inspector-decorator');
|
|
9
6
|
|
|
10
7
|
/** Parent → iframe (also `window` CustomEvent / `game.emit`): toggle pick mode. Payload: `{ enabled: boolean }`. */
|
|
@@ -45,8 +42,7 @@ const COMBOS_DEVELOPMENT_TOOL_MARKER_OVERLAY_SUCCESS = 'combos-development-tool:
|
|
|
45
42
|
|
|
46
43
|
/**
|
|
47
44
|
* Marks a `GameObject` as selectable in editor pick mode. While the development tool is enabled,
|
|
48
|
-
* only nodes carrying this component
|
|
49
|
-
* are removed and cached until disable.
|
|
45
|
+
* only nodes carrying this component are hit-tested on the canvas overlay.
|
|
50
46
|
*/
|
|
51
47
|
class CombosDevelopmentToolTarget extends engine.Component {
|
|
52
48
|
constructor() {
|
|
@@ -59,6 +55,69 @@ class CombosDevelopmentToolTarget extends engine.Component {
|
|
|
59
55
|
}
|
|
60
56
|
}
|
|
61
57
|
|
|
58
|
+
function cssRgba(color, alpha = 1) {
|
|
59
|
+
const r = (color >> 16) & 0xff;
|
|
60
|
+
const g = (color >> 8) & 0xff;
|
|
61
|
+
const b = color & 0xff;
|
|
62
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
63
|
+
}
|
|
64
|
+
/** Pixi-v8-style path builder backed by `CanvasRenderingContext2D`. */
|
|
65
|
+
class CanvasMarkerGraphics {
|
|
66
|
+
constructor(ctx) {
|
|
67
|
+
this.ctx = ctx;
|
|
68
|
+
}
|
|
69
|
+
clear() {
|
|
70
|
+
this.ctx.beginPath();
|
|
71
|
+
}
|
|
72
|
+
roundRect(x, y, width, height, radius = 0) {
|
|
73
|
+
const ctx = this.ctx;
|
|
74
|
+
if (typeof ctx.roundRect === 'function') {
|
|
75
|
+
ctx.roundRect(x, y, width, height, radius);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const r = Math.max(0, Math.min(radius, Math.min(width, height) / 2));
|
|
79
|
+
ctx.moveTo(x + r, y);
|
|
80
|
+
ctx.lineTo(x + width - r, y);
|
|
81
|
+
ctx.quadraticCurveTo(x + width, y, x + width, y + r);
|
|
82
|
+
ctx.lineTo(x + width, y + height - r);
|
|
83
|
+
ctx.quadraticCurveTo(x + width, y + height, x + width - r, y + height);
|
|
84
|
+
ctx.lineTo(x + r, y + height);
|
|
85
|
+
ctx.quadraticCurveTo(x, y + height, x, y + height - r);
|
|
86
|
+
ctx.lineTo(x, y + r);
|
|
87
|
+
ctx.quadraticCurveTo(x, y, x + r, y);
|
|
88
|
+
ctx.closePath();
|
|
89
|
+
}
|
|
90
|
+
rect(x, y, width, height) {
|
|
91
|
+
this.ctx.rect(x, y, width, height);
|
|
92
|
+
}
|
|
93
|
+
circle(x, y, radius) {
|
|
94
|
+
this.ctx.moveTo(x + radius, y);
|
|
95
|
+
this.ctx.arc(x, y, radius, 0, Math.PI * 2);
|
|
96
|
+
}
|
|
97
|
+
poly(points, close = true) {
|
|
98
|
+
if (points.length < 4)
|
|
99
|
+
return;
|
|
100
|
+
this.ctx.moveTo(points[0], points[1]);
|
|
101
|
+
for (let i = 2; i < points.length; i += 2) {
|
|
102
|
+
this.ctx.lineTo(points[i], points[i + 1]);
|
|
103
|
+
}
|
|
104
|
+
if (close) {
|
|
105
|
+
this.ctx.closePath();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
fill(style) {
|
|
109
|
+
this.ctx.fillStyle = cssRgba(style.color, style.alpha ?? 1);
|
|
110
|
+
this.ctx.fill();
|
|
111
|
+
this.ctx.beginPath();
|
|
112
|
+
}
|
|
113
|
+
stroke(style) {
|
|
114
|
+
this.ctx.strokeStyle = cssRgba(style.color, style.alpha ?? 1);
|
|
115
|
+
this.ctx.lineWidth = style.width;
|
|
116
|
+
this.ctx.stroke();
|
|
117
|
+
this.ctx.beginPath();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
62
121
|
/**
|
|
63
122
|
* Registered marker kinds, in priority order: a GameObject is marked by the
|
|
64
123
|
* first registered component it owns. For now this is **`Sound` only**; the
|
|
@@ -68,7 +127,7 @@ class CombosDevelopmentToolTarget extends engine.Component {
|
|
|
68
127
|
const MARKER_COMPONENTS = [
|
|
69
128
|
{ componentName: 'Sound', badgeColor: 0x8b5cf6, glyph: 'speaker' },
|
|
70
129
|
];
|
|
71
|
-
/** Fixed badge size in
|
|
130
|
+
/** Fixed badge size in overlay CSS pixels. */
|
|
72
131
|
const MARKER_ICON_SIZE = 28;
|
|
73
132
|
/**
|
|
74
133
|
* First registered marker def whose component is present on `go`, else `null`.
|
|
@@ -112,7 +171,7 @@ function drawDotGlyph(g, s) {
|
|
|
112
171
|
g.circle(s * 0.5, s * 0.5, s * 0.18);
|
|
113
172
|
g.fill({ color: GLYPH_COLOR, alpha: 0.96 });
|
|
114
173
|
}
|
|
115
|
-
/** Render a full marker (badge + glyph) into `g`, sized to `size` (
|
|
174
|
+
/** Render a full marker (badge + glyph) into `g`, sized to `size` (overlay CSS pixels). */
|
|
116
175
|
function drawMarkerIcon(g, def, size = MARKER_ICON_SIZE) {
|
|
117
176
|
drawBadge(g, def, size);
|
|
118
177
|
switch (def.glyph) {
|
|
@@ -194,6 +253,85 @@ function mergeAllowedMessageOrigins(extra) {
|
|
|
194
253
|
return result;
|
|
195
254
|
}
|
|
196
255
|
|
|
256
|
+
const OVERLAY_Z_INDEX = '2147483000';
|
|
257
|
+
const OVERLAY_ATTR = 'data-combos-development-tool-overlay';
|
|
258
|
+
function ensureParent(gameCanvas, overlay) {
|
|
259
|
+
const parent = gameCanvas.parentElement ?? document.body;
|
|
260
|
+
if (overlay.parentElement !== parent) {
|
|
261
|
+
parent.appendChild(overlay);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Transparent HTML canvas stacked on the game canvas.
|
|
266
|
+
* Pick mode sets `pointer-events: auto` so game Event / Event3D do not receive the tap.
|
|
267
|
+
*/
|
|
268
|
+
function createOverlayCanvas() {
|
|
269
|
+
if (typeof document === 'undefined') {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
const canvas = document.createElement('canvas');
|
|
273
|
+
canvas.setAttribute(OVERLAY_ATTR, 'true');
|
|
274
|
+
canvas.style.position = 'fixed';
|
|
275
|
+
canvas.style.left = '0';
|
|
276
|
+
canvas.style.top = '0';
|
|
277
|
+
canvas.style.width = '0';
|
|
278
|
+
canvas.style.height = '0';
|
|
279
|
+
canvas.style.margin = '0';
|
|
280
|
+
canvas.style.padding = '0';
|
|
281
|
+
canvas.style.border = '0';
|
|
282
|
+
canvas.style.background = 'transparent';
|
|
283
|
+
canvas.style.pointerEvents = 'none';
|
|
284
|
+
canvas.style.touchAction = 'none';
|
|
285
|
+
canvas.style.userSelect = 'none';
|
|
286
|
+
canvas.style.zIndex = OVERLAY_Z_INDEX;
|
|
287
|
+
canvas.style.display = 'none';
|
|
288
|
+
const ctx = canvas.getContext('2d');
|
|
289
|
+
if (!ctx) {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
let pickEnabled = false;
|
|
293
|
+
let visible = false;
|
|
294
|
+
const sync = (gameCanvas) => {
|
|
295
|
+
ensureParent(gameCanvas, canvas);
|
|
296
|
+
const rect = gameCanvas.getBoundingClientRect();
|
|
297
|
+
if (rect.width <= 0 || rect.height <= 0) {
|
|
298
|
+
canvas.style.display = 'none';
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
canvas.style.display = visible ? 'block' : 'none';
|
|
302
|
+
canvas.style.left = `${rect.left}px`;
|
|
303
|
+
canvas.style.top = `${rect.top}px`;
|
|
304
|
+
canvas.style.width = `${rect.width}px`;
|
|
305
|
+
canvas.style.height = `${rect.height}px`;
|
|
306
|
+
const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;
|
|
307
|
+
const pixelW = Math.max(1, Math.round(rect.width * dpr));
|
|
308
|
+
const pixelH = Math.max(1, Math.round(rect.height * dpr));
|
|
309
|
+
if (canvas.width !== pixelW || canvas.height !== pixelH) {
|
|
310
|
+
canvas.width = pixelW;
|
|
311
|
+
canvas.height = pixelH;
|
|
312
|
+
}
|
|
313
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
314
|
+
return { width: rect.width, height: rect.height };
|
|
315
|
+
};
|
|
316
|
+
return {
|
|
317
|
+
canvas,
|
|
318
|
+
ctx,
|
|
319
|
+
sync,
|
|
320
|
+
setPickEnabled(on) {
|
|
321
|
+
pickEnabled = on;
|
|
322
|
+
canvas.style.pointerEvents = pickEnabled ? 'auto' : 'none';
|
|
323
|
+
canvas.style.cursor = pickEnabled ? 'crosshair' : 'default';
|
|
324
|
+
},
|
|
325
|
+
setVisible(on) {
|
|
326
|
+
visible = on;
|
|
327
|
+
canvas.style.display = on ? 'block' : 'none';
|
|
328
|
+
},
|
|
329
|
+
destroy() {
|
|
330
|
+
canvas.remove();
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
197
335
|
function isSceneSourceAnchor(v) {
|
|
198
336
|
return (!!v &&
|
|
199
337
|
typeof v === 'object' &&
|
|
@@ -587,12 +725,295 @@ function isClearSelectionMessage(data) {
|
|
|
587
725
|
return data.type === COMBOS_DEVELOPMENT_TOOL_CLEAR_SELECTION;
|
|
588
726
|
}
|
|
589
727
|
|
|
728
|
+
/** Screen-space pad used when a target has no positive size / projected extent. */
|
|
729
|
+
const DEFAULT_POINT_EXTENT = 32;
|
|
730
|
+
function identityMat() {
|
|
731
|
+
return { a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 };
|
|
732
|
+
}
|
|
733
|
+
function multiplyMat(p, l) {
|
|
734
|
+
return {
|
|
735
|
+
a: p.a * l.a + p.c * l.b,
|
|
736
|
+
b: p.b * l.a + p.d * l.b,
|
|
737
|
+
c: p.a * l.c + p.c * l.d,
|
|
738
|
+
d: p.b * l.c + p.d * l.d,
|
|
739
|
+
tx: p.a * l.tx + p.c * l.ty + p.tx,
|
|
740
|
+
ty: p.b * l.tx + p.d * l.ty + p.ty,
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
function applyMat(m, x, y) {
|
|
744
|
+
return {
|
|
745
|
+
x: m.a * x + m.c * y + m.tx,
|
|
746
|
+
y: m.b * x + m.d * y + m.ty,
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* Local matrix matching `plugin-renderer` ContainerManager:
|
|
751
|
+
* position (+ parent size * anchor), rotation, scale, pivot = size * origin.
|
|
752
|
+
*/
|
|
753
|
+
function localTransformMatrix(t) {
|
|
754
|
+
const x = t.position.x + (t.parent ? t.parent.size.width * t.anchor.x : 0);
|
|
755
|
+
const y = t.position.y + (t.parent ? t.parent.size.height * t.anchor.y : 0);
|
|
756
|
+
const pivotX = t.size.width * t.origin.x;
|
|
757
|
+
const pivotY = t.size.height * t.origin.y;
|
|
758
|
+
const cos = Math.cos(t.rotation);
|
|
759
|
+
const sin = Math.sin(t.rotation);
|
|
760
|
+
const a = cos * t.scale.x;
|
|
761
|
+
const b = sin * t.scale.x;
|
|
762
|
+
const c = -sin * t.scale.y;
|
|
763
|
+
const d = cos * t.scale.y;
|
|
764
|
+
return {
|
|
765
|
+
a,
|
|
766
|
+
b,
|
|
767
|
+
c,
|
|
768
|
+
d,
|
|
769
|
+
tx: x - (pivotX * a + pivotY * c),
|
|
770
|
+
ty: y - (pivotX * b + pivotY * d),
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
function worldTransformMatrix(t) {
|
|
774
|
+
const chain = [];
|
|
775
|
+
let cur = t;
|
|
776
|
+
while (cur) {
|
|
777
|
+
chain.push(cur);
|
|
778
|
+
cur = cur.parent;
|
|
779
|
+
}
|
|
780
|
+
chain.reverse();
|
|
781
|
+
let m = identityMat();
|
|
782
|
+
for (const node of chain) {
|
|
783
|
+
m = multiplyMat(m, localTransformMatrix(node));
|
|
784
|
+
}
|
|
785
|
+
return m;
|
|
786
|
+
}
|
|
787
|
+
function aabbFromPoints(points) {
|
|
788
|
+
if (points.length === 0)
|
|
789
|
+
return null;
|
|
790
|
+
let minX = Infinity;
|
|
791
|
+
let minY = Infinity;
|
|
792
|
+
let maxX = -Infinity;
|
|
793
|
+
let maxY = -Infinity;
|
|
794
|
+
for (const p of points) {
|
|
795
|
+
if (!Number.isFinite(p.x) || !Number.isFinite(p.y))
|
|
796
|
+
continue;
|
|
797
|
+
minX = Math.min(minX, p.x);
|
|
798
|
+
minY = Math.min(minY, p.y);
|
|
799
|
+
maxX = Math.max(maxX, p.x);
|
|
800
|
+
maxY = Math.max(maxY, p.y);
|
|
801
|
+
}
|
|
802
|
+
if (!Number.isFinite(minX) || !Number.isFinite(minY))
|
|
803
|
+
return null;
|
|
804
|
+
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
805
|
+
}
|
|
806
|
+
function designToCss(point, design, css) {
|
|
807
|
+
if (!design || design.width <= 0 || design.height <= 0) {
|
|
808
|
+
return { x: point.x, y: point.y };
|
|
809
|
+
}
|
|
810
|
+
return {
|
|
811
|
+
x: point.x * (css.width / design.width),
|
|
812
|
+
y: point.y * (css.height / design.height),
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
function cssToDesign(point, design, css) {
|
|
816
|
+
if (!design || design.width <= 0 || design.height <= 0 || css.width <= 0 || css.height <= 0) {
|
|
817
|
+
return { x: point.x, y: point.y };
|
|
818
|
+
}
|
|
819
|
+
return {
|
|
820
|
+
x: point.x * (design.width / css.width),
|
|
821
|
+
y: point.y * (design.height / css.height),
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
function readPositiveNumber(value) {
|
|
825
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : null;
|
|
826
|
+
}
|
|
827
|
+
function resolveDesignSize(game) {
|
|
828
|
+
const renderer = game?.getSystem?.('Renderer');
|
|
829
|
+
const screen = renderer?.application?.screen;
|
|
830
|
+
const sw = readPositiveNumber(screen?.width);
|
|
831
|
+
const sh = readPositiveNumber(screen?.height);
|
|
832
|
+
if (sw && sh) {
|
|
833
|
+
return { width: sw, height: sh };
|
|
834
|
+
}
|
|
835
|
+
const pw = readPositiveNumber(renderer?.params?.width);
|
|
836
|
+
const ph = readPositiveNumber(renderer?.params?.height);
|
|
837
|
+
if (pw && ph) {
|
|
838
|
+
return { width: pw, height: ph };
|
|
839
|
+
}
|
|
840
|
+
return null;
|
|
841
|
+
}
|
|
842
|
+
function resolveCamera(game) {
|
|
843
|
+
const renderer = game?.getSystem?.('Renderer3DSystem');
|
|
844
|
+
return renderer?.threeContext?.camera ?? null;
|
|
845
|
+
}
|
|
846
|
+
function resolveObject3D(game, id) {
|
|
847
|
+
const renderer = game?.getSystem?.('Renderer3DSystem');
|
|
848
|
+
return renderer?.threeContext?.nodes?.get?.(id) ?? null;
|
|
849
|
+
}
|
|
850
|
+
function asTransform3D(value) {
|
|
851
|
+
if (!value || typeof value !== 'object')
|
|
852
|
+
return null;
|
|
853
|
+
const t = value;
|
|
854
|
+
const hasWorld = [t.worldPositionX, t.worldPositionY, t.worldPositionZ].some(v => typeof v === 'number' && Number.isFinite(v));
|
|
855
|
+
const hasLocal = [t.positionX, t.positionY, t.positionZ].some(v => typeof v === 'number' && Number.isFinite(v));
|
|
856
|
+
return hasWorld || hasLocal ? t : null;
|
|
857
|
+
}
|
|
858
|
+
function multiplyMat4Vec4(m, x, y, z, w) {
|
|
859
|
+
return [
|
|
860
|
+
m[0] * x + m[4] * y + m[8] * z + m[12] * w,
|
|
861
|
+
m[1] * x + m[5] * y + m[9] * z + m[13] * w,
|
|
862
|
+
m[2] * x + m[6] * y + m[10] * z + m[14] * w,
|
|
863
|
+
m[3] * x + m[7] * y + m[11] * z + m[15] * w,
|
|
864
|
+
];
|
|
865
|
+
}
|
|
866
|
+
function projectWorldPoint(x, y, z, camera, css) {
|
|
867
|
+
const view = camera.matrixWorldInverse?.elements;
|
|
868
|
+
const proj = camera.projectionMatrix?.elements;
|
|
869
|
+
if (!view || view.length < 16 || !proj || proj.length < 16) {
|
|
870
|
+
return null;
|
|
871
|
+
}
|
|
872
|
+
if (css.width <= 0 || css.height <= 0) {
|
|
873
|
+
return null;
|
|
874
|
+
}
|
|
875
|
+
const viewed = multiplyMat4Vec4(view, x, y, z, 1);
|
|
876
|
+
const clip = multiplyMat4Vec4(proj, viewed[0], viewed[1], viewed[2], viewed[3]);
|
|
877
|
+
if (!Number.isFinite(clip[3]) || Math.abs(clip[3]) < 1e-8) {
|
|
878
|
+
return null;
|
|
879
|
+
}
|
|
880
|
+
if (clip[3] < 0) {
|
|
881
|
+
return null;
|
|
882
|
+
}
|
|
883
|
+
const ndcX = clip[0] / clip[3];
|
|
884
|
+
const ndcY = clip[1] / clip[3];
|
|
885
|
+
const ndcZ = clip[2] / clip[3];
|
|
886
|
+
return {
|
|
887
|
+
x: (ndcX + 1) * 0.5 * css.width,
|
|
888
|
+
y: (1 - ndcY) * 0.5 * css.height,
|
|
889
|
+
depth: ndcZ,
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
function transformMat4Point(m, x, y, z) {
|
|
893
|
+
const w = m[3] * x + m[7] * y + m[11] * z + m[15];
|
|
894
|
+
const invW = Math.abs(w) < 1e-8 ? 1 : 1 / w;
|
|
895
|
+
return [
|
|
896
|
+
(m[0] * x + m[4] * y + m[8] * z + m[12]) * invW,
|
|
897
|
+
(m[1] * x + m[5] * y + m[9] * z + m[13]) * invW,
|
|
898
|
+
(m[2] * x + m[6] * y + m[10] * z + m[14]) * invW,
|
|
899
|
+
];
|
|
900
|
+
}
|
|
901
|
+
function collectObject3DWorldCorners(node) {
|
|
902
|
+
const corners = [];
|
|
903
|
+
const visit = (obj) => {
|
|
904
|
+
const geo = obj.geometry;
|
|
905
|
+
if (!geo)
|
|
906
|
+
return;
|
|
907
|
+
geo.computeBoundingBox?.();
|
|
908
|
+
const box = geo.boundingBox;
|
|
909
|
+
const m = obj.matrixWorld?.elements;
|
|
910
|
+
if (!box || !m || m.length < 16)
|
|
911
|
+
return;
|
|
912
|
+
const { min, max } = box;
|
|
913
|
+
for (const x of [min.x, max.x]) {
|
|
914
|
+
for (const y of [min.y, max.y]) {
|
|
915
|
+
for (const z of [min.z, max.z]) {
|
|
916
|
+
corners.push(transformMat4Point(m, x, y, z));
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
node.updateWorldMatrix?.(true, true);
|
|
922
|
+
if (typeof node.traverse === 'function') {
|
|
923
|
+
node.traverse(visit);
|
|
924
|
+
}
|
|
925
|
+
else {
|
|
926
|
+
visit(node);
|
|
927
|
+
}
|
|
928
|
+
return corners;
|
|
929
|
+
}
|
|
930
|
+
function padAround(point, extent) {
|
|
931
|
+
const size = Math.max(1, extent);
|
|
932
|
+
return {
|
|
933
|
+
x: point.x - size / 2,
|
|
934
|
+
y: point.y - size / 2,
|
|
935
|
+
width: size,
|
|
936
|
+
height: size,
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
function projectTransform2D(transform, css, design) {
|
|
940
|
+
const matrix = worldTransformMatrix(transform);
|
|
941
|
+
const width = transform.size.width;
|
|
942
|
+
const height = transform.size.height;
|
|
943
|
+
const localCorners = width > 0 && height > 0
|
|
944
|
+
? [
|
|
945
|
+
{ x: 0, y: 0 },
|
|
946
|
+
{ x: width, y: 0 },
|
|
947
|
+
{ x: width, y: height },
|
|
948
|
+
{ x: 0, y: height },
|
|
949
|
+
]
|
|
950
|
+
: [{ x: 0, y: 0 }];
|
|
951
|
+
const world = localCorners.map(p => applyMat(matrix, p.x, p.y));
|
|
952
|
+
const screen = world.map(p => designToCss(p, design, css));
|
|
953
|
+
const aabb = aabbFromPoints(screen);
|
|
954
|
+
if (aabb && aabb.width > 0 && aabb.height > 0) {
|
|
955
|
+
return aabb;
|
|
956
|
+
}
|
|
957
|
+
const origin = screen[0] ?? { x: 0, y: 0 };
|
|
958
|
+
return padAround(origin, 1);
|
|
959
|
+
}
|
|
960
|
+
function projectTransform3D(transform, camera, css, object3D) {
|
|
961
|
+
if (object3D) {
|
|
962
|
+
const corners = collectObject3DWorldCorners(object3D);
|
|
963
|
+
const projected = corners
|
|
964
|
+
.map(([x, y, z]) => projectWorldPoint(x, y, z, camera, css))
|
|
965
|
+
.filter((p) => p != null);
|
|
966
|
+
const aabb = aabbFromPoints(projected);
|
|
967
|
+
if (aabb && aabb.width > 0 && aabb.height > 0) {
|
|
968
|
+
return aabb;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
const x = transform.worldPositionX ?? transform.positionX ?? 0;
|
|
972
|
+
const y = transform.worldPositionY ?? transform.positionY ?? 0;
|
|
973
|
+
const z = transform.worldPositionZ ?? transform.positionZ ?? 0;
|
|
974
|
+
const projected = projectWorldPoint(x, y, z, camera, css);
|
|
975
|
+
if (!projected)
|
|
976
|
+
return null;
|
|
977
|
+
const scale = Math.max(Math.abs(transform.scaleX ?? 1), Math.abs(transform.scaleY ?? 1), Math.abs(transform.scaleZ ?? 1), 1);
|
|
978
|
+
return padAround(projected, DEFAULT_POINT_EXTENT * scale);
|
|
979
|
+
}
|
|
980
|
+
function projectGameObjectToScreen(go, opts) {
|
|
981
|
+
if (opts.css.width <= 0 || opts.css.height <= 0) {
|
|
982
|
+
return null;
|
|
983
|
+
}
|
|
984
|
+
const t3d = asTransform3D(go.getComponent('Transform3D'));
|
|
985
|
+
if (t3d && opts.camera) {
|
|
986
|
+
return projectTransform3D(t3d, opts.camera, opts.css, opts.object3D);
|
|
987
|
+
}
|
|
988
|
+
return projectTransform2D(go.transform, opts.css, opts.design ?? null);
|
|
989
|
+
}
|
|
990
|
+
function rectContains(rect, x, y) {
|
|
991
|
+
return x >= rect.x && y >= rect.y && x <= rect.x + rect.width && y <= rect.y + rect.height;
|
|
992
|
+
}
|
|
993
|
+
/** Smallest containing rect wins; later items win ties (front-most in walk order). */
|
|
994
|
+
function hitTestRects(x, y, items) {
|
|
995
|
+
let best = null;
|
|
996
|
+
for (let i = 0; i < items.length; i++) {
|
|
997
|
+
const item = items[i];
|
|
998
|
+
if (!rectContains(item.rect, x, y))
|
|
999
|
+
continue;
|
|
1000
|
+
const area = Math.max(item.rect.width, 0) * Math.max(item.rect.height, 0);
|
|
1001
|
+
if (!best || area < best.area || (area === best.area && i > best.index)) {
|
|
1002
|
+
best = { id: item.id, area, index: i };
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
return best ? best.id : null;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
590
1008
|
const OUTLINE_GO_NAME = '__combosDevelopmentToolOutline';
|
|
591
1009
|
const MARKER_GO_NAME_PREFIX = '__combosDevelopmentToolMarker:';
|
|
1010
|
+
const OUTLINE_COLOR = '#55ffaa';
|
|
592
1011
|
/**
|
|
593
1012
|
* **Off** at start. While enabled:
|
|
594
|
-
* -
|
|
595
|
-
*
|
|
1013
|
+
* - a transparent HTML canvas overlay captures pointer events (game Event / Event3D
|
|
1014
|
+
* do not receive the tap);
|
|
1015
|
+
* - only nodes with {@link CombosDevelopmentToolTarget} are pickable;
|
|
1016
|
+
* - selection outline and markers are drawn with the Canvas 2D API (no renderer plugin).
|
|
596
1017
|
*
|
|
597
1018
|
* Turn on/off via:
|
|
598
1019
|
* - `window.dispatchEvent(new CustomEvent(COMBOS_DEVELOPMENT_TOOL_SET_PICK_MODE, { detail: { enabled: true } }))`
|
|
@@ -607,7 +1028,7 @@ const MARKER_GO_NAME_PREFIX = '__combosDevelopmentToolMarker:';
|
|
|
607
1028
|
* (`combos-game:set-playing` / `combos-game:state-changed`), not this tool.
|
|
608
1029
|
*
|
|
609
1030
|
* While enabled: `game.emit(COMBOS_DEVELOPMENT_TOOL_REFRESH)`, `window` event `combos-development-tool:refresh`,
|
|
610
|
-
* or `postMessage({ type: 'combos-development-tool:refresh' })`
|
|
1031
|
+
* or `postMessage({ type: 'combos-development-tool:refresh' })` after adding/removing objects.
|
|
611
1032
|
*/
|
|
612
1033
|
let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends engine.System {
|
|
613
1034
|
constructor() {
|
|
@@ -615,30 +1036,22 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
615
1036
|
this.postMessageOrigin = '*';
|
|
616
1037
|
this.allowedMessageOrigins = mergeAllowedMessageOrigins();
|
|
617
1038
|
this.enabled = false;
|
|
618
|
-
this.outlineGo = null;
|
|
619
1039
|
this.selected = null;
|
|
620
|
-
this.
|
|
621
|
-
this.
|
|
622
|
-
|
|
623
|
-
this.disabledGameEventGoIds = new Set();
|
|
624
|
-
/** Pick `Event` injected for {@link CombosDevelopmentToolTarget} nodes. */
|
|
625
|
-
this.pickEvents = new Set();
|
|
1040
|
+
this.overlay = null;
|
|
1041
|
+
this.overlayCss = { width: 0, height: 0 };
|
|
1042
|
+
this.pointerDownGoId = null;
|
|
626
1043
|
this.needsRescan = false;
|
|
627
|
-
this.lastOutlineBounds = null;
|
|
628
1044
|
/** Remembered mute when `SoundSystem` is not registered yet. */
|
|
629
1045
|
this.mutedWithoutSoundSystem = false;
|
|
630
1046
|
/**
|
|
631
1047
|
* Marker overlay (see {@link markerLayer}): a visual-only icon layer, toggled
|
|
632
|
-
* independently of pick mode + mute. Selection/persistence
|
|
633
|
-
*
|
|
634
|
-
* (added by the game code), so it is picked and serialized through the normal
|
|
635
|
-
* Target path. The icon just makes the invisible object visible and, by adding
|
|
636
|
-
* to the owner's rendered bounds, gives its Target pick a clickable hit area.
|
|
1048
|
+
* independently of pick mode + mute. Selection/persistence still go through
|
|
1049
|
+
* `CombosDevelopmentToolTarget` (added by the Vite plugin).
|
|
637
1050
|
*/
|
|
638
1051
|
this.markerOverlayEnabled = false;
|
|
639
1052
|
this.needsMarkerRescan = false;
|
|
640
|
-
/** ownerGoId →
|
|
641
|
-
this.
|
|
1053
|
+
/** ownerGoId → marker def currently shown. */
|
|
1054
|
+
this.markerOwners = new Set();
|
|
642
1055
|
this.onWindowSetPickMode = (e) => {
|
|
643
1056
|
const d = e.detail;
|
|
644
1057
|
if (d && typeof d.enabled === 'boolean') {
|
|
@@ -691,11 +1104,41 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
691
1104
|
this.onWindowRefresh = () => {
|
|
692
1105
|
this.requestSceneRescan();
|
|
693
1106
|
};
|
|
1107
|
+
this.onPointerDown = (e) => {
|
|
1108
|
+
if (!this.enabled)
|
|
1109
|
+
return;
|
|
1110
|
+
e.preventDefault();
|
|
1111
|
+
e.stopPropagation();
|
|
1112
|
+
try {
|
|
1113
|
+
this.overlay?.canvas.setPointerCapture(e.pointerId);
|
|
1114
|
+
}
|
|
1115
|
+
catch {
|
|
1116
|
+
/* ignore */
|
|
1117
|
+
}
|
|
1118
|
+
const hit = this.hitTestPointer(e);
|
|
1119
|
+
this.pointerDownGoId = hit?.id ?? null;
|
|
1120
|
+
};
|
|
1121
|
+
this.onPointerUp = (e) => {
|
|
1122
|
+
if (!this.enabled)
|
|
1123
|
+
return;
|
|
1124
|
+
e.preventDefault();
|
|
1125
|
+
e.stopPropagation();
|
|
1126
|
+
const hit = this.hitTestPointer(e);
|
|
1127
|
+
if (hit && hit.id === this.pointerDownGoId) {
|
|
1128
|
+
this.onSelect(hit, e);
|
|
1129
|
+
}
|
|
1130
|
+
this.pointerDownGoId = null;
|
|
1131
|
+
};
|
|
694
1132
|
}
|
|
695
1133
|
static { this.systemName = 'CombosDevelopmentToolSystem'; }
|
|
696
1134
|
init(params) {
|
|
697
1135
|
this.postMessageOrigin = params?.postMessageOrigin ?? '*';
|
|
698
1136
|
this.allowedMessageOrigins = mergeAllowedMessageOrigins(params?.allowedMessageOrigins);
|
|
1137
|
+
this.overlay = createOverlayCanvas();
|
|
1138
|
+
if (this.overlay) {
|
|
1139
|
+
this.overlay.canvas.addEventListener('pointerdown', this.onPointerDown);
|
|
1140
|
+
this.overlay.canvas.addEventListener('pointerup', this.onPointerUp);
|
|
1141
|
+
}
|
|
699
1142
|
if (typeof window !== 'undefined') {
|
|
700
1143
|
window.addEventListener(COMBOS_DEVELOPMENT_TOOL_SET_PICK_MODE, this.onWindowSetPickMode);
|
|
701
1144
|
window.addEventListener(COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY, this.onWindowSetMarkerOverlay);
|
|
@@ -705,11 +1148,18 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
705
1148
|
this.game.on(COMBOS_DEVELOPMENT_TOOL_SET_PICK_MODE, this.onGameSetPickMode);
|
|
706
1149
|
this.game.on(COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY, this.onGameSetMarkerOverlay);
|
|
707
1150
|
this.game.on(COMBOS_DEVELOPMENT_TOOL_REFRESH, this.onGameRefresh);
|
|
1151
|
+
this.syncOverlayMode();
|
|
708
1152
|
if (this.enabled) {
|
|
709
1153
|
this.needsRescan = true;
|
|
710
1154
|
}
|
|
711
1155
|
}
|
|
712
1156
|
onDestroy() {
|
|
1157
|
+
if (this.overlay) {
|
|
1158
|
+
this.overlay.canvas.removeEventListener('pointerdown', this.onPointerDown);
|
|
1159
|
+
this.overlay.canvas.removeEventListener('pointerup', this.onPointerUp);
|
|
1160
|
+
this.overlay.destroy();
|
|
1161
|
+
this.overlay = null;
|
|
1162
|
+
}
|
|
713
1163
|
if (typeof window !== 'undefined') {
|
|
714
1164
|
window.removeEventListener(COMBOS_DEVELOPMENT_TOOL_SET_PICK_MODE, this.onWindowSetPickMode);
|
|
715
1165
|
window.removeEventListener(COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY, this.onWindowSetMarkerOverlay);
|
|
@@ -719,9 +1169,7 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
719
1169
|
this.game.off(COMBOS_DEVELOPMENT_TOOL_SET_PICK_MODE, this.onGameSetPickMode);
|
|
720
1170
|
this.game.off(COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY, this.onGameSetMarkerOverlay);
|
|
721
1171
|
this.game.off(COMBOS_DEVELOPMENT_TOOL_REFRESH, this.onGameRefresh);
|
|
722
|
-
this.
|
|
723
|
-
this.detachAllMarkers();
|
|
724
|
-
this.restoreDisabledGameEvents();
|
|
1172
|
+
this.markerOwners.clear();
|
|
725
1173
|
this.clearSelectionAndNotify('pick-disabled');
|
|
726
1174
|
}
|
|
727
1175
|
/** Programmatic toggle (same effect as events). */
|
|
@@ -729,14 +1177,14 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
729
1177
|
if (this.enabled === on)
|
|
730
1178
|
return;
|
|
731
1179
|
this.enabled = on;
|
|
1180
|
+
this.syncOverlayMode();
|
|
732
1181
|
if (!on) {
|
|
733
1182
|
this.clearSelectionAndNotify('pick-disabled');
|
|
734
|
-
this.detachAllPicks();
|
|
735
|
-
this.restoreDisabledGameEvents();
|
|
736
1183
|
this.postSetSuccess(false);
|
|
737
1184
|
}
|
|
738
1185
|
else {
|
|
739
1186
|
this.needsRescan = true;
|
|
1187
|
+
this.redrawOverlay();
|
|
740
1188
|
}
|
|
741
1189
|
}
|
|
742
1190
|
get isEnabled() {
|
|
@@ -745,27 +1193,21 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
745
1193
|
/**
|
|
746
1194
|
* Toggle the marker overlay: pin a persistent icon on every object that owns a
|
|
747
1195
|
* registered invisible component ({@link markerLayer}, Sound first). Independent
|
|
748
|
-
* of pick mode / mute.
|
|
749
|
-
*
|
|
750
|
-
* `CombosDevelopmentToolTarget` pick gets a clickable hit area. Selecting and
|
|
751
|
-
* persisting edits (e.g. volume) still go through that Target, exactly like any
|
|
752
|
-
* other scene edit, so the object must carry a Target (added by the game code).
|
|
1196
|
+
* of pick mode / mute. Selecting and persisting edits still go through the
|
|
1197
|
+
* owner's `CombosDevelopmentToolTarget`.
|
|
753
1198
|
*/
|
|
754
1199
|
setMarkerOverlay(on) {
|
|
755
1200
|
if (this.markerOverlayEnabled === on)
|
|
756
1201
|
return;
|
|
757
1202
|
this.markerOverlayEnabled = on;
|
|
1203
|
+
this.syncOverlayMode();
|
|
758
1204
|
if (on) {
|
|
759
|
-
// `attachMarkers` requests the pick rescan itself, once the icons exist.
|
|
760
1205
|
this.needsMarkerRescan = true;
|
|
761
1206
|
}
|
|
762
1207
|
else {
|
|
763
|
-
this.
|
|
1208
|
+
this.markerOwners.clear();
|
|
764
1209
|
this.postMarkerOverlaySuccess(false, []);
|
|
765
|
-
|
|
766
|
-
if (this.enabled) {
|
|
767
|
-
this.needsRescan = true;
|
|
768
|
-
}
|
|
1210
|
+
this.redrawOverlay();
|
|
769
1211
|
}
|
|
770
1212
|
}
|
|
771
1213
|
get isMarkerOverlayEnabled() {
|
|
@@ -791,51 +1233,31 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
791
1233
|
}
|
|
792
1234
|
this.mutedWithoutSoundSystem = muted;
|
|
793
1235
|
}
|
|
794
|
-
update(
|
|
1236
|
+
update() {
|
|
795
1237
|
if (this.enabled && this.needsRescan) {
|
|
796
1238
|
this.needsRescan = false;
|
|
797
|
-
this.attachEditMode();
|
|
798
1239
|
this.postSetSuccess(true);
|
|
799
1240
|
}
|
|
800
|
-
// Attaching markers is deferred to run *after* the pick rescan above so that a
|
|
801
|
-
// newly drawn icon's rebuild lands on a later frame (see `attachMarkers`): the
|
|
802
|
-
// icon child needs a rendered container before it contributes to the owner's
|
|
803
|
-
// bounds, and only then does the owner's Target pick get a hit area over it.
|
|
804
1241
|
if (this.markerOverlayEnabled && this.needsMarkerRescan) {
|
|
805
1242
|
this.needsMarkerRescan = false;
|
|
806
1243
|
this.attachMarkers();
|
|
807
1244
|
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
this.releasePick(go);
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
// Drop markers whose owner disappeared while the overlay is on.
|
|
814
|
-
for (const [ownerId, marker] of [...this.markerGoByOwner.entries()]) {
|
|
815
|
-
const owner = this.findGameObjectById(ownerId);
|
|
816
|
-
if (!owner || owner.destroyed || marker.destroyed) {
|
|
817
|
-
this.removeMarker(ownerId);
|
|
818
|
-
}
|
|
1245
|
+
if (this.selected?.destroyed) {
|
|
1246
|
+
this.clearSelectionAndNotify('target-removed');
|
|
819
1247
|
}
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
this.
|
|
1248
|
+
}
|
|
1249
|
+
lateUpdate(_e) {
|
|
1250
|
+
this.redrawOverlay();
|
|
823
1251
|
}
|
|
824
1252
|
componentChanged(changed) {
|
|
825
|
-
if (!this.enabled)
|
|
826
|
-
return;
|
|
827
1253
|
const { type, gameObject, componentName } = changed;
|
|
828
1254
|
if (!gameObject || componentName !== 'CombosDevelopmentToolTarget')
|
|
829
1255
|
return;
|
|
830
|
-
if (type === engine.OBSERVER_TYPE.
|
|
831
|
-
this.
|
|
832
|
-
this.ensurePick(gameObject);
|
|
1256
|
+
if (type === engine.OBSERVER_TYPE.REMOVE && this.selected === gameObject) {
|
|
1257
|
+
this.clearSelectionAndNotify('target-removed');
|
|
833
1258
|
}
|
|
834
|
-
|
|
835
|
-
this.
|
|
836
|
-
if (this.selected === gameObject) {
|
|
837
|
-
this.clearSelectionAndNotify('target-removed');
|
|
838
|
-
}
|
|
1259
|
+
if (this.markerOverlayEnabled) {
|
|
1260
|
+
this.needsMarkerRescan = true;
|
|
839
1261
|
}
|
|
840
1262
|
}
|
|
841
1263
|
/** Re-bind pick targets / markers after dynamic scene changes while active. */
|
|
@@ -847,24 +1269,10 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
847
1269
|
this.needsMarkerRescan = true;
|
|
848
1270
|
}
|
|
849
1271
|
}
|
|
850
|
-
|
|
851
|
-
this.
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
this.collectGameObjects(tr.gameObject, list);
|
|
855
|
-
}
|
|
856
|
-
for (const go of list) {
|
|
857
|
-
if (this.isIgnoredPickGo(go))
|
|
858
|
-
continue;
|
|
859
|
-
this.stripGameEvent(go);
|
|
860
|
-
}
|
|
861
|
-
for (const go of list) {
|
|
862
|
-
if (this.isIgnoredPickGo(go))
|
|
863
|
-
continue;
|
|
864
|
-
if (!go.getComponent(CombosDevelopmentToolTarget))
|
|
865
|
-
continue;
|
|
866
|
-
this.ensurePick(go);
|
|
867
|
-
}
|
|
1272
|
+
syncOverlayMode() {
|
|
1273
|
+
const active = this.enabled || this.markerOverlayEnabled;
|
|
1274
|
+
this.overlay?.setVisible(active);
|
|
1275
|
+
this.overlay?.setPickEnabled(this.enabled);
|
|
868
1276
|
}
|
|
869
1277
|
collectGameObjects(go, out) {
|
|
870
1278
|
out.push(go);
|
|
@@ -872,203 +1280,134 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
872
1280
|
this.collectGameObjects(tr.gameObject, out);
|
|
873
1281
|
}
|
|
874
1282
|
}
|
|
1283
|
+
listSceneObjects() {
|
|
1284
|
+
const list = [];
|
|
1285
|
+
const scene = this.game.scene;
|
|
1286
|
+
if (!scene?.transform?.children)
|
|
1287
|
+
return list;
|
|
1288
|
+
for (const tr of scene.transform.children) {
|
|
1289
|
+
this.collectGameObjects(tr.gameObject, list);
|
|
1290
|
+
}
|
|
1291
|
+
return list;
|
|
1292
|
+
}
|
|
875
1293
|
isIgnoredPickGo(go) {
|
|
876
|
-
return go.name === OUTLINE_GO_NAME ||
|
|
1294
|
+
return go.name === OUTLINE_GO_NAME || this.isMarkerGo(go);
|
|
877
1295
|
}
|
|
878
1296
|
isMarkerGo(go) {
|
|
879
1297
|
return typeof go.name === 'string' && go.name.startsWith(MARKER_GO_NAME_PREFIX);
|
|
880
1298
|
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
if (!container)
|
|
889
|
-
return;
|
|
890
|
-
try {
|
|
891
|
-
container.interactive = false;
|
|
892
|
-
this.disabledGameEventGoIds.add(go.id);
|
|
893
|
-
}
|
|
894
|
-
catch {
|
|
895
|
-
/* ignore */
|
|
896
|
-
}
|
|
1299
|
+
projectGo(go) {
|
|
1300
|
+
return projectGameObjectToScreen(go, {
|
|
1301
|
+
css: this.overlayCss,
|
|
1302
|
+
design: resolveDesignSize(this.game),
|
|
1303
|
+
camera: resolveCamera(this.game),
|
|
1304
|
+
object3D: resolveObject3D(this.game, go.id),
|
|
1305
|
+
});
|
|
897
1306
|
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
if (
|
|
902
|
-
this.disabledGameEventGoIds.delete(id);
|
|
1307
|
+
collectOverlayHits() {
|
|
1308
|
+
const hits = [];
|
|
1309
|
+
for (const go of this.listSceneObjects()) {
|
|
1310
|
+
if (go.destroyed || this.isIgnoredPickGo(go))
|
|
903
1311
|
continue;
|
|
1312
|
+
if (!go.getComponent(CombosDevelopmentToolTarget))
|
|
1313
|
+
continue;
|
|
1314
|
+
const rect = this.projectGo(go);
|
|
1315
|
+
if (rect) {
|
|
1316
|
+
hits.push({ go, rect });
|
|
904
1317
|
}
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
}
|
|
910
|
-
catch {
|
|
911
|
-
/* ignore */
|
|
1318
|
+
if (this.markerOverlayEnabled && resolveMarkerDef(go)) {
|
|
1319
|
+
const markerRect = this.markerRectFor(go, rect);
|
|
1320
|
+
if (markerRect) {
|
|
1321
|
+
hits.push({ go, rect: markerRect });
|
|
912
1322
|
}
|
|
913
1323
|
}
|
|
914
|
-
this.disabledGameEventGoIds.delete(id);
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
detachAllPicks() {
|
|
918
|
-
for (const go of [...this.tapOwners.values()]) {
|
|
919
|
-
this.releasePick(go);
|
|
920
1324
|
}
|
|
1325
|
+
return hits;
|
|
921
1326
|
}
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
const container = this.getRendererContainer(go);
|
|
926
|
-
if (container) {
|
|
927
|
-
try {
|
|
928
|
-
container.interactive = true;
|
|
929
|
-
}
|
|
930
|
-
catch {
|
|
931
|
-
/* ignore */
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
let ev = go.getComponent(pluginRendererEvent.Event);
|
|
935
|
-
if (!ev) {
|
|
936
|
-
ev = go.addComponent(this.createPickEvent(go));
|
|
937
|
-
this.pickEvents.add(go.id);
|
|
938
|
-
}
|
|
939
|
-
const handler = payload => this.onSelect(go, payload);
|
|
940
|
-
this.tapHandlers.set(go.id, handler);
|
|
941
|
-
this.tapOwners.set(go.id, go);
|
|
942
|
-
ev.on('tap', handler);
|
|
943
|
-
}
|
|
944
|
-
createPickEvent(go) {
|
|
945
|
-
const bounds = this.resolvePickBounds(go);
|
|
946
|
-
if (bounds.width > 0 && bounds.height > 0) {
|
|
947
|
-
return new pluginRendererEvent.Event({
|
|
948
|
-
hitArea: {
|
|
949
|
-
type: pluginRendererEvent.HIT_AREA_TYPE.Rect,
|
|
950
|
-
style: {
|
|
951
|
-
x: bounds.x,
|
|
952
|
-
y: bounds.y,
|
|
953
|
-
width: bounds.width,
|
|
954
|
-
height: bounds.height,
|
|
955
|
-
},
|
|
956
|
-
},
|
|
957
|
-
});
|
|
958
|
-
}
|
|
959
|
-
return new pluginRendererEvent.Event();
|
|
960
|
-
}
|
|
961
|
-
resolvePickBounds(go) {
|
|
962
|
-
if (this.shouldPreferRenderedBounds(go)) {
|
|
963
|
-
const fromOwnGraphics = this.resolveOwnGraphicsLocalBounds(go);
|
|
964
|
-
if (fromOwnGraphics) {
|
|
965
|
-
return fromOwnGraphics;
|
|
966
|
-
}
|
|
967
|
-
const fromGraphics = this.resolveContainerLocalBounds(go);
|
|
968
|
-
if (fromGraphics) {
|
|
969
|
-
return fromGraphics;
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
const { width, height } = go.transform.size;
|
|
973
|
-
if (width > 0 && height > 0) {
|
|
974
|
-
return { x: 0, y: 0, width, height };
|
|
975
|
-
}
|
|
976
|
-
const fromContainer = this.resolveContainerLocalBounds(go);
|
|
977
|
-
if (fromContainer) {
|
|
978
|
-
return fromContainer;
|
|
979
|
-
}
|
|
980
|
-
return { x: 0, y: 0, width: 1, height: 1 };
|
|
981
|
-
}
|
|
982
|
-
shouldPreferRenderedBounds(go) {
|
|
983
|
-
return go.getComponent(pluginRendererGraphics.Graphics) != null;
|
|
984
|
-
}
|
|
985
|
-
/** Bounds of this GO's own Graphics only — excludes child GOs such as the selection outline. */
|
|
986
|
-
resolveOwnGraphicsLocalBounds(go) {
|
|
987
|
-
const gfx = go.getComponent(pluginRendererGraphics.Graphics);
|
|
988
|
-
if (!gfx?.graphics)
|
|
1327
|
+
markerRectFor(go, objectRect) {
|
|
1328
|
+
const rect = objectRect ?? this.projectGo(go);
|
|
1329
|
+
if (!rect)
|
|
989
1330
|
return null;
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
height: bounds.height,
|
|
998
|
-
};
|
|
999
|
-
}
|
|
1331
|
+
if (go.getComponent('Transform3D')) {
|
|
1332
|
+
return {
|
|
1333
|
+
x: rect.x + rect.width / 2 - MARKER_ICON_SIZE / 2,
|
|
1334
|
+
y: rect.y + rect.height / 2 - MARKER_ICON_SIZE / 2,
|
|
1335
|
+
width: MARKER_ICON_SIZE,
|
|
1336
|
+
height: MARKER_ICON_SIZE,
|
|
1337
|
+
};
|
|
1000
1338
|
}
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
const container = this.getRendererContainer(go);
|
|
1008
|
-
if (!container)
|
|
1009
|
-
return null;
|
|
1010
|
-
const outlineContainer = this.getOutlineContainerIfChildOf(go);
|
|
1011
|
-
let outlineDetached = false;
|
|
1012
|
-
if (outlineContainer?.parent === container) {
|
|
1013
|
-
container.removeChild(outlineContainer);
|
|
1014
|
-
outlineDetached = true;
|
|
1015
|
-
}
|
|
1016
|
-
try {
|
|
1017
|
-
const bounds = container.getLocalBounds();
|
|
1018
|
-
if (bounds.width > 0 && bounds.height > 0) {
|
|
1019
|
-
return {
|
|
1020
|
-
x: bounds.x,
|
|
1021
|
-
y: bounds.y,
|
|
1022
|
-
width: bounds.width,
|
|
1023
|
-
height: bounds.height,
|
|
1024
|
-
};
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
catch {
|
|
1028
|
-
/* ignore */
|
|
1029
|
-
}
|
|
1030
|
-
finally {
|
|
1031
|
-
if (outlineDetached && outlineContainer) {
|
|
1032
|
-
container.addChild(outlineContainer);
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
return null;
|
|
1339
|
+
return {
|
|
1340
|
+
x: rect.x,
|
|
1341
|
+
y: rect.y,
|
|
1342
|
+
width: MARKER_ICON_SIZE,
|
|
1343
|
+
height: MARKER_ICON_SIZE,
|
|
1344
|
+
};
|
|
1036
1345
|
}
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
if (!
|
|
1040
|
-
return
|
|
1346
|
+
overlayPoint(e) {
|
|
1347
|
+
const canvas = this.overlay?.canvas;
|
|
1348
|
+
if (!canvas)
|
|
1349
|
+
return { x: 0, y: 0 };
|
|
1350
|
+
const rect = canvas.getBoundingClientRect();
|
|
1351
|
+
return { x: e.clientX - rect.left, y: e.clientY - rect.top };
|
|
1352
|
+
}
|
|
1353
|
+
hitTestPointer(e) {
|
|
1354
|
+
const pt = this.overlayPoint(e);
|
|
1355
|
+
const hits = this.collectOverlayHits();
|
|
1356
|
+
return hitTestRects(pt.x, pt.y, hits.map(h => ({ id: h.go, rect: h.rect })));
|
|
1357
|
+
}
|
|
1358
|
+
redrawOverlay() {
|
|
1359
|
+
const overlay = this.overlay;
|
|
1360
|
+
if (!overlay)
|
|
1361
|
+
return;
|
|
1362
|
+
const active = this.enabled || this.markerOverlayEnabled;
|
|
1363
|
+
if (!active) {
|
|
1364
|
+
overlay.setVisible(false);
|
|
1365
|
+
return;
|
|
1041
1366
|
}
|
|
1042
|
-
|
|
1367
|
+
const gameCanvas = this.game.canvas ?? this.game.scene?.canvas;
|
|
1368
|
+
if (!gameCanvas)
|
|
1369
|
+
return;
|
|
1370
|
+
const css = overlay.sync(gameCanvas);
|
|
1371
|
+
if (!css)
|
|
1372
|
+
return;
|
|
1373
|
+
this.overlayCss = css;
|
|
1374
|
+
overlay.ctx.clearRect(0, 0, css.width, css.height);
|
|
1375
|
+
this.drawMarkers(overlay.ctx);
|
|
1376
|
+
this.drawSelectionOutline(overlay.ctx);
|
|
1043
1377
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1378
|
+
drawMarkers(ctx) {
|
|
1379
|
+
if (!this.markerOverlayEnabled)
|
|
1380
|
+
return;
|
|
1381
|
+
const gfx = new CanvasMarkerGraphics(ctx);
|
|
1382
|
+
for (const go of this.listSceneObjects()) {
|
|
1383
|
+
if (go.destroyed || this.isIgnoredPickGo(go))
|
|
1384
|
+
continue;
|
|
1385
|
+
const def = resolveMarkerDef(go);
|
|
1386
|
+
if (!def)
|
|
1387
|
+
continue;
|
|
1388
|
+
const objectRect = this.projectGo(go);
|
|
1389
|
+
const rect = this.markerRectFor(go, objectRect);
|
|
1390
|
+
if (!rect)
|
|
1391
|
+
continue;
|
|
1392
|
+
ctx.save();
|
|
1393
|
+
ctx.translate(rect.x, rect.y);
|
|
1394
|
+
drawMarkerIcon(gfx, def, MARKER_ICON_SIZE);
|
|
1395
|
+
ctx.restore();
|
|
1053
1396
|
}
|
|
1054
1397
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
if (!handler)
|
|
1398
|
+
drawSelectionOutline(ctx) {
|
|
1399
|
+
if (!this.enabled || !this.selected || this.selected.destroyed)
|
|
1058
1400
|
return;
|
|
1059
|
-
const
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
this.
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
}
|
|
1070
|
-
this.pickEvents.delete(go.id);
|
|
1071
|
-
}
|
|
1401
|
+
const rect = this.projectGo(this.selected);
|
|
1402
|
+
if (!rect)
|
|
1403
|
+
return;
|
|
1404
|
+
const design = resolveDesignSize(this.game);
|
|
1405
|
+
const scale = design ? this.overlayCss.width / design.width : 1;
|
|
1406
|
+
ctx.save();
|
|
1407
|
+
ctx.strokeStyle = OUTLINE_COLOR;
|
|
1408
|
+
ctx.lineWidth = Math.max(2, 4 * (Number.isFinite(scale) && scale > 0 ? scale : 1));
|
|
1409
|
+
ctx.strokeRect(rect.x, rect.y, rect.width, rect.height);
|
|
1410
|
+
ctx.restore();
|
|
1072
1411
|
}
|
|
1073
1412
|
postSetSuccess(enabled) {
|
|
1074
1413
|
const payload = {
|
|
@@ -1080,21 +1419,16 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
1080
1419
|
}
|
|
1081
1420
|
this.game.emit(COMBOS_DEVELOPMENT_TOOL_PICK_MODE_SUCCESS, { enabled });
|
|
1082
1421
|
}
|
|
1083
|
-
/** (Re)build markers for every object owning a registered marker component. */
|
|
1084
1422
|
attachMarkers() {
|
|
1085
|
-
this.
|
|
1086
|
-
const list = [];
|
|
1087
|
-
for (const tr of this.game.scene.transform.children) {
|
|
1088
|
-
this.collectGameObjects(tr.gameObject, list);
|
|
1089
|
-
}
|
|
1423
|
+
this.markerOwners.clear();
|
|
1090
1424
|
const counts = new Map();
|
|
1091
|
-
for (const go of
|
|
1425
|
+
for (const go of this.listSceneObjects()) {
|
|
1092
1426
|
if (this.isIgnoredPickGo(go))
|
|
1093
1427
|
continue;
|
|
1094
1428
|
const def = resolveMarkerDef(go);
|
|
1095
1429
|
if (!def)
|
|
1096
1430
|
continue;
|
|
1097
|
-
this.
|
|
1431
|
+
this.markerOwners.add(go.id);
|
|
1098
1432
|
counts.set(def.componentName, (counts.get(def.componentName) ?? 0) + 1);
|
|
1099
1433
|
}
|
|
1100
1434
|
const markers = [...counts.entries()].map(([componentName, count]) => ({
|
|
@@ -1102,43 +1436,7 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
1102
1436
|
count,
|
|
1103
1437
|
}));
|
|
1104
1438
|
this.postMarkerOverlaySuccess(true, markers);
|
|
1105
|
-
|
|
1106
|
-
// before they enlarge their owners' bounds into a clickable hit area. Runs on
|
|
1107
|
-
// a later frame because the pick rescan block already executed above.
|
|
1108
|
-
if (this.enabled) {
|
|
1109
|
-
this.needsRescan = true;
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
createMarker(owner, def) {
|
|
1113
|
-
if (this.markerGoByOwner.has(owner.id))
|
|
1114
|
-
return;
|
|
1115
|
-
const bounds = this.resolvePickBounds(owner);
|
|
1116
|
-
const marker = new engine.GameObject(MARKER_GO_NAME_PREFIX + owner.id, {
|
|
1117
|
-
size: { width: MARKER_ICON_SIZE, height: MARKER_ICON_SIZE },
|
|
1118
|
-
position: { x: bounds.x, y: bounds.y },
|
|
1119
|
-
origin: { x: 0, y: 0 },
|
|
1120
|
-
});
|
|
1121
|
-
const gfx = marker.addComponent(new pluginRendererGraphics.Graphics());
|
|
1122
|
-
if (gfx?.graphics) {
|
|
1123
|
-
drawMarkerIcon(gfx.graphics, def, MARKER_ICON_SIZE);
|
|
1124
|
-
}
|
|
1125
|
-
owner.addChild(marker);
|
|
1126
|
-
this.markerGoByOwner.set(owner.id, marker);
|
|
1127
|
-
}
|
|
1128
|
-
removeMarker(ownerId) {
|
|
1129
|
-
const marker = this.markerGoByOwner.get(ownerId);
|
|
1130
|
-
if (!marker)
|
|
1131
|
-
return;
|
|
1132
|
-
this.markerGoByOwner.delete(ownerId);
|
|
1133
|
-
if (!marker.destroyed) {
|
|
1134
|
-
marker.destroy();
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
detachAllMarkers() {
|
|
1138
|
-
for (const ownerId of [...this.markerGoByOwner.keys()]) {
|
|
1139
|
-
this.removeMarker(ownerId);
|
|
1140
|
-
}
|
|
1141
|
-
this.markerGoByOwner.clear();
|
|
1439
|
+
this.redrawOverlay();
|
|
1142
1440
|
}
|
|
1143
1441
|
postMarkerOverlaySuccess(enabled, markers) {
|
|
1144
1442
|
const total = markers.reduce((sum, m) => sum + m.count, 0);
|
|
@@ -1162,59 +1460,33 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
1162
1460
|
}
|
|
1163
1461
|
return system;
|
|
1164
1462
|
}
|
|
1165
|
-
|
|
1166
|
-
if (this.outlineGo)
|
|
1167
|
-
return;
|
|
1168
|
-
const go = new engine.GameObject(OUTLINE_GO_NAME, {
|
|
1169
|
-
size: { width: 1, height: 1 },
|
|
1170
|
-
position: { x: 0, y: 0 },
|
|
1171
|
-
origin: { x: 0, y: 0 },
|
|
1172
|
-
});
|
|
1173
|
-
go.addComponent(new pluginRendererGraphics.Graphics());
|
|
1174
|
-
this.outlineGo = go;
|
|
1175
|
-
}
|
|
1176
|
-
onSelect(go, tap) {
|
|
1463
|
+
onSelect(go, e) {
|
|
1177
1464
|
if (!this.enabled)
|
|
1178
1465
|
return;
|
|
1179
|
-
tap?.stopPropagation?.();
|
|
1180
|
-
this.ensureOutline();
|
|
1181
|
-
if (!this.outlineGo)
|
|
1182
|
-
return;
|
|
1183
1466
|
this.selected = go;
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
go.addChild(this.outlineGo);
|
|
1188
|
-
this.invalidateOutlineBounds();
|
|
1189
|
-
this.updateSelectionOutline();
|
|
1467
|
+
this.redrawOverlay();
|
|
1468
|
+
const overlayPt = this.overlayPoint(e);
|
|
1469
|
+
const pointer = cssToDesign(overlayPt, resolveDesignSize(this.game), this.overlayCss);
|
|
1190
1470
|
const snapshot = buildGameObjectSnapshot(go);
|
|
1191
1471
|
const payload = {
|
|
1192
1472
|
type: COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_SELECTED,
|
|
1193
1473
|
snapshot,
|
|
1194
|
-
pointer
|
|
1474
|
+
pointer,
|
|
1195
1475
|
};
|
|
1196
1476
|
if (typeof window !== 'undefined' && window.parent && window.parent !== window) {
|
|
1197
1477
|
window.parent.postMessage(payload, this.postMessageOrigin);
|
|
1198
1478
|
}
|
|
1199
1479
|
}
|
|
1200
1480
|
findGameObjectById(id) {
|
|
1201
|
-
|
|
1202
|
-
for (const tr of this.game.scene.transform.children) {
|
|
1203
|
-
this.collectGameObjects(tr.gameObject, stack);
|
|
1204
|
-
}
|
|
1205
|
-
return stack.find(go => go.id === id) ?? null;
|
|
1481
|
+
return this.listSceneObjects().find(go => go.id === id) ?? null;
|
|
1206
1482
|
}
|
|
1207
1483
|
findGameObjectBySource(source) {
|
|
1208
|
-
const stack = [];
|
|
1209
|
-
for (const tr of this.game.scene.transform.children) {
|
|
1210
|
-
this.collectGameObjects(tr.gameObject, stack);
|
|
1211
|
-
}
|
|
1212
1484
|
const wantFile = source.file.trim();
|
|
1213
1485
|
const wantAnchor = (source.anchor ?? '').trim();
|
|
1214
1486
|
if (!wantAnchor) {
|
|
1215
1487
|
return null;
|
|
1216
1488
|
}
|
|
1217
|
-
for (const go of
|
|
1489
|
+
for (const go of this.listSceneObjects()) {
|
|
1218
1490
|
const anchor = readSourceAnchor(go);
|
|
1219
1491
|
if (!anchor || anchor.file !== wantFile) {
|
|
1220
1492
|
continue;
|
|
@@ -1258,8 +1530,7 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
1258
1530
|
if (!applied)
|
|
1259
1531
|
return;
|
|
1260
1532
|
if (this.selected?.id === go.id) {
|
|
1261
|
-
this.
|
|
1262
|
-
this.updateSelectionOutline();
|
|
1533
|
+
this.redrawOverlay();
|
|
1263
1534
|
this.postSnapshotUpdate(go);
|
|
1264
1535
|
}
|
|
1265
1536
|
}
|
|
@@ -1276,7 +1547,8 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
1276
1547
|
}
|
|
1277
1548
|
clearSelectionAndNotify(reason) {
|
|
1278
1549
|
const hadSelection = this.selected != null;
|
|
1279
|
-
this.
|
|
1550
|
+
this.selected = null;
|
|
1551
|
+
this.redrawOverlay();
|
|
1280
1552
|
if (!shouldNotifyGameObjectDeselected(hadSelection)) {
|
|
1281
1553
|
return;
|
|
1282
1554
|
}
|
|
@@ -1289,44 +1561,6 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
1289
1561
|
}
|
|
1290
1562
|
this.game.emit(payload.type, { reason });
|
|
1291
1563
|
}
|
|
1292
|
-
clearSelection() {
|
|
1293
|
-
this.selected = null;
|
|
1294
|
-
this.invalidateOutlineBounds();
|
|
1295
|
-
if (this.outlineGo?.parent) {
|
|
1296
|
-
this.outlineGo.remove();
|
|
1297
|
-
}
|
|
1298
|
-
const gfx = this.outlineGo?.getComponent(pluginRendererGraphics.Graphics);
|
|
1299
|
-
if (gfx?.graphics) {
|
|
1300
|
-
gfx.graphics.clear();
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
/** Redraw the green outline only when content bounds change (movement follows via child transform). */
|
|
1304
|
-
updateSelectionOutline() {
|
|
1305
|
-
if (!this.enabled || !this.selected || !this.outlineGo)
|
|
1306
|
-
return;
|
|
1307
|
-
const gfxComp = this.outlineGo.getComponent(pluginRendererGraphics.Graphics);
|
|
1308
|
-
if (!gfxComp?.graphics)
|
|
1309
|
-
return;
|
|
1310
|
-
const bounds = this.resolvePickBounds(this.selected);
|
|
1311
|
-
if (this.lastOutlineBounds && this.pickBoundsEqual(this.lastOutlineBounds, bounds)) {
|
|
1312
|
-
return;
|
|
1313
|
-
}
|
|
1314
|
-
this.lastOutlineBounds = bounds;
|
|
1315
|
-
const g = gfxComp.graphics;
|
|
1316
|
-
g.clear();
|
|
1317
|
-
g.rect(bounds.x, bounds.y, bounds.width, bounds.height);
|
|
1318
|
-
g.stroke({ width: 4, color: 0x55ffaa, alpha: 1 });
|
|
1319
|
-
}
|
|
1320
|
-
invalidateOutlineBounds() {
|
|
1321
|
-
this.lastOutlineBounds = null;
|
|
1322
|
-
}
|
|
1323
|
-
pickBoundsEqual(a, b) {
|
|
1324
|
-
const eps = 0.01;
|
|
1325
|
-
return (Math.abs(a.x - b.x) < eps &&
|
|
1326
|
-
Math.abs(a.y - b.y) < eps &&
|
|
1327
|
-
Math.abs(a.width - b.width) < eps &&
|
|
1328
|
-
Math.abs(a.height - b.height) < eps);
|
|
1329
|
-
}
|
|
1330
1564
|
};
|
|
1331
1565
|
CombosDevelopmentToolSystem$1 = tslib.__decorate([
|
|
1332
1566
|
engine.decorators.componentObserver({
|
|
@@ -1338,9 +1572,52 @@ var CombosDevelopmentToolSystem = CombosDevelopmentToolSystem$1;
|
|
|
1338
1572
|
/** Auto-generated by scripts/build-package.mjs — do not edit. */
|
|
1339
1573
|
Object.assign(CombosDevelopmentToolSystem, {
|
|
1340
1574
|
packageName: "@combos-fun/plugin-development-tool",
|
|
1341
|
-
packageVersion: "0.0.
|
|
1575
|
+
packageVersion: "0.0.50",
|
|
1342
1576
|
});
|
|
1343
1577
|
|
|
1578
|
+
function isInternalEditorGo(go) {
|
|
1579
|
+
return typeof go.name === 'string' && go.name.startsWith('__');
|
|
1580
|
+
}
|
|
1581
|
+
function isSceneLike(go) {
|
|
1582
|
+
return Array.isArray(go.gameObjects);
|
|
1583
|
+
}
|
|
1584
|
+
function sourceIsComplete(raw) {
|
|
1585
|
+
if (!isSceneSourceAnchor(raw))
|
|
1586
|
+
return false;
|
|
1587
|
+
return Boolean(raw.file.trim()) && Boolean((raw.anchor ?? '').trim());
|
|
1588
|
+
}
|
|
1589
|
+
/**
|
|
1590
|
+
* Marks `go` for editor pick / persist. Idempotent: existing Targets keep a
|
|
1591
|
+
* complete `payload.source`; missing identity is filled from `source`.
|
|
1592
|
+
*
|
|
1593
|
+
* Inserted by `@combos-fun/plugin-development-tool/vite`. Game code should
|
|
1594
|
+
* not call this by hand.
|
|
1595
|
+
*/
|
|
1596
|
+
function attachDevelopmentTarget(go, source) {
|
|
1597
|
+
if (!go || go.destroyed || isInternalEditorGo(go) || isSceneLike(go)) {
|
|
1598
|
+
return go;
|
|
1599
|
+
}
|
|
1600
|
+
const file = source.file.trim();
|
|
1601
|
+
const anchor = source.anchor.trim();
|
|
1602
|
+
if (!file || !anchor) {
|
|
1603
|
+
return go;
|
|
1604
|
+
}
|
|
1605
|
+
const existing = go.getComponent(CombosDevelopmentToolTarget);
|
|
1606
|
+
if (existing) {
|
|
1607
|
+
if (!sourceIsComplete(existing.payload?.source)) {
|
|
1608
|
+
existing.payload = {
|
|
1609
|
+
...(existing.payload ?? {}),
|
|
1610
|
+
source: { file, anchor },
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
return go;
|
|
1614
|
+
}
|
|
1615
|
+
go.addComponent(new CombosDevelopmentToolTarget({
|
|
1616
|
+
payload: { source: { file, anchor } },
|
|
1617
|
+
}));
|
|
1618
|
+
return go;
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1344
1621
|
exports.COMBOS_DEVELOPMENT_TOOL_APPLY_PROPERTY = COMBOS_DEVELOPMENT_TOOL_APPLY_PROPERTY;
|
|
1345
1622
|
exports.COMBOS_DEVELOPMENT_TOOL_CLEAR_SELECTION = COMBOS_DEVELOPMENT_TOOL_CLEAR_SELECTION;
|
|
1346
1623
|
exports.COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_DESELECTED = COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_DESELECTED;
|
|
@@ -1360,6 +1637,7 @@ exports.MARKER_COMPONENTS = MARKER_COMPONENTS;
|
|
|
1360
1637
|
exports.MARKER_ICON_SIZE = MARKER_ICON_SIZE;
|
|
1361
1638
|
exports.applyPropertyValue = applyPropertyValue;
|
|
1362
1639
|
exports.applyPropertyWithHooks = applyPropertyWithHooks;
|
|
1640
|
+
exports.attachDevelopmentTarget = attachDevelopmentTarget;
|
|
1363
1641
|
exports.buildGameObjectDeselectedPayload = buildGameObjectDeselectedPayload;
|
|
1364
1642
|
exports.buildGameObjectSnapshot = buildGameObjectSnapshot;
|
|
1365
1643
|
exports.drawMarkerIcon = drawMarkerIcon;
|