@umicat/phaser-sdk 1.0.7 → 1.0.8

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/dist/index.d.ts CHANGED
@@ -39,5 +39,5 @@ export { setupEditorBridge } from './editor/EditorBridge.js';
39
39
  export { EditorOverlayScene, EDITOR_OVERLAY_KEY } from './editor/EditorOverlayScene.js';
40
40
  export type { EditorEntityPatch, EditorEnterMessage, EditorExitMessage, EditorGetSceneMessage, EditorApplyEditMessage, EditorSetSelectionMessage, EditorPanZoomMessage, EditorHostToSdkMessage, EditorSceneLoadedMessage, EditorSelectionPickedMessage, EditorDragEndMessage, EditorShortcutMessage, EditorCreateEntityMessage, EditorDeleteEntityMessage, EditorSetEditModeMessage, EditorSelectionRectMessage, EditorSdkToHostMessage, } from './protocol.js';
41
41
  export { SCHEMA_VERSION, isPerFrameNinePatch, isPerFrameHitbox, } from './scene/types.js';
42
- export type { Manifest, ManifestGroup, SceneRef, HudRef, AssetRecord, AssetKind, SceneType, SceneFile, WorldScene, HudScene, WorldSceneConfig, CameraConfig, WorldEntity, WorldEntityKind, NonGroupWorldEntity, RenderableEntity, SpriteEntity, RectEntity, CircleEntity, CodeRenderedEntity, GroupEntity, TilemapEntity, TilemapLayer, TilesetMetadata, TileMetadata, TilesetAutotile, TilesetAnimation, WangTerrain, TriggerEntity, TriggerShape, HudEntity, HudEntityKind, HudEntityBase, HudTextEntity, HudImageEntity, HudIconButtonEntity, HudProgressBarEntity, HudPanelEntity, HudTextSource, HudNumberSource, HudLayer, Transform, Anchor, AnchorSide, NinePatchConfig, NinePatchPerFrame, HitboxRect, HitboxPerFrame, DepthAnchor, } from './scene/types.js';
42
+ export type { Manifest, ManifestGroup, SceneRef, HudRef, AssetRecord, AssetKind, SceneType, SceneFile, WorldScene, HudScene, WorldSceneConfig, CameraConfig, WorldEntity, WorldEntityKind, NonGroupWorldEntity, RenderableEntity, SpriteEntity, RectEntity, CircleEntity, CodeRenderedEntity, GroupEntity, TilemapEntity, TilemapLayer, TilesetMetadata, TileMetadata, TilesetAutotile, TilesetAnimation, WangTerrain, TriggerEntity, PrefabRefEntity, TriggerShape, HudEntity, HudEntityKind, HudEntityBase, HudTextEntity, HudImageEntity, HudIconButtonEntity, HudProgressBarEntity, HudPanelEntity, HudTextSource, HudNumberSource, HudLayer, Transform, Anchor, AnchorSide, NinePatchConfig, NinePatchPerFrame, HitboxRect, HitboxPerFrame, DepthAnchor, } from './scene/types.js';
43
43
  export { PROTOCOL_VERSION, type HelloMessage, type InitMessage, type RpcRequestMessage, type RpcResultOk, type RpcResultError, type HostToSdkMessage, type SdkToHostMessage, type RpcErrorPayload, type RpcMethod, type SavesGetParams, type SavesGetResult, type SavesSetParams, type SavesSetResult, type SavesDeleteParams, type SavesDeleteResult, type SavesListResult, type GameDataGetParams, type GameDataGetResult, type GameDataSetParams, type GameDataSetResult, type GameDataDeleteParams, type GameDataDeleteResult, type GameDataListResult, type RealtimeGetTokenParams, type RealtimeGetTokenResult, } from './protocol.js';
@@ -78,7 +78,7 @@ export function preloadSceneAssets(scene, sceneFile, manifest) {
78
78
  if (sceneFile.type !== 'world')
79
79
  return; // HUD slice will add its own walker
80
80
  const state = getOrInitState(scene, manifest);
81
- const ids = collectAssetIds(sceneFile.entities);
81
+ const ids = collectAssetIds(sceneFile.entities, manifest);
82
82
  for (const id of ids) {
83
83
  if (state.requestedAssetIds.has(id))
84
84
  continue;
@@ -98,7 +98,7 @@ export function preloadSceneAssets(scene, sceneFile, manifest) {
98
98
  state.requestedAssetIds.add(id);
99
99
  }
100
100
  }
101
- function collectAssetIds(entities) {
101
+ function collectAssetIds(entities, manifest) {
102
102
  const ids = new Set();
103
103
  function walk(e) {
104
104
  if (e.kind === 'sprite') {
@@ -119,6 +119,14 @@ function collectAssetIds(entities) {
119
119
  ids.add(id);
120
120
  }
121
121
  }
122
+ else if (e.kind === 'prefab-ref') {
123
+ // Authored prefab instance — preload whatever the prefab's visual
124
+ // references (sprite prefabs carry an assetId at the record root).
125
+ const prefab = manifest?.prefabs?.find((p) => p.id === e.prefabId);
126
+ const assetId = prefab?.assetId;
127
+ if (typeof assetId === 'string')
128
+ ids.add(assetId);
129
+ }
122
130
  // rect / circle / code-rendered / trigger don't reference assets.
123
131
  }
124
132
  for (const e of entities)
@@ -30,6 +30,14 @@ export function spawnEntity(ctx, entity) {
30
30
  case 'trigger':
31
31
  go = createTriggerStub(ctx, entity);
32
32
  break;
33
+ case 'prefab-ref':
34
+ // Authored prefab INSTANCE (editor "Convert to Type" / drag-from-Types,
35
+ // 2026-06-12). Resolve the prefab record, build a synthetic entity at
36
+ // this instance's transform, and recurse — the inner call creates the
37
+ // GameObject, applies the prefab's physics, tags, and registers under
38
+ // THIS entity's id. We then re-tag kind + prefabId below.
39
+ go = createPrefabRefInstance(ctx, entity);
40
+ break;
33
41
  default: {
34
42
  const exhaustive = entity;
35
43
  throw new Error(`[umicat/scene] unknown entity kind: ${JSON.stringify(exhaustive)}`);
@@ -38,9 +46,71 @@ export function spawnEntity(ctx, entity) {
38
46
  applyTransform(go, entity.transform);
39
47
  tagGameObject(go, entity);
40
48
  applyEntityPhysicsIfDeclared(ctx.scene, go, entity);
49
+ if (entity.kind === 'prefab-ref') {
50
+ // Tag for editor live-edit: `umicat:editor:editPrefab` walks instances
51
+ // via this data key, and canvas clicks route to the Prefab inspector
52
+ // (editing one instance = editing the type). Registry re-registers with
53
+ // the prefabId so byPrefabId() finds authored instances too.
54
+ go.setData('entityPrefabId', entity.prefabId);
55
+ ctx.registry.unregister(entity.id);
56
+ ctx.registry.register(entity.id, entity.role ?? prefabRoleOf(ctx.scene, entity.prefabId), go, entity.prefabId);
57
+ return go;
58
+ }
41
59
  ctx.registry.register(entity.id, entity.role, go);
42
60
  return go;
43
61
  }
62
+ /**
63
+ * Read a prefab record straight from the cached manifest. Deliberately NOT
64
+ * importing SceneLoader's getManifest — that module imports this one, and
65
+ * keeping the read inline avoids the import cycle.
66
+ */
67
+ function lookupPrefab(scene, prefabId) {
68
+ const manifest = scene.cache.json.get('umicat:manifest');
69
+ const prefab = manifest?.prefabs?.find((p) => p.id === prefabId);
70
+ return prefab ?? null;
71
+ }
72
+ function prefabRoleOf(scene, prefabId) {
73
+ const prefab = lookupPrefab(scene, prefabId);
74
+ return prefab?.role ?? undefined;
75
+ }
76
+ function createPrefabRefInstance(ctx, entity) {
77
+ const prefab = lookupPrefab(ctx.scene, entity.prefabId);
78
+ if (!prefab) {
79
+ console.warn(`[umicat/scene] prefab-ref '${entity.id}' references prefab '${entity.prefabId}' but the manifest has no such prefab; rendering placeholder`);
80
+ return createMissingPlaceholder(ctx.scene, `prefab: ${entity.prefabId}?`);
81
+ }
82
+ // Synthetic entity = prefab fields + this instance's id/transform. Instance
83
+ // role/properties override the prefab's when set (per-instance variance
84
+ // without breaking the type link).
85
+ const synthetic = {
86
+ ...prefab,
87
+ id: entity.id,
88
+ transform: entity.transform,
89
+ ...(entity.role !== undefined ? { role: entity.role } : {}),
90
+ ...(entity.properties !== undefined
91
+ ? { properties: { ...(prefab.properties ?? {}), ...entity.properties } }
92
+ : {}),
93
+ };
94
+ if (synthetic.kind === 'prefab-ref') {
95
+ // A prefab record can never be kind prefab-ref, but guard against a
96
+ // hand-edited manifest producing infinite recursion.
97
+ console.warn(`[umicat/scene] prefab '${entity.prefabId}' has invalid kind 'prefab-ref'`);
98
+ return createMissingPlaceholder(ctx.scene, `prefab: ${entity.prefabId}?`);
99
+ }
100
+ return spawnEntity(ctx, synthetic);
101
+ }
102
+ /** Magenta-bordered "?" placeholder — same soft-fail pattern as missing assets. */
103
+ function createMissingPlaceholder(scene, label) {
104
+ const g = scene.add.graphics();
105
+ g.lineStyle(2, 0xff00ff, 1);
106
+ g.strokeRect(-32, -32, 64, 64);
107
+ g.lineBetween(-32, -32, 32, 32);
108
+ g.lineBetween(-32, 32, 32, -32);
109
+ g.setData('editorHitWidth', 64);
110
+ g.setData('editorHitHeight', 64);
111
+ void label;
112
+ return g;
113
+ }
44
114
  /**
45
115
  * Apply a scene entity's optional `physics` block. Only renderable entity
46
116
  * kinds (sprite / rect / circle / code-rendered) carry a body — they mirror
@@ -753,7 +753,7 @@ export interface Anchor {
753
753
  offsetX?: number;
754
754
  offsetY?: number;
755
755
  }
756
- export type WorldEntityKind = 'sprite' | 'rect' | 'circle' | 'code-rendered' | 'group' | 'tilemap' | 'trigger';
756
+ export type WorldEntityKind = 'sprite' | 'rect' | 'circle' | 'code-rendered' | 'group' | 'tilemap' | 'trigger' | 'prefab-ref';
757
757
  export interface WorldEntityBase {
758
758
  id: string;
759
759
  /** Optional semantic tag. Behavior code keys off this. */
@@ -913,7 +913,23 @@ export interface TriggerEntity extends WorldEntityBase {
913
913
  /** Optional preset name (open-door / damage-zone / scene-transition / spawner / heal). */
914
914
  preset?: string;
915
915
  }
916
- export type NonGroupWorldEntity = SpriteEntity | RectEntity | CircleEntity | CodeRenderedEntity | TilemapEntity | TriggerEntity;
916
+ /**
917
+ * Authored INSTANCE of a prefab — the Figma component/instance model
918
+ * brought to scene data (editor "Convert to Type" + drag-from-Types,
919
+ * 2026-06-12). The scene stores only "which type + where"; every render /
920
+ * physics / properties field comes from the prefab record at spawn time,
921
+ * so editing the prefab updates every placed instance across all scenes.
922
+ *
923
+ * Unlike `spawnPrefab` runtime instances (`<prefabId>#<n>` ids, created by
924
+ * behavior code), prefab-refs are scene-file entities: they appear in the
925
+ * editor Hierarchy, are draggable (transform lives here), and persist.
926
+ * `role` defaults to the prefab's role; an explicit role here overrides.
927
+ */
928
+ export interface PrefabRefEntity extends WorldEntityBase {
929
+ kind: 'prefab-ref';
930
+ prefabId: string;
931
+ }
932
+ export type NonGroupWorldEntity = SpriteEntity | RectEntity | CircleEntity | CodeRenderedEntity | TilemapEntity | TriggerEntity | PrefabRefEntity;
917
933
  export type WorldEntity = NonGroupWorldEntity | GroupEntity;
918
934
  /**
919
935
  * Renderable entity kinds — those that produce a single GameObject with
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umicat/phaser-sdk",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "Umicat Phaser 3 SDK — game infrastructure for the Umicat platform",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",