incanto 0.27.0 → 0.29.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.
Files changed (58) hide show
  1. package/README.md +44 -0
  2. package/bin/incanto-editor.mjs +118 -1
  3. package/dist/2d.d.ts +38 -5
  4. package/dist/2d.js +3 -3
  5. package/dist/3d.d.ts +496 -150
  6. package/dist/3d.js +5 -5
  7. package/dist/{behavior-CPibUfnH.d.ts → behavior-BAc0erXF.d.ts} +21 -0
  8. package/dist/{create-game-DUAtTqID.js → create-game-CNKXGfpr.js} +270 -47
  9. package/dist/{create-game-C0bPoURs.js → create-game-CbuLWorm.js} +53 -14
  10. package/dist/debug.d.ts +51 -6
  11. package/dist/debug.js +242 -19
  12. package/dist/editor-switch-B0wB_DSr.d.ts +59 -0
  13. package/dist/editor-switch-CAKlJMIY.js +161 -0
  14. package/dist/editor.d.ts +49 -0
  15. package/dist/editor.js +8439 -0
  16. package/dist/{gameplay-Ddk13pQ6.js → gameplay-L05WgWd1.js} +220 -37
  17. package/dist/gameplay.d.ts +1 -1
  18. package/dist/gameplay.js +1 -1
  19. package/dist/index.d.ts +22 -3
  20. package/dist/index.js +2 -2
  21. package/dist/{loader-DILt9PGC.d.ts → loader-B242FF6N.d.ts} +1 -1
  22. package/dist/net.d.ts +1 -1
  23. package/dist/net.js +2 -2
  24. package/dist/{pathfinding-RWYkNKx9.d.ts → pathfinding-BwD974Ss.d.ts} +1 -1
  25. package/dist/{physics-2d-DiVFFlH3.js → physics-2d-CCVTrKOd.js} +62 -2
  26. package/dist/{physics-3d-4mGzOZ2J.js → physics-3d-BZZLtwJu.js} +439 -8
  27. package/dist/react.d.ts +1 -1
  28. package/dist/react.js +1 -1
  29. package/dist/{register-nObreUQR.js → register-BTg0EM7s.js} +35 -3
  30. package/dist/{register-DcHXS1MA.js → register-C44aSduO.js} +5694 -5108
  31. package/dist/{register-CvpSUU3O.js → register-DWcWq4QG.js} +22 -2
  32. package/dist/{register-BFFE1Mh1.js → register-MelqEdza.js} +1 -1
  33. package/dist/test.d.ts +35 -3
  34. package/dist/test.js +104 -10
  35. package/dist/vite.d.ts +53 -1
  36. package/dist/vite.js +94 -2
  37. package/editor/assets/{agent8-DElIPozC.js → agent8-DEVkEa3d.js} +1 -1
  38. package/editor/assets/debug-zGAtpDF0.js +2 -0
  39. package/editor/assets/index-Bx4UtWYY.js +10586 -0
  40. package/editor/index.html +3 -157
  41. package/package.json +3 -2
  42. package/schemas/scene.schema.json +110 -4
  43. package/skills/incanto-3d-models.md +38 -0
  44. package/skills/incanto-assets.md +13 -0
  45. package/skills/incanto-building-3d-games.md +83 -6
  46. package/skills/incanto-editor.md +151 -0
  47. package/skills/incanto-environment.md +88 -1
  48. package/skills/incanto-node-reference.md +35 -1
  49. package/skills/incanto-physics-and-input.md +45 -6
  50. package/skills/incanto-verifying-your-game.md +51 -2
  51. package/templates-app/beacon-isle-3d/docs/project-3d-rules.md +5 -0
  52. package/templates-app/beacon-isle-3d/package.json +1 -1
  53. package/templates-app/tps-3d/docs/project-3d-rules.md +5 -0
  54. package/templates-app/tps-3d/package.json +1 -1
  55. package/templates-app/village-quest-3d/docs/project-3d-rules.md +5 -0
  56. package/templates-app/village-quest-3d/package.json +1 -1
  57. package/editor/assets/index-BrneVqN0.js +0 -8330
  58. package/editor/assets/index-D8QvwvOm.css +0 -1
@@ -0,0 +1,161 @@
1
+ //#region src/core/editor-switch.ts
2
+ /**
3
+ * Cover the page while it changes what it is.
4
+ *
5
+ * A mode switch tears down a whole game and builds a whole IDE in its place;
6
+ * between those two the window shows a dead canvas, a flash of white body, and
7
+ * then a fully-formed editor. Every one of those frames is honest and none of
8
+ * them is pleasant. A veil makes the crossing read as one movement: fade to the
9
+ * editor's own background, do the work behind it, fade back.
10
+ *
11
+ * Deliberately CSS-only and dependency-free — it has to work in a game page
12
+ * whose styles we know nothing about, so it sits at the top of the stacking
13
+ * order, eats pointer events (nobody clicks a UI that is being replaced), and
14
+ * removes itself completely. In a headless boot there is no document and this
15
+ * is a pair of no-ops.
16
+ */
17
+ const VEIL_MS = 160;
18
+ function raiseVeil(label = "", background = "#0b0d14") {
19
+ if (typeof document === "undefined") return { lift: () => {} };
20
+ const el = document.createElement("div");
21
+ el.dataset.incantoSwitch = "";
22
+ el.style.cssText = `position:fixed;inset:0;z-index:2147483646;background:${background};display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity ${VEIL_MS}ms ease;pointer-events:auto;`;
23
+ if (label) {
24
+ const text = document.createElement("div");
25
+ text.textContent = label;
26
+ text.style.cssText = "font:13px/1 ui-monospace,SFMono-Regular,Menlo,monospace;color:#5d6680;letter-spacing:0.04em;";
27
+ el.appendChild(text);
28
+ }
29
+ document.body.appendChild(el);
30
+ requestAnimationFrame(() => {
31
+ el.style.opacity = "1";
32
+ });
33
+ let lifted = false;
34
+ return { lift() {
35
+ if (lifted) return;
36
+ lifted = true;
37
+ requestAnimationFrame(() => {
38
+ el.style.opacity = "0";
39
+ setTimeout(() => el.remove(), 200);
40
+ });
41
+ } };
42
+ }
43
+ /** Hold the veil up for at least one paint so the fade is visible at all. */
44
+ async function settle(ms = VEIL_MS) {
45
+ if (typeof requestAnimationFrame !== "function") return;
46
+ await new Promise((resolve) => setTimeout(resolve, ms));
47
+ }
48
+ /**
49
+ * The `/api/library` route `incanto/vite`'s `incantoLibrary()` (and the
50
+ * `incanto-editor` server) serve. Used when a game passes `library: true`.
51
+ */
52
+ function devServerLibrary(token) {
53
+ return async (query) => {
54
+ const params = new URLSearchParams({
55
+ tags: query.tags.join(","),
56
+ page: String(query.page ?? 1),
57
+ limit: String(query.limit ?? 24)
58
+ });
59
+ if (query.keyword?.trim()) params.set("keyword", query.keyword.trim());
60
+ const res = await fetch(`/api/library?${params}`, { headers: token ? { "x-incanto-v8-token": token } : {} });
61
+ if (res.status === 401) {
62
+ const body = await res.json().catch(() => ({}));
63
+ const error = new Error(body.error ?? "the asset library needs a Verse8 access token");
64
+ error.needsToken = true;
65
+ throw error;
66
+ }
67
+ if (!res.ok) throw new Error(`asset library: HTTP ${res.status}`);
68
+ return await res.json();
69
+ };
70
+ }
71
+ /**
72
+ * The default opener: pull `incanto/editor` in on demand and mount it.
73
+ *
74
+ * It is a dynamic import so the editor — an IDE's worth of UI — never enters a
75
+ * production bundle. A build that cannot resolve it fails HERE, with the two
76
+ * ways to fix it, rather than silently offering a menu item that does nothing.
77
+ */
78
+ async function openBundledEditor(opts) {
79
+ let mod;
80
+ try {
81
+ mod = await import("incanto/editor");
82
+ } catch (error) {
83
+ throw new Error(`createGame3D editor: could not load 'incanto/editor'. If this app aliases incanto to the engine SOURCE (the examples and playground do), add an alias for 'incanto/editor' → packages/editor/src/main.ts — or pass editor: { open } and load it yourself. (${error instanceof Error ? error.message : String(error)})`);
84
+ }
85
+ let handle = null;
86
+ handle = await mod.mountEditor({
87
+ ...opts.camera ? { camera: opts.camera } : {},
88
+ api: {
89
+ meta: async () => ({
90
+ version: "live",
91
+ mode: "single",
92
+ root: "",
93
+ input: null,
94
+ output: null
95
+ }),
96
+ loadScene: async () => opts.scene,
97
+ ...opts.save ? { saveScene: async (json) => void opts.save?.(json) } : {},
98
+ ...opts.library ? { library: opts.library } : {}
99
+ },
100
+ onPlay: (choice, working) => {
101
+ if (choice !== "debug") return void 0;
102
+ const camera = handle?.camera();
103
+ return crossFade(async () => {
104
+ handle?.dispose();
105
+ handle = null;
106
+ await opts.play(working, camera);
107
+ }, "starting the game…");
108
+ },
109
+ ...opts.exit ? { onExit: () => crossFade(async () => {
110
+ handle?.dispose();
111
+ handle = null;
112
+ await opts.exit?.();
113
+ }, "back to the game…") } : {}
114
+ });
115
+ }
116
+ /**
117
+ * Run a mode change behind the veil: cover, swap, reveal.
118
+ *
119
+ * The reveal waits for the new page to have PAINTED (two frames — one for the
120
+ * layout the swap just produced, one for it to reach the screen), otherwise the
121
+ * fade-out races the boot and shows the very flash it exists to hide.
122
+ */
123
+ async function crossFade(swap, label = "") {
124
+ const veil = raiseVeil(label);
125
+ await settle();
126
+ try {
127
+ await swap();
128
+ } finally {
129
+ await settle(0);
130
+ veil.lift();
131
+ }
132
+ }
133
+ /**
134
+ * The camera the player is actually looking through, as a pose.
135
+ *
136
+ * Reads the renderer's LAST rendered camera rather than the scene's Camera3D:
137
+ * a third-person rig, a cutscene camera and a debug free-fly all end up in the
138
+ * same place, and it is the view on screen — the thing continuity is about.
139
+ */
140
+ function poseFromRenderer(renderer, fallbackDistance = 8) {
141
+ const basis = renderer.cameraBasis;
142
+ const posOf = renderer.lastCameraPosition;
143
+ if (typeof basis !== "function" || typeof posOf !== "function") return void 0;
144
+ const eye = posOf.call(renderer);
145
+ if (!eye) return void 0;
146
+ const { forward } = basis.call(renderer);
147
+ return {
148
+ position: [
149
+ eye.x,
150
+ eye.y,
151
+ eye.z
152
+ ],
153
+ target: [
154
+ eye.x + forward.x * fallbackDistance,
155
+ eye.y + forward.y * fallbackDistance,
156
+ eye.z + forward.z * fallbackDistance
157
+ ]
158
+ };
159
+ }
160
+ //#endregion
161
+ export { poseFromRenderer as i, devServerLibrary as n, openBundledEditor as r, crossFade as t };
@@ -0,0 +1,49 @@
1
+ // Generated by packages/editor/scripts/emit-types.mjs — do not edit.
2
+ type Json = Record<string, unknown>;
3
+
4
+ export interface EditorMeta {
5
+ version: string;
6
+ mode: 'single' | 'project';
7
+ root: string;
8
+ input: string | null;
9
+ output: string | null;
10
+ }
11
+
12
+ export interface SceneEntry {
13
+ rel: string;
14
+ abs: string;
15
+ }
16
+
17
+ /** Where a mounted editor reads and writes scenes. */
18
+ export interface EditorApi {
19
+ meta(): Promise<EditorMeta>;
20
+ loadScene(file?: string): Promise<Json>;
21
+ saveScene?(json: Json, file?: string): Promise<void>;
22
+ scenes?(): Promise<SceneEntry[]>;
23
+ createScene?(path: string): Promise<SceneEntry>;
24
+ }
25
+
26
+ export type PlayChoice = 'preview' | 'debug';
27
+
28
+ export interface MountEditorOptions {
29
+ host?: HTMLElement;
30
+ api?: EditorApi;
31
+ camera?: { position: readonly number[]; target: readonly number[] };
32
+ onPlay?(choice: PlayChoice, working: Json): void | Promise<void>;
33
+ onExit?(): void | Promise<void>;
34
+ }
35
+
36
+ export interface EditorHandle {
37
+ dispose(): void;
38
+ working(): Json;
39
+ camera(): { position: [number, number, number]; target: [number, number, number] };
40
+ }
41
+
42
+ /**
43
+ * Mount the scene editor over the page. It OWNS the window while it is up —
44
+ * dispose() puts the page back exactly as it was.
45
+ */
46
+ export declare function mountEditor(opts?: MountEditorOptions): Promise<EditorHandle>;
47
+
48
+ /** The `incanto-editor` server's /api routes, as an EditorApi. */
49
+ export declare const httpEditorApi: EditorApi;