@umicat/phaser-sdk 1.0.0
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/SDK-GUIDE.md +1726 -0
- package/dist/core/Transport.d.ts +28 -0
- package/dist/core/Transport.js +7 -0
- package/dist/core/Umicat.d.ts +45 -0
- package/dist/core/Umicat.js +60 -0
- package/dist/core/UmicatGame.d.ts +43 -0
- package/dist/core/UmicatGame.js +64 -0
- package/dist/core/UmicatScene.d.ts +19 -0
- package/dist/core/UmicatScene.js +38 -0
- package/dist/core/transports/LocalStorageTransport.d.ts +22 -0
- package/dist/core/transports/LocalStorageTransport.js +78 -0
- package/dist/core/transports/PostMessageTransport.d.ts +28 -0
- package/dist/core/transports/PostMessageTransport.js +105 -0
- package/dist/editor/EditorBridge.d.ts +114 -0
- package/dist/editor/EditorBridge.js +2608 -0
- package/dist/editor/EditorOverlayScene.d.ts +333 -0
- package/dist/editor/EditorOverlayScene.js +1896 -0
- package/dist/editor/EditorState.d.ts +251 -0
- package/dist/editor/EditorState.js +197 -0
- package/dist/gamedata/GameDataModule.d.ts +45 -0
- package/dist/gamedata/GameDataModule.js +59 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +43 -0
- package/dist/orientation.d.ts +5 -0
- package/dist/orientation.js +4 -0
- package/dist/protocol.d.ts +807 -0
- package/dist/protocol.js +3 -0
- package/dist/realtime/RealtimeModule.d.ts +93 -0
- package/dist/realtime/RealtimeModule.js +115 -0
- package/dist/realtime/UmicatRoom.d.ts +197 -0
- package/dist/realtime/UmicatRoom.js +353 -0
- package/dist/recording/RecordingManager.d.ts +11 -0
- package/dist/recording/RecordingManager.js +59 -0
- package/dist/saves/SavesModule.d.ts +23 -0
- package/dist/saves/SavesModule.js +37 -0
- package/dist/scene/EditorMode.d.ts +17 -0
- package/dist/scene/EditorMode.js +22 -0
- package/dist/scene/EntityRegistry.d.ts +39 -0
- package/dist/scene/EntityRegistry.js +103 -0
- package/dist/scene/GameConfig.d.ts +60 -0
- package/dist/scene/GameConfig.js +50 -0
- package/dist/scene/HudRuntime.d.ts +131 -0
- package/dist/scene/HudRuntime.js +1224 -0
- package/dist/scene/Prefabs.d.ts +92 -0
- package/dist/scene/Prefabs.js +175 -0
- package/dist/scene/Rules.d.ts +73 -0
- package/dist/scene/Rules.js +164 -0
- package/dist/scene/SceneLoader.d.ts +118 -0
- package/dist/scene/SceneLoader.js +615 -0
- package/dist/scene/Waves.d.ts +85 -0
- package/dist/scene/Waves.js +365 -0
- package/dist/scene/autotile.d.ts +103 -0
- package/dist/scene/autotile.js +321 -0
- package/dist/scene/renderScripts.d.ts +53 -0
- package/dist/scene/renderScripts.js +67 -0
- package/dist/scene/spawnEntity.d.ts +201 -0
- package/dist/scene/spawnEntity.js +1326 -0
- package/dist/scene/types.d.ts +1166 -0
- package/dist/scene/types.js +34 -0
- package/dist/screenshot/ScreenshotManager.d.ts +14 -0
- package/dist/screenshot/ScreenshotManager.js +33 -0
- package/package.json +35 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-game editor state, shared between EditorBridge (postMessage handler)
|
|
3
|
+
* and EditorOverlayScene (canvas renderer + pointer source).
|
|
4
|
+
*
|
|
5
|
+
* Design 03 §13.5: in Edit mode the editor overlay captures clicks before
|
|
6
|
+
* they reach game systems; this state carries the selection + drag-in-progress
|
|
7
|
+
* info both sides read.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Editor mode — slice 5. World mode edits the active world scene; HUD mode
|
|
11
|
+
* edits the HUD scene attached to that world (manifest's `scenes[].hud`).
|
|
12
|
+
* Toggled by host via `umicat:editor:setEditMode`.
|
|
13
|
+
*/
|
|
14
|
+
export type EditorMode = 'world' | 'hud';
|
|
15
|
+
/**
|
|
16
|
+
* Editor debug-overlay flags (slice 8). Toggled by the host via
|
|
17
|
+
* `umicat:editor:setDebugOverlay`; read each frame by EditorOverlayScene.
|
|
18
|
+
*/
|
|
19
|
+
export interface EditorDebugOverlayState {
|
|
20
|
+
/** Draw hitbox rects + depth-anchor crosshairs on sprites with metadata. */
|
|
21
|
+
showHitboxes: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Tilemap painter active state (slice 6 Phase B).
|
|
25
|
+
*
|
|
26
|
+
* When the user selects a tilemap entity, the host pushes the current tool
|
|
27
|
+
* + active tile + active layer via `umicat:editor:setTilemapTool`. The SDK
|
|
28
|
+
* caches this; on canvas pointer events over the tilemap, the overlay
|
|
29
|
+
* scene reads it to drive paint behavior locally (no per-cell host
|
|
30
|
+
* round-trip — same split-state pattern as drag-to-move).
|
|
31
|
+
*
|
|
32
|
+
* After each completed stroke (pointerup), the SDK posts
|
|
33
|
+
* `umicat:sdk:tilemapEdited { entityId, op }` so the host can record an
|
|
34
|
+
* undo command + persist on save.
|
|
35
|
+
*
|
|
36
|
+
* `tilemapEditingId` doubles as the "are we in paint mode?" flag — when
|
|
37
|
+
* non-null AND the selected entity is a tilemap, paint mode is active.
|
|
38
|
+
*/
|
|
39
|
+
export type TilemapTool = 'brush' | 'eraser' | 'rect' | 'fill' | 'picker';
|
|
40
|
+
export interface TilemapToolState {
|
|
41
|
+
/** Tilemap entity currently being painted. Null = not in paint mode. */
|
|
42
|
+
tilemapEditingId: string | null;
|
|
43
|
+
/** Which layer (within the tilemap) the brush affects. */
|
|
44
|
+
activeLayerId: string | null;
|
|
45
|
+
/** Active tool — defaults to 'brush' when entering paint mode. */
|
|
46
|
+
tool: TilemapTool;
|
|
47
|
+
/**
|
|
48
|
+
* Tile index in the layer's tileset image (0-based, row-major).
|
|
49
|
+
* Null when no tile is picked yet — brush/rect/fill no-op in that case.
|
|
50
|
+
* Eraser ignores this field.
|
|
51
|
+
*/
|
|
52
|
+
activeTile: number | null;
|
|
53
|
+
/**
|
|
54
|
+
* Slice 6 Phase D — Wang terrain id when in autotile mode. Non-null
|
|
55
|
+
* flips brush + eraser to autotile semantics: the click cascades
|
|
56
|
+
* across the 3×3 cell neighborhood, vertices toggle, ruleMap[bitmask]
|
|
57
|
+
* picks the tile per cell. Null = stamp-mode (Phase B behavior).
|
|
58
|
+
*/
|
|
59
|
+
activeTerrainId: string | null;
|
|
60
|
+
}
|
|
61
|
+
interface EditorStateShape {
|
|
62
|
+
active: boolean;
|
|
63
|
+
selectedId: string | null;
|
|
64
|
+
mode: EditorMode;
|
|
65
|
+
/**
|
|
66
|
+
* Drag-in-progress info. While set, the overlay scene mutates the entity's
|
|
67
|
+
* x/y directly per pointermove without going through the host. On
|
|
68
|
+
* pointerup it posts dragEnd with before/after, and the host pushes a
|
|
69
|
+
* single command. This is the "SDK holds short-lived visual state" path
|
|
70
|
+
* from the slice-2 design discussion.
|
|
71
|
+
*/
|
|
72
|
+
drag: {
|
|
73
|
+
entityId: string;
|
|
74
|
+
startWorld: {
|
|
75
|
+
x: number;
|
|
76
|
+
y: number;
|
|
77
|
+
};
|
|
78
|
+
startEntity: {
|
|
79
|
+
x: number;
|
|
80
|
+
y: number;
|
|
81
|
+
};
|
|
82
|
+
} | null;
|
|
83
|
+
debugOverlay: EditorDebugOverlayState;
|
|
84
|
+
/** Slice 6 Phase B — tilemap painter active state. See TilemapToolState docs. */
|
|
85
|
+
tilemap: TilemapToolState;
|
|
86
|
+
/**
|
|
87
|
+
* Slice 6 Phase B — paint-stroke accumulator. Brush/eraser collect cells
|
|
88
|
+
* during a pointer drag; on pointerup the SDK emits ONE `tilemapEdited`
|
|
89
|
+
* op carrying the whole stroke so undo reverts the entire stroke
|
|
90
|
+
* atomically. Null when no stroke is in flight.
|
|
91
|
+
*/
|
|
92
|
+
tilemapStroke: {
|
|
93
|
+
entityId: string;
|
|
94
|
+
layerId: string;
|
|
95
|
+
/** Cells touched by the stroke. `index = null` for eraser cells. */
|
|
96
|
+
cells: Array<{
|
|
97
|
+
x: number;
|
|
98
|
+
y: number;
|
|
99
|
+
index: number | null;
|
|
100
|
+
prevIndex: number | null;
|
|
101
|
+
}>;
|
|
102
|
+
/**
|
|
103
|
+
* Slice 6 Phase D — autotile-mode stroke metadata. Present only when
|
|
104
|
+
* the stroke is painting with a terrain (not stamping a tile index).
|
|
105
|
+
*
|
|
106
|
+
* The `cells` array above tracks EVERY affected cell (clicked +
|
|
107
|
+
* cascade neighbors) for undo; `autotile.clickedCells` records only
|
|
108
|
+
* the user's actual click cells — that's what gets emitted in the
|
|
109
|
+
* `autotilePaint` op's `cells` field so undo can replay the inverse
|
|
110
|
+
* stroke and reproduce the cascade exactly.
|
|
111
|
+
*/
|
|
112
|
+
autotile?: {
|
|
113
|
+
terrainId: string;
|
|
114
|
+
erase: boolean;
|
|
115
|
+
clickedCells: Array<{
|
|
116
|
+
x: number;
|
|
117
|
+
y: number;
|
|
118
|
+
}>;
|
|
119
|
+
};
|
|
120
|
+
} | null;
|
|
121
|
+
/**
|
|
122
|
+
* Slice 6 Phase B.4 — rect-drag in-flight state. EditorOverlayScene.update
|
|
123
|
+
* draws a preview rectangle while non-null. On pointerup the SDK composes
|
|
124
|
+
* a `fillRect` op covering startCell → endCell. Null when no rect drag.
|
|
125
|
+
*/
|
|
126
|
+
tilemapRect: {
|
|
127
|
+
entityId: string;
|
|
128
|
+
layerId: string;
|
|
129
|
+
/** Cell coords (tile grid units), inclusive on both ends. */
|
|
130
|
+
startX: number;
|
|
131
|
+
startY: number;
|
|
132
|
+
endX: number;
|
|
133
|
+
endY: number;
|
|
134
|
+
/**
|
|
135
|
+
* Slice 6 Phase D — autotile-mode rect. When set, the rect commits
|
|
136
|
+
* as one `autotilePaint` op (each in-rect cell counts as a clicked
|
|
137
|
+
* cell) instead of `fillRect`.
|
|
138
|
+
*/
|
|
139
|
+
autotile?: {
|
|
140
|
+
terrainId: string;
|
|
141
|
+
erase: boolean;
|
|
142
|
+
};
|
|
143
|
+
} | null;
|
|
144
|
+
/**
|
|
145
|
+
* Slice 6 Phase B.6.1 — tilemap resize-handle drag in flight. Set on
|
|
146
|
+
* pointerdown over a corner/edge handle, cleared on pointerup. Drives:
|
|
147
|
+
* (a) live ghost-rect preview in update(), (b) computing the final
|
|
148
|
+
* resize op + transformDelta on commit.
|
|
149
|
+
*
|
|
150
|
+
* Anchor flags (left/right/top/bottom = true) mark which edges the
|
|
151
|
+
* drag moves. The opposite edges stay fixed → transformDelta keeps
|
|
152
|
+
* them anchored after the resize op rebuilds layers around the new
|
|
153
|
+
* center.
|
|
154
|
+
*/
|
|
155
|
+
tilemapResize: {
|
|
156
|
+
entityId: string;
|
|
157
|
+
handle: TilemapResizeHandle;
|
|
158
|
+
/** Original size + center at drag-start, in cells + world px. */
|
|
159
|
+
startSize: {
|
|
160
|
+
width: number;
|
|
161
|
+
height: number;
|
|
162
|
+
};
|
|
163
|
+
startCenter: {
|
|
164
|
+
x: number;
|
|
165
|
+
y: number;
|
|
166
|
+
};
|
|
167
|
+
tileSize: {
|
|
168
|
+
width: number;
|
|
169
|
+
height: number;
|
|
170
|
+
};
|
|
171
|
+
/** Current preview dims (snapped to integer cells). Updated per pointermove. */
|
|
172
|
+
previewWidth: number;
|
|
173
|
+
previewHeight: number;
|
|
174
|
+
/** Current preview center (shifts as edges move). */
|
|
175
|
+
previewCenter: {
|
|
176
|
+
x: number;
|
|
177
|
+
y: number;
|
|
178
|
+
};
|
|
179
|
+
} | null;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Tilemap resize handle identity. 4 corners + 4 edge midpoints. Each
|
|
183
|
+
* handle defines which edges move during the drag — opposite edges stay
|
|
184
|
+
* fixed (transformDelta computed to keep them anchored).
|
|
185
|
+
*/
|
|
186
|
+
export type TilemapResizeHandle = 'nw' | 'ne' | 'sw' | 'se' | 'n' | 's' | 'w' | 'e';
|
|
187
|
+
/**
|
|
188
|
+
* Accept any object — we only use a single internal symbol-style key, so
|
|
189
|
+
* there's no risk of collision with Phaser's own props.
|
|
190
|
+
*/
|
|
191
|
+
type AnyObject = object;
|
|
192
|
+
export declare function getEditorState(game: AnyObject): EditorStateShape;
|
|
193
|
+
export declare function setEditorActive(game: AnyObject, active: boolean): void;
|
|
194
|
+
export declare function getEditorMode(game: AnyObject): EditorMode;
|
|
195
|
+
export declare function setEditorMode(game: AnyObject, mode: EditorMode): void;
|
|
196
|
+
export declare function setSelection(game: AnyObject, id: string | null): void;
|
|
197
|
+
export declare function getSelection(game: AnyObject): string | null;
|
|
198
|
+
export declare function startDrag(game: AnyObject, entityId: string, startWorld: {
|
|
199
|
+
x: number;
|
|
200
|
+
y: number;
|
|
201
|
+
}, startEntity: {
|
|
202
|
+
x: number;
|
|
203
|
+
y: number;
|
|
204
|
+
}): void;
|
|
205
|
+
export declare function clearDrag(game: AnyObject): void;
|
|
206
|
+
export declare function getDrag(game: AnyObject): EditorStateShape['drag'];
|
|
207
|
+
export declare function getDebugOverlayState(game: AnyObject): EditorDebugOverlayState;
|
|
208
|
+
export declare function setDebugOverlayState(game: AnyObject, patch: Partial<EditorDebugOverlayState>): void;
|
|
209
|
+
export declare function getTilemapToolState(game: AnyObject): TilemapToolState;
|
|
210
|
+
export declare function setTilemapToolState(game: AnyObject, patch: Partial<TilemapToolState>): void;
|
|
211
|
+
export declare function isTilemapPaintMode(game: AnyObject): boolean;
|
|
212
|
+
export declare function getTilemapStroke(game: AnyObject): EditorStateShape['tilemapStroke'];
|
|
213
|
+
export declare function beginTilemapStroke(game: AnyObject, entityId: string, layerId: string): void;
|
|
214
|
+
/**
|
|
215
|
+
* Slice 6 Phase D — start an autotile-mode stroke. Same shape as
|
|
216
|
+
* `beginTilemapStroke` but carries the autotile discriminator so the
|
|
217
|
+
* pointerup commit emits an `autotilePaint` op (not `paint` / `erase`).
|
|
218
|
+
*/
|
|
219
|
+
export declare function beginAutotileStroke(game: AnyObject, entityId: string, layerId: string, terrainId: string, erase: boolean): void;
|
|
220
|
+
export declare function appendTilemapStrokeCell(game: AnyObject, cell: {
|
|
221
|
+
x: number;
|
|
222
|
+
y: number;
|
|
223
|
+
index: number | null;
|
|
224
|
+
prevIndex: number | null;
|
|
225
|
+
}): void;
|
|
226
|
+
/**
|
|
227
|
+
* Slice 6 Phase D — record a user-clicked cell + its cascade-affected
|
|
228
|
+
* cells into the stroke. Click cell goes into `autotile.clickedCells`
|
|
229
|
+
* (dedup'd, last-wins by x/y); every affected cell (including the click
|
|
230
|
+
* itself and cascade neighbors) goes into `cells` (dedup'd, FIRST-wins
|
|
231
|
+
* by x/y so `prevIndex` reflects the state at the start of the stroke).
|
|
232
|
+
*/
|
|
233
|
+
export declare function appendAutotileStrokeClick(game: AnyObject, clickX: number, clickY: number, affected: Array<{
|
|
234
|
+
x: number;
|
|
235
|
+
y: number;
|
|
236
|
+
index: number;
|
|
237
|
+
prevIndex: number;
|
|
238
|
+
}>): void;
|
|
239
|
+
export declare function endTilemapStroke(game: AnyObject): EditorStateShape['tilemapStroke'];
|
|
240
|
+
export declare function getTilemapRect(game: AnyObject): EditorStateShape['tilemapRect'];
|
|
241
|
+
export declare function beginTilemapRect(game: AnyObject, entityId: string, layerId: string, x: number, y: number, autotile?: {
|
|
242
|
+
terrainId: string;
|
|
243
|
+
erase: boolean;
|
|
244
|
+
}): void;
|
|
245
|
+
export declare function updateTilemapRect(game: AnyObject, x: number, y: number): void;
|
|
246
|
+
export declare function endTilemapRect(game: AnyObject): EditorStateShape['tilemapRect'];
|
|
247
|
+
export declare function getTilemapResize(game: AnyObject): EditorStateShape['tilemapResize'];
|
|
248
|
+
export declare function beginTilemapResize(game: AnyObject, resize: NonNullable<EditorStateShape['tilemapResize']>): void;
|
|
249
|
+
export declare function updateTilemapResize(game: AnyObject, patch: Partial<Pick<NonNullable<EditorStateShape['tilemapResize']>, 'previewWidth' | 'previewHeight' | 'previewCenter'>>): void;
|
|
250
|
+
export declare function endTilemapResize(game: AnyObject): EditorStateShape['tilemapResize'];
|
|
251
|
+
export {};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-game editor state, shared between EditorBridge (postMessage handler)
|
|
3
|
+
* and EditorOverlayScene (canvas renderer + pointer source).
|
|
4
|
+
*
|
|
5
|
+
* Design 03 §13.5: in Edit mode the editor overlay captures clicks before
|
|
6
|
+
* they reach game systems; this state carries the selection + drag-in-progress
|
|
7
|
+
* info both sides read.
|
|
8
|
+
*/
|
|
9
|
+
const KEY = '__unboxyEditorState';
|
|
10
|
+
function bag(game) {
|
|
11
|
+
return game;
|
|
12
|
+
}
|
|
13
|
+
export function getEditorState(game) {
|
|
14
|
+
const b = bag(game);
|
|
15
|
+
const existing = b[KEY];
|
|
16
|
+
if (existing)
|
|
17
|
+
return existing;
|
|
18
|
+
const fresh = {
|
|
19
|
+
active: false,
|
|
20
|
+
selectedId: null,
|
|
21
|
+
mode: 'world',
|
|
22
|
+
drag: null,
|
|
23
|
+
debugOverlay: { showHitboxes: false },
|
|
24
|
+
tilemap: {
|
|
25
|
+
tilemapEditingId: null,
|
|
26
|
+
activeLayerId: null,
|
|
27
|
+
tool: 'brush',
|
|
28
|
+
activeTile: null,
|
|
29
|
+
activeTerrainId: null,
|
|
30
|
+
},
|
|
31
|
+
tilemapStroke: null,
|
|
32
|
+
tilemapRect: null,
|
|
33
|
+
tilemapResize: null,
|
|
34
|
+
};
|
|
35
|
+
b[KEY] = fresh;
|
|
36
|
+
return fresh;
|
|
37
|
+
}
|
|
38
|
+
export function setEditorActive(game, active) {
|
|
39
|
+
getEditorState(game).active = active;
|
|
40
|
+
}
|
|
41
|
+
export function getEditorMode(game) {
|
|
42
|
+
return getEditorState(game).mode;
|
|
43
|
+
}
|
|
44
|
+
export function setEditorMode(game, mode) {
|
|
45
|
+
getEditorState(game).mode = mode;
|
|
46
|
+
}
|
|
47
|
+
export function setSelection(game, id) {
|
|
48
|
+
getEditorState(game).selectedId = id;
|
|
49
|
+
}
|
|
50
|
+
export function getSelection(game) {
|
|
51
|
+
return getEditorState(game).selectedId;
|
|
52
|
+
}
|
|
53
|
+
export function startDrag(game, entityId, startWorld, startEntity) {
|
|
54
|
+
getEditorState(game).drag = { entityId, startWorld, startEntity };
|
|
55
|
+
}
|
|
56
|
+
export function clearDrag(game) {
|
|
57
|
+
getEditorState(game).drag = null;
|
|
58
|
+
}
|
|
59
|
+
export function getDrag(game) {
|
|
60
|
+
return getEditorState(game).drag;
|
|
61
|
+
}
|
|
62
|
+
export function getDebugOverlayState(game) {
|
|
63
|
+
return getEditorState(game).debugOverlay;
|
|
64
|
+
}
|
|
65
|
+
export function setDebugOverlayState(game, patch) {
|
|
66
|
+
const state = getEditorState(game).debugOverlay;
|
|
67
|
+
Object.assign(state, patch);
|
|
68
|
+
}
|
|
69
|
+
// --- Slice 6 Phase B — tilemap painter state -----------------------------
|
|
70
|
+
export function getTilemapToolState(game) {
|
|
71
|
+
return getEditorState(game).tilemap;
|
|
72
|
+
}
|
|
73
|
+
export function setTilemapToolState(game, patch) {
|
|
74
|
+
const state = getEditorState(game).tilemap;
|
|
75
|
+
Object.assign(state, patch);
|
|
76
|
+
}
|
|
77
|
+
export function isTilemapPaintMode(game) {
|
|
78
|
+
const s = getEditorState(game);
|
|
79
|
+
return !!s.tilemap.tilemapEditingId && s.tilemap.tilemapEditingId === s.selectedId;
|
|
80
|
+
}
|
|
81
|
+
export function getTilemapStroke(game) {
|
|
82
|
+
return getEditorState(game).tilemapStroke;
|
|
83
|
+
}
|
|
84
|
+
export function beginTilemapStroke(game, entityId, layerId) {
|
|
85
|
+
getEditorState(game).tilemapStroke = { entityId, layerId, cells: [] };
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Slice 6 Phase D — start an autotile-mode stroke. Same shape as
|
|
89
|
+
* `beginTilemapStroke` but carries the autotile discriminator so the
|
|
90
|
+
* pointerup commit emits an `autotilePaint` op (not `paint` / `erase`).
|
|
91
|
+
*/
|
|
92
|
+
export function beginAutotileStroke(game, entityId, layerId, terrainId, erase) {
|
|
93
|
+
getEditorState(game).tilemapStroke = {
|
|
94
|
+
entityId,
|
|
95
|
+
layerId,
|
|
96
|
+
cells: [],
|
|
97
|
+
autotile: { terrainId, erase, clickedCells: [] },
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export function appendTilemapStrokeCell(game, cell) {
|
|
101
|
+
const stroke = getEditorState(game).tilemapStroke;
|
|
102
|
+
if (!stroke)
|
|
103
|
+
return;
|
|
104
|
+
// Dedupe — pointermove fires many times per cell on slow drags.
|
|
105
|
+
const last = stroke.cells[stroke.cells.length - 1];
|
|
106
|
+
if (last && last.x === cell.x && last.y === cell.y)
|
|
107
|
+
return;
|
|
108
|
+
stroke.cells.push(cell);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Slice 6 Phase D — record a user-clicked cell + its cascade-affected
|
|
112
|
+
* cells into the stroke. Click cell goes into `autotile.clickedCells`
|
|
113
|
+
* (dedup'd, last-wins by x/y); every affected cell (including the click
|
|
114
|
+
* itself and cascade neighbors) goes into `cells` (dedup'd, FIRST-wins
|
|
115
|
+
* by x/y so `prevIndex` reflects the state at the start of the stroke).
|
|
116
|
+
*/
|
|
117
|
+
export function appendAutotileStrokeClick(game, clickX, clickY, affected) {
|
|
118
|
+
const stroke = getEditorState(game).tilemapStroke;
|
|
119
|
+
if (!stroke || !stroke.autotile)
|
|
120
|
+
return;
|
|
121
|
+
const clicked = stroke.autotile.clickedCells;
|
|
122
|
+
const clickedLast = clicked[clicked.length - 1];
|
|
123
|
+
if (!clickedLast || clickedLast.x !== clickX || clickedLast.y !== clickY) {
|
|
124
|
+
clicked.push({ x: clickX, y: clickY });
|
|
125
|
+
}
|
|
126
|
+
// Build a fast (x,y) → index lookup into stroke.cells so we don't
|
|
127
|
+
// overwrite the first-seen prevIndex of cells already in the stroke.
|
|
128
|
+
// (Cascade neighbors get re-touched on every adjacent click; preserving
|
|
129
|
+
// their original prevIndex is what makes undo restore the pre-stroke
|
|
130
|
+
// state.) O(stroke.cells) per click is fine — strokes max out at a
|
|
131
|
+
// few hundred cells.
|
|
132
|
+
for (const a of affected) {
|
|
133
|
+
const existing = stroke.cells.findIndex((c) => c.x === a.x && c.y === a.y);
|
|
134
|
+
if (existing >= 0) {
|
|
135
|
+
// Update the latest tile index (so the resulting visual matches
|
|
136
|
+
// the on-canvas state), keep prevIndex from the first touch.
|
|
137
|
+
stroke.cells[existing].index = a.index < 0 ? null : a.index;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
stroke.cells.push({
|
|
141
|
+
x: a.x,
|
|
142
|
+
y: a.y,
|
|
143
|
+
index: a.index < 0 ? null : a.index,
|
|
144
|
+
prevIndex: a.prevIndex < 0 ? null : a.prevIndex,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
export function endTilemapStroke(game) {
|
|
150
|
+
const stroke = getEditorState(game).tilemapStroke;
|
|
151
|
+
getEditorState(game).tilemapStroke = null;
|
|
152
|
+
return stroke;
|
|
153
|
+
}
|
|
154
|
+
export function getTilemapRect(game) {
|
|
155
|
+
return getEditorState(game).tilemapRect;
|
|
156
|
+
}
|
|
157
|
+
export function beginTilemapRect(game, entityId, layerId, x, y, autotile) {
|
|
158
|
+
getEditorState(game).tilemapRect = {
|
|
159
|
+
entityId,
|
|
160
|
+
layerId,
|
|
161
|
+
startX: x,
|
|
162
|
+
startY: y,
|
|
163
|
+
endX: x,
|
|
164
|
+
endY: y,
|
|
165
|
+
autotile,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
export function updateTilemapRect(game, x, y) {
|
|
169
|
+
const rect = getEditorState(game).tilemapRect;
|
|
170
|
+
if (!rect)
|
|
171
|
+
return;
|
|
172
|
+
rect.endX = x;
|
|
173
|
+
rect.endY = y;
|
|
174
|
+
}
|
|
175
|
+
export function endTilemapRect(game) {
|
|
176
|
+
const rect = getEditorState(game).tilemapRect;
|
|
177
|
+
getEditorState(game).tilemapRect = null;
|
|
178
|
+
return rect;
|
|
179
|
+
}
|
|
180
|
+
// --- Slice 6 Phase B.6.1 — resize-handle drag ----------------------------
|
|
181
|
+
export function getTilemapResize(game) {
|
|
182
|
+
return getEditorState(game).tilemapResize;
|
|
183
|
+
}
|
|
184
|
+
export function beginTilemapResize(game, resize) {
|
|
185
|
+
getEditorState(game).tilemapResize = resize;
|
|
186
|
+
}
|
|
187
|
+
export function updateTilemapResize(game, patch) {
|
|
188
|
+
const r = getEditorState(game).tilemapResize;
|
|
189
|
+
if (!r)
|
|
190
|
+
return;
|
|
191
|
+
Object.assign(r, patch);
|
|
192
|
+
}
|
|
193
|
+
export function endTilemapResize(game) {
|
|
194
|
+
const r = getEditorState(game).tilemapResize;
|
|
195
|
+
getEditorState(game).tilemapResize = null;
|
|
196
|
+
return r;
|
|
197
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Transport } from '../core/Transport.js';
|
|
2
|
+
/**
|
|
3
|
+
* Game-scope key-value store shared by all players of a game.
|
|
4
|
+
*
|
|
5
|
+
* Unlike `umicat.saves` (per-user), `gameData` lives at the game level — one
|
|
6
|
+
* value per key, visible to everyone. Reads are public (anonymous players
|
|
7
|
+
* see the same data). Writes require an authenticated user; calling
|
|
8
|
+
* `set()` or `delete()` when `umicat.user === null` throws an `RpcError`
|
|
9
|
+
* with code `UNAUTHENTICATED`.
|
|
10
|
+
*
|
|
11
|
+
* Typical uses: scoreboards, shared inventories, tournament state, level
|
|
12
|
+
* of the day. Values are opaque JSON — primitive, object, or collection.
|
|
13
|
+
*
|
|
14
|
+
* Trust model (see SDK-GUIDE.md for details):
|
|
15
|
+
* - The backend does not enforce invariants INSIDE a value. If you store
|
|
16
|
+
* a list and append your entry, you own the read-modify-write loop —
|
|
17
|
+
* including not mutating other players' entries, truncating to cap
|
|
18
|
+
* the list size, and retrying on 409 conflicts.
|
|
19
|
+
* - Use `ifVersion` to avoid lost updates when concurrent writes race.
|
|
20
|
+
*
|
|
21
|
+
* Quotas (enforced at the backend):
|
|
22
|
+
* - 100 KB per value
|
|
23
|
+
* - 1 MB total per game
|
|
24
|
+
* - 64 keys per game
|
|
25
|
+
*/
|
|
26
|
+
export declare class GameDataModule {
|
|
27
|
+
private transport;
|
|
28
|
+
constructor(transport: Transport);
|
|
29
|
+
/** Read the value under `key`. Returns `null` if unset. Public — works for anonymous viewers. */
|
|
30
|
+
get<T = unknown>(key: string): Promise<T | null>;
|
|
31
|
+
/**
|
|
32
|
+
* Write `value` under `key`. Requires an authenticated user.
|
|
33
|
+
* @param options.ifVersion — only succeed if the stored version matches; otherwise throws
|
|
34
|
+
* an RpcError with code `VERSION_MISMATCH`. Use this to implement safe read-modify-write
|
|
35
|
+
* loops for list values (scoreboards, shared lists).
|
|
36
|
+
* @returns the new version number.
|
|
37
|
+
*/
|
|
38
|
+
set(key: string, value: unknown, options?: {
|
|
39
|
+
ifVersion?: number;
|
|
40
|
+
}): Promise<number>;
|
|
41
|
+
/** Delete the value at `key`. Requires an authenticated user. */
|
|
42
|
+
delete(key: string): Promise<boolean>;
|
|
43
|
+
/** List all keys that have a value set for this game. Public. */
|
|
44
|
+
list(): Promise<string[]>;
|
|
45
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Game-scope key-value store shared by all players of a game.
|
|
3
|
+
*
|
|
4
|
+
* Unlike `umicat.saves` (per-user), `gameData` lives at the game level — one
|
|
5
|
+
* value per key, visible to everyone. Reads are public (anonymous players
|
|
6
|
+
* see the same data). Writes require an authenticated user; calling
|
|
7
|
+
* `set()` or `delete()` when `umicat.user === null` throws an `RpcError`
|
|
8
|
+
* with code `UNAUTHENTICATED`.
|
|
9
|
+
*
|
|
10
|
+
* Typical uses: scoreboards, shared inventories, tournament state, level
|
|
11
|
+
* of the day. Values are opaque JSON — primitive, object, or collection.
|
|
12
|
+
*
|
|
13
|
+
* Trust model (see SDK-GUIDE.md for details):
|
|
14
|
+
* - The backend does not enforce invariants INSIDE a value. If you store
|
|
15
|
+
* a list and append your entry, you own the read-modify-write loop —
|
|
16
|
+
* including not mutating other players' entries, truncating to cap
|
|
17
|
+
* the list size, and retrying on 409 conflicts.
|
|
18
|
+
* - Use `ifVersion` to avoid lost updates when concurrent writes race.
|
|
19
|
+
*
|
|
20
|
+
* Quotas (enforced at the backend):
|
|
21
|
+
* - 100 KB per value
|
|
22
|
+
* - 1 MB total per game
|
|
23
|
+
* - 64 keys per game
|
|
24
|
+
*/
|
|
25
|
+
export class GameDataModule {
|
|
26
|
+
constructor(transport) {
|
|
27
|
+
this.transport = transport;
|
|
28
|
+
}
|
|
29
|
+
/** Read the value under `key`. Returns `null` if unset. Public — works for anonymous viewers. */
|
|
30
|
+
async get(key) {
|
|
31
|
+
const res = await this.transport.call('gameData.get', { key });
|
|
32
|
+
return (res?.value ?? null);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Write `value` under `key`. Requires an authenticated user.
|
|
36
|
+
* @param options.ifVersion — only succeed if the stored version matches; otherwise throws
|
|
37
|
+
* an RpcError with code `VERSION_MISMATCH`. Use this to implement safe read-modify-write
|
|
38
|
+
* loops for list values (scoreboards, shared lists).
|
|
39
|
+
* @returns the new version number.
|
|
40
|
+
*/
|
|
41
|
+
async set(key, value, options) {
|
|
42
|
+
const res = await this.transport.call('gameData.set', {
|
|
43
|
+
key,
|
|
44
|
+
value,
|
|
45
|
+
ifVersion: options?.ifVersion,
|
|
46
|
+
});
|
|
47
|
+
return res.version;
|
|
48
|
+
}
|
|
49
|
+
/** Delete the value at `key`. Requires an authenticated user. */
|
|
50
|
+
async delete(key) {
|
|
51
|
+
const res = await this.transport.call('gameData.delete', { key });
|
|
52
|
+
return res.deleted;
|
|
53
|
+
}
|
|
54
|
+
/** List all keys that have a value set for this game. Public. */
|
|
55
|
+
async list() {
|
|
56
|
+
const res = await this.transport.call('gameData.list');
|
|
57
|
+
return res.keys;
|
|
58
|
+
}
|
|
59
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export { createUmicatGame } from './core/UmicatGame.js';
|
|
2
|
+
export type { UmicatGameOptions } from './core/UmicatGame.js';
|
|
3
|
+
export { UmicatScene } from './core/UmicatScene.js';
|
|
4
|
+
export { ORIENTATION_DIMENSIONS } from './orientation.js';
|
|
5
|
+
export type { Orientation } from './orientation.js';
|
|
6
|
+
export { setupScreenshotListener, takeScreenshot } from './screenshot/ScreenshotManager.js';
|
|
7
|
+
export { setupRecordingListener } from './recording/RecordingManager.js';
|
|
8
|
+
export { Umicat } from './core/Umicat.js';
|
|
9
|
+
export type { UmicatInitOptions } from './core/Umicat.js';
|
|
10
|
+
export { SavesModule } from './saves/SavesModule.js';
|
|
11
|
+
export { GameDataModule } from './gamedata/GameDataModule.js';
|
|
12
|
+
export { RealtimeModule } from './realtime/RealtimeModule.js';
|
|
13
|
+
export type { JoinOptions, RoomListEntry, RoomListOptions } from './realtime/RealtimeModule.js';
|
|
14
|
+
export { UmicatRoom, PlayerDataFacade, RoomDataFacade, ChatFacade, MAX_CHAT_TEXT_LEN, } from './realtime/UmicatRoom.js';
|
|
15
|
+
export type { ChatMessage, ChatMessageKind } from './realtime/UmicatRoom.js';
|
|
16
|
+
export { RpcError } from './core/Transport.js';
|
|
17
|
+
export type { Transport, TransportKind } from './core/Transport.js';
|
|
18
|
+
export type { UmicatUser } from './protocol.js';
|
|
19
|
+
export { loadWorldScene, preloadManifest, preloadSceneAssets, applyPixelArtFilters, getManifest, SCENES_BASE, MANIFEST_PATH, suspendSceneUpdates, } from './scene/SceneLoader.js';
|
|
20
|
+
export type { LoadWorldSceneOptions } from './scene/SceneLoader.js';
|
|
21
|
+
export { spawnEntity, parseColor, applyAssetHitbox, applyTilesetTileMetadata, getTilemapAt, addTilemapCollider, } from './scene/spawnEntity.js';
|
|
22
|
+
export type { SpawnContext, AssetResolver, RenderScriptResolver, TilemapHit, } from './scene/spawnEntity.js';
|
|
23
|
+
export { applyAutotile, findTerrain, getAutotileKind, invalidateAutotileCells, invalidateAutotileVertices, } from './scene/autotile.js';
|
|
24
|
+
export type { AutotileAffectedCell } from './scene/autotile.js';
|
|
25
|
+
export { EntityRegistry, attachEntityRegistry, getEntityRegistry, } from './scene/EntityRegistry.js';
|
|
26
|
+
export { spawnPrefab, getPrefab, listPrefabs } from './scene/Prefabs.js';
|
|
27
|
+
export type { SpawnPrefabOverrides, SpawnPrefabOptions } from './scene/Prefabs.js';
|
|
28
|
+
export type { PrefabRecord, PrefabKind, PrefabPhysics, PrefabBase, SpritePrefab, RectPrefab, CirclePrefab, CodeRenderedPrefab, } from './scene/types.js';
|
|
29
|
+
export { preloadRules, getRule, getRules, onRuleChange, patchRule } from './scene/Rules.js';
|
|
30
|
+
export { loadWaveSchedule, runWaveSchedule } from './scene/Waves.js';
|
|
31
|
+
export type { WaveCallbacks, WaveController, SpawnInstance } from './scene/Waves.js';
|
|
32
|
+
export { loadGameConfig } from './scene/GameConfig.js';
|
|
33
|
+
export type { GameConfig } from './scene/GameConfig.js';
|
|
34
|
+
export type { WaveScheduleRecord, WaveRecord, SpawnInstruction, SpawnPointInstruction, SpawnFormationInstruction, FormationRecord, FormationShape, WaveEndCondition, } from './scene/types.js';
|
|
35
|
+
export { setupEditorModeListener, isEditMode } from './scene/EditorMode.js';
|
|
36
|
+
export { setRenderScriptRegistry, getRenderScriptRegistry, resolveRenderScript, } from './scene/renderScripts.js';
|
|
37
|
+
export type { RenderScriptModule } from './scene/renderScripts.js';
|
|
38
|
+
export { setupEditorBridge } from './editor/EditorBridge.js';
|
|
39
|
+
export { EditorOverlayScene, EDITOR_OVERLAY_KEY } from './editor/EditorOverlayScene.js';
|
|
40
|
+
export type { EditorEntityPatch, EditorEnterMessage, EditorExitMessage, EditorGetSceneMessage, EditorApplyEditMessage, EditorSetSelectionMessage, EditorPanZoomMessage, EditorHostToSdkMessage, EditorSceneLoadedMessage, EditorSelectionPickedMessage, EditorDragEndMessage, EditorShortcutMessage, EditorCreateEntityMessage, EditorDeleteEntityMessage, EditorSetEditModeMessage, EditorSelectionRectMessage, EditorSdkToHostMessage, } from './protocol.js';
|
|
41
|
+
export { SCHEMA_VERSION, isPerFrameNinePatch, isPerFrameHitbox, } from './scene/types.js';
|
|
42
|
+
export type { Manifest, 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';
|
|
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';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Core
|
|
2
|
+
export { createUmicatGame } from './core/UmicatGame.js';
|
|
3
|
+
export { UmicatScene } from './core/UmicatScene.js';
|
|
4
|
+
export { ORIENTATION_DIMENSIONS } from './orientation.js';
|
|
5
|
+
// Screenshot
|
|
6
|
+
export { setupScreenshotListener, takeScreenshot } from './screenshot/ScreenshotManager.js';
|
|
7
|
+
// Recording
|
|
8
|
+
export { setupRecordingListener } from './recording/RecordingManager.js';
|
|
9
|
+
// Platform services (v0.2.1+)
|
|
10
|
+
export { Umicat } from './core/Umicat.js';
|
|
11
|
+
export { SavesModule } from './saves/SavesModule.js';
|
|
12
|
+
export { GameDataModule } from './gamedata/GameDataModule.js';
|
|
13
|
+
export { RealtimeModule } from './realtime/RealtimeModule.js';
|
|
14
|
+
export { UmicatRoom, PlayerDataFacade, RoomDataFacade, ChatFacade, MAX_CHAT_TEXT_LEN, } from './realtime/UmicatRoom.js';
|
|
15
|
+
export { RpcError } from './core/Transport.js';
|
|
16
|
+
// Scene-as-data (visual editor foundation, slice 1)
|
|
17
|
+
export { loadWorldScene, preloadManifest, preloadSceneAssets, applyPixelArtFilters, getManifest, SCENES_BASE, MANIFEST_PATH, suspendSceneUpdates, } from './scene/SceneLoader.js';
|
|
18
|
+
export { spawnEntity, parseColor, applyAssetHitbox, applyTilesetTileMetadata, getTilemapAt, addTilemapCollider, } from './scene/spawnEntity.js';
|
|
19
|
+
// Slice 6 Phase D — Wang autotile runtime.
|
|
20
|
+
export { applyAutotile, findTerrain, getAutotileKind, invalidateAutotileCells, invalidateAutotileVertices, // deprecated alias kept for 0.2.122–0.2.124 callers
|
|
21
|
+
} from './scene/autotile.js';
|
|
22
|
+
export { EntityRegistry, attachEntityRegistry, getEntityRegistry, } from './scene/EntityRegistry.js';
|
|
23
|
+
// Prefabs — slice 11 Phase B.1 (2026-05-12). Runtime spawning of entity TYPES
|
|
24
|
+
// from manifest.prefabs[]. Replaces inline `this.physics.add.sprite(...)`.
|
|
25
|
+
export { spawnPrefab, getPrefab, listPrefabs } from './scene/Prefabs.js';
|
|
26
|
+
// Rules — slice 11 Phase B.2 (2026-05-12). Tunable game parameters in
|
|
27
|
+
// `public/rules.json`, read by behavior code via dot-notation paths.
|
|
28
|
+
// Replaces `const X = ...` TS declarations for any user-tunable value.
|
|
29
|
+
export { preloadRules, getRule, getRules, onRuleChange, patchRule } from './scene/Rules.js';
|
|
30
|
+
// Waves — slice 11 Phase B.4 (2026-05-12). Spawn schedules as data in
|
|
31
|
+
// `public/waves/<id>.json`. Replaces hand-rolled wave loops in
|
|
32
|
+
// `GameScene.ts` for shooters / runners / survivors.
|
|
33
|
+
export { loadWaveSchedule, runWaveSchedule } from './scene/Waves.js';
|
|
34
|
+
// Game config — slice 11 Phase B.3 (2026-05-12). Top-level game settings
|
|
35
|
+
// in `public/game.json` (orientation, dimensions, physics, controls).
|
|
36
|
+
// Replaces hardcoded src/config.ts constants.
|
|
37
|
+
export { loadGameConfig } from './scene/GameConfig.js';
|
|
38
|
+
export { setupEditorModeListener, isEditMode } from './scene/EditorMode.js';
|
|
39
|
+
export { setRenderScriptRegistry, getRenderScriptRegistry, resolveRenderScript, } from './scene/renderScripts.js';
|
|
40
|
+
export { setupEditorBridge } from './editor/EditorBridge.js';
|
|
41
|
+
export { EditorOverlayScene, EDITOR_OVERLAY_KEY } from './editor/EditorOverlayScene.js';
|
|
42
|
+
export { SCHEMA_VERSION, isPerFrameNinePatch, isPerFrameHitbox, } from './scene/types.js';
|
|
43
|
+
export { PROTOCOL_VERSION, } from './protocol.js';
|