@combos-fun/plugin-development-tool 0.0.27 → 0.0.28
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/agent-skill.md +24 -0
- package/dist/plugin-development-tool.cjs.js +245 -5
- 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 +102 -4
- package/dist/plugin-development-tool.esm.js +240 -6
- package/dist/plugin-development-tool.esm.js.map +1 -1
- package/package.json +5 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component, ComponentParams, System, UpdateParams, ComponentChanged
|
|
1
|
+
import { GameObject, Component, ComponentParams, System, UpdateParams, ComponentChanged } from '@combos-fun/engine';
|
|
2
2
|
|
|
3
3
|
/** Toggle development-tool selection / parent postMessage. Payload: `{ enabled: boolean }`. */
|
|
4
4
|
declare const COMBOS_DEVELOPMENT_TOOL_SET: "combos-development-tool:set";
|
|
@@ -25,6 +25,72 @@ declare const COMBOS_DEVELOPMENT_TOOL_READY: "combos-development-tool:ready";
|
|
|
25
25
|
declare const COMBOS_DEVELOPMENT_TOOL_SET_MUTED: "combos-development-tool:set-muted";
|
|
26
26
|
/** iframe → parent (and `game.emit`): current mute snapshot after a successful change. */
|
|
27
27
|
declare const COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED: "combos-development-tool:state-changed";
|
|
28
|
+
/**
|
|
29
|
+
* Parent → iframe (also `window` CustomEvent / `game.emit`): toggle the marker
|
|
30
|
+
* overlay — a persistent, clickable icon on every object owning an otherwise
|
|
31
|
+
* invisible registered component (Sound first). Independent of pick mode and
|
|
32
|
+
* mute. Payload: `{ enabled: boolean }`.
|
|
33
|
+
*/
|
|
34
|
+
declare const COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY: "combos-development-tool:set-marker-overlay";
|
|
35
|
+
/**
|
|
36
|
+
* iframe → parent (and `game.emit`): marker overlay finished (re)building.
|
|
37
|
+
* Payload: `{ enabled: boolean, total: number, markers: { componentName, count }[] }`.
|
|
38
|
+
*/
|
|
39
|
+
declare const COMBOS_DEVELOPMENT_TOOL_MARKER_OVERLAY_SUCCESS: "combos-development-tool:marker-overlay-success";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Marker overlay = a reusable "pin a persistent, clickable icon on every object
|
|
43
|
+
* that owns an otherwise-invisible component" capability. Audio (`Sound`) is the
|
|
44
|
+
* first consumer; register more component kinds below (Physics, Camera, Trigger,
|
|
45
|
+
* timers, spawn points, …) to reuse the exact same layer.
|
|
46
|
+
*/
|
|
47
|
+
/** Glyph drawn inside a marker badge. Extend as new component kinds are added. */
|
|
48
|
+
type MarkerGlyph = 'speaker' | 'dot';
|
|
49
|
+
/** One kind of invisible component the overlay surfaces with a pinned icon. */
|
|
50
|
+
interface MarkerComponentDef {
|
|
51
|
+
/** Engine component name to look for on each GameObject, e.g. `'Sound'`. */
|
|
52
|
+
componentName: string;
|
|
53
|
+
/** Badge fill color, `0xRRGGBB`. */
|
|
54
|
+
badgeColor: number;
|
|
55
|
+
/** Which glyph to draw inside the badge. */
|
|
56
|
+
glyph: MarkerGlyph;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Registered marker kinds, in priority order: a GameObject is marked by the
|
|
60
|
+
* first registered component it owns. For now this is **`Sound` only**; the
|
|
61
|
+
* mechanism is generic (append Physics / Camera / triggers / spawn points later
|
|
62
|
+
* to reuse the same layer) but only audio is wanted right now.
|
|
63
|
+
*/
|
|
64
|
+
declare const MARKER_COMPONENTS: readonly MarkerComponentDef[];
|
|
65
|
+
/** Fixed badge size in the owner's local space. */
|
|
66
|
+
declare const MARKER_ICON_SIZE = 28;
|
|
67
|
+
/**
|
|
68
|
+
* First registered marker def whose component is present on `go`, else `null`.
|
|
69
|
+
* Priority follows {@link MARKER_COMPONENTS} order.
|
|
70
|
+
*/
|
|
71
|
+
declare function resolveMarkerDef(go: Pick<GameObject, 'getComponent'>, registry?: readonly MarkerComponentDef[]): MarkerComponentDef | null;
|
|
72
|
+
/**
|
|
73
|
+
* Minimal subset of the PixiGraphics v8 surface the marker drawing uses. Keeps
|
|
74
|
+
* this module decoupled from the renderer and unit-testable with a mock.
|
|
75
|
+
*/
|
|
76
|
+
interface MarkerGraphicsLike {
|
|
77
|
+
clear(): unknown;
|
|
78
|
+
roundRect(x: number, y: number, width: number, height: number, radius?: number): unknown;
|
|
79
|
+
rect(x: number, y: number, width: number, height: number): unknown;
|
|
80
|
+
circle(x: number, y: number, radius: number): unknown;
|
|
81
|
+
poly(points: number[], close?: boolean): unknown;
|
|
82
|
+
fill(style: {
|
|
83
|
+
color: number;
|
|
84
|
+
alpha?: number;
|
|
85
|
+
}): unknown;
|
|
86
|
+
stroke(style: {
|
|
87
|
+
width: number;
|
|
88
|
+
color: number;
|
|
89
|
+
alpha?: number;
|
|
90
|
+
}): unknown;
|
|
91
|
+
}
|
|
92
|
+
/** Render a full marker (badge + glyph) into `g`, sized to `size` (local units). */
|
|
93
|
+
declare function drawMarkerIcon(g: MarkerGraphicsLike, def: MarkerComponentDef, size?: number): void;
|
|
28
94
|
|
|
29
95
|
/** Default inbound postMessage host suffixes (host + all subdomains). */
|
|
30
96
|
declare const DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES: readonly ["knoffice.tech", "converge.ai"];
|
|
@@ -103,8 +169,22 @@ declare class CombosDevelopmentToolSystem extends System<CombosDevelopmentToolSy
|
|
|
103
169
|
private lastOutlineBounds;
|
|
104
170
|
/** Remembered mute when `SoundSystem` is not registered yet. */
|
|
105
171
|
private mutedWithoutSoundSystem;
|
|
172
|
+
/**
|
|
173
|
+
* Marker overlay (see {@link markerLayer}): a visual-only icon layer, toggled
|
|
174
|
+
* independently of pick mode + mute. Selection/persistence are NOT handled here
|
|
175
|
+
* — a marked object (e.g. Sound) carries its own `CombosDevelopmentToolTarget`
|
|
176
|
+
* (added by the game code), so it is picked and serialized through the normal
|
|
177
|
+
* Target path. The icon just makes the invisible object visible and, by adding
|
|
178
|
+
* to the owner's rendered bounds, gives its Target pick a clickable hit area.
|
|
179
|
+
*/
|
|
180
|
+
private markerOverlayEnabled;
|
|
181
|
+
private needsMarkerRescan;
|
|
182
|
+
/** ownerGoId → its marker GO (icon child, Graphics only). */
|
|
183
|
+
private readonly markerGoByOwner;
|
|
106
184
|
private readonly onWindowSet;
|
|
107
185
|
private readonly onWindowSetMuted;
|
|
186
|
+
private readonly onWindowSetMarkerOverlay;
|
|
187
|
+
private readonly onGameSetMarkerOverlay;
|
|
108
188
|
private readonly onWindowMessage;
|
|
109
189
|
private readonly onGameSet;
|
|
110
190
|
private readonly onGameRefresh;
|
|
@@ -115,6 +195,17 @@ declare class CombosDevelopmentToolSystem extends System<CombosDevelopmentToolSy
|
|
|
115
195
|
/** Programmatic toggle (same effect as events). */
|
|
116
196
|
setEnabled(on: boolean): void;
|
|
117
197
|
get isEnabled(): boolean;
|
|
198
|
+
/**
|
|
199
|
+
* Toggle the marker overlay: pin a persistent icon on every object that owns a
|
|
200
|
+
* registered invisible component ({@link markerLayer}, Sound first). Independent
|
|
201
|
+
* of pick mode / mute. This layer is purely visual — the icon makes the object
|
|
202
|
+
* visible and adds to its rendered bounds so the owner's own
|
|
203
|
+
* `CombosDevelopmentToolTarget` pick gets a clickable hit area. Selecting and
|
|
204
|
+
* persisting edits (e.g. volume) still go through that Target, exactly like any
|
|
205
|
+
* other scene edit, so the object must carry a Target (added by the game code).
|
|
206
|
+
*/
|
|
207
|
+
setMarkerOverlay(on: boolean): void;
|
|
208
|
+
get isMarkerOverlayEnabled(): boolean;
|
|
118
209
|
/** Master mute when `SoundSystem` exists; otherwise the last requested value. */
|
|
119
210
|
get isMuted(): boolean;
|
|
120
211
|
/**
|
|
@@ -124,11 +215,12 @@ declare class CombosDevelopmentToolSystem extends System<CombosDevelopmentToolSy
|
|
|
124
215
|
setMuted(muted: boolean): void;
|
|
125
216
|
update(e: UpdateParams): void;
|
|
126
217
|
componentChanged(changed: ComponentChanged): void;
|
|
127
|
-
/** Re-bind pick targets after dynamic scene changes while
|
|
218
|
+
/** Re-bind pick targets / markers after dynamic scene changes while active. */
|
|
128
219
|
requestSceneRescan(): void;
|
|
129
220
|
private attachEditMode;
|
|
130
221
|
private collectGameObjects;
|
|
131
222
|
private isIgnoredPickGo;
|
|
223
|
+
private isMarkerGo;
|
|
132
224
|
private stripGameEvent;
|
|
133
225
|
private restoreDisabledGameEvents;
|
|
134
226
|
private detachAllPicks;
|
|
@@ -145,6 +237,12 @@ declare class CombosDevelopmentToolSystem extends System<CombosDevelopmentToolSy
|
|
|
145
237
|
private releasePick;
|
|
146
238
|
private postSetSuccess;
|
|
147
239
|
private postStateChanged;
|
|
240
|
+
/** (Re)build markers for every object owning a registered marker component. */
|
|
241
|
+
private attachMarkers;
|
|
242
|
+
private createMarker;
|
|
243
|
+
private removeMarker;
|
|
244
|
+
private detachAllMarkers;
|
|
245
|
+
private postMarkerOverlaySuccess;
|
|
148
246
|
private getSoundSystem;
|
|
149
247
|
private ensureOutline;
|
|
150
248
|
private onSelect;
|
|
@@ -201,5 +299,5 @@ declare function buildGameObjectDeselectedPayload(reason: DevelopmentToolDeselec
|
|
|
201
299
|
declare function shouldNotifyGameObjectDeselected(hadSelection: boolean): boolean;
|
|
202
300
|
declare function isClearSelectionMessage(data: unknown): boolean;
|
|
203
301
|
|
|
204
|
-
export { COMBOS_DEVELOPMENT_TOOL_APPLY_PROPERTY, COMBOS_DEVELOPMENT_TOOL_CLEAR_SELECTION, COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_DESELECTED, COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_SELECTED, COMBOS_DEVELOPMENT_TOOL_READY, COMBOS_DEVELOPMENT_TOOL_REFRESH, COMBOS_DEVELOPMENT_TOOL_SET, COMBOS_DEVELOPMENT_TOOL_SET_MUTED, COMBOS_DEVELOPMENT_TOOL_SET_SUCCESS, COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED, CombosDevelopmentToolSystem, CombosDevelopmentToolTarget, DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES, applyPropertyValue, applyPropertyWithHooks, buildGameObjectDeselectedPayload, buildGameObjectSnapshot, isAllowedMessageOrigin, isClearSelectionMessage, mergeAllowedMessageOrigins, readPropertyValue, readSourceAnchor, shouldNotifyGameObjectDeselected };
|
|
205
|
-
export type { CombosDevelopmentToolSystemParams, CombosDevelopmentToolTargetParams, DevelopmentToolDeselectReason, SceneEditComponentSnapshot, SceneEditGameObjectSnapshot, SceneEditPropertyRef, SceneSourceAnchor };
|
|
302
|
+
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_READY, COMBOS_DEVELOPMENT_TOOL_REFRESH, COMBOS_DEVELOPMENT_TOOL_SET, COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY, COMBOS_DEVELOPMENT_TOOL_SET_MUTED, COMBOS_DEVELOPMENT_TOOL_SET_SUCCESS, COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED, CombosDevelopmentToolSystem, CombosDevelopmentToolTarget, DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES, MARKER_COMPONENTS, MARKER_ICON_SIZE, applyPropertyValue, applyPropertyWithHooks, buildGameObjectDeselectedPayload, buildGameObjectSnapshot, drawMarkerIcon, isAllowedMessageOrigin, isClearSelectionMessage, mergeAllowedMessageOrigins, readPropertyValue, readSourceAnchor, resolveMarkerDef, shouldNotifyGameObjectDeselected };
|
|
303
|
+
export type { CombosDevelopmentToolSystemParams, CombosDevelopmentToolTargetParams, DevelopmentToolDeselectReason, MarkerComponentDef, MarkerGlyph, MarkerGraphicsLike, SceneEditComponentSnapshot, SceneEditGameObjectSnapshot, SceneEditPropertyRef, SceneSourceAnchor };
|
|
@@ -29,6 +29,18 @@ const COMBOS_DEVELOPMENT_TOOL_READY = 'combos-development-tool:ready';
|
|
|
29
29
|
const COMBOS_DEVELOPMENT_TOOL_SET_MUTED = 'combos-development-tool:set-muted';
|
|
30
30
|
/** iframe → parent (and `game.emit`): current mute snapshot after a successful change. */
|
|
31
31
|
const COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED = 'combos-development-tool:state-changed';
|
|
32
|
+
/**
|
|
33
|
+
* Parent → iframe (also `window` CustomEvent / `game.emit`): toggle the marker
|
|
34
|
+
* overlay — a persistent, clickable icon on every object owning an otherwise
|
|
35
|
+
* invisible registered component (Sound first). Independent of pick mode and
|
|
36
|
+
* mute. Payload: `{ enabled: boolean }`.
|
|
37
|
+
*/
|
|
38
|
+
const COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY = 'combos-development-tool:set-marker-overlay';
|
|
39
|
+
/**
|
|
40
|
+
* iframe → parent (and `game.emit`): marker overlay finished (re)building.
|
|
41
|
+
* Payload: `{ enabled: boolean, total: number, markers: { componentName, count }[] }`.
|
|
42
|
+
*/
|
|
43
|
+
const COMBOS_DEVELOPMENT_TOOL_MARKER_OVERLAY_SUCCESS = 'combos-development-tool:marker-overlay-success';
|
|
32
44
|
|
|
33
45
|
/**
|
|
34
46
|
* Marks a `GameObject` as selectable in editor pick mode. While the development tool is enabled,
|
|
@@ -46,6 +58,73 @@ class CombosDevelopmentToolTarget extends Component {
|
|
|
46
58
|
}
|
|
47
59
|
}
|
|
48
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Registered marker kinds, in priority order: a GameObject is marked by the
|
|
63
|
+
* first registered component it owns. For now this is **`Sound` only**; the
|
|
64
|
+
* mechanism is generic (append Physics / Camera / triggers / spawn points later
|
|
65
|
+
* to reuse the same layer) but only audio is wanted right now.
|
|
66
|
+
*/
|
|
67
|
+
const MARKER_COMPONENTS = [
|
|
68
|
+
{ componentName: 'Sound', badgeColor: 0x8b5cf6, glyph: 'speaker' },
|
|
69
|
+
];
|
|
70
|
+
/** Fixed badge size in the owner's local space. */
|
|
71
|
+
const MARKER_ICON_SIZE = 28;
|
|
72
|
+
/**
|
|
73
|
+
* First registered marker def whose component is present on `go`, else `null`.
|
|
74
|
+
* Priority follows {@link MARKER_COMPONENTS} order.
|
|
75
|
+
*/
|
|
76
|
+
function resolveMarkerDef(go, registry = MARKER_COMPONENTS) {
|
|
77
|
+
for (const def of registry) {
|
|
78
|
+
if (go.getComponent(def.componentName)) {
|
|
79
|
+
return def;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
const GLYPH_COLOR = 0xffffff;
|
|
85
|
+
/** Draw the rounded badge background shared by every glyph. */
|
|
86
|
+
function drawBadge(g, def, size) {
|
|
87
|
+
g.clear();
|
|
88
|
+
g.roundRect(0, 0, size, size, size * 0.22);
|
|
89
|
+
g.fill({ color: def.badgeColor, alpha: 0.92 });
|
|
90
|
+
g.roundRect(0, 0, size, size, size * 0.22);
|
|
91
|
+
g.stroke({ width: Math.max(1, size * 0.05), color: 0xffffff, alpha: 0.6 });
|
|
92
|
+
}
|
|
93
|
+
/** A speaker cone + two volume bars, normalized to the `size` box. */
|
|
94
|
+
function drawSpeakerGlyph(g, s) {
|
|
95
|
+
g.poly([
|
|
96
|
+
s * 0.28, s * 0.4,
|
|
97
|
+
s * 0.42, s * 0.4,
|
|
98
|
+
s * 0.55, s * 0.26,
|
|
99
|
+
s * 0.55, s * 0.74,
|
|
100
|
+
s * 0.42, s * 0.6,
|
|
101
|
+
s * 0.28, s * 0.6,
|
|
102
|
+
]);
|
|
103
|
+
g.fill({ color: GLYPH_COLOR, alpha: 0.96 });
|
|
104
|
+
g.rect(s * 0.62, s * 0.44, s * 0.045, s * 0.12);
|
|
105
|
+
g.fill({ color: GLYPH_COLOR, alpha: 0.96 });
|
|
106
|
+
g.rect(s * 0.71, s * 0.39, s * 0.045, s * 0.22);
|
|
107
|
+
g.fill({ color: GLYPH_COLOR, alpha: 0.96 });
|
|
108
|
+
}
|
|
109
|
+
/** Generic fallback glyph for component kinds without a dedicated icon. */
|
|
110
|
+
function drawDotGlyph(g, s) {
|
|
111
|
+
g.circle(s * 0.5, s * 0.5, s * 0.18);
|
|
112
|
+
g.fill({ color: GLYPH_COLOR, alpha: 0.96 });
|
|
113
|
+
}
|
|
114
|
+
/** Render a full marker (badge + glyph) into `g`, sized to `size` (local units). */
|
|
115
|
+
function drawMarkerIcon(g, def, size = MARKER_ICON_SIZE) {
|
|
116
|
+
drawBadge(g, def, size);
|
|
117
|
+
switch (def.glyph) {
|
|
118
|
+
case 'speaker':
|
|
119
|
+
drawSpeakerGlyph(g, size);
|
|
120
|
+
break;
|
|
121
|
+
case 'dot':
|
|
122
|
+
default:
|
|
123
|
+
drawDotGlyph(g, size);
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
49
128
|
/** Default inbound postMessage host suffixes (host + all subdomains). */
|
|
50
129
|
const DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES = [
|
|
51
130
|
'knoffice.tech',
|
|
@@ -369,6 +448,7 @@ function isClearSelectionMessage(data) {
|
|
|
369
448
|
}
|
|
370
449
|
|
|
371
450
|
const OUTLINE_GO_NAME = '__combosDevelopmentToolOutline';
|
|
451
|
+
const MARKER_GO_NAME_PREFIX = '__combosDevelopmentToolMarker:';
|
|
372
452
|
/**
|
|
373
453
|
* **Off** at start. While enabled:
|
|
374
454
|
* - soft-disables game `Event` hit targets (`container.interactive = false`, restored on disable);
|
|
@@ -409,6 +489,18 @@ let CombosDevelopmentToolSystem = class CombosDevelopmentToolSystem extends Syst
|
|
|
409
489
|
this.lastOutlineBounds = null;
|
|
410
490
|
/** Remembered mute when `SoundSystem` is not registered yet. */
|
|
411
491
|
this.mutedWithoutSoundSystem = false;
|
|
492
|
+
/**
|
|
493
|
+
* Marker overlay (see {@link markerLayer}): a visual-only icon layer, toggled
|
|
494
|
+
* independently of pick mode + mute. Selection/persistence are NOT handled here
|
|
495
|
+
* — a marked object (e.g. Sound) carries its own `CombosDevelopmentToolTarget`
|
|
496
|
+
* (added by the game code), so it is picked and serialized through the normal
|
|
497
|
+
* Target path. The icon just makes the invisible object visible and, by adding
|
|
498
|
+
* to the owner's rendered bounds, gives its Target pick a clickable hit area.
|
|
499
|
+
*/
|
|
500
|
+
this.markerOverlayEnabled = false;
|
|
501
|
+
this.needsMarkerRescan = false;
|
|
502
|
+
/** ownerGoId → its marker GO (icon child, Graphics only). */
|
|
503
|
+
this.markerGoByOwner = new Map();
|
|
412
504
|
this.onWindowSet = (e) => {
|
|
413
505
|
const d = e.detail;
|
|
414
506
|
if (d && typeof d.enabled === 'boolean') {
|
|
@@ -421,6 +513,17 @@ let CombosDevelopmentToolSystem = class CombosDevelopmentToolSystem extends Syst
|
|
|
421
513
|
this.setMuted(d.muted);
|
|
422
514
|
}
|
|
423
515
|
};
|
|
516
|
+
this.onWindowSetMarkerOverlay = (e) => {
|
|
517
|
+
const d = e.detail;
|
|
518
|
+
if (d && typeof d.enabled === 'boolean') {
|
|
519
|
+
this.setMarkerOverlay(d.enabled);
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
this.onGameSetMarkerOverlay = (payload) => {
|
|
523
|
+
if (payload && typeof payload.enabled === 'boolean') {
|
|
524
|
+
this.setMarkerOverlay(payload.enabled);
|
|
525
|
+
}
|
|
526
|
+
};
|
|
424
527
|
this.onWindowMessage = (e) => {
|
|
425
528
|
if (!isAllowedMessageOrigin(e.origin, this.allowedMessageOrigins)) {
|
|
426
529
|
return;
|
|
@@ -434,6 +537,10 @@ let CombosDevelopmentToolSystem = class CombosDevelopmentToolSystem extends Syst
|
|
|
434
537
|
else if (d.type === COMBOS_DEVELOPMENT_TOOL_SET_MUTED && typeof d.muted === 'boolean') {
|
|
435
538
|
this.setMuted(d.muted);
|
|
436
539
|
}
|
|
540
|
+
else if (d.type === COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY &&
|
|
541
|
+
typeof d.enabled === 'boolean') {
|
|
542
|
+
this.setMarkerOverlay(d.enabled);
|
|
543
|
+
}
|
|
437
544
|
else if (d.type === COMBOS_DEVELOPMENT_TOOL_REFRESH) {
|
|
438
545
|
this.requestSceneRescan();
|
|
439
546
|
}
|
|
@@ -468,11 +575,13 @@ let CombosDevelopmentToolSystem = class CombosDevelopmentToolSystem extends Syst
|
|
|
468
575
|
if (typeof window !== 'undefined') {
|
|
469
576
|
window.addEventListener(COMBOS_DEVELOPMENT_TOOL_SET, this.onWindowSet);
|
|
470
577
|
window.addEventListener(COMBOS_DEVELOPMENT_TOOL_SET_MUTED, this.onWindowSetMuted);
|
|
578
|
+
window.addEventListener(COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY, this.onWindowSetMarkerOverlay);
|
|
471
579
|
window.addEventListener(COMBOS_DEVELOPMENT_TOOL_REFRESH, this.onWindowRefresh);
|
|
472
580
|
window.addEventListener('message', this.onWindowMessage);
|
|
473
581
|
}
|
|
474
582
|
this.game.on(COMBOS_DEVELOPMENT_TOOL_SET, this.onGameSet);
|
|
475
583
|
this.game.on(COMBOS_DEVELOPMENT_TOOL_SET_MUTED, this.onGameSetMuted);
|
|
584
|
+
this.game.on(COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY, this.onGameSetMarkerOverlay);
|
|
476
585
|
this.game.on(COMBOS_DEVELOPMENT_TOOL_REFRESH, this.onGameRefresh);
|
|
477
586
|
if (this.enabled) {
|
|
478
587
|
this.needsRescan = true;
|
|
@@ -482,13 +591,16 @@ let CombosDevelopmentToolSystem = class CombosDevelopmentToolSystem extends Syst
|
|
|
482
591
|
if (typeof window !== 'undefined') {
|
|
483
592
|
window.removeEventListener(COMBOS_DEVELOPMENT_TOOL_SET, this.onWindowSet);
|
|
484
593
|
window.removeEventListener(COMBOS_DEVELOPMENT_TOOL_SET_MUTED, this.onWindowSetMuted);
|
|
594
|
+
window.removeEventListener(COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY, this.onWindowSetMarkerOverlay);
|
|
485
595
|
window.removeEventListener(COMBOS_DEVELOPMENT_TOOL_REFRESH, this.onWindowRefresh);
|
|
486
596
|
window.removeEventListener('message', this.onWindowMessage);
|
|
487
597
|
}
|
|
488
598
|
this.game.off(COMBOS_DEVELOPMENT_TOOL_SET, this.onGameSet);
|
|
489
599
|
this.game.off(COMBOS_DEVELOPMENT_TOOL_SET_MUTED, this.onGameSetMuted);
|
|
600
|
+
this.game.off(COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY, this.onGameSetMarkerOverlay);
|
|
490
601
|
this.game.off(COMBOS_DEVELOPMENT_TOOL_REFRESH, this.onGameRefresh);
|
|
491
602
|
this.detachAllPicks();
|
|
603
|
+
this.detachAllMarkers();
|
|
492
604
|
this.restoreDisabledGameEvents();
|
|
493
605
|
this.clearSelectionAndNotify('pick-disabled');
|
|
494
606
|
}
|
|
@@ -510,6 +622,35 @@ let CombosDevelopmentToolSystem = class CombosDevelopmentToolSystem extends Syst
|
|
|
510
622
|
get isEnabled() {
|
|
511
623
|
return this.enabled;
|
|
512
624
|
}
|
|
625
|
+
/**
|
|
626
|
+
* Toggle the marker overlay: pin a persistent icon on every object that owns a
|
|
627
|
+
* registered invisible component ({@link markerLayer}, Sound first). Independent
|
|
628
|
+
* of pick mode / mute. This layer is purely visual — the icon makes the object
|
|
629
|
+
* visible and adds to its rendered bounds so the owner's own
|
|
630
|
+
* `CombosDevelopmentToolTarget` pick gets a clickable hit area. Selecting and
|
|
631
|
+
* persisting edits (e.g. volume) still go through that Target, exactly like any
|
|
632
|
+
* other scene edit, so the object must carry a Target (added by the game code).
|
|
633
|
+
*/
|
|
634
|
+
setMarkerOverlay(on) {
|
|
635
|
+
if (this.markerOverlayEnabled === on)
|
|
636
|
+
return;
|
|
637
|
+
this.markerOverlayEnabled = on;
|
|
638
|
+
if (on) {
|
|
639
|
+
// `attachMarkers` requests the pick rescan itself, once the icons exist.
|
|
640
|
+
this.needsMarkerRescan = true;
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
this.detachAllMarkers();
|
|
644
|
+
this.postMarkerOverlaySuccess(false, []);
|
|
645
|
+
// Shrink owners' pick hit areas back now that the icons are gone.
|
|
646
|
+
if (this.enabled) {
|
|
647
|
+
this.needsRescan = true;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
get isMarkerOverlayEnabled() {
|
|
652
|
+
return this.markerOverlayEnabled;
|
|
653
|
+
}
|
|
513
654
|
/** Master mute when `SoundSystem` exists; otherwise the last requested value. */
|
|
514
655
|
get isMuted() {
|
|
515
656
|
const sound = this.getSoundSystem();
|
|
@@ -538,11 +679,26 @@ let CombosDevelopmentToolSystem = class CombosDevelopmentToolSystem extends Syst
|
|
|
538
679
|
this.attachEditMode();
|
|
539
680
|
this.postSetSuccess(true);
|
|
540
681
|
}
|
|
682
|
+
// Attaching markers is deferred to run *after* the pick rescan above so that a
|
|
683
|
+
// newly drawn icon's rebuild lands on a later frame (see `attachMarkers`): the
|
|
684
|
+
// icon child needs a rendered container before it contributes to the owner's
|
|
685
|
+
// bounds, and only then does the owner's Target pick get a hit area over it.
|
|
686
|
+
if (this.markerOverlayEnabled && this.needsMarkerRescan) {
|
|
687
|
+
this.needsMarkerRescan = false;
|
|
688
|
+
this.attachMarkers();
|
|
689
|
+
}
|
|
541
690
|
for (const go of [...this.tapOwners.values()]) {
|
|
542
691
|
if (go.destroyed) {
|
|
543
692
|
this.releasePick(go);
|
|
544
693
|
}
|
|
545
694
|
}
|
|
695
|
+
// Drop markers whose owner disappeared while the overlay is on.
|
|
696
|
+
for (const [ownerId, marker] of [...this.markerGoByOwner.entries()]) {
|
|
697
|
+
const owner = this.findGameObjectById(ownerId);
|
|
698
|
+
if (!owner || owner.destroyed || marker.destroyed) {
|
|
699
|
+
this.removeMarker(ownerId);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
546
702
|
if (!this.enabled || !this.selected || !this.outlineGo)
|
|
547
703
|
return;
|
|
548
704
|
this.updateSelectionOutline();
|
|
@@ -564,11 +720,14 @@ let CombosDevelopmentToolSystem = class CombosDevelopmentToolSystem extends Syst
|
|
|
564
720
|
}
|
|
565
721
|
}
|
|
566
722
|
}
|
|
567
|
-
/** Re-bind pick targets after dynamic scene changes while
|
|
723
|
+
/** Re-bind pick targets / markers after dynamic scene changes while active. */
|
|
568
724
|
requestSceneRescan() {
|
|
569
|
-
if (
|
|
570
|
-
|
|
571
|
-
|
|
725
|
+
if (this.enabled) {
|
|
726
|
+
this.needsRescan = true;
|
|
727
|
+
}
|
|
728
|
+
if (this.markerOverlayEnabled) {
|
|
729
|
+
this.needsMarkerRescan = true;
|
|
730
|
+
}
|
|
572
731
|
}
|
|
573
732
|
attachEditMode() {
|
|
574
733
|
this.detachAllPicks();
|
|
@@ -596,7 +755,10 @@ let CombosDevelopmentToolSystem = class CombosDevelopmentToolSystem extends Syst
|
|
|
596
755
|
}
|
|
597
756
|
}
|
|
598
757
|
isIgnoredPickGo(go) {
|
|
599
|
-
return go.name === OUTLINE_GO_NAME || go === this.outlineGo;
|
|
758
|
+
return go.name === OUTLINE_GO_NAME || go === this.outlineGo || this.isMarkerGo(go);
|
|
759
|
+
}
|
|
760
|
+
isMarkerGo(go) {
|
|
761
|
+
return typeof go.name === 'string' && go.name.startsWith(MARKER_GO_NAME_PREFIX);
|
|
600
762
|
}
|
|
601
763
|
stripGameEvent(go) {
|
|
602
764
|
if (this.disabledGameEventGoIds.has(go.id))
|
|
@@ -813,6 +975,78 @@ let CombosDevelopmentToolSystem = class CombosDevelopmentToolSystem extends Syst
|
|
|
813
975
|
}
|
|
814
976
|
this.game.emit(COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED, state);
|
|
815
977
|
}
|
|
978
|
+
/** (Re)build markers for every object owning a registered marker component. */
|
|
979
|
+
attachMarkers() {
|
|
980
|
+
this.detachAllMarkers();
|
|
981
|
+
const list = [];
|
|
982
|
+
for (const tr of this.game.scene.transform.children) {
|
|
983
|
+
this.collectGameObjects(tr.gameObject, list);
|
|
984
|
+
}
|
|
985
|
+
const counts = new Map();
|
|
986
|
+
for (const go of list) {
|
|
987
|
+
if (this.isIgnoredPickGo(go))
|
|
988
|
+
continue;
|
|
989
|
+
const def = resolveMarkerDef(go);
|
|
990
|
+
if (!def)
|
|
991
|
+
continue;
|
|
992
|
+
this.createMarker(go, def);
|
|
993
|
+
counts.set(def.componentName, (counts.get(def.componentName) ?? 0) + 1);
|
|
994
|
+
}
|
|
995
|
+
const markers = [...counts.entries()].map(([componentName, count]) => ({
|
|
996
|
+
componentName,
|
|
997
|
+
count,
|
|
998
|
+
}));
|
|
999
|
+
this.postMarkerOverlaySuccess(true, markers);
|
|
1000
|
+
// Rebuild picks next frame: the icons just added need a rendered container
|
|
1001
|
+
// before they enlarge their owners' bounds into a clickable hit area. Runs on
|
|
1002
|
+
// a later frame because the pick rescan block already executed above.
|
|
1003
|
+
if (this.enabled) {
|
|
1004
|
+
this.needsRescan = true;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
createMarker(owner, def) {
|
|
1008
|
+
if (this.markerGoByOwner.has(owner.id))
|
|
1009
|
+
return;
|
|
1010
|
+
const bounds = this.resolvePickBounds(owner);
|
|
1011
|
+
const marker = new GameObject(MARKER_GO_NAME_PREFIX + owner.id, {
|
|
1012
|
+
size: { width: MARKER_ICON_SIZE, height: MARKER_ICON_SIZE },
|
|
1013
|
+
position: { x: bounds.x, y: bounds.y },
|
|
1014
|
+
origin: { x: 0, y: 0 },
|
|
1015
|
+
});
|
|
1016
|
+
const gfx = marker.addComponent(new Graphics());
|
|
1017
|
+
if (gfx?.graphics) {
|
|
1018
|
+
drawMarkerIcon(gfx.graphics, def, MARKER_ICON_SIZE);
|
|
1019
|
+
}
|
|
1020
|
+
owner.addChild(marker);
|
|
1021
|
+
this.markerGoByOwner.set(owner.id, marker);
|
|
1022
|
+
}
|
|
1023
|
+
removeMarker(ownerId) {
|
|
1024
|
+
const marker = this.markerGoByOwner.get(ownerId);
|
|
1025
|
+
if (!marker)
|
|
1026
|
+
return;
|
|
1027
|
+
this.markerGoByOwner.delete(ownerId);
|
|
1028
|
+
if (!marker.destroyed) {
|
|
1029
|
+
marker.destroy();
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
detachAllMarkers() {
|
|
1033
|
+
for (const ownerId of [...this.markerGoByOwner.keys()]) {
|
|
1034
|
+
this.removeMarker(ownerId);
|
|
1035
|
+
}
|
|
1036
|
+
this.markerGoByOwner.clear();
|
|
1037
|
+
}
|
|
1038
|
+
postMarkerOverlaySuccess(enabled, markers) {
|
|
1039
|
+
const total = markers.reduce((sum, m) => sum + m.count, 0);
|
|
1040
|
+
const state = { enabled, total, markers };
|
|
1041
|
+
const payload = {
|
|
1042
|
+
type: COMBOS_DEVELOPMENT_TOOL_MARKER_OVERLAY_SUCCESS,
|
|
1043
|
+
...state,
|
|
1044
|
+
};
|
|
1045
|
+
if (typeof window !== 'undefined' && window.parent && window.parent !== window) {
|
|
1046
|
+
window.parent.postMessage(payload, this.postMessageOrigin);
|
|
1047
|
+
}
|
|
1048
|
+
this.game.emit(COMBOS_DEVELOPMENT_TOOL_MARKER_OVERLAY_SUCCESS, state);
|
|
1049
|
+
}
|
|
816
1050
|
getSoundSystem() {
|
|
817
1051
|
const system = this.game.getSystem('SoundSystem');
|
|
818
1052
|
if (!system ||
|
|
@@ -1002,5 +1236,5 @@ Object.assign(CombosDevelopmentToolSystem$1, {
|
|
|
1002
1236
|
packageVersion: "0.0.27",
|
|
1003
1237
|
});
|
|
1004
1238
|
|
|
1005
|
-
export { COMBOS_DEVELOPMENT_TOOL_APPLY_PROPERTY, COMBOS_DEVELOPMENT_TOOL_CLEAR_SELECTION, COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_DESELECTED, COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_SELECTED, COMBOS_DEVELOPMENT_TOOL_READY, COMBOS_DEVELOPMENT_TOOL_REFRESH, COMBOS_DEVELOPMENT_TOOL_SET, COMBOS_DEVELOPMENT_TOOL_SET_MUTED, COMBOS_DEVELOPMENT_TOOL_SET_SUCCESS, COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED, CombosDevelopmentToolSystem$1 as CombosDevelopmentToolSystem, CombosDevelopmentToolTarget, DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES, applyPropertyValue, applyPropertyWithHooks, buildGameObjectDeselectedPayload, buildGameObjectSnapshot, isAllowedMessageOrigin, isClearSelectionMessage, mergeAllowedMessageOrigins, readPropertyValue, readSourceAnchor, shouldNotifyGameObjectDeselected };
|
|
1239
|
+
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_READY, COMBOS_DEVELOPMENT_TOOL_REFRESH, COMBOS_DEVELOPMENT_TOOL_SET, COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY, COMBOS_DEVELOPMENT_TOOL_SET_MUTED, COMBOS_DEVELOPMENT_TOOL_SET_SUCCESS, COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED, CombosDevelopmentToolSystem$1 as CombosDevelopmentToolSystem, CombosDevelopmentToolTarget, DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES, MARKER_COMPONENTS, MARKER_ICON_SIZE, applyPropertyValue, applyPropertyWithHooks, buildGameObjectDeselectedPayload, buildGameObjectSnapshot, drawMarkerIcon, isAllowedMessageOrigin, isClearSelectionMessage, mergeAllowedMessageOrigins, readPropertyValue, readSourceAnchor, resolveMarkerDef, shouldNotifyGameObjectDeselected };
|
|
1006
1240
|
//# sourceMappingURL=plugin-development-tool.esm.js.map
|