@combos-fun/plugin-development-tool 0.0.26 → 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 +258 -49
- 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 +115 -18
- package/dist/plugin-development-tool.esm.js +253 -49
- package/dist/plugin-development-tool.esm.js.map +1 -1
- package/package.json +9 -9
package/agent-skill.md
CHANGED
|
@@ -78,8 +78,32 @@ Outbound `postMessageOrigin` is separate — it limits which parent origin recei
|
|
|
78
78
|
| parent → iframe | `combos-development-tool:set` | `{ enabled: boolean }` | Enable/disable pick mode |
|
|
79
79
|
| parent → iframe | `combos-development-tool:clear-selection` | `{}` | Clear in-canvas outline |
|
|
80
80
|
| parent → iframe | `combos-development-tool:apply-property` | component field mutation | Live preview |
|
|
81
|
+
| parent → iframe | `combos-development-tool:set-marker-overlay` | `{ enabled: boolean }` | Toggle the marker overlay (independent of pick mode) |
|
|
81
82
|
| iframe → parent | `combos-development-tool:gameobject-selected` | `{ snapshot, pointer }` | Object picked |
|
|
82
83
|
| iframe → parent | `combos-development-tool:gameobject-deselected` | `{ reason }` | Selection cleared (`parent-request`, `pick-disabled`, `target-removed`) |
|
|
84
|
+
| iframe → parent | `combos-development-tool:marker-overlay-success` | `{ enabled, total, markers: { componentName, count }[] }` | Marker overlay finished (re)building |
|
|
85
|
+
|
|
86
|
+
## Marker overlay (surface invisible components)
|
|
87
|
+
|
|
88
|
+
A reusable "pin a persistent icon on every object owning an otherwise invisible
|
|
89
|
+
component" layer. Registry-driven (`markerLayer.ts` → `MARKER_COMPONENTS`).
|
|
90
|
+
**For now it is `Sound` only** — the mechanism is generic (append `Physics` / `Camera` /
|
|
91
|
+
triggers / spawn points later) but nothing else is registered yet.
|
|
92
|
+
|
|
93
|
+
- Independent toggle, orthogonal to pick mode and mute: `setMarkerOverlay(true)` or
|
|
94
|
+
`postMessage({ type: 'combos-development-tool:set-marker-overlay', enabled: true })`
|
|
95
|
+
(also `window` CustomEvent / `game.emit`).
|
|
96
|
+
- Draws the icon **in-engine** (a `Graphics` child GO pinned to the owner, so it
|
|
97
|
+
follows the object). The layer is **purely visual** — it does not wire up its own
|
|
98
|
+
selection. Because the icon adds to the owner's rendered bounds, the owner's own
|
|
99
|
+
`CombosDevelopmentToolTarget` pick gets a clickable hit area, so selecting and
|
|
100
|
+
editing (e.g. `Sound.config.volume`) go through the normal
|
|
101
|
+
selection → snapshot → `apply-property` flow and **persist to source** like any
|
|
102
|
+
other scene edit. **The marked object must therefore carry a
|
|
103
|
+
`CombosDevelopmentToolTarget`** (added by the game code — see the game template's
|
|
104
|
+
Scene Edit rules; objects with a `Sound` component must be given a Target even
|
|
105
|
+
when they render nothing).
|
|
106
|
+
- On (re)build it posts `combos-development-tool:marker-overlay-success` with per-component counts.
|
|
83
107
|
|
|
84
108
|
## Verification
|
|
85
109
|
|
|
@@ -22,12 +22,27 @@ const COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_DESELECTED = 'combos-development-tool:g
|
|
|
22
22
|
const COMBOS_DEVELOPMENT_TOOL_APPLY_PROPERTY = 'combos-development-tool:apply-property';
|
|
23
23
|
/** iframe → parent: game scene bootstrap finished; parent may enable pick mode. */
|
|
24
24
|
const COMBOS_DEVELOPMENT_TOOL_READY = 'combos-development-tool:ready';
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Parent → iframe: mute or unmute audio when `SoundSystem` is registered. Payload: `{ muted: boolean }`.
|
|
27
|
+
*
|
|
28
|
+
* Note: play / pause / resume are owned by the engine core lifecycle protocol
|
|
29
|
+
* (`combos-game:set-playing` / `combos-game:state-changed`), not this tool.
|
|
30
|
+
*/
|
|
28
31
|
const COMBOS_DEVELOPMENT_TOOL_SET_MUTED = 'combos-development-tool:set-muted';
|
|
29
|
-
/** iframe → parent (and `game.emit`): current
|
|
32
|
+
/** iframe → parent (and `game.emit`): current mute snapshot after a successful change. */
|
|
30
33
|
const COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED = 'combos-development-tool:state-changed';
|
|
34
|
+
/**
|
|
35
|
+
* Parent → iframe (also `window` CustomEvent / `game.emit`): toggle the marker
|
|
36
|
+
* overlay — a persistent, clickable icon on every object owning an otherwise
|
|
37
|
+
* invisible registered component (Sound first). Independent of pick mode and
|
|
38
|
+
* mute. Payload: `{ enabled: boolean }`.
|
|
39
|
+
*/
|
|
40
|
+
const COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY = 'combos-development-tool:set-marker-overlay';
|
|
41
|
+
/**
|
|
42
|
+
* iframe → parent (and `game.emit`): marker overlay finished (re)building.
|
|
43
|
+
* Payload: `{ enabled: boolean, total: number, markers: { componentName, count }[] }`.
|
|
44
|
+
*/
|
|
45
|
+
const COMBOS_DEVELOPMENT_TOOL_MARKER_OVERLAY_SUCCESS = 'combos-development-tool:marker-overlay-success';
|
|
31
46
|
|
|
32
47
|
/**
|
|
33
48
|
* Marks a `GameObject` as selectable in editor pick mode. While the development tool is enabled,
|
|
@@ -45,6 +60,73 @@ class CombosDevelopmentToolTarget extends engine.Component {
|
|
|
45
60
|
}
|
|
46
61
|
}
|
|
47
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Registered marker kinds, in priority order: a GameObject is marked by the
|
|
65
|
+
* first registered component it owns. For now this is **`Sound` only**; the
|
|
66
|
+
* mechanism is generic (append Physics / Camera / triggers / spawn points later
|
|
67
|
+
* to reuse the same layer) but only audio is wanted right now.
|
|
68
|
+
*/
|
|
69
|
+
const MARKER_COMPONENTS = [
|
|
70
|
+
{ componentName: 'Sound', badgeColor: 0x8b5cf6, glyph: 'speaker' },
|
|
71
|
+
];
|
|
72
|
+
/** Fixed badge size in the owner's local space. */
|
|
73
|
+
const MARKER_ICON_SIZE = 28;
|
|
74
|
+
/**
|
|
75
|
+
* First registered marker def whose component is present on `go`, else `null`.
|
|
76
|
+
* Priority follows {@link MARKER_COMPONENTS} order.
|
|
77
|
+
*/
|
|
78
|
+
function resolveMarkerDef(go, registry = MARKER_COMPONENTS) {
|
|
79
|
+
for (const def of registry) {
|
|
80
|
+
if (go.getComponent(def.componentName)) {
|
|
81
|
+
return def;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const GLYPH_COLOR = 0xffffff;
|
|
87
|
+
/** Draw the rounded badge background shared by every glyph. */
|
|
88
|
+
function drawBadge(g, def, size) {
|
|
89
|
+
g.clear();
|
|
90
|
+
g.roundRect(0, 0, size, size, size * 0.22);
|
|
91
|
+
g.fill({ color: def.badgeColor, alpha: 0.92 });
|
|
92
|
+
g.roundRect(0, 0, size, size, size * 0.22);
|
|
93
|
+
g.stroke({ width: Math.max(1, size * 0.05), color: 0xffffff, alpha: 0.6 });
|
|
94
|
+
}
|
|
95
|
+
/** A speaker cone + two volume bars, normalized to the `size` box. */
|
|
96
|
+
function drawSpeakerGlyph(g, s) {
|
|
97
|
+
g.poly([
|
|
98
|
+
s * 0.28, s * 0.4,
|
|
99
|
+
s * 0.42, s * 0.4,
|
|
100
|
+
s * 0.55, s * 0.26,
|
|
101
|
+
s * 0.55, s * 0.74,
|
|
102
|
+
s * 0.42, s * 0.6,
|
|
103
|
+
s * 0.28, s * 0.6,
|
|
104
|
+
]);
|
|
105
|
+
g.fill({ color: GLYPH_COLOR, alpha: 0.96 });
|
|
106
|
+
g.rect(s * 0.62, s * 0.44, s * 0.045, s * 0.12);
|
|
107
|
+
g.fill({ color: GLYPH_COLOR, alpha: 0.96 });
|
|
108
|
+
g.rect(s * 0.71, s * 0.39, s * 0.045, s * 0.22);
|
|
109
|
+
g.fill({ color: GLYPH_COLOR, alpha: 0.96 });
|
|
110
|
+
}
|
|
111
|
+
/** Generic fallback glyph for component kinds without a dedicated icon. */
|
|
112
|
+
function drawDotGlyph(g, s) {
|
|
113
|
+
g.circle(s * 0.5, s * 0.5, s * 0.18);
|
|
114
|
+
g.fill({ color: GLYPH_COLOR, alpha: 0.96 });
|
|
115
|
+
}
|
|
116
|
+
/** Render a full marker (badge + glyph) into `g`, sized to `size` (local units). */
|
|
117
|
+
function drawMarkerIcon(g, def, size = MARKER_ICON_SIZE) {
|
|
118
|
+
drawBadge(g, def, size);
|
|
119
|
+
switch (def.glyph) {
|
|
120
|
+
case 'speaker':
|
|
121
|
+
drawSpeakerGlyph(g, size);
|
|
122
|
+
break;
|
|
123
|
+
case 'dot':
|
|
124
|
+
default:
|
|
125
|
+
drawDotGlyph(g, size);
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
48
130
|
/** Default inbound postMessage host suffixes (host + all subdomains). */
|
|
49
131
|
const DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES = [
|
|
50
132
|
'knoffice.tech',
|
|
@@ -368,6 +450,7 @@ function isClearSelectionMessage(data) {
|
|
|
368
450
|
}
|
|
369
451
|
|
|
370
452
|
const OUTLINE_GO_NAME = '__combosDevelopmentToolOutline';
|
|
453
|
+
const MARKER_GO_NAME_PREFIX = '__combosDevelopmentToolMarker:';
|
|
371
454
|
/**
|
|
372
455
|
* **Off** at start. While enabled:
|
|
373
456
|
* - soft-disables game `Event` hit targets (`container.interactive = false`, restored on disable);
|
|
@@ -378,12 +461,14 @@ const OUTLINE_GO_NAME = '__combosDevelopmentToolOutline';
|
|
|
378
461
|
* - `window.postMessage({ type: COMBOS_DEVELOPMENT_TOOL_SET, enabled: true }, targetOrigin)` (e.g. from parent iframe)
|
|
379
462
|
* - `game.emit(COMBOS_DEVELOPMENT_TOOL_SET, { enabled: true })` or `getSystem(CombosDevelopmentToolSystem).setEnabled(true)`
|
|
380
463
|
*
|
|
381
|
-
*
|
|
382
|
-
* - `postMessage({ type: COMBOS_DEVELOPMENT_TOOL_SET_PAUSED, paused: true })`
|
|
464
|
+
* Mute (independent of pick mode; parent toolbar or in-game debug UI):
|
|
383
465
|
* - `postMessage({ type: COMBOS_DEVELOPMENT_TOOL_SET_MUTED, muted: true })`
|
|
384
|
-
* - `game.emit(
|
|
466
|
+
* - `game.emit(COMBOS_DEVELOPMENT_TOOL_SET_MUTED, { muted: true })` or `.setMuted(true)`
|
|
385
467
|
* Mute applies when `SoundSystem` is registered; otherwise the desired value is remembered until it appears.
|
|
386
|
-
*
|
|
468
|
+
* Emits / posts `COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED` with `{ muted }`.
|
|
469
|
+
*
|
|
470
|
+
* Play / pause / resume are handled by the engine core lifecycle protocol
|
|
471
|
+
* (`combos-game:set-playing` / `combos-game:state-changed`), not this tool.
|
|
387
472
|
*
|
|
388
473
|
* While enabled: `game.emit(COMBOS_DEVELOPMENT_TOOL_REFRESH)`, `window` event `combos-development-tool:refresh`,
|
|
389
474
|
* or `postMessage({ type: 'combos-development-tool:refresh' })` to re-bind after adding/removing objects.
|
|
@@ -406,24 +491,41 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
406
491
|
this.lastOutlineBounds = null;
|
|
407
492
|
/** Remembered mute when `SoundSystem` is not registered yet. */
|
|
408
493
|
this.mutedWithoutSoundSystem = false;
|
|
494
|
+
/**
|
|
495
|
+
* Marker overlay (see {@link markerLayer}): a visual-only icon layer, toggled
|
|
496
|
+
* independently of pick mode + mute. Selection/persistence are NOT handled here
|
|
497
|
+
* — a marked object (e.g. Sound) carries its own `CombosDevelopmentToolTarget`
|
|
498
|
+
* (added by the game code), so it is picked and serialized through the normal
|
|
499
|
+
* Target path. The icon just makes the invisible object visible and, by adding
|
|
500
|
+
* to the owner's rendered bounds, gives its Target pick a clickable hit area.
|
|
501
|
+
*/
|
|
502
|
+
this.markerOverlayEnabled = false;
|
|
503
|
+
this.needsMarkerRescan = false;
|
|
504
|
+
/** ownerGoId → its marker GO (icon child, Graphics only). */
|
|
505
|
+
this.markerGoByOwner = new Map();
|
|
409
506
|
this.onWindowSet = (e) => {
|
|
410
507
|
const d = e.detail;
|
|
411
508
|
if (d && typeof d.enabled === 'boolean') {
|
|
412
509
|
this.setEnabled(d.enabled);
|
|
413
510
|
}
|
|
414
511
|
};
|
|
415
|
-
this.onWindowSetPaused = (e) => {
|
|
416
|
-
const d = e.detail;
|
|
417
|
-
if (d && typeof d.paused === 'boolean') {
|
|
418
|
-
this.setPaused(d.paused);
|
|
419
|
-
}
|
|
420
|
-
};
|
|
421
512
|
this.onWindowSetMuted = (e) => {
|
|
422
513
|
const d = e.detail;
|
|
423
514
|
if (d && typeof d.muted === 'boolean') {
|
|
424
515
|
this.setMuted(d.muted);
|
|
425
516
|
}
|
|
426
517
|
};
|
|
518
|
+
this.onWindowSetMarkerOverlay = (e) => {
|
|
519
|
+
const d = e.detail;
|
|
520
|
+
if (d && typeof d.enabled === 'boolean') {
|
|
521
|
+
this.setMarkerOverlay(d.enabled);
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
this.onGameSetMarkerOverlay = (payload) => {
|
|
525
|
+
if (payload && typeof payload.enabled === 'boolean') {
|
|
526
|
+
this.setMarkerOverlay(payload.enabled);
|
|
527
|
+
}
|
|
528
|
+
};
|
|
427
529
|
this.onWindowMessage = (e) => {
|
|
428
530
|
if (!isAllowedMessageOrigin(e.origin, this.allowedMessageOrigins)) {
|
|
429
531
|
return;
|
|
@@ -434,12 +536,13 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
434
536
|
if (d.type === COMBOS_DEVELOPMENT_TOOL_SET && typeof d.enabled === 'boolean') {
|
|
435
537
|
this.setEnabled(d.enabled);
|
|
436
538
|
}
|
|
437
|
-
else if (d.type === COMBOS_DEVELOPMENT_TOOL_SET_PAUSED && typeof d.paused === 'boolean') {
|
|
438
|
-
this.setPaused(d.paused);
|
|
439
|
-
}
|
|
440
539
|
else if (d.type === COMBOS_DEVELOPMENT_TOOL_SET_MUTED && typeof d.muted === 'boolean') {
|
|
441
540
|
this.setMuted(d.muted);
|
|
442
541
|
}
|
|
542
|
+
else if (d.type === COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY &&
|
|
543
|
+
typeof d.enabled === 'boolean') {
|
|
544
|
+
this.setMarkerOverlay(d.enabled);
|
|
545
|
+
}
|
|
443
546
|
else if (d.type === COMBOS_DEVELOPMENT_TOOL_REFRESH) {
|
|
444
547
|
this.requestSceneRescan();
|
|
445
548
|
}
|
|
@@ -458,11 +561,6 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
458
561
|
this.onGameRefresh = () => {
|
|
459
562
|
this.requestSceneRescan();
|
|
460
563
|
};
|
|
461
|
-
this.onGameSetPaused = (payload) => {
|
|
462
|
-
if (payload && typeof payload.paused === 'boolean') {
|
|
463
|
-
this.setPaused(payload.paused);
|
|
464
|
-
}
|
|
465
|
-
};
|
|
466
564
|
this.onGameSetMuted = (payload) => {
|
|
467
565
|
if (payload && typeof payload.muted === 'boolean') {
|
|
468
566
|
this.setMuted(payload.muted);
|
|
@@ -478,14 +576,14 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
478
576
|
this.allowedMessageOrigins = mergeAllowedMessageOrigins(params?.allowedMessageOrigins);
|
|
479
577
|
if (typeof window !== 'undefined') {
|
|
480
578
|
window.addEventListener(COMBOS_DEVELOPMENT_TOOL_SET, this.onWindowSet);
|
|
481
|
-
window.addEventListener(COMBOS_DEVELOPMENT_TOOL_SET_PAUSED, this.onWindowSetPaused);
|
|
482
579
|
window.addEventListener(COMBOS_DEVELOPMENT_TOOL_SET_MUTED, this.onWindowSetMuted);
|
|
580
|
+
window.addEventListener(COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY, this.onWindowSetMarkerOverlay);
|
|
483
581
|
window.addEventListener(COMBOS_DEVELOPMENT_TOOL_REFRESH, this.onWindowRefresh);
|
|
484
582
|
window.addEventListener('message', this.onWindowMessage);
|
|
485
583
|
}
|
|
486
584
|
this.game.on(COMBOS_DEVELOPMENT_TOOL_SET, this.onGameSet);
|
|
487
|
-
this.game.on(COMBOS_DEVELOPMENT_TOOL_SET_PAUSED, this.onGameSetPaused);
|
|
488
585
|
this.game.on(COMBOS_DEVELOPMENT_TOOL_SET_MUTED, this.onGameSetMuted);
|
|
586
|
+
this.game.on(COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY, this.onGameSetMarkerOverlay);
|
|
489
587
|
this.game.on(COMBOS_DEVELOPMENT_TOOL_REFRESH, this.onGameRefresh);
|
|
490
588
|
if (this.enabled) {
|
|
491
589
|
this.needsRescan = true;
|
|
@@ -494,16 +592,17 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
494
592
|
onDestroy() {
|
|
495
593
|
if (typeof window !== 'undefined') {
|
|
496
594
|
window.removeEventListener(COMBOS_DEVELOPMENT_TOOL_SET, this.onWindowSet);
|
|
497
|
-
window.removeEventListener(COMBOS_DEVELOPMENT_TOOL_SET_PAUSED, this.onWindowSetPaused);
|
|
498
595
|
window.removeEventListener(COMBOS_DEVELOPMENT_TOOL_SET_MUTED, this.onWindowSetMuted);
|
|
596
|
+
window.removeEventListener(COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY, this.onWindowSetMarkerOverlay);
|
|
499
597
|
window.removeEventListener(COMBOS_DEVELOPMENT_TOOL_REFRESH, this.onWindowRefresh);
|
|
500
598
|
window.removeEventListener('message', this.onWindowMessage);
|
|
501
599
|
}
|
|
502
600
|
this.game.off(COMBOS_DEVELOPMENT_TOOL_SET, this.onGameSet);
|
|
503
|
-
this.game.off(COMBOS_DEVELOPMENT_TOOL_SET_PAUSED, this.onGameSetPaused);
|
|
504
601
|
this.game.off(COMBOS_DEVELOPMENT_TOOL_SET_MUTED, this.onGameSetMuted);
|
|
602
|
+
this.game.off(COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY, this.onGameSetMarkerOverlay);
|
|
505
603
|
this.game.off(COMBOS_DEVELOPMENT_TOOL_REFRESH, this.onGameRefresh);
|
|
506
604
|
this.detachAllPicks();
|
|
605
|
+
this.detachAllMarkers();
|
|
507
606
|
this.restoreDisabledGameEvents();
|
|
508
607
|
this.clearSelectionAndNotify('pick-disabled');
|
|
509
608
|
}
|
|
@@ -525,9 +624,34 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
525
624
|
get isEnabled() {
|
|
526
625
|
return this.enabled;
|
|
527
626
|
}
|
|
528
|
-
/**
|
|
529
|
-
|
|
530
|
-
|
|
627
|
+
/**
|
|
628
|
+
* Toggle the marker overlay: pin a persistent icon on every object that owns a
|
|
629
|
+
* registered invisible component ({@link markerLayer}, Sound first). Independent
|
|
630
|
+
* of pick mode / mute. This layer is purely visual — the icon makes the object
|
|
631
|
+
* visible and adds to its rendered bounds so the owner's own
|
|
632
|
+
* `CombosDevelopmentToolTarget` pick gets a clickable hit area. Selecting and
|
|
633
|
+
* persisting edits (e.g. volume) still go through that Target, exactly like any
|
|
634
|
+
* other scene edit, so the object must carry a Target (added by the game code).
|
|
635
|
+
*/
|
|
636
|
+
setMarkerOverlay(on) {
|
|
637
|
+
if (this.markerOverlayEnabled === on)
|
|
638
|
+
return;
|
|
639
|
+
this.markerOverlayEnabled = on;
|
|
640
|
+
if (on) {
|
|
641
|
+
// `attachMarkers` requests the pick rescan itself, once the icons exist.
|
|
642
|
+
this.needsMarkerRescan = true;
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
this.detachAllMarkers();
|
|
646
|
+
this.postMarkerOverlaySuccess(false, []);
|
|
647
|
+
// Shrink owners' pick hit areas back now that the icons are gone.
|
|
648
|
+
if (this.enabled) {
|
|
649
|
+
this.needsRescan = true;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
get isMarkerOverlayEnabled() {
|
|
654
|
+
return this.markerOverlayEnabled;
|
|
531
655
|
}
|
|
532
656
|
/** Master mute when `SoundSystem` exists; otherwise the last requested value. */
|
|
533
657
|
get isMuted() {
|
|
@@ -537,18 +661,6 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
537
661
|
}
|
|
538
662
|
return this.mutedWithoutSoundSystem;
|
|
539
663
|
}
|
|
540
|
-
/** Pause or resume the game loop (same as `game.pause()` / `game.resume()`). */
|
|
541
|
-
setPaused(paused) {
|
|
542
|
-
if (paused) {
|
|
543
|
-
if (this.game.playing) {
|
|
544
|
-
this.game.pause();
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
else if (!this.game.playing) {
|
|
548
|
-
this.game.resume();
|
|
549
|
-
}
|
|
550
|
-
this.postStateChanged();
|
|
551
|
-
}
|
|
552
664
|
/**
|
|
553
665
|
* Mute or unmute audio via `SoundSystem` when registered.
|
|
554
666
|
* No-op on the audio graph if `SoundSystem` is missing; value is remembered for `isMuted`.
|
|
@@ -569,11 +681,26 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
569
681
|
this.attachEditMode();
|
|
570
682
|
this.postSetSuccess(true);
|
|
571
683
|
}
|
|
684
|
+
// Attaching markers is deferred to run *after* the pick rescan above so that a
|
|
685
|
+
// newly drawn icon's rebuild lands on a later frame (see `attachMarkers`): the
|
|
686
|
+
// icon child needs a rendered container before it contributes to the owner's
|
|
687
|
+
// bounds, and only then does the owner's Target pick get a hit area over it.
|
|
688
|
+
if (this.markerOverlayEnabled && this.needsMarkerRescan) {
|
|
689
|
+
this.needsMarkerRescan = false;
|
|
690
|
+
this.attachMarkers();
|
|
691
|
+
}
|
|
572
692
|
for (const go of [...this.tapOwners.values()]) {
|
|
573
693
|
if (go.destroyed) {
|
|
574
694
|
this.releasePick(go);
|
|
575
695
|
}
|
|
576
696
|
}
|
|
697
|
+
// Drop markers whose owner disappeared while the overlay is on.
|
|
698
|
+
for (const [ownerId, marker] of [...this.markerGoByOwner.entries()]) {
|
|
699
|
+
const owner = this.findGameObjectById(ownerId);
|
|
700
|
+
if (!owner || owner.destroyed || marker.destroyed) {
|
|
701
|
+
this.removeMarker(ownerId);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
577
704
|
if (!this.enabled || !this.selected || !this.outlineGo)
|
|
578
705
|
return;
|
|
579
706
|
this.updateSelectionOutline();
|
|
@@ -595,11 +722,14 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
595
722
|
}
|
|
596
723
|
}
|
|
597
724
|
}
|
|
598
|
-
/** Re-bind pick targets after dynamic scene changes while
|
|
725
|
+
/** Re-bind pick targets / markers after dynamic scene changes while active. */
|
|
599
726
|
requestSceneRescan() {
|
|
600
|
-
if (
|
|
601
|
-
|
|
602
|
-
|
|
727
|
+
if (this.enabled) {
|
|
728
|
+
this.needsRescan = true;
|
|
729
|
+
}
|
|
730
|
+
if (this.markerOverlayEnabled) {
|
|
731
|
+
this.needsMarkerRescan = true;
|
|
732
|
+
}
|
|
603
733
|
}
|
|
604
734
|
attachEditMode() {
|
|
605
735
|
this.detachAllPicks();
|
|
@@ -627,7 +757,10 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
627
757
|
}
|
|
628
758
|
}
|
|
629
759
|
isIgnoredPickGo(go) {
|
|
630
|
-
return go.name === OUTLINE_GO_NAME || go === this.outlineGo;
|
|
760
|
+
return go.name === OUTLINE_GO_NAME || go === this.outlineGo || this.isMarkerGo(go);
|
|
761
|
+
}
|
|
762
|
+
isMarkerGo(go) {
|
|
763
|
+
return typeof go.name === 'string' && go.name.startsWith(MARKER_GO_NAME_PREFIX);
|
|
631
764
|
}
|
|
632
765
|
stripGameEvent(go) {
|
|
633
766
|
if (this.disabledGameEventGoIds.has(go.id))
|
|
@@ -833,7 +966,6 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
833
966
|
}
|
|
834
967
|
postStateChanged() {
|
|
835
968
|
const state = {
|
|
836
|
-
paused: this.isPaused,
|
|
837
969
|
muted: this.isMuted,
|
|
838
970
|
};
|
|
839
971
|
const payload = {
|
|
@@ -845,6 +977,78 @@ let CombosDevelopmentToolSystem$1 = class CombosDevelopmentToolSystem extends en
|
|
|
845
977
|
}
|
|
846
978
|
this.game.emit(COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED, state);
|
|
847
979
|
}
|
|
980
|
+
/** (Re)build markers for every object owning a registered marker component. */
|
|
981
|
+
attachMarkers() {
|
|
982
|
+
this.detachAllMarkers();
|
|
983
|
+
const list = [];
|
|
984
|
+
for (const tr of this.game.scene.transform.children) {
|
|
985
|
+
this.collectGameObjects(tr.gameObject, list);
|
|
986
|
+
}
|
|
987
|
+
const counts = new Map();
|
|
988
|
+
for (const go of list) {
|
|
989
|
+
if (this.isIgnoredPickGo(go))
|
|
990
|
+
continue;
|
|
991
|
+
const def = resolveMarkerDef(go);
|
|
992
|
+
if (!def)
|
|
993
|
+
continue;
|
|
994
|
+
this.createMarker(go, def);
|
|
995
|
+
counts.set(def.componentName, (counts.get(def.componentName) ?? 0) + 1);
|
|
996
|
+
}
|
|
997
|
+
const markers = [...counts.entries()].map(([componentName, count]) => ({
|
|
998
|
+
componentName,
|
|
999
|
+
count,
|
|
1000
|
+
}));
|
|
1001
|
+
this.postMarkerOverlaySuccess(true, markers);
|
|
1002
|
+
// Rebuild picks next frame: the icons just added need a rendered container
|
|
1003
|
+
// before they enlarge their owners' bounds into a clickable hit area. Runs on
|
|
1004
|
+
// a later frame because the pick rescan block already executed above.
|
|
1005
|
+
if (this.enabled) {
|
|
1006
|
+
this.needsRescan = true;
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
createMarker(owner, def) {
|
|
1010
|
+
if (this.markerGoByOwner.has(owner.id))
|
|
1011
|
+
return;
|
|
1012
|
+
const bounds = this.resolvePickBounds(owner);
|
|
1013
|
+
const marker = new engine.GameObject(MARKER_GO_NAME_PREFIX + owner.id, {
|
|
1014
|
+
size: { width: MARKER_ICON_SIZE, height: MARKER_ICON_SIZE },
|
|
1015
|
+
position: { x: bounds.x, y: bounds.y },
|
|
1016
|
+
origin: { x: 0, y: 0 },
|
|
1017
|
+
});
|
|
1018
|
+
const gfx = marker.addComponent(new pluginRendererGraphics.Graphics());
|
|
1019
|
+
if (gfx?.graphics) {
|
|
1020
|
+
drawMarkerIcon(gfx.graphics, def, MARKER_ICON_SIZE);
|
|
1021
|
+
}
|
|
1022
|
+
owner.addChild(marker);
|
|
1023
|
+
this.markerGoByOwner.set(owner.id, marker);
|
|
1024
|
+
}
|
|
1025
|
+
removeMarker(ownerId) {
|
|
1026
|
+
const marker = this.markerGoByOwner.get(ownerId);
|
|
1027
|
+
if (!marker)
|
|
1028
|
+
return;
|
|
1029
|
+
this.markerGoByOwner.delete(ownerId);
|
|
1030
|
+
if (!marker.destroyed) {
|
|
1031
|
+
marker.destroy();
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
detachAllMarkers() {
|
|
1035
|
+
for (const ownerId of [...this.markerGoByOwner.keys()]) {
|
|
1036
|
+
this.removeMarker(ownerId);
|
|
1037
|
+
}
|
|
1038
|
+
this.markerGoByOwner.clear();
|
|
1039
|
+
}
|
|
1040
|
+
postMarkerOverlaySuccess(enabled, markers) {
|
|
1041
|
+
const total = markers.reduce((sum, m) => sum + m.count, 0);
|
|
1042
|
+
const state = { enabled, total, markers };
|
|
1043
|
+
const payload = {
|
|
1044
|
+
type: COMBOS_DEVELOPMENT_TOOL_MARKER_OVERLAY_SUCCESS,
|
|
1045
|
+
...state,
|
|
1046
|
+
};
|
|
1047
|
+
if (typeof window !== 'undefined' && window.parent && window.parent !== window) {
|
|
1048
|
+
window.parent.postMessage(payload, this.postMessageOrigin);
|
|
1049
|
+
}
|
|
1050
|
+
this.game.emit(COMBOS_DEVELOPMENT_TOOL_MARKER_OVERLAY_SUCCESS, state);
|
|
1051
|
+
}
|
|
848
1052
|
getSoundSystem() {
|
|
849
1053
|
const system = this.game.getSystem('SoundSystem');
|
|
850
1054
|
if (!system ||
|
|
@@ -1031,31 +1235,36 @@ var CombosDevelopmentToolSystem = CombosDevelopmentToolSystem$1;
|
|
|
1031
1235
|
/** Auto-generated by scripts/build-package.mjs — do not edit. */
|
|
1032
1236
|
Object.assign(CombosDevelopmentToolSystem, {
|
|
1033
1237
|
packageName: "@combos-fun/plugin-development-tool",
|
|
1034
|
-
packageVersion: "0.0.
|
|
1238
|
+
packageVersion: "0.0.27",
|
|
1035
1239
|
});
|
|
1036
1240
|
|
|
1037
1241
|
exports.COMBOS_DEVELOPMENT_TOOL_APPLY_PROPERTY = COMBOS_DEVELOPMENT_TOOL_APPLY_PROPERTY;
|
|
1038
1242
|
exports.COMBOS_DEVELOPMENT_TOOL_CLEAR_SELECTION = COMBOS_DEVELOPMENT_TOOL_CLEAR_SELECTION;
|
|
1039
1243
|
exports.COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_DESELECTED = COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_DESELECTED;
|
|
1040
1244
|
exports.COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_SELECTED = COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_SELECTED;
|
|
1245
|
+
exports.COMBOS_DEVELOPMENT_TOOL_MARKER_OVERLAY_SUCCESS = COMBOS_DEVELOPMENT_TOOL_MARKER_OVERLAY_SUCCESS;
|
|
1041
1246
|
exports.COMBOS_DEVELOPMENT_TOOL_READY = COMBOS_DEVELOPMENT_TOOL_READY;
|
|
1042
1247
|
exports.COMBOS_DEVELOPMENT_TOOL_REFRESH = COMBOS_DEVELOPMENT_TOOL_REFRESH;
|
|
1043
1248
|
exports.COMBOS_DEVELOPMENT_TOOL_SET = COMBOS_DEVELOPMENT_TOOL_SET;
|
|
1249
|
+
exports.COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY = COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY;
|
|
1044
1250
|
exports.COMBOS_DEVELOPMENT_TOOL_SET_MUTED = COMBOS_DEVELOPMENT_TOOL_SET_MUTED;
|
|
1045
|
-
exports.COMBOS_DEVELOPMENT_TOOL_SET_PAUSED = COMBOS_DEVELOPMENT_TOOL_SET_PAUSED;
|
|
1046
1251
|
exports.COMBOS_DEVELOPMENT_TOOL_SET_SUCCESS = COMBOS_DEVELOPMENT_TOOL_SET_SUCCESS;
|
|
1047
1252
|
exports.COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED = COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED;
|
|
1048
1253
|
exports.CombosDevelopmentToolSystem = CombosDevelopmentToolSystem;
|
|
1049
1254
|
exports.CombosDevelopmentToolTarget = CombosDevelopmentToolTarget;
|
|
1050
1255
|
exports.DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES = DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES;
|
|
1256
|
+
exports.MARKER_COMPONENTS = MARKER_COMPONENTS;
|
|
1257
|
+
exports.MARKER_ICON_SIZE = MARKER_ICON_SIZE;
|
|
1051
1258
|
exports.applyPropertyValue = applyPropertyValue;
|
|
1052
1259
|
exports.applyPropertyWithHooks = applyPropertyWithHooks;
|
|
1053
1260
|
exports.buildGameObjectDeselectedPayload = buildGameObjectDeselectedPayload;
|
|
1054
1261
|
exports.buildGameObjectSnapshot = buildGameObjectSnapshot;
|
|
1262
|
+
exports.drawMarkerIcon = drawMarkerIcon;
|
|
1055
1263
|
exports.isAllowedMessageOrigin = isAllowedMessageOrigin;
|
|
1056
1264
|
exports.isClearSelectionMessage = isClearSelectionMessage;
|
|
1057
1265
|
exports.mergeAllowedMessageOrigins = mergeAllowedMessageOrigins;
|
|
1058
1266
|
exports.readPropertyValue = readPropertyValue;
|
|
1059
1267
|
exports.readSourceAnchor = readSourceAnchor;
|
|
1268
|
+
exports.resolveMarkerDef = resolveMarkerDef;
|
|
1060
1269
|
exports.shouldNotifyGameObjectDeselected = shouldNotifyGameObjectDeselected;
|
|
1061
1270
|
//# sourceMappingURL=plugin-development-tool.cjs.js.map
|