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