@waica/cli 0.6.1 → 0.7.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/README.md +1 -1
- package/dist/editor/assets/{cssMode-D3SZSKEA.js → cssMode-eS4pAOyh.js} +1 -1
- package/dist/editor/assets/{freemarker2-BH1pa3TU.js → freemarker2-FMQjZKQk.js} +1 -1
- package/dist/editor/assets/{handlebars-Cf7meNHo.js → handlebars-dDYOnqat.js} +1 -1
- package/dist/editor/assets/{html-6hqYzl5P.js → html-Ba6xZh4T.js} +1 -1
- package/dist/editor/assets/{htmlMode-D_IiyUdi.js → htmlMode-DC9qJr2p.js} +1 -1
- package/dist/editor/assets/{index-CJyB1cBF.js → index-CoyeInOT.js} +2029 -268
- package/dist/editor/assets/{javascript-CTeTqLj8.js → javascript-DOwFvmvM.js} +1 -1
- package/dist/editor/assets/{jsonMode-t8AC-38H.js → jsonMode-CcG7zlSf.js} +1 -1
- package/dist/editor/assets/{liquid-Dc6NoKas.js → liquid-Dfp71r7J.js} +1 -1
- package/dist/editor/assets/{mdx-DCPtX-bs.js → mdx-Ci7vVeht.js} +1 -1
- package/dist/editor/assets/{python-DXAju_oJ.js → python-Bluv3Y9Q.js} +1 -1
- package/dist/editor/assets/{razor-DqQldpv6.js → razor-BtwTXMaK.js} +1 -1
- package/dist/editor/assets/{tsMode-B60EhHlZ.js → tsMode-D3_D-H85.js} +1 -1
- package/dist/editor/assets/{typescript-C4LyYAns.js → typescript-Ch_PS5f7.js} +1 -1
- package/dist/editor/assets/{xml-B0QcT5_Z.js → xml-CI_bH_MX.js} +1 -1
- package/dist/editor/assets/{yaml-CEpyO2Qc.js → yaml-LD9GTZyy.js} +1 -1
- package/dist/editor/index.html +1 -1
- package/dist/mcp/archetypes.js +6 -2
- package/dist/mcp/create-project.js +12 -6
- package/dist/mcp/known-archetypes.js +19 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/package.json +3 -3
- package/dist/mcp/node_modules/@waica/archetype-topdown/assets/ATTRIBUTION.md +28 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/assets/waica-blob.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/assets/waica-fence.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/assets/waica-grass.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/assets/waica-hero.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/assets/waica-npc.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/assets/waica-path.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/assets/waica-potion.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/assets/waica-tree.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/assets/waica-water.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/animation.d.ts +7 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/animation.js +10 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/art.d.ts +10 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/art.js +19 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/bundle.d.ts +3 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/bundle.js +10 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/controls.d.ts +5 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/controls.js +16 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/index.d.ts +25 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/index.js +16 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/manifest.d.ts +16 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/manifest.js +29 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/prefabs.d.ts +11 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/prefabs.js +178 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/registry-data.d.ts +9 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/registry-data.js +54 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/registry.d.ts +6 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/registry.js +21 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/scene-default.d.ts +57 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/scene-default.js +110 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/ui.d.ts +10 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/ui.js +43 -0
- package/dist/mcp/node_modules/@waica/archetype-topdown/package.json +30 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/index.d.ts +3 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/index.js +3 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/interactable.d.ts +31 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/interactable.js +49 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/platformer-motor.d.ts +4 -2
- package/dist/mcp/node_modules/@waica/behaviors/dist/platformer-motor.js +5 -1
- package/dist/mcp/node_modules/@waica/behaviors/dist/topdown-motor.d.ts +72 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/topdown-motor.js +102 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/topdown-player-states.d.ts +18 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/topdown-player-states.js +86 -0
- package/dist/mcp/node_modules/@waica/behaviors/package.json +2 -2
- package/dist/mcp/node_modules/@waica/engine/dist/animation/directional.d.ts +48 -0
- package/dist/mcp/node_modules/@waica/engine/dist/animation/directional.js +68 -0
- package/dist/mcp/node_modules/@waica/engine/dist/archetype.d.ts +3 -0
- package/dist/mcp/node_modules/@waica/engine/dist/camera.d.ts +19 -0
- package/dist/mcp/node_modules/@waica/engine/dist/camera.js +10 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/animated-sprite.d.ts +8 -1
- package/dist/mcp/node_modules/@waica/engine/dist/components/animated-sprite.js +19 -2
- package/dist/mcp/node_modules/@waica/engine/dist/components/sprite.d.ts +4 -1
- package/dist/mcp/node_modules/@waica/engine/dist/components/sprite.js +5 -0
- package/dist/mcp/node_modules/@waica/engine/dist/game.d.ts +9 -1
- package/dist/mcp/node_modules/@waica/engine/dist/game.js +32 -3
- package/dist/mcp/node_modules/@waica/engine/dist/index.d.ts +7 -3
- package/dist/mcp/node_modules/@waica/engine/dist/index.js +3 -1
- package/dist/mcp/node_modules/@waica/engine/dist/render-sort.d.ts +31 -0
- package/dist/mcp/node_modules/@waica/engine/dist/render-sort.js +39 -0
- package/dist/mcp/node_modules/@waica/engine/dist/scene.d.ts +10 -0
- package/dist/mcp/node_modules/@waica/engine/dist/scene.js +1 -0
- package/dist/mcp/node_modules/@waica/engine/dist/state/hooks.d.ts +1 -1
- package/dist/mcp/node_modules/@waica/engine/dist/state/hooks.js +3 -1
- package/dist/mcp/node_modules/@waica/engine/dist/state/state-machine.d.ts +18 -0
- package/dist/mcp/node_modules/@waica/engine/dist/state/state-machine.js +57 -1
- package/dist/mcp/node_modules/@waica/engine/package.json +1 -1
- package/dist/mcp/package-resolver.js +10 -4
- package/dist/mcp/project-component-loader.js +2 -1
- package/dist/mcp/project-component-runner.js +4 -0
- package/dist/mcp/server.js +18 -1
- package/dist/mcp/template/package.json.tpl +1 -1
- package/dist/mcp/template/src/main.ts +8 -4
- package/dist/mcp/workspace-runtime.js +41 -10
- package/package.json +1 -1
|
@@ -21,6 +21,8 @@ export interface SceneCameraJson {
|
|
|
21
21
|
deadzoneWidth?: number;
|
|
22
22
|
deadzoneHeight?: number;
|
|
23
23
|
lookahead?: number;
|
|
24
|
+
/** Vertical lookahead in world units; 0 (the default) disables it. */
|
|
25
|
+
lookaheadY?: number;
|
|
24
26
|
smoothing?: number;
|
|
25
27
|
limits?: CameraLimitsJson;
|
|
26
28
|
}
|
|
@@ -31,6 +33,7 @@ export interface ResolvedSceneCamera {
|
|
|
31
33
|
deadzoneWidth: number;
|
|
32
34
|
deadzoneHeight: number;
|
|
33
35
|
lookahead: number;
|
|
36
|
+
lookaheadY: number;
|
|
34
37
|
smoothing: number;
|
|
35
38
|
limits: CameraLimitsJson | null;
|
|
36
39
|
}
|
|
@@ -41,10 +44,24 @@ export declare const CAMERA_DEFAULTS: {
|
|
|
41
44
|
readonly deadzoneWidth: 2;
|
|
42
45
|
readonly deadzoneHeight: 2.5;
|
|
43
46
|
readonly lookahead: 1.5;
|
|
47
|
+
readonly lookaheadY: 0;
|
|
44
48
|
readonly smoothing: 6;
|
|
45
49
|
};
|
|
46
50
|
/** Fills a scene's camera block with the engine defaults. */
|
|
47
51
|
export declare function resolveSceneCamera(json?: SceneCameraJson): ResolvedSceneCamera;
|
|
52
|
+
/** Two-axis velocity a followed entity reports for camera lookahead. */
|
|
53
|
+
export interface CameraVelocity {
|
|
54
|
+
vx: number;
|
|
55
|
+
vy: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The explicit seam a component opts into so the scene camera can read the
|
|
59
|
+
* followed entity's velocity — the engine never guesses from field names.
|
|
60
|
+
*/
|
|
61
|
+
export interface CameraVelocityProvider {
|
|
62
|
+
getCameraVelocity(): CameraVelocity;
|
|
63
|
+
}
|
|
64
|
+
export declare function isCameraVelocityProvider(value: unknown): value is CameraVelocityProvider;
|
|
48
65
|
export interface CameraStepInput {
|
|
49
66
|
/** Current camera center. */
|
|
50
67
|
x: number;
|
|
@@ -59,6 +76,8 @@ export interface CameraStepInput {
|
|
|
59
76
|
} | null;
|
|
60
77
|
/** Followed entity's horizontal velocity, for lookahead. */
|
|
61
78
|
vx: number;
|
|
79
|
+
/** Followed entity's vertical velocity, for lookaheadY; omitted = 0. */
|
|
80
|
+
vy?: number;
|
|
62
81
|
dt: number;
|
|
63
82
|
}
|
|
64
83
|
/**
|
|
@@ -6,6 +6,7 @@ export const CAMERA_DEFAULTS = {
|
|
|
6
6
|
deadzoneWidth: 2,
|
|
7
7
|
deadzoneHeight: 2.5,
|
|
8
8
|
lookahead: 1.5,
|
|
9
|
+
lookaheadY: 0,
|
|
9
10
|
smoothing: 6,
|
|
10
11
|
};
|
|
11
12
|
/** Fills a scene's camera block with the engine defaults. */
|
|
@@ -17,6 +18,7 @@ export function resolveSceneCamera(json) {
|
|
|
17
18
|
deadzoneWidth: json?.deadzoneWidth ?? CAMERA_DEFAULTS.deadzoneWidth,
|
|
18
19
|
deadzoneHeight: json?.deadzoneHeight ?? CAMERA_DEFAULTS.deadzoneHeight,
|
|
19
20
|
lookahead: json?.lookahead ?? CAMERA_DEFAULTS.lookahead,
|
|
21
|
+
lookaheadY: json?.lookaheadY ?? CAMERA_DEFAULTS.lookaheadY,
|
|
20
22
|
smoothing: json?.smoothing ?? CAMERA_DEFAULTS.smoothing,
|
|
21
23
|
limits: json?.limits ?? null,
|
|
22
24
|
};
|
|
@@ -28,6 +30,11 @@ function clampAxis(center, halfView, min, max) {
|
|
|
28
30
|
return (min + max) / 2;
|
|
29
31
|
return Math.min(Math.max(center, min + halfView), max - halfView);
|
|
30
32
|
}
|
|
33
|
+
export function isCameraVelocityProvider(value) {
|
|
34
|
+
return (typeof value === 'object' &&
|
|
35
|
+
value !== null &&
|
|
36
|
+
typeof value.getCameraVelocity === 'function');
|
|
37
|
+
}
|
|
31
38
|
/**
|
|
32
39
|
* One simulation step of the camera: deadzone-follow with lookahead and
|
|
33
40
|
* exponential smoothing, then limits. Pure — returns the next center.
|
|
@@ -48,6 +55,9 @@ export function stepSceneCamera(cam, input) {
|
|
|
48
55
|
wantY = input.target.y - Math.sign(dy) * halfDzH;
|
|
49
56
|
if (Math.abs(input.vx) > 1)
|
|
50
57
|
wantX += Math.sign(input.vx) * cam.lookahead;
|
|
58
|
+
const vy = input.vy ?? 0;
|
|
59
|
+
if (Math.abs(vy) > 1)
|
|
60
|
+
wantY += Math.sign(vy) * cam.lookaheadY;
|
|
51
61
|
x = THREE.MathUtils.damp(x, wantX, cam.smoothing, input.dt);
|
|
52
62
|
y = THREE.MathUtils.damp(y, wantY, cam.smoothing, input.dt);
|
|
53
63
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Component } from '../component.js';
|
|
2
2
|
import { type ClipDef } from '../animation/clip-player.js';
|
|
3
3
|
import { type SheetCell, type SheetDef } from '../animation/sheet.js';
|
|
4
|
+
import type { YSortParticipant } from '../render-sort.js';
|
|
4
5
|
/**
|
|
5
6
|
* Sprite animated from one or more spritesheets. The main sheet is the
|
|
6
7
|
* top-level texture/cols/rows (or explicit cells); extraSheets append after
|
|
@@ -10,7 +11,7 @@ import { type SheetCell, type SheetDef } from '../animation/sheet.js';
|
|
|
10
11
|
* so the quad rescales per frame, anchored bottom-center — width/height size
|
|
11
12
|
* the sheet's largest frame and smaller ones keep their feet planted.
|
|
12
13
|
*/
|
|
13
|
-
export declare class AnimatedSprite extends Component {
|
|
14
|
+
export declare class AnimatedSprite extends Component implements YSortParticipant {
|
|
14
15
|
static componentName: string;
|
|
15
16
|
static updateAfter: readonly string[];
|
|
16
17
|
static params: {
|
|
@@ -59,6 +60,12 @@ export declare class AnimatedSprite extends Component {
|
|
|
59
60
|
private _layer;
|
|
60
61
|
get layer(): number;
|
|
61
62
|
set layer(value: number);
|
|
63
|
+
/** Y-sort pass hook: overrides the layer-derived z for this frame. */
|
|
64
|
+
setSortZ(z: number): void;
|
|
65
|
+
/** Mirrored state, readable by Runtime Snapshots; write via setFlipX. */
|
|
66
|
+
flipX: boolean;
|
|
67
|
+
/** Mirrors the quad horizontally — directional clips reuse east art for west. */
|
|
68
|
+
setFlipX(value: boolean): void;
|
|
62
69
|
clips: Record<string, ClipDef>;
|
|
63
70
|
initialClip?: string;
|
|
64
71
|
/** Clip currently playing. */
|
|
@@ -22,6 +22,7 @@ export class AnimatedSprite extends Component {
|
|
|
22
22
|
};
|
|
23
23
|
static transient = [
|
|
24
24
|
'current',
|
|
25
|
+
'flipX',
|
|
25
26
|
'player',
|
|
26
27
|
'sheets',
|
|
27
28
|
'texs',
|
|
@@ -93,6 +94,20 @@ export class AnimatedSprite extends Component {
|
|
|
93
94
|
if (this.mesh)
|
|
94
95
|
this.mesh.position.z = value * 0.01;
|
|
95
96
|
}
|
|
97
|
+
/** Y-sort pass hook: overrides the layer-derived z for this frame. */
|
|
98
|
+
setSortZ(z) {
|
|
99
|
+
if (this.mesh)
|
|
100
|
+
this.mesh.position.z = z;
|
|
101
|
+
}
|
|
102
|
+
/** Mirrored state, readable by Runtime Snapshots; write via setFlipX. */
|
|
103
|
+
flipX = false;
|
|
104
|
+
/** Mirrors the quad horizontally — directional clips reuse east art for west. */
|
|
105
|
+
setFlipX(value) {
|
|
106
|
+
if (this.flipX === value)
|
|
107
|
+
return;
|
|
108
|
+
this.flipX = value;
|
|
109
|
+
this.syncQuad();
|
|
110
|
+
}
|
|
96
111
|
clips = {};
|
|
97
112
|
initialClip;
|
|
98
113
|
/** Clip currently playing. */
|
|
@@ -171,9 +186,11 @@ export class AnimatedSprite extends Component {
|
|
|
171
186
|
syncQuad() {
|
|
172
187
|
if (!this.mesh)
|
|
173
188
|
return;
|
|
174
|
-
this.mesh.scale.set(this._width * this.frameScaleX, this._height * this.frameScaleY, 1);
|
|
189
|
+
this.mesh.scale.set(this._width * this.frameScaleX * (this.flipX ? -1 : 1), this._height * this.frameScaleY, 1);
|
|
175
190
|
// Bottom-center anchor: a shrunk frame keeps its feet on the quad's floor.
|
|
176
|
-
|
|
191
|
+
// The offset mirrors along with the art, or a flipped sprite with a
|
|
192
|
+
// nonzero offsetX would shift to the wrong side.
|
|
193
|
+
this.mesh.position.x = this.flipX ? -this._offsetX : this._offsetX;
|
|
177
194
|
this.mesh.position.y = this._offsetY - (this._height * (1 - this.frameScaleY)) / 2;
|
|
178
195
|
}
|
|
179
196
|
applyFrame() {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Component } from '../component.js';
|
|
2
|
+
import type { YSortParticipant } from '../render-sort.js';
|
|
2
3
|
export type SpriteShape = 'rectangle' | 'circle';
|
|
3
4
|
/**
|
|
4
5
|
* Textured or flat-color quad. In the unified pipeline, a 2D sprite is a
|
|
5
6
|
* plane in front of the orthographic camera (see DESIGN.md §6, decision 2).
|
|
6
7
|
*/
|
|
7
|
-
export declare class Sprite extends Component {
|
|
8
|
+
export declare class Sprite extends Component implements YSortParticipant {
|
|
8
9
|
static componentName: string;
|
|
9
10
|
static params: {
|
|
10
11
|
offsetX: {
|
|
@@ -42,6 +43,8 @@ export declare class Sprite extends Component {
|
|
|
42
43
|
private _layer;
|
|
43
44
|
get layer(): number;
|
|
44
45
|
set layer(value: number);
|
|
46
|
+
/** Y-sort pass hook: overrides the layer-derived z for this frame. */
|
|
47
|
+
setSortZ(z: number): void;
|
|
45
48
|
private _shape;
|
|
46
49
|
get shape(): SpriteShape;
|
|
47
50
|
set shape(value: SpriteShape);
|
|
@@ -71,6 +71,11 @@ export class Sprite extends Component {
|
|
|
71
71
|
if (this.mesh)
|
|
72
72
|
this.mesh.position.z = value * 0.01;
|
|
73
73
|
}
|
|
74
|
+
/** Y-sort pass hook: overrides the layer-derived z for this frame. */
|
|
75
|
+
setSortZ(z) {
|
|
76
|
+
if (this.mesh)
|
|
77
|
+
this.mesh.position.z = z;
|
|
78
|
+
}
|
|
74
79
|
_shape = 'rectangle';
|
|
75
80
|
get shape() {
|
|
76
81
|
return this._shape;
|
|
@@ -4,7 +4,7 @@ import type { Component } from './component.js';
|
|
|
4
4
|
import { Entity } from './entity.js';
|
|
5
5
|
import { Emitter } from './events.js';
|
|
6
6
|
import { Input, type InputBindings } from './input.js';
|
|
7
|
-
import { type SceneRegistry } from './scene.js';
|
|
7
|
+
import { type SceneRegistry, type SceneRenderJson } from './scene.js';
|
|
8
8
|
import { Stats, type StatValue } from './stats.js';
|
|
9
9
|
import { GameUi } from './ui.js';
|
|
10
10
|
/** Fixed game resolution: the view keeps this aspect, letterboxed. */
|
|
@@ -61,6 +61,7 @@ export declare class Game {
|
|
|
61
61
|
private readonly resolution;
|
|
62
62
|
private viewHeight;
|
|
63
63
|
private sceneCamera;
|
|
64
|
+
private renderSort;
|
|
64
65
|
private lastTime;
|
|
65
66
|
private runtimeBridge;
|
|
66
67
|
constructor(options: GameOptions);
|
|
@@ -76,6 +77,11 @@ export declare class Game {
|
|
|
76
77
|
applyParamOverrides(entity: Entity, component: Component): void;
|
|
77
78
|
/** Registers a function that runs once per frame. Returns the unsubscribe. */
|
|
78
79
|
onUpdate(fn: UpdateFn): () => void;
|
|
80
|
+
/**
|
|
81
|
+
* Adopts a scene's render block. Called by loadScene; without a block the
|
|
82
|
+
* draw order stays layer-banded with spawn-order ties.
|
|
83
|
+
*/
|
|
84
|
+
setSceneRender(json?: SceneRenderJson): void;
|
|
79
85
|
/**
|
|
80
86
|
* Adopts a scene's camera block: jumps to its framing and, while
|
|
81
87
|
* simulating, follows/clamps per its settings. Called by loadScene.
|
|
@@ -94,6 +100,8 @@ export declare class Game {
|
|
|
94
100
|
private tick;
|
|
95
101
|
private runFrame;
|
|
96
102
|
private unregisterRuntimeBridge;
|
|
103
|
+
/** Under y-sort, re-derives every participant's z from layer band + entity Y. */
|
|
104
|
+
private applyYSort;
|
|
97
105
|
private renderSurface;
|
|
98
106
|
private componentUpdateSchedule;
|
|
99
107
|
private updateSceneCamera;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import { collisionOverlap } from './collision-shape.js';
|
|
3
|
-
import { resolveSceneCamera, stepSceneCamera } from './camera.js';
|
|
3
|
+
import { isCameraVelocityProvider, resolveSceneCamera, stepSceneCamera, } from './camera.js';
|
|
4
4
|
import { resolveComponentUpdateSchedule } from './component-update-schedule.js';
|
|
5
5
|
import { Hitbox } from './components/hitbox.js';
|
|
6
6
|
import { Entity } from './entity.js';
|
|
@@ -8,6 +8,7 @@ import { Emitter } from './events.js';
|
|
|
8
8
|
import { Input } from './input.js';
|
|
9
9
|
import { activeRuntimeBridgeHook, EngineRuntimeBridge, } from './runtime-bridge.js';
|
|
10
10
|
import { RuntimeInspector } from './runtime-inspection.js';
|
|
11
|
+
import { isYSortParticipant, ySortZ } from './render-sort.js';
|
|
11
12
|
import { registryEntry, spawnFromJson } from './scene.js';
|
|
12
13
|
import { Stats } from './stats.js';
|
|
13
14
|
import { GameUi } from './ui.js';
|
|
@@ -40,6 +41,7 @@ export class Game {
|
|
|
40
41
|
resolution;
|
|
41
42
|
viewHeight;
|
|
42
43
|
sceneCamera = null;
|
|
44
|
+
renderSort = null;
|
|
43
45
|
lastTime = 0;
|
|
44
46
|
runtimeBridge = null;
|
|
45
47
|
constructor(options) {
|
|
@@ -107,6 +109,13 @@ export class Game {
|
|
|
107
109
|
this.updateFns.add(fn);
|
|
108
110
|
return () => this.updateFns.delete(fn);
|
|
109
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Adopts a scene's render block. Called by loadScene; without a block the
|
|
114
|
+
* draw order stays layer-banded with spawn-order ties.
|
|
115
|
+
*/
|
|
116
|
+
setSceneRender(json) {
|
|
117
|
+
this.renderSort = json?.sort === 'y' ? 'y' : null;
|
|
118
|
+
}
|
|
110
119
|
/**
|
|
111
120
|
* Adopts a scene's camera block: jumps to its framing and, while
|
|
112
121
|
* simulating, follows/clamps per its settings. Called by loadScene.
|
|
@@ -216,7 +225,25 @@ export class Game {
|
|
|
216
225
|
this.runtimeBridge?.unregister();
|
|
217
226
|
this.runtimeBridge = null;
|
|
218
227
|
};
|
|
228
|
+
/** Under y-sort, re-derives every participant's z from layer band + entity Y. */
|
|
229
|
+
applyYSort() {
|
|
230
|
+
const participants = [];
|
|
231
|
+
const entries = [];
|
|
232
|
+
for (const entity of this.entities) {
|
|
233
|
+
for (const component of entity.components) {
|
|
234
|
+
if (isYSortParticipant(component)) {
|
|
235
|
+
participants.push(component);
|
|
236
|
+
entries.push({ layer: component.layer, y: entity.position.y });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const z = ySortZ(entries);
|
|
241
|
+
for (const [index, participant] of participants.entries())
|
|
242
|
+
participant.setSortZ(z[index]);
|
|
243
|
+
}
|
|
219
244
|
renderSurface() {
|
|
245
|
+
if (this.renderSort === 'y')
|
|
246
|
+
this.applyYSort();
|
|
220
247
|
this.ui.setActive(this.simulate);
|
|
221
248
|
if (this.resolution) {
|
|
222
249
|
// Letterbox bars: clear the whole canvas, then render inside the scissor.
|
|
@@ -262,14 +289,16 @@ export class Game {
|
|
|
262
289
|
if (!cam)
|
|
263
290
|
return;
|
|
264
291
|
const followed = cam.follow ? this.find(cam.follow) : undefined;
|
|
265
|
-
const
|
|
292
|
+
const provider = followed?.components.find((c) => isCameraVelocityProvider(c));
|
|
293
|
+
const velocity = provider?.getCameraVelocity();
|
|
266
294
|
const next = stepSceneCamera(cam, {
|
|
267
295
|
x: this.camera.position.x,
|
|
268
296
|
y: this.camera.position.y,
|
|
269
297
|
halfW: (this.camera.right - this.camera.left) / 2,
|
|
270
298
|
halfH: this.viewHeight / 2,
|
|
271
299
|
target: followed ? { x: followed.position.x, y: followed.position.y } : null,
|
|
272
|
-
vx:
|
|
300
|
+
vx: velocity?.vx ?? 0,
|
|
301
|
+
vy: velocity?.vy ?? 0,
|
|
273
302
|
dt,
|
|
274
303
|
});
|
|
275
304
|
this.camera.position.x = next.x;
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
export { Game } from './game.js';
|
|
2
2
|
export type { GameOptions, GameResolution, SpawnPrefabOptions, UpdateFn, ParamOverrides, } from './game.js';
|
|
3
|
-
export {
|
|
4
|
-
export type {
|
|
3
|
+
export { installDirectionalAnimation, installedDirectionalAnimation, isAnimationFacingProvider, resolveDirectionalClip, } from './animation/directional.js';
|
|
4
|
+
export type { AnimationFacingProvider, DirectionalAnimation, DirectionalFallback, ResolvedDirectionalClip, } from './animation/directional.js';
|
|
5
|
+
export { isYSortParticipant, ySortZ } from './render-sort.js';
|
|
6
|
+
export type { YSortEntry, YSortParticipant } from './render-sort.js';
|
|
7
|
+
export { CAMERA_DEFAULTS, isCameraVelocityProvider, resolveSceneCamera, stepSceneCamera } from './camera.js';
|
|
8
|
+
export type { SceneCameraJson, CameraLimitsJson, CameraVelocity, CameraVelocityProvider, ResolvedSceneCamera, } from './camera.js';
|
|
5
9
|
export { Entity } from './entity.js';
|
|
6
10
|
export { Component } from './component.js';
|
|
7
11
|
export { authoringDefaults } from './authoring-defaults.js';
|
|
@@ -32,7 +36,7 @@ export { COLLISION_SHAPES, DEFAULT_COLLISION_POLYGON, collisionBounds, collision
|
|
|
32
36
|
export type { CollisionBody, CollisionBounds, CollisionPoint, CollisionShape, } from './collision-shape.js';
|
|
33
37
|
export { Emitter } from './events.js';
|
|
34
38
|
export { loadScene, spawnFromJson, resolveEntityComponents, resolveProps } from './scene.js';
|
|
35
|
-
export type { SceneJson, SceneEntityJson, SceneComponentJson, SceneRegistry, PrefabJson } from './scene.js';
|
|
39
|
+
export type { SceneJson, SceneEntityJson, SceneComponentJson, SceneRegistry, SceneRenderJson, PrefabJson, } from './scene.js';
|
|
36
40
|
export { ClipPlayer } from './animation/clip-player.js';
|
|
37
41
|
export type { ClipDef } from './animation/clip-player.js';
|
|
38
42
|
export { sheetCell, sheetFrameCount, locateFrame } from './animation/sheet.js';
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { Game } from './game.js';
|
|
2
|
-
export {
|
|
2
|
+
export { installDirectionalAnimation, installedDirectionalAnimation, isAnimationFacingProvider, resolveDirectionalClip, } from './animation/directional.js';
|
|
3
|
+
export { isYSortParticipant, ySortZ } from './render-sort.js';
|
|
4
|
+
export { CAMERA_DEFAULTS, isCameraVelocityProvider, resolveSceneCamera, stepSceneCamera } from './camera.js';
|
|
3
5
|
export { Entity } from './entity.js';
|
|
4
6
|
export { Component } from './component.js';
|
|
5
7
|
export { authoringDefaults } from './authoring-defaults.js';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Y-sort draw ordering: an opt-in render mode (scene JSON `render.sort: 'y'`)
|
|
3
|
+
* for top-down scenes where "lower on screen" means "closer to the camera".
|
|
4
|
+
*/
|
|
5
|
+
export interface YSortEntry {
|
|
6
|
+
/** The sprite's layer — the primary draw-order band, exactly as without y-sort. */
|
|
7
|
+
layer: number;
|
|
8
|
+
/** The owning entity's world Y — the sort key. Sprite offsets don't shift it. */
|
|
9
|
+
y: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The explicit seam a component opts into to participate in y-sort: it
|
|
13
|
+
* exposes its draw-order layer and accepts the per-frame z the pass derives.
|
|
14
|
+
* Both stock sprite classes implement it; a custom renderable can too.
|
|
15
|
+
*/
|
|
16
|
+
export interface YSortParticipant {
|
|
17
|
+
readonly layer: number;
|
|
18
|
+
/** Y-sort pass hook: overrides the layer-derived z for this frame. */
|
|
19
|
+
setSortZ(z: number): void;
|
|
20
|
+
}
|
|
21
|
+
export declare function isYSortParticipant(value: unknown): value is YSortParticipant;
|
|
22
|
+
/**
|
|
23
|
+
* Z per entry under y-sort. Each layer keeps its 0.01 band; within a band,
|
|
24
|
+
* lower Y gets a higher z (renders in front), and exact Y ties keep input
|
|
25
|
+
* order. Offsets stay strictly inside (layer, layer + 1) × 0.01 for integer
|
|
26
|
+
* layers — but a fractional layer (e.g. 0.5) can sit closer than that to the
|
|
27
|
+
* next one present, so each band is capped at the gap to the next distinct
|
|
28
|
+
* layer above it, never wider than 0.01. Integer layers are always >= 1
|
|
29
|
+
* apart, so their band is exactly the old fixed 0.01.
|
|
30
|
+
*/
|
|
31
|
+
export declare function ySortZ(entries: readonly YSortEntry[]): number[];
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export function isYSortParticipant(value) {
|
|
2
|
+
return (typeof value === 'object' &&
|
|
3
|
+
value !== null &&
|
|
4
|
+
typeof value.layer === 'number' &&
|
|
5
|
+
typeof value.setSortZ === 'function');
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Z per entry under y-sort. Each layer keeps its 0.01 band; within a band,
|
|
9
|
+
* lower Y gets a higher z (renders in front), and exact Y ties keep input
|
|
10
|
+
* order. Offsets stay strictly inside (layer, layer + 1) × 0.01 for integer
|
|
11
|
+
* layers — but a fractional layer (e.g. 0.5) can sit closer than that to the
|
|
12
|
+
* next one present, so each band is capped at the gap to the next distinct
|
|
13
|
+
* layer above it, never wider than 0.01. Integer layers are always >= 1
|
|
14
|
+
* apart, so their band is exactly the old fixed 0.01.
|
|
15
|
+
*/
|
|
16
|
+
export function ySortZ(entries) {
|
|
17
|
+
const byLayer = new Map();
|
|
18
|
+
for (const [index, entry] of entries.entries()) {
|
|
19
|
+
const group = byLayer.get(entry.layer);
|
|
20
|
+
if (group)
|
|
21
|
+
group.push(index);
|
|
22
|
+
else
|
|
23
|
+
byLayer.set(entry.layer, [index]);
|
|
24
|
+
}
|
|
25
|
+
const layers = [...byLayer.keys()].sort((a, b) => a - b);
|
|
26
|
+
const z = new Array(entries.length);
|
|
27
|
+
for (const [i, layer] of layers.entries()) {
|
|
28
|
+
const indices = byLayer.get(layer);
|
|
29
|
+
const next = layers[i + 1];
|
|
30
|
+
const width = next === undefined ? 0.01 : Math.min(0.01, (next - layer) * 0.01);
|
|
31
|
+
// Stable sort: back-to-front is descending Y, ties keep input order.
|
|
32
|
+
const ordered = [...indices].sort((a, b) => entries[b].y - entries[a].y);
|
|
33
|
+
const step = width / (ordered.length + 1);
|
|
34
|
+
for (const [rank, index] of ordered.entries()) {
|
|
35
|
+
z[index] = layer * 0.01 + (rank + 1) * step;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return z;
|
|
39
|
+
}
|
|
@@ -28,10 +28,20 @@ export interface PrefabJson {
|
|
|
28
28
|
type: 'character' | 'object' | 'tile';
|
|
29
29
|
components: SceneComponentJson[];
|
|
30
30
|
}
|
|
31
|
+
/** Scene-wide render options (v3). */
|
|
32
|
+
export interface SceneRenderJson {
|
|
33
|
+
/**
|
|
34
|
+
* 'y' orders same-layer sprites by their entity's world Y — lower Y renders
|
|
35
|
+
* in front (top-down depth). Absent: spawn order breaks same-layer ties.
|
|
36
|
+
*/
|
|
37
|
+
sort?: 'y';
|
|
38
|
+
}
|
|
31
39
|
export interface SceneJson {
|
|
32
40
|
waicaScene: 1 | 2 | 3;
|
|
33
41
|
/** The scene's built-in camera (v3); absent = the host keeps control. */
|
|
34
42
|
camera?: SceneCameraJson;
|
|
43
|
+
/** Draw-order policy (v3); absent = layer bands with spawn-order ties. */
|
|
44
|
+
render?: SceneRenderJson;
|
|
35
45
|
entities: SceneEntityJson[];
|
|
36
46
|
/** UI pieces (src/ui/*.html) mounted visible when the scene loads. */
|
|
37
47
|
ui?: string[];
|
|
@@ -82,6 +82,7 @@ export function spawnFromJson(game, json, registry) {
|
|
|
82
82
|
/** Loads a full scene into the game. */
|
|
83
83
|
export function loadScene(game, scene, registry) {
|
|
84
84
|
game.registry = registry;
|
|
85
|
+
game.setSceneRender(scene.render);
|
|
85
86
|
for (const entityJson of scene.entities)
|
|
86
87
|
spawnFromJson(game, entityJson, registry);
|
|
87
88
|
// After the spawns: with a follow target, the camera starts centered on it.
|
|
@@ -63,7 +63,7 @@ export interface ArchetypeBundle {
|
|
|
63
63
|
/** Extra named logic sets that are not themselves roles. */
|
|
64
64
|
logicSets?: Readonly<Record<string, StateLogic>>;
|
|
65
65
|
}
|
|
66
|
-
/** Clears role definitions
|
|
66
|
+
/** Clears role definitions, every named logic set and the directional contract. */
|
|
67
67
|
export declare function resetRegistries(): void;
|
|
68
68
|
/**
|
|
69
69
|
* Replaces the active registry contents with one archetype's complete bundle.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { installDirectionalAnimation } from '../animation/directional.js';
|
|
1
2
|
const sets = new Map();
|
|
2
3
|
/**
|
|
3
4
|
* Registers state code under a logic-set name. A role's name is also its
|
|
@@ -8,10 +9,11 @@ export function defineStates(name, states) {
|
|
|
8
9
|
sets.set(name, { ...sets.get(name), ...states });
|
|
9
10
|
}
|
|
10
11
|
const roles = new Map();
|
|
11
|
-
/** Clears role definitions
|
|
12
|
+
/** Clears role definitions, every named logic set and the directional contract. */
|
|
12
13
|
export function resetRegistries() {
|
|
13
14
|
roles.clear();
|
|
14
15
|
sets.clear();
|
|
16
|
+
installDirectionalAnimation(null);
|
|
15
17
|
}
|
|
16
18
|
/**
|
|
17
19
|
* Replaces the active registry contents with one archetype's complete bundle.
|
|
@@ -66,6 +66,8 @@ export declare class StateMachine extends Component {
|
|
|
66
66
|
private readonly instanceHooks;
|
|
67
67
|
private readonly signals;
|
|
68
68
|
private readonly warnedClips;
|
|
69
|
+
/** Facing last used to resolve a directional clip — detects a turn mid-state. */
|
|
70
|
+
private lastFacing;
|
|
69
71
|
onReady(): void;
|
|
70
72
|
/** Adds instance-level hooks on top of the logic set — the escape hatch. */
|
|
71
73
|
on(state: string, hooks: StateHooks): void;
|
|
@@ -81,4 +83,20 @@ export declare class StateMachine extends Component {
|
|
|
81
83
|
private runCollision;
|
|
82
84
|
private enter;
|
|
83
85
|
private playClip;
|
|
86
|
+
/**
|
|
87
|
+
* Directional resolution: with an installed contract AND a sibling that
|
|
88
|
+
* reports facing, plays state × facing (mirroring included). Returns false
|
|
89
|
+
* to keep the name-based path — no contract, no facing, no playable clip.
|
|
90
|
+
*/
|
|
91
|
+
private playDirectionalClip;
|
|
92
|
+
private facingProvider;
|
|
93
|
+
/**
|
|
94
|
+
* playDirectionalClip only runs at enter() time (via playClip), so a
|
|
95
|
+
* facing change that doesn't cross a state boundary — turning while
|
|
96
|
+
* still walking, say — never re-resolves the clip on its own. Catch that
|
|
97
|
+
* here every frame: same state, new facing, re-run the resolution.
|
|
98
|
+
* AnimatedSprite.play() no-ops on an unchanged clip name, so this is
|
|
99
|
+
* cheap when facing hasn't moved the resolved clip.
|
|
100
|
+
*/
|
|
101
|
+
private reresolveOnFacingChange;
|
|
84
102
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { installedDirectionalAnimation, isAnimationFacingProvider, resolveDirectionalClip, } from '../animation/directional.js';
|
|
1
2
|
import { Component } from '../component.js';
|
|
2
3
|
import { AnimatedSprite } from '../components/animated-sprite.js';
|
|
3
4
|
import { closestLogicSet, logicSet, registeredLogicSets, } from './hooks.js';
|
|
@@ -47,7 +48,14 @@ export class StateMachine extends Component {
|
|
|
47
48
|
static params = {
|
|
48
49
|
role: { label: 'Role' },
|
|
49
50
|
};
|
|
50
|
-
static transient = [
|
|
51
|
+
static transient = [
|
|
52
|
+
'current',
|
|
53
|
+
'elapsed',
|
|
54
|
+
'instanceHooks',
|
|
55
|
+
'signals',
|
|
56
|
+
'warnedClips',
|
|
57
|
+
'lastFacing',
|
|
58
|
+
];
|
|
51
59
|
/** The character's role — names the logic set providing its state code. */
|
|
52
60
|
role = '';
|
|
53
61
|
/** Starting state; defaults to the first declared state. */
|
|
@@ -60,6 +68,8 @@ export class StateMachine extends Component {
|
|
|
60
68
|
instanceHooks = new Map();
|
|
61
69
|
signals = new Set();
|
|
62
70
|
warnedClips = new Set();
|
|
71
|
+
/** Facing last used to resolve a directional clip — detects a turn mid-state. */
|
|
72
|
+
lastFacing;
|
|
63
73
|
onReady() {
|
|
64
74
|
if (this.role && !logicSet(this.role)) {
|
|
65
75
|
const sets = registeredLogicSets();
|
|
@@ -111,6 +121,7 @@ export class StateMachine extends Component {
|
|
|
111
121
|
this.enter(edge.to);
|
|
112
122
|
}
|
|
113
123
|
this.signals.clear();
|
|
124
|
+
this.reresolveOnFacingChange();
|
|
114
125
|
}
|
|
115
126
|
onCollide(other) {
|
|
116
127
|
// The state that was active when the contact happened owns it: a
|
|
@@ -167,6 +178,8 @@ export class StateMachine extends Component {
|
|
|
167
178
|
if (!sprite)
|
|
168
179
|
return;
|
|
169
180
|
const clip = this.states[state]?.clip ?? state;
|
|
181
|
+
if (this.playDirectionalClip(sprite, clip))
|
|
182
|
+
return;
|
|
170
183
|
if (sprite.clips[clip]) {
|
|
171
184
|
sprite.play(clip);
|
|
172
185
|
}
|
|
@@ -176,4 +189,47 @@ export class StateMachine extends Component {
|
|
|
176
189
|
`keeping "${sprite.current ?? 'none'}"`);
|
|
177
190
|
}
|
|
178
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Directional resolution: with an installed contract AND a sibling that
|
|
194
|
+
* reports facing, plays state × facing (mirroring included). Returns false
|
|
195
|
+
* to keep the name-based path — no contract, no facing, no playable clip.
|
|
196
|
+
*/
|
|
197
|
+
playDirectionalClip(sprite, clip) {
|
|
198
|
+
const animation = installedDirectionalAnimation();
|
|
199
|
+
if (!animation)
|
|
200
|
+
return false;
|
|
201
|
+
const provider = this.facingProvider();
|
|
202
|
+
if (!provider)
|
|
203
|
+
return false;
|
|
204
|
+
const facing = provider.getAnimationFacing();
|
|
205
|
+
this.lastFacing = facing;
|
|
206
|
+
const resolved = resolveDirectionalClip(animation, Object.keys(sprite.clips), clip, facing);
|
|
207
|
+
if (!resolved.clip || !sprite.clips[resolved.clip])
|
|
208
|
+
return false;
|
|
209
|
+
sprite.setFlipX(resolved.flip);
|
|
210
|
+
sprite.play(resolved.clip);
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
facingProvider() {
|
|
214
|
+
return this.entity.components.find((c) => isAnimationFacingProvider(c));
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* playDirectionalClip only runs at enter() time (via playClip), so a
|
|
218
|
+
* facing change that doesn't cross a state boundary — turning while
|
|
219
|
+
* still walking, say — never re-resolves the clip on its own. Catch that
|
|
220
|
+
* here every frame: same state, new facing, re-run the resolution.
|
|
221
|
+
* AnimatedSprite.play() no-ops on an unchanged clip name, so this is
|
|
222
|
+
* cheap when facing hasn't moved the resolved clip.
|
|
223
|
+
*/
|
|
224
|
+
reresolveOnFacingChange() {
|
|
225
|
+
if (!this.current || !installedDirectionalAnimation())
|
|
226
|
+
return;
|
|
227
|
+
const sprite = this.entity.get(AnimatedSprite);
|
|
228
|
+
if (!sprite)
|
|
229
|
+
return;
|
|
230
|
+
const provider = this.facingProvider();
|
|
231
|
+
if (!provider || provider.getAnimationFacing() === this.lastFacing)
|
|
232
|
+
return;
|
|
233
|
+
this.playDirectionalClip(sprite, this.states[this.current]?.clip ?? this.current);
|
|
234
|
+
}
|
|
179
235
|
}
|
|
@@ -2,6 +2,7 @@ import { access, readFile, realpath } from 'node:fs/promises';
|
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { KNOWN_ARCHETYPES } from './known-archetypes.js';
|
|
5
6
|
import { workspacePackageRoot } from './workspace-runtime.js';
|
|
6
7
|
/** A declared package that is absent from both the project and MCP bundle. */
|
|
7
8
|
export class PackageUnavailableError extends Error {
|
|
@@ -15,8 +16,7 @@ export class PackageUnavailableError extends Error {
|
|
|
15
16
|
const BUNDLED_SPECIFIERS = new Set([
|
|
16
17
|
'@waica/engine',
|
|
17
18
|
'@waica/behaviors',
|
|
18
|
-
|
|
19
|
-
'@waica/archetype-platformer/manifest',
|
|
19
|
+
...KNOWN_ARCHETYPES.flatMap(({ packageName }) => [packageName, `${packageName}/manifest`]),
|
|
20
20
|
]);
|
|
21
21
|
const requireBundled = createRequire(import.meta.url);
|
|
22
22
|
/** Node resolution anchored at a game project rather than the MCP host process. */
|
|
@@ -26,8 +26,10 @@ export function projectAnchoredRequire(projectPath) {
|
|
|
26
26
|
const BUILT_ENTRY_BY_SPECIFIER = {
|
|
27
27
|
'@waica/engine': 'dist/index.js',
|
|
28
28
|
'@waica/behaviors': 'dist/index.js',
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
...Object.fromEntries(KNOWN_ARCHETYPES.flatMap(({ packageName }) => [
|
|
30
|
+
[packageName, 'dist/index.js'],
|
|
31
|
+
[`${packageName}/manifest`, 'dist/manifest.js'],
|
|
32
|
+
])),
|
|
31
33
|
};
|
|
32
34
|
function specifierFor(packageName, subpath) {
|
|
33
35
|
if (!subpath || subpath === '.')
|
|
@@ -127,6 +129,10 @@ async function loadBundledModule(specifier, packageRoot) {
|
|
|
127
129
|
return import('@waica/archetype-platformer');
|
|
128
130
|
case '@waica/archetype-platformer/manifest':
|
|
129
131
|
return import('@waica/archetype-platformer/manifest');
|
|
132
|
+
case '@waica/archetype-topdown':
|
|
133
|
+
return import('@waica/archetype-topdown');
|
|
134
|
+
case '@waica/archetype-topdown/manifest':
|
|
135
|
+
return import('@waica/archetype-topdown/manifest');
|
|
130
136
|
default:
|
|
131
137
|
throw new PackageUnavailableError(specifier);
|
|
132
138
|
}
|