incanto 0.34.0 → 0.35.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/dist/2d.d.ts +5 -5
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +122 -14
- package/dist/3d.js +5 -5
- package/dist/{audit-C4kmDK0o.js → audit-C5oZGAru.js} +37 -0
- package/dist/{behavior-CtPScDMo.d.ts → behavior-CGSCWOHB.d.ts} +310 -22
- package/dist/{create-game-CFNqoqOX.js → create-game-B0Wfhi2-.js} +6 -5
- package/dist/{create-game-QTfghzwM.js → create-game-BUD89Kqh.js} +20 -6
- package/dist/debug.d.ts +1 -1
- package/dist/{duplicate-DlOvknDO.js → duplicate-C716f-97.js} +1 -1
- package/dist/{editor-switch-BJb-CWfA.d.ts → editor-switch-DyXEtH36.d.ts} +1 -1
- package/dist/editor.js +1739 -1312
- package/dist/env.d.ts +1 -1
- package/dist/{environment-presets-B6WiUsaO.js → environment-presets-TAGvmM3z.js} +240 -74
- package/dist/{errors-1dXlIwoR.d.ts → errors-BY2kL0hv.d.ts} +1 -1
- package/dist/{gameplay-BxFtmfSe.js → gameplay-BvhcQbfJ.js} +75 -11
- package/dist/gameplay.d.ts +12 -5
- package/dist/gameplay.js +1 -1
- package/dist/index.d.ts +22 -7
- package/dist/index.js +6 -6
- package/dist/{loader-DmXfj6Uv.d.ts → loader-CUcj00M8.d.ts} +19 -2
- package/dist/{loader-BwR0DxeM.js → loader-Mig5fY4n.js} +97 -18
- package/dist/net.d.ts +2 -2
- package/dist/net.js +3 -3
- package/dist/{particle-sim-CwJ5rI_P.d.ts → particle-sim-B-vZBF5R.d.ts} +1 -1
- package/dist/{pathfinding-DRCe89SI.d.ts → pathfinding-DgOo2KNF.d.ts} +1 -1
- package/dist/{physics-2d-BuyAwsW5.js → physics-2d-DqAclql-.js} +22 -9
- package/dist/{physics-3d-C8Kn_Nyb.js → physics-3d-DxBH4sIF.js} +27 -11
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/dist/{register-G3edsjJ2.js → register-BFLg0-_i.js} +490 -12
- package/dist/{register-OodmuUMK.js → register-BjbPMA5B.js} +2 -2
- package/dist/{register-BLPC10Mj.js → register-DSmIRAf7.js} +11 -5
- package/dist/{schema-CcoWb32N.d.ts → schema-3ywbdlrv.d.ts} +10 -0
- package/dist/{test-C4B5znap.js → test-9EokzbRd.js} +10 -10
- package/dist/test.d.ts +4 -4
- package/dist/test.js +2 -2
- package/dist/vite.js +1 -1
- package/editor/assets/{agent8-BTODHtdM.js → agent8-D3_GWeuh.js} +1 -1
- package/editor/assets/{debug-CJEz4EwC.js → debug-CX0LDd1r.js} +1 -1
- package/editor/assets/index-CMlKFT0C.js +10773 -0
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +109 -0
- package/skills/incanto-assets.md +21 -0
- package/skills/incanto-behaviors-and-scripts.md +16 -0
- package/skills/incanto-editor.md +17 -1
- package/skills/incanto-gameplay-behaviors.md +6 -0
- package/skills/incanto-hud.md +16 -0
- package/skills/incanto-localization.md +132 -0
- package/skills/incanto-node-reference.md +33 -18
- package/skills/incanto-save-slots.md +152 -0
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/village-quest-3d/PROJECT/Context.md +16 -0
- package/templates-app/village-quest-3d/package.json +1 -1
- package/templates-app/village-quest-3d/src/village.scene.json +25 -3
- package/editor/assets/index-BYCso8Bf.js +0 -10696
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as Rng, c as JsonValue, i as SceneJson, s as JsonObject } from "./schema-
|
|
1
|
+
import { a as Rng, c as JsonValue, i as SceneJson, s as JsonObject } from "./schema-3ywbdlrv.js";
|
|
2
2
|
|
|
3
3
|
//#region src/core/signal.d.ts
|
|
4
4
|
/**
|
|
@@ -80,6 +80,35 @@ declare class SceneTree {
|
|
|
80
80
|
* change shape?" check for per-step body gathers and similar caches.
|
|
81
81
|
*/
|
|
82
82
|
_structureVersion: number;
|
|
83
|
+
/**
|
|
84
|
+
* WHAT changed, not just that something did.
|
|
85
|
+
*
|
|
86
|
+
* The version alone forces every consumer into a full-tree re-walk, and the
|
|
87
|
+
* physics step is gated on it: one bullet spawned per frame means
|
|
88
|
+
* `collectBodies` + `collectJoints` + `syncScatters` walk the entire scene at
|
|
89
|
+
* 60 Hz. A bullet-hell or a survivors-like does nothing else all game.
|
|
90
|
+
*
|
|
91
|
+
* This log lets a consumer replay just the attaches and detaches it has not
|
|
92
|
+
* seen. It is bounded — a scene load appends thousands of entries and nobody
|
|
93
|
+
* benefits from replaying those — so a consumer that falls behind
|
|
94
|
+
* `_structureLogBase` takes the full walk, exactly as before. Correctness does
|
|
95
|
+
* not depend on the log; only the fast path does.
|
|
96
|
+
*/
|
|
97
|
+
private _structureLog;
|
|
98
|
+
/** The `_structureVersion` the first entry in the log corresponds to. */
|
|
99
|
+
private _structureLogBase;
|
|
100
|
+
private static readonly STRUCTURE_LOG_MAX;
|
|
101
|
+
/**
|
|
102
|
+
* Attaches and detaches since `sinceVersion`, or null when the log no longer
|
|
103
|
+
* reaches back that far (take the full walk).
|
|
104
|
+
*
|
|
105
|
+
* The returned array is the live buffer's tail — do not hold it across frames.
|
|
106
|
+
*/
|
|
107
|
+
_structureSince(sinceVersion: number): ReadonlyArray<{
|
|
108
|
+
node: Node;
|
|
109
|
+
added: boolean;
|
|
110
|
+
}> | null;
|
|
111
|
+
private _logStructure;
|
|
83
112
|
private _frameId;
|
|
84
113
|
/**
|
|
85
114
|
* Monotonic update-frame counter — bumped once per `update()`. Per-frame
|
|
@@ -89,7 +118,15 @@ declare class SceneTree {
|
|
|
89
118
|
get frameId(): number;
|
|
90
119
|
/** @internal Node attach hook. */
|
|
91
120
|
_indexName(node: Node): void;
|
|
92
|
-
/**
|
|
121
|
+
/**
|
|
122
|
+
* @internal A RENAME: same node, same place, different key in the name index.
|
|
123
|
+
*
|
|
124
|
+
* Deliberately not logged as a detach + attach. It is not a structural change,
|
|
125
|
+
* and treating it as one would make the physics step tear down and rebuild the
|
|
126
|
+
* node's Rapier body for a name edit.
|
|
127
|
+
*/
|
|
128
|
+
_reindexName(node: Node, oldName: string): void;
|
|
129
|
+
/** @internal Node detach hook (`name` = the entry to remove). */
|
|
93
130
|
_unindexName(node: Node, name?: string): void;
|
|
94
131
|
/** @internal Every ATTACHED node named `name` (undefined = none). */
|
|
95
132
|
_nodesNamed(name: string): ReadonlySet<Node> | undefined;
|
|
@@ -763,6 +800,184 @@ declare class InputMap {
|
|
|
763
800
|
private button;
|
|
764
801
|
}
|
|
765
802
|
//#endregion
|
|
803
|
+
//#region src/core/localization.d.ts
|
|
804
|
+
/** The base locale. Every other locale falls back to it, one key at a time. */
|
|
805
|
+
declare const BASE_LOCALE = "en";
|
|
806
|
+
/**
|
|
807
|
+
* The marker that makes a string prop translatable: `"@t:hud.score"`.
|
|
808
|
+
*
|
|
809
|
+
* A prefix rather than an object wrapper, matching the `"$assetKey"` reference
|
|
810
|
+
* already in the engine — the prop is declared a string, so its value should
|
|
811
|
+
* stay a string. It also stays authorable by hand:
|
|
812
|
+
*
|
|
813
|
+
* ```json
|
|
814
|
+
* { "type": "UiText", "props": { "text": "@t:hud.score" } }
|
|
815
|
+
* ```
|
|
816
|
+
*/
|
|
817
|
+
declare const T_PREFIX = "@t:";
|
|
818
|
+
/** `locale → key → string`. A locale may declare any subset of the base's keys. */
|
|
819
|
+
type LocaleTables = Record<string, Record<string, string>>;
|
|
820
|
+
/** The key a `"@t:…"` value names, or null for an ordinary string. */
|
|
821
|
+
declare function translationKey(value: string): string | null;
|
|
822
|
+
declare class Localization {
|
|
823
|
+
/** Fires with the new locale after it changes — for anything that caches. */
|
|
824
|
+
readonly changed: Signal<[string]>;
|
|
825
|
+
private tables;
|
|
826
|
+
private current;
|
|
827
|
+
constructor(tables?: LocaleTables, locale?: string);
|
|
828
|
+
/**
|
|
829
|
+
* Merge a scene's `strings` table in.
|
|
830
|
+
*
|
|
831
|
+
* Merged rather than replaced: a game's shared UI strings live in the scene it
|
|
832
|
+
* boots from, and a level that adds three lines of its own should not wipe
|
|
833
|
+
* them. Per-key, so a level CAN override one string.
|
|
834
|
+
*/
|
|
835
|
+
load(tables: LocaleTables): void;
|
|
836
|
+
get locale(): string;
|
|
837
|
+
/**
|
|
838
|
+
* Switch language. Setting one nobody declared is not an error — it simply
|
|
839
|
+
* resolves everything through the base locale, which is the correct outcome
|
|
840
|
+
* for a player whose browser reports a language this game does not ship.
|
|
841
|
+
*/
|
|
842
|
+
set locale(next: string);
|
|
843
|
+
/** Every locale with at least one string — what a language picker offers. */
|
|
844
|
+
available(): string[];
|
|
845
|
+
/**
|
|
846
|
+
* The string for `key`: current locale, else English, else the key itself.
|
|
847
|
+
*
|
|
848
|
+
* Returning the key rather than an empty string is deliberate — a screen
|
|
849
|
+
* reading `menu.credits` tells an author exactly what to add, where a blank
|
|
850
|
+
* one tells them nothing and looks like a rendering bug.
|
|
851
|
+
*/
|
|
852
|
+
t(key: string, params?: Record<string, JsonValue>): string;
|
|
853
|
+
/**
|
|
854
|
+
* Resolve a prop value: `"@t:key"` translates, anything else passes through.
|
|
855
|
+
*
|
|
856
|
+
* This is what every text-bearing widget calls, every frame. A plain string is
|
|
857
|
+
* returned untouched, so a game that never localizes anything pays a
|
|
858
|
+
* `startsWith` per widget per frame and nothing else.
|
|
859
|
+
*/
|
|
860
|
+
resolve(value: string, params?: Record<string, JsonValue>): string;
|
|
861
|
+
/** Whether any locale declares this key — for the audit, not for the runtime. */
|
|
862
|
+
has(key: string): boolean;
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* The player's language, if the browser will say and the game ships it.
|
|
866
|
+
*
|
|
867
|
+
* A starting point only — the language setting is the real answer, exactly as
|
|
868
|
+
* `suggestQuality` is a starting point for the quality tier.
|
|
869
|
+
*/
|
|
870
|
+
declare function suggestLocale(available: readonly string[], languages?: readonly string[]): string;
|
|
871
|
+
//#endregion
|
|
872
|
+
//#region src/core/save.d.ts
|
|
873
|
+
/**
|
|
874
|
+
* Namespaced game persistence — high scores, unlocks, settings. localStorage
|
|
875
|
+
* in the browser, in-memory headless (tests and SSR stay green). Values are
|
|
876
|
+
* JSON round-tripped, so what you get back is what you saved.
|
|
877
|
+
*
|
|
878
|
+
* const save = createSaveStore('my-game');
|
|
879
|
+
* save.set('highScore', 4200);
|
|
880
|
+
* const best = save.get('highScore', 0);
|
|
881
|
+
*/
|
|
882
|
+
interface SaveStore {
|
|
883
|
+
get<T extends JsonValue>(key: string, fallback: T): T;
|
|
884
|
+
set(key: string, value: JsonValue): void;
|
|
885
|
+
remove(key: string): void;
|
|
886
|
+
/** Wipe THIS namespace only. */
|
|
887
|
+
clear(): void;
|
|
888
|
+
}
|
|
889
|
+
declare function createSaveStore(namespace: string): SaveStore;
|
|
890
|
+
//#endregion
|
|
891
|
+
//#region src/core/save-slots.d.ts
|
|
892
|
+
/** Behavior state, keyed by the uid of the node carrying it. */
|
|
893
|
+
type BehaviorState = Record<string, JsonValue>;
|
|
894
|
+
interface SaveSlot {
|
|
895
|
+
/** Which scene to load before restoring. Whatever key your game routes on. */
|
|
896
|
+
scene: string;
|
|
897
|
+
/** `uid → whatever that behavior's serialize() returned`. */
|
|
898
|
+
state: BehaviorState;
|
|
899
|
+
/** Shown in a load menu. Free text — "Chapter 2 · Emberwood". */
|
|
900
|
+
label: string;
|
|
901
|
+
/** Unix ms. Passed in, never read from a clock here, so saves stay testable. */
|
|
902
|
+
savedAt: number;
|
|
903
|
+
/** Seconds played, if the game tracks it. */
|
|
904
|
+
playtime: number;
|
|
905
|
+
/** Anything else the game wants in the slot (difficulty, seed, a thumbnail). */
|
|
906
|
+
data: JsonObject;
|
|
907
|
+
}
|
|
908
|
+
/** What a restore could not place, so a game can decide whether that matters. */
|
|
909
|
+
interface RestoreReport {
|
|
910
|
+
/** Entries whose uid is not in this scene — a node deleted since the save. */
|
|
911
|
+
missing: string[];
|
|
912
|
+
/** Entries whose node has no behavior, or one that cannot deserialize. */
|
|
913
|
+
skipped: string[];
|
|
914
|
+
/** How many behaviors actually took their state back. */
|
|
915
|
+
restored: number;
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Every behavior in the tree that has something to say, by uid.
|
|
919
|
+
*
|
|
920
|
+
* A behavior with no `serialize` contributes nothing — the hook is optional in
|
|
921
|
+
* exactly the way the other five are, so adding save support to a game is
|
|
922
|
+
* additive and a game that never saves pays nothing.
|
|
923
|
+
*
|
|
924
|
+
* A node with no `uid` is skipped and reported, because state keyed by nothing
|
|
925
|
+
* cannot be given back. The editor assigns a uid to every node it touches; a
|
|
926
|
+
* hand-written scene may not have.
|
|
927
|
+
*/
|
|
928
|
+
declare function captureBehaviors(root: Node): {
|
|
929
|
+
state: BehaviorState; /** Paths of nodes that had state to save and no uid to save it under. */
|
|
930
|
+
unaddressable: string[];
|
|
931
|
+
};
|
|
932
|
+
/**
|
|
933
|
+
* Hand each behavior its state back.
|
|
934
|
+
*
|
|
935
|
+
* Call this AFTER the scene has loaded and `onReady` has run: `onReady` is where
|
|
936
|
+
* a behavior sets its starting values, so restoring first would be overwritten
|
|
937
|
+
* by the fresh start.
|
|
938
|
+
*
|
|
939
|
+
* Never throws on a mismatch. A save from an older build of the game will name
|
|
940
|
+
* uids that no longer exist, and refusing to load it would mean a patch that
|
|
941
|
+
* moves one node deletes everyone's progress. It reports instead.
|
|
942
|
+
*/
|
|
943
|
+
declare function restoreBehaviors(root: Node, state: BehaviorState): RestoreReport;
|
|
944
|
+
/**
|
|
945
|
+
* The slot layer: a named list of saves over the existing store.
|
|
946
|
+
*
|
|
947
|
+
* Slot ids are yours (`'auto'`, `'1'`, `'2'`, `'3'`) — the engine does not
|
|
948
|
+
* decide how many you have or whether one of them autosaves.
|
|
949
|
+
*/
|
|
950
|
+
declare class SaveSlots {
|
|
951
|
+
private readonly store;
|
|
952
|
+
constructor(namespace?: string, store?: SaveStore);
|
|
953
|
+
/** Slot ids that have a save, newest first. */
|
|
954
|
+
list(): string[];
|
|
955
|
+
/** Every slot with its contents — what a load menu actually renders. */
|
|
956
|
+
all(): Array<SaveSlot & {
|
|
957
|
+
id: string;
|
|
958
|
+
}>;
|
|
959
|
+
read(id: string): SaveSlot | null;
|
|
960
|
+
/**
|
|
961
|
+
* Write a slot.
|
|
962
|
+
*
|
|
963
|
+
* `savedAt` is a parameter rather than `Date.now()` so a test can assert an
|
|
964
|
+
* ordering without sleeping, and so a game that wants server time can use it.
|
|
965
|
+
*/
|
|
966
|
+
write(id: string, slot: Omit<SaveSlot, "label" | "savedAt" | "playtime" | "data"> & Partial<SaveSlot>): void;
|
|
967
|
+
remove(id: string): void;
|
|
968
|
+
/** Wipe every slot — a "delete all data" button, and test isolation. */
|
|
969
|
+
clear(): void;
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* Behaviors in this tree that hold state and cannot save it — the audit's
|
|
973
|
+
* question, answerable before a player loses an hour.
|
|
974
|
+
*
|
|
975
|
+
* A behavior with props but no `serialize` is not necessarily wrong (an
|
|
976
|
+
* `Oscillate` derives everything from time), so this reports rather than warns,
|
|
977
|
+
* and the caller decides.
|
|
978
|
+
*/
|
|
979
|
+
declare function behaviorsWithoutSave(root: Node): string[];
|
|
980
|
+
//#endregion
|
|
766
981
|
//#region src/core/scene/scene.d.ts
|
|
767
982
|
/**
|
|
768
983
|
* A loaded, live scene: the node tree plus everything needed to round-trip
|
|
@@ -786,6 +1001,8 @@ declare class Scene {
|
|
|
786
1001
|
readonly constants: Record<string, JsonValue> | undefined;
|
|
787
1002
|
/** Input action declarations (loaded into `Engine.input` on setScene). */
|
|
788
1003
|
readonly input: JsonObject | undefined;
|
|
1004
|
+
/** Translations by locale (merged into `Engine.locale` on setScene). */
|
|
1005
|
+
readonly strings: Record<string, Record<string, string>> | undefined;
|
|
789
1006
|
/** Physics config (gravity etc. — interpreted by the physics modules). */
|
|
790
1007
|
readonly physics: JsonObject | undefined;
|
|
791
1008
|
/** Multiplayer config (room etc. — interpreted by incanto/net). */
|
|
@@ -800,25 +1017,6 @@ declare class Scene {
|
|
|
800
1017
|
toJSON(): SceneJson;
|
|
801
1018
|
}
|
|
802
1019
|
//#endregion
|
|
803
|
-
//#region src/core/save.d.ts
|
|
804
|
-
/**
|
|
805
|
-
* Namespaced game persistence — high scores, unlocks, settings. localStorage
|
|
806
|
-
* in the browser, in-memory headless (tests and SSR stay green). Values are
|
|
807
|
-
* JSON round-tripped, so what you get back is what you saved.
|
|
808
|
-
*
|
|
809
|
-
* const save = createSaveStore('my-game');
|
|
810
|
-
* save.set('highScore', 4200);
|
|
811
|
-
* const best = save.get('highScore', 0);
|
|
812
|
-
*/
|
|
813
|
-
interface SaveStore {
|
|
814
|
-
get<T extends JsonValue>(key: string, fallback: T): T;
|
|
815
|
-
set(key: string, value: JsonValue): void;
|
|
816
|
-
remove(key: string): void;
|
|
817
|
-
/** Wipe THIS namespace only. */
|
|
818
|
-
clear(): void;
|
|
819
|
-
}
|
|
820
|
-
declare function createSaveStore(namespace: string): SaveStore;
|
|
821
|
-
//#endregion
|
|
822
1020
|
//#region src/core/settings.d.ts
|
|
823
1021
|
/** One of three tiers, resolved once at boot and adjustable in an options menu. */
|
|
824
1022
|
type QualityTier = "low" | "medium" | "high";
|
|
@@ -833,6 +1031,11 @@ interface SettingsValues {
|
|
|
833
1031
|
invertY: boolean;
|
|
834
1032
|
/** Reduce camera shake, flashes and other motion. */
|
|
835
1033
|
reduceMotion: boolean;
|
|
1034
|
+
/**
|
|
1035
|
+
* The player's language. `'en'` is the base and the default; anything a locale
|
|
1036
|
+
* does not translate falls back to English, silently and on purpose.
|
|
1037
|
+
*/
|
|
1038
|
+
language: string;
|
|
836
1039
|
}
|
|
837
1040
|
/** What a device probe can see without a GPU context. */
|
|
838
1041
|
interface DeviceHints {
|
|
@@ -864,6 +1067,14 @@ declare class Settings {
|
|
|
864
1067
|
/** Back to defaults, on disk too. */
|
|
865
1068
|
reset(): void;
|
|
866
1069
|
/**
|
|
1070
|
+
* Apply the saved language, and keep it in step both ways.
|
|
1071
|
+
*
|
|
1072
|
+
* The same shape as `bindAudio`: a game that writes
|
|
1073
|
+
* `engine.locale.locale = 'ko'` from an options menu has already persisted it
|
|
1074
|
+
* without knowing this class exists. Returns the unsubscribe.
|
|
1075
|
+
*/
|
|
1076
|
+
bindLocale(locale: Localization): () => void;
|
|
1077
|
+
/**
|
|
867
1078
|
* Push the saved volumes into the engine's buses, and keep writing them back
|
|
868
1079
|
* whenever anything changes them.
|
|
869
1080
|
*
|
|
@@ -975,6 +1186,11 @@ declare class Engine {
|
|
|
975
1186
|
* `bindAudio` makes the volumes write themselves.
|
|
976
1187
|
*/
|
|
977
1188
|
readonly settings: Settings;
|
|
1189
|
+
/**
|
|
1190
|
+
* Translations, and the locale in force. English is the base and the default;
|
|
1191
|
+
* anything a locale does not declare falls back to it silently.
|
|
1192
|
+
*/
|
|
1193
|
+
readonly locale: Localization;
|
|
978
1194
|
/** Low-latency procedural-SFX player (WebAudio, headless-safe). AudioPlayer
|
|
979
1195
|
* presets play through it; games may call `engine.sfx.play(...)` directly. */
|
|
980
1196
|
readonly sfx: SfxEngine;
|
|
@@ -1045,6 +1261,25 @@ declare class Engine {
|
|
|
1045
1261
|
* both — so a tool can list them and put them back. Walks on demand; there is
|
|
1046
1262
|
* no per-frame bookkeeping for this.
|
|
1047
1263
|
*/
|
|
1264
|
+
/**
|
|
1265
|
+
* The string for `key` in the current locale — English if this locale does not
|
|
1266
|
+
* declare it, the key itself if nothing does.
|
|
1267
|
+
*
|
|
1268
|
+
* The short form of `engine.locale.t`, because a behavior reaching for a
|
|
1269
|
+
* translated string is the common case:
|
|
1270
|
+
* `banner.show(this.engine.t('wave.start', { n: wave }))`.
|
|
1271
|
+
*/
|
|
1272
|
+
t(key: string, params?: Record<string, JsonValue>): string;
|
|
1273
|
+
/**
|
|
1274
|
+
* Every behavior's state, ready to put in a save slot.
|
|
1275
|
+
*
|
|
1276
|
+
* Pairs with `restoreState` after the scene has RELOADED — see
|
|
1277
|
+
* core/save-slots.ts for why a save carries state rather than a tree
|
|
1278
|
+
* snapshot.
|
|
1279
|
+
*/
|
|
1280
|
+
captureState(): BehaviorState;
|
|
1281
|
+
/** Give it back, after the scene is loaded and onReady has run. */
|
|
1282
|
+
restoreState(state: BehaviorState): RestoreReport;
|
|
1048
1283
|
erroredNodes(): Node[];
|
|
1049
1284
|
/** Un-skip every quarantined node — the "try again" after you fix the code. */
|
|
1050
1285
|
resumeErroredNodes(): number;
|
|
@@ -1091,6 +1326,16 @@ interface PropDef {
|
|
|
1091
1326
|
* dropdowns and the generated JSON-Schema enum — keep it exhaustive. */
|
|
1092
1327
|
options?: readonly string[];
|
|
1093
1328
|
/**
|
|
1329
|
+
* This prop holds a NODE PATH (`%Unique`, `/Absolute/From/Root`, or one
|
|
1330
|
+
* relative to the node that owns the prop) resolved with `getNodeOrNull`.
|
|
1331
|
+
*
|
|
1332
|
+
* Declaring it is what lets a tool rewrite the reference when the target is
|
|
1333
|
+
* renamed. Without it a rename silently breaks the link — the resolve returns
|
|
1334
|
+
* null and the game keeps running with an enemy that never chases anyone.
|
|
1335
|
+
* Set it on every prop you feed to `getNode`/`getNodeOrNull`.
|
|
1336
|
+
*/
|
|
1337
|
+
nodePath?: boolean;
|
|
1338
|
+
/**
|
|
1094
1339
|
* For an OBJECT-valued prop shaped as a tagged union — `collider`, whose
|
|
1095
1340
|
* `shape` decides which other keys are required. One declaration gives an
|
|
1096
1341
|
* inspector both the dropdown AND something valid to write when the tag
|
|
@@ -1173,14 +1418,57 @@ declare abstract class Behavior {
|
|
|
1173
1418
|
onExitTree?(): void;
|
|
1174
1419
|
update?(dt: number): void;
|
|
1175
1420
|
fixedUpdate?(dt: number): void;
|
|
1421
|
+
/**
|
|
1422
|
+
* What this behavior needs in a save file — the sixth optional hook, and the
|
|
1423
|
+
* one that made "continue where you left off" possible at all.
|
|
1424
|
+
*
|
|
1425
|
+
* Return the state a fresh `onReady` could NOT recreate: current health,
|
|
1426
|
+
* accumulated score, which quest flags are set. Do not return anything the
|
|
1427
|
+
* scene JSON already says (`maxHealth`, `speed`) — that comes back from the
|
|
1428
|
+
* file, and duplicating it just makes old saves fight new balance patches.
|
|
1429
|
+
*
|
|
1430
|
+
* Must be plain JSON. Never a Node, a Texture, or a callback.
|
|
1431
|
+
*
|
|
1432
|
+
* ```ts
|
|
1433
|
+
* override serialize() { return { current: this.current }; }
|
|
1434
|
+
* override deserialize(data: JsonValue) {
|
|
1435
|
+
* const d = data as { current?: number };
|
|
1436
|
+
* if (typeof d.current === 'number') this.current = d.current;
|
|
1437
|
+
* }
|
|
1438
|
+
* ```
|
|
1439
|
+
*
|
|
1440
|
+
* Returning `undefined` writes nothing, so a behavior can decline to save on
|
|
1441
|
+
* a given frame (a boss that is mid-death-animation, say).
|
|
1442
|
+
*/
|
|
1443
|
+
serialize?(): JsonValue | undefined;
|
|
1444
|
+
/**
|
|
1445
|
+
* Take that state back, AFTER `onReady` has run.
|
|
1446
|
+
*
|
|
1447
|
+
* Defensive on purpose: this data may come from a save written by an older
|
|
1448
|
+
* build of the game. Check what you read; do not assume shape. Throwing here
|
|
1449
|
+
* costs only this one behavior's state — the restore keeps going — but
|
|
1450
|
+
* quietly ignoring a field you renamed is usually the better answer.
|
|
1451
|
+
*/
|
|
1452
|
+
deserialize?(data: JsonValue): void;
|
|
1176
1453
|
}
|
|
1177
1454
|
/** Explicit registration — never an import side effect (tree-shaking safety). */
|
|
1178
1455
|
declare function registerBehavior(name: string, ctor: BehaviorCtor, opts?: {
|
|
1179
1456
|
replace?: boolean;
|
|
1180
1457
|
}): void;
|
|
1181
1458
|
declare function getBehavior(name: string): BehaviorCtor;
|
|
1459
|
+
/**
|
|
1460
|
+
* A registered behavior's merged prop schema, `{}` for one nobody registered.
|
|
1461
|
+
*
|
|
1462
|
+
* The node side has had `getNodeSchema` for a long time; a behavior's props were
|
|
1463
|
+
* reachable only by whoever already held the class, which a tool reading scene
|
|
1464
|
+
* JSON never does. Deliberately non-throwing: a tool inspecting a scene that
|
|
1465
|
+
* names a behavior from a game it did not load should degrade, not blow up.
|
|
1466
|
+
*/
|
|
1467
|
+
declare function behaviorSchema(name: string): PropSchema;
|
|
1468
|
+
/** A registered behavior's signals (own + inherited), `[]` for an unknown one. */
|
|
1469
|
+
declare function behaviorSignals(name: string): string[];
|
|
1182
1470
|
declare function registeredBehaviors(): string[];
|
|
1183
1471
|
/** Test isolation helper. */
|
|
1184
1472
|
declare function clearBehaviors(): void;
|
|
1185
1473
|
//#endregion
|
|
1186
|
-
export {
|
|
1474
|
+
export { Voice as $, qualityEnvironment as A, restoreBehaviors as B, EngineStats as C, LogEntry as Ct, QualityTier as D, SignalListener as Dt, DeviceHints as E, Signal as Et, RestoreReport as F, Localization as G, createSaveStore as H, SaveSlot as I, translationKey as J, T_PREFIX as K, SaveSlots as L, suggestQuality as M, Scene as N, Settings as O, BehaviorState as P, isAudioContextAvailable as Q, behaviorsWithoutSave as R, Scheduler as S, SceneTree as St, RendererStats as T, LogManager as Tt, BASE_LOCALE as U, SaveStore as V, LocaleTables as W, SfxEngine as X, InputMap as Y, SfxPlayOptions as Z, mergeStaticSignals as _, PlayMusicOptions as _t, clearBehaviors as a, Vec3 as at, Engine as b, Node as bt, registeredBehaviors as c, SFX_PRESETS as ct, PropSchema as d, SfxWave as dt, VoicePreset as et, clearRegistry as f, SynthOptions as ft, getNodeType as g, MusicTrack as gt, getNodeSignals as h, MusicManager as ht, behaviorSignals as i, SpatialParams as it, readDeviceHints as j, SettingsValues as k, NodeCtor as l, SFX_PRESET_NAMES as lt, getNodeSchema as m, MusicBackend as mt, BehaviorCtor as n, ROLLOFF_MODELS as nt, getBehavior as o, spatialGain as ot, createNode as p, synthSfx as pt, suggestLocale as q, behaviorSchema as r, RolloffModel as rt, registerBehavior as s, spatialPan as st, Behavior as t, Listener as tt, PropDef as u, SfxParams as ut, registerNode as v, AudioBuses as vt, GameStats as w, LogLevel as wt, EngineOptions as x, NodeLifecycle as xt, registeredTypes as y, BusName as yt, captureBehaviors as z };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import {
|
|
3
|
-
import { h as Engine, m as AudioPlayer } from "./register-
|
|
2
|
+
import { h as diagnose, n as loadScene, o as computeViewport, s as resolveViewport, x as registerBehavior } from "./loader-Mig5fY4n.js";
|
|
3
|
+
import { h as Engine, m as AudioPlayer } from "./register-BFLg0-_i.js";
|
|
4
4
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
5
5
|
import { i as resolveRendering, n as attachTouchControls } from "./touch-BoNg_MnF.js";
|
|
6
6
|
import { i as openBundledEditor, n as crossFade, r as devServerLibrary, t as teardown } from "./teardown-ByzfDPyu.js";
|
|
7
|
-
import { n as registerGameplayBehaviors } from "./gameplay-
|
|
8
|
-
import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-
|
|
7
|
+
import { n as registerGameplayBehaviors } from "./gameplay-BvhcQbfJ.js";
|
|
8
|
+
import { g as PhysicsBody2D, n as UILayer, t as registerNodes2D, u as Camera2D, y as Node2D } from "./register-DSmIRAf7.js";
|
|
9
9
|
import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
|
|
10
|
-
import { n as enablePhysics2D } from "./physics-2d-
|
|
10
|
+
import { n as enablePhysics2D } from "./physics-2d-DqAclql-.js";
|
|
11
11
|
import { Box3, BufferAttribute, BufferGeometry, Color, LineBasicMaterial, LineSegments, LinearFilter, NearestFilter, OrthographicCamera, Raycaster, SRGBColorSpace, Scene, TextureLoader, Vector2, Vector3, WebGLRenderer } from "three";
|
|
12
12
|
//#region src/2d/assets.ts
|
|
13
13
|
/**
|
|
@@ -545,6 +545,7 @@ async function createGame2D(opts) {
|
|
|
545
545
|
if (keyboard) engine.input.attachKeyboard(keyboard);
|
|
546
546
|
cleanups.push(wireAudioUnlock(engine, scene.root, opts.canvas));
|
|
547
547
|
cleanups.push(engine.settings.bindAudio(engine.audio));
|
|
548
|
+
cleanups.push(engine.settings.bindLocale(engine.locale));
|
|
548
549
|
const touchMode = opts.touch ?? "auto";
|
|
549
550
|
if (touchMode !== false) {
|
|
550
551
|
const host = opts.touchContainer ?? opts.canvas.parentElement ?? null;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import {
|
|
3
|
-
import { h as Engine, m as AudioPlayer } from "./register-
|
|
2
|
+
import { h as diagnose, n as loadScene, x as registerBehavior } from "./loader-Mig5fY4n.js";
|
|
3
|
+
import { h as Engine, m as AudioPlayer } from "./register-BFLg0-_i.js";
|
|
4
4
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
5
5
|
import { i as resolveRendering, n as attachTouchControls } from "./touch-BoNg_MnF.js";
|
|
6
6
|
import { a as poseFromRenderer, i as openBundledEditor, n as crossFade, r as devServerLibrary, t as teardown } from "./teardown-ByzfDPyu.js";
|
|
7
|
-
import { B as createCausticsQuad, F as PhysicsBody3D, R as Node3D, n as registerGameplayBehaviors } from "./gameplay-
|
|
7
|
+
import { B as createCausticsQuad, F as PhysicsBody3D, R as Node3D, n as registerGameplayBehaviors } from "./gameplay-BvhcQbfJ.js";
|
|
8
8
|
import { t as debugSources } from "./debug-draw-BM3DsvtT.js";
|
|
9
|
-
import { E as Camera3D, g as ModelInstance3D, n as registerNodes3D, t as resolveEnvironmentHdri, v as DirectionalLight3D } from "./environment-presets-
|
|
10
|
-
import { n as enablePhysics3D } from "./physics-3d-
|
|
9
|
+
import { E as Camera3D, U as TextureCache3D, g as ModelInstance3D, n as registerNodes3D, t as resolveEnvironmentHdri, v as DirectionalLight3D } from "./environment-presets-TAGvmM3z.js";
|
|
10
|
+
import { n as enablePhysics3D } from "./physics-3d-DxBH4sIF.js";
|
|
11
11
|
import { ACESFilmicToneMapping, AmbientLight, Box3, BufferAttribute, BufferGeometry, Color, DepthTexture, EquirectangularReflectionMapping, FloatType, Fog, HalfFloatType, LineBasicMaterial, LineSegments, Matrix4, Mesh, PCFShadowMap, PMREMGenerator, PerspectiveCamera, PlaneGeometry, Quaternion, Raycaster, Scene, ShaderMaterial, Vector2, Vector3, WebGLRenderTarget, WebGLRenderer } from "three";
|
|
12
12
|
import { VRMLoaderPlugin, VRMUtils } from "@pixiv/three-vrm";
|
|
13
13
|
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
|
@@ -32,6 +32,12 @@ var AssetStore3D = class {
|
|
|
32
32
|
animationKeys = /* @__PURE__ */ new Map();
|
|
33
33
|
loader;
|
|
34
34
|
/**
|
|
35
|
+
* Shared textures, store-scoped like everything else here. Instance state, not
|
|
36
|
+
* a module map: two games on one page must not share a decode cache, and the
|
|
37
|
+
* repo has one rule about module-level engine state.
|
|
38
|
+
*/
|
|
39
|
+
textures;
|
|
40
|
+
/**
|
|
35
41
|
* `engine` is optional only so a bare store still constructs in tests. Give it
|
|
36
42
|
* one and every load failure lands in `engine.log` — where the debug overlay,
|
|
37
43
|
* `runScript()` and any agent tool can see it. Without it a 404'd GLB was a
|
|
@@ -41,6 +47,7 @@ var AssetStore3D = class {
|
|
|
41
47
|
this.engine = engine;
|
|
42
48
|
this.loader = new GLTFLoader();
|
|
43
49
|
this.loader.register((parser) => new VRMLoaderPlugin(parser));
|
|
50
|
+
this.textures = new TextureCache3D({ diagnostics: engine ? { warn: (...parts) => diagnose(engine, "warn", ...parts) } : null });
|
|
44
51
|
}
|
|
45
52
|
/**
|
|
46
53
|
* Every asset that failed, with the reason — the answer to "why is my model
|
|
@@ -48,6 +55,11 @@ var AssetStore3D = class {
|
|
|
48
55
|
*/
|
|
49
56
|
errors() {
|
|
50
57
|
const out = [];
|
|
58
|
+
for (const { url, error } of this.textures.errors()) out.push({
|
|
59
|
+
ref: url,
|
|
60
|
+
url,
|
|
61
|
+
error
|
|
62
|
+
});
|
|
51
63
|
for (const [ref, url] of this.modelKeys) {
|
|
52
64
|
const entry = this.models.get(url);
|
|
53
65
|
if (entry?.error) out.push({
|
|
@@ -144,6 +156,7 @@ var AssetStore3D = class {
|
|
|
144
156
|
return entry;
|
|
145
157
|
}
|
|
146
158
|
dispose() {
|
|
159
|
+
this.textures.dispose();
|
|
147
160
|
for (const entry of this.models.values()) entry.scene?.traverse((obj) => {
|
|
148
161
|
const mesh = obj;
|
|
149
162
|
mesh.geometry?.dispose();
|
|
@@ -1419,7 +1432,7 @@ function walk(node, parentObj, state) {
|
|
|
1419
1432
|
return;
|
|
1420
1433
|
}
|
|
1421
1434
|
if (obj.parent !== parentObj) parentObj.add(obj);
|
|
1422
|
-
node._syncObject3D(state.alpha);
|
|
1435
|
+
node._syncObject3D(state.alpha, state.assets ?? null);
|
|
1423
1436
|
if (state.assets && node instanceof ModelInstance3D) node._syncModel(state.assets);
|
|
1424
1437
|
if (typeof node._onRender3D === "function") state.renderHooks.push(node);
|
|
1425
1438
|
if (state.sunDirection) {
|
|
@@ -2263,6 +2276,7 @@ async function createGame3D(opts) {
|
|
|
2263
2276
|
});
|
|
2264
2277
|
} catch {}
|
|
2265
2278
|
cleanups.push(engine.settings.bindAudio(engine.audio));
|
|
2279
|
+
cleanups.push(engine.settings.bindLocale(engine.locale));
|
|
2266
2280
|
await report(1, "ready");
|
|
2267
2281
|
function disposeGame() {
|
|
2268
2282
|
teardown([
|
package/dist/debug.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { T as RendererStats, b as Engine, wt as LogLevel } from "./behavior-CGSCWOHB.js";
|
|
2
2
|
|
|
3
3
|
//#region src/debug/panel.d.ts
|
|
4
4
|
/** Minimal document surface the overlay needs (injectable for tests). */
|