incanto 0.65.0 → 0.67.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/bin/_behaviors-loader.mjs +87 -0
- package/bin/incanto-editor.mjs +39 -2
- package/bin/incanto-feel.mjs +4 -1
- package/bin/incanto-frame.mjs +52 -3
- package/bin/incanto-logs.mjs +33 -3
- package/bin/incanto-multiplay.mjs +50 -1
- package/bin/incanto-play.mjs +3 -1
- package/bin/incanto-playtest.mjs +3 -25
- package/bin/incanto-verify.mjs +15 -2
- package/dist/2d.d.ts +76 -4
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +213 -14
- package/dist/3d.js +6 -6
- package/dist/{audio-player-BrEHwbK1.d.ts → audio-player-BNUHo0JF.d.ts} +1 -1
- package/dist/{behavior-CmMuQ6CL.d.ts → behavior-ZnfzKDo3.d.ts} +58 -1
- package/dist/{create-game-DIyh5Zy9.js → create-game-DMetd3vR.js} +14 -8
- package/dist/{create-game-W2xzTJhs.js → create-game-FAXdHta3.js} +138 -11
- package/dist/debug.d.ts +1 -1
- package/dist/debug.js +3 -2
- package/dist/{duplicate-CqSAtdrh.js → duplicate-B8QnX2-r.js} +1 -1
- package/dist/editor.js +7 -2
- package/dist/{environment-presets-BvHs6HEk.js → environment-presets-C-5g2pMh.js} +113 -9
- package/dist/{gameplay-D-H986A4.js → gameplay-CGSv-9XR.js} +295 -12
- package/dist/gameplay.d.ts +102 -6
- package/dist/gameplay.js +2 -2
- package/dist/index.d.ts +50 -6
- package/dist/index.js +7 -6
- package/dist/{loader-CTB441z4.d.ts → loader-DyT30ArB.d.ts} +1 -1
- package/dist/{loader-lQDCwNag.js → loader-KZESOZmE.js} +148 -1
- package/dist/net.d.ts +2 -2
- package/dist/net.js +1 -1
- package/dist/{physics-2d-CmnunaR2.js → physics-2d-_B6zj3zU.js} +57 -4
- package/dist/{physics-3d-DULB1lD1.js → physics-3d-i1IeAY0F.js} +74 -5
- package/dist/{quiet-rapier-BAJ4K94N.js → quiet-rapier-C6fW4zcW.js} +14 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-CgeR4ckF.js → register-Cs53CY7s.js} +110 -16
- package/dist/{register-DO5_Qp0Q.js → register-vIxU3Xbv.js} +38 -4
- package/dist/rendering-options-ByAdi8PF.js +184 -0
- package/dist/{replay-CEw2xAhG.js → replay-BkpDV51O.js} +4 -3
- package/dist/{replay-CThvwkcQ.d.ts → replay-D0GcEHYU.d.ts} +1 -1
- package/dist/{split-screen-DxZzC1nm.d.ts → split-screen-DD5eYIgW.d.ts} +2 -2
- package/dist/{split-screen-gb0c3xdb.js → split-screen-DS8RaUV_.js} +2 -2
- package/dist/{src-DYffzHbd.js → src-BLlsGJW9.js} +1 -1
- package/dist/{teardown-BordHL9o.js → teardown-LB9cTnXJ.js} +2 -2
- package/dist/{test-CXZuXs8k.js → test-81uZ967a.js} +127 -24
- package/dist/test.d.ts +7 -5
- package/dist/test.js +2 -2
- package/dist/{touch-DESwnpOc.js → touch-DEAmqGdf.js} +30 -186
- package/dist/vite.d.ts +0 -7
- package/dist/vite.js +13 -3
- package/editor/assets/{agent8-DO5qzIOD.js → agent8-C2jtNKbx.js} +1 -1
- package/editor/assets/debug-DoU3KeqS.js +3 -0
- package/editor/assets/{index-CqEUVaPN.js → index-CNqHojcC.js} +92 -92
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +20 -1
- package/skills/incanto-building-3d-games.md +19 -0
- package/skills/incanto-editor.md +5 -1
- package/skills/incanto-environment.md +2 -2
- package/skills/incanto-gameplay-behaviors.md +86 -7
- package/skills/incanto-hud.md +14 -1
- package/skills/incanto-multiplayer.md +14 -0
- package/skills/incanto-node-reference.md +60 -2
- package/skills/incanto-performance.md +35 -0
- package/skills/incanto-physics-and-input.md +69 -0
- package/skills/incanto-playtesting.md +15 -1
- package/skills/incanto-save-slots.md +21 -1
- package/skills/incanto-verifying-your-game.md +19 -0
- package/skills/incanto-web-integration.md +24 -3
- package/skills/incanto-your-first-game.md +4 -4
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/beacon-isle-3d/src/game.scene.json +38 -19
- package/templates-app/platformer-2d/package.json +1 -1
- package/templates-app/platformer-2d/src/game.scene.json +99 -53
- package/templates-app/star-survivor/package.json +1 -1
- package/templates-app/star-survivor/src/game.scene.json +31 -17
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/tps-3d/src/game.scene.json +14 -8
- package/templates-app/village-quest-3d/package.json +1 -1
- package/templates-app/village-quest-3d/src/grove.scene.json +20 -11
- package/templates-app/village-quest-3d/src/village.scene.json +10 -7
- package/editor/assets/debug-DntG9CVD.js +0 -3
- package/templates-app/beacon-isle-3d/coverage.json +0 -9
- package/templates-app/platformer-2d/coverage.json +0 -5
- package/templates-app/star-survivor/coverage.json +0 -5
- package/templates-app/tps-3d/coverage.json +0 -5
- package/templates-app/village-quest-3d/coverage.json +0 -9
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { $ as T_PREFIX, A as SettingsValues, At as BusName, B as captureBehaviors, C as EngineStats, Ct as sfxDuration, Dt as MusicTrack, E as DeviceHints, Et as MusicManager, F as BehaviorState, Ft as LogLevel, G as ORDER_GROUP_BASE, H as savesWithoutUid, I as RestoreReport, It as LogManager, J as effectiveOrder, K as OrderGroup, L as SaveSlot, Lt as Signal, M as readDeviceHints, Mt as NodeLifecycle, N as suggestQuality, Nt as SceneTree, O as QualityTier, Ot as PlayMusicOptions, P as Scene, Pt as LogEntry, Q as Localization, R as SaveSlots, Rt as SignalListener, S as Scheduler, St as SynthOptions, T as RendererStats, Tt as MusicBackend, U as SaveStore, V as restoreBehaviors, W as createSaveStore, X as BASE_LOCALE, Y as resolveOrderGroups, Z as LocaleTables, _ as mergeStaticSignals, _t as spatialPan, a as clearBehaviors, at as EffectLog, b as Engine, bt as SfxParams, c as registeredBehaviors, ct as isAudioContextAvailable, d as PropSchema, dt as Listener, et as suggestLocale, f as clearRegistry, ft as ROLLOFF_MODELS, g as getNodeType, gt as spatialGain, h as getNodeSignals, ht as Vec3, i as behaviorSignals, it as EffectKind, j as qualityEnvironment, jt as Node, k as Settings, kt as AudioBuses, l as NodeCtor, lt as Voice, m as getNodeSchema, mt as SpatialParams, n as BehaviorCtor, nt as InputMap, o as getBehavior, ot as SfxEngine, p as createNode, pt as RolloffModel, q as OrderGroupTable, r as behaviorSchema, rt as EffectEvent, s as registerBehavior, st as SfxPlayOptions, t as Behavior, tt as translationKey, u as PropDef, ut as VoicePreset, v as registerNode, vt as SFX_PRESETS, w as GameStats, wt as synthSfx, x as EngineOptions, xt as SfxWave, y as registeredTypes, yt as SFX_PRESET_NAMES, z as behaviorsWithoutSave } from "./behavior-
|
|
1
|
+
import { $ as T_PREFIX, A as SettingsValues, At as BusName, B as captureBehaviors, C as EngineStats, Ct as sfxDuration, Dt as MusicTrack, E as DeviceHints, Et as MusicManager, F as BehaviorState, Ft as LogLevel, G as ORDER_GROUP_BASE, H as savesWithoutUid, I as RestoreReport, It as LogManager, J as effectiveOrder, K as OrderGroup, L as SaveSlot, Lt as Signal, M as readDeviceHints, Mt as NodeLifecycle, N as suggestQuality, Nt as SceneTree, O as QualityTier, Ot as PlayMusicOptions, P as Scene, Pt as LogEntry, Q as Localization, R as SaveSlots, Rt as SignalListener, S as Scheduler, St as SynthOptions, T as RendererStats, Tt as MusicBackend, U as SaveStore, V as restoreBehaviors, W as createSaveStore, X as BASE_LOCALE, Y as resolveOrderGroups, Z as LocaleTables, _ as mergeStaticSignals, _t as spatialPan, a as clearBehaviors, at as EffectLog, b as Engine, bt as SfxParams, c as registeredBehaviors, ct as isAudioContextAvailable, d as PropSchema, dt as Listener, et as suggestLocale, f as clearRegistry, ft as ROLLOFF_MODELS, g as getNodeType, gt as spatialGain, h as getNodeSignals, ht as Vec3, i as behaviorSignals, it as EffectKind, j as qualityEnvironment, jt as Node, k as Settings, kt as AudioBuses, l as NodeCtor, lt as Voice, m as getNodeSchema, mt as SpatialParams, n as BehaviorCtor, nt as InputMap, o as getBehavior, ot as SfxEngine, p as createNode, pt as RolloffModel, q as OrderGroupTable, r as behaviorSchema, rt as EffectEvent, s as registerBehavior, st as SfxPlayOptions, t as Behavior, tt as translationKey, u as PropDef, ut as VoicePreset, v as registerNode, vt as SFX_PRESETS, w as GameStats, wt as synthSfx, x as EngineOptions, xt as SfxWave, y as registeredTypes, yt as SFX_PRESET_NAMES, z as behaviorsWithoutSave } from "./behavior-ZnfzKDo3.js";
|
|
2
2
|
import { a as Rng, c as JsonValue, d as jsonKind, i as SceneJson, l as jsonClone, n as NodeJson, o as JsonKind, r as SCENE_FORMAT, s as JsonObject, t as ConnectionJson, u as jsonEquals } from "./schema-CFeioQRE.js";
|
|
3
|
-
import { n as loadScene, t as LoadSceneOptions } from "./loader-
|
|
3
|
+
import { n as loadScene, t as LoadSceneOptions } from "./loader-DyT30ArB.js";
|
|
4
4
|
import { i as resolveFrames, n as AnimationEntry, r as resolveAnimation, t as AnimationDef } from "./sprite-animation-CMr6f1K2.js";
|
|
5
|
-
import { a as ParticleSimConfig, i as ParticleSim, n as AudioPlayer, o as ParticleView, t as AudioElementLike } from "./audio-player-
|
|
5
|
+
import { a as ParticleSimConfig, i as ParticleSim, n as AudioPlayer, o as ParticleView, t as AudioElementLike } from "./audio-player-BNUHo0JF.js";
|
|
6
6
|
import { i as gridFromRows, n as PathGrid, r as findPath, t as FindPathOptions } from "./pathfinding-_fGrCFmH.js";
|
|
7
|
-
import { a as startRecording, c as IncantoErrorDetails, i as replay, l as auditScene, n as ReplayEvent, o as IncantoError, r as ReplayJson, s as IncantoErrorCode, t as Recorder } from "./replay-
|
|
7
|
+
import { a as startRecording, c as IncantoErrorDetails, i as replay, l as auditScene, n as ReplayEvent, o as IncantoError, r as ReplayJson, s as IncantoErrorCode, t as Recorder } from "./replay-D0GcEHYU.js";
|
|
8
8
|
|
|
9
9
|
//#region src/core/audio/crossfade.d.ts
|
|
10
10
|
/**
|
|
@@ -313,8 +313,24 @@ declare class HudLayer extends Node {
|
|
|
313
313
|
* play resumes.
|
|
314
314
|
*/
|
|
315
315
|
private stepFocus;
|
|
316
|
-
/**
|
|
317
|
-
|
|
316
|
+
/**
|
|
317
|
+
* Focusable widgets under this layer, in tree order — **on screen only**.
|
|
318
|
+
*
|
|
319
|
+
* This filtered on each widget's OWN `visible` and recursed into hidden
|
|
320
|
+
* containers anyway, so arrow-keys walked into closed panels and Enter really
|
|
321
|
+
* pressed what it found. Measured on a title screen: 23 focus stops, 21 of
|
|
322
|
+
* them under an ancestor with `visible = false`, and
|
|
323
|
+
*
|
|
324
|
+
* gold before = 150 -> Enter on the hidden Shop/Row0/Buy -> gold 120,
|
|
325
|
+
* screen still 'title'
|
|
326
|
+
*
|
|
327
|
+
* The layer's own visibility was not a gate either. It is now: a HUD nobody
|
|
328
|
+
* can see has no ring, and a subtree nobody can see is not walked.
|
|
329
|
+
*
|
|
330
|
+
* PUBLIC, because a game that wants to know what its ring contains could not
|
|
331
|
+
* ask — and "why is focus on that" is exactly the question this answers.
|
|
332
|
+
*/
|
|
333
|
+
focusables(): HudWidgetBase[];
|
|
318
334
|
/** Move focus to a widget by hand — opening a menu should start somewhere. */
|
|
319
335
|
focus(widget: HudWidgetBase | null): void;
|
|
320
336
|
/** @internal Widgets mount into per-anchor flex columns. */
|
|
@@ -377,6 +393,11 @@ declare abstract class HudWidgetBase extends Node {
|
|
|
377
393
|
private host;
|
|
378
394
|
override onExitTree(): void;
|
|
379
395
|
override update(_dt: number): void;
|
|
396
|
+
/**
|
|
397
|
+
* The display this widget's own css asks for while shown — `grid` for a grid
|
|
398
|
+
* panel, `flex` for a row, `''` for a widget that never says.
|
|
399
|
+
*/
|
|
400
|
+
private _shownDisplay;
|
|
380
401
|
/** Which anchor slot this widget was mounted into. */
|
|
381
402
|
private mountedAnchor;
|
|
382
403
|
/**
|
|
@@ -607,6 +628,29 @@ declare class UiDialogue extends HudWidgetBase {
|
|
|
607
628
|
/** Queue a line; optional `choices` renders buttons after the line types out. */
|
|
608
629
|
say(speaker: string, text: string, choices?: string[]): void;
|
|
609
630
|
/**
|
|
631
|
+
* A dialogue takes focus while it is up.
|
|
632
|
+
*
|
|
633
|
+
* It paints a focus ring and the generated reference advertises the prop, and
|
|
634
|
+
* it was inert: choices are raw DOM buttons inside the widget, so
|
|
635
|
+
* `HudLayer.focusables()` can never see them, and this class — unlike
|
|
636
|
+
* `UiButton` a hundred lines above it — never overrode `_activate`. Eleven
|
|
637
|
+
* keys and eight pad codes did nothing to a waiting choice while the same
|
|
638
|
+
* Enter pressed a sibling button. Native Tab worked, and is closed by any
|
|
639
|
+
* game that binds Tab; a pad has no Tab at all.
|
|
640
|
+
*/
|
|
641
|
+
override focusable: boolean;
|
|
642
|
+
/** Which choice the keyboard/pad is on. Reset by every new line. */
|
|
643
|
+
private cursor;
|
|
644
|
+
/**
|
|
645
|
+
* Enter / A, and left / right between the options.
|
|
646
|
+
*
|
|
647
|
+
* `dir` is 0 for "activate" and ±1 for a nudge, the same shape `UiSelect`
|
|
648
|
+
* uses — so one key layout drives a menu, a slider and a conversation.
|
|
649
|
+
*/
|
|
650
|
+
override _activate(dir: number): void;
|
|
651
|
+
/** Show which option the keyboard is on — a ring nobody can see is not one. */
|
|
652
|
+
private paintCursor;
|
|
653
|
+
/**
|
|
610
654
|
* Player pressed "next": mid-typing reveals the whole line; a fully-typed
|
|
611
655
|
* line without choices advances (choice lines wait for `choose`).
|
|
612
656
|
*/
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { A as describeRefProblem, B as registeredBehaviors, C as ORDER_GROUP_BASE, E as InputMap, F as behaviorSchema, I as behaviorSignals, L as clearBehaviors, M as resolveRefInJson, N as parseNodePath, O as Node, P as Behavior, R as getBehavior, S as createSaveStore, T as resolveOrderGroups, V as Signal, _ as SaveSlots, a as serializeNode, b as restoreBehaviors, d as computeViewport, f as resolveViewport, g as resolveConstants, h as isConstRef, i as Scene, j as nodeRefWarnings, m as CONST_REF_KEY, n as loadScene, o as SCENE_FORMAT, p as SceneTree, v as behaviorsWithoutSave, w as effectiveOrder, x as savesWithoutUid, y as captureBehaviors, z as registerBehavior } from "./loader-
|
|
2
|
-
import { A as UiBanner, B as isAudioContextAvailable, D as EffectLog, E as LogManager, F as T_PREFIX, G as SFX_PRESET_NAMES, H as spatialGain, I as suggestLocale, J as MusicManager, K as sfxDuration, M as UiText, N as BASE_LOCALE, O as HudLayer, P as Localization, Q as AudioBuses, R as translationKey, S as qualityEnvironment, T as suggestQuality, U as spatialPan, V as ROLLOFF_MODELS, W as SFX_PRESETS, X as crossfadeGains, Y as WebAudioMusicBackend, Z as fadeGain, _ as AudioPlayer, a as UiMuteToggle, b as Settings, c as UiRenderScaleSelect, d as UiToggle, f as UiVolumeSlider, g as UiDialogue, h as UiButton, i as UiLanguageSelect, j as UiBar, l as UiSelect, m as Respawn, n as UiFrameCapSelect, o as UiPanel, p as Timer, q as synthSfx, r as UiImage, s as UiQualitySelect, t as registerCoreNodes, u as UiSlider, v as Engine, w as readDeviceHints, z as SfxEngine } from "./register-
|
|
1
|
+
import { A as describeRefProblem, B as registeredBehaviors, C as ORDER_GROUP_BASE, E as InputMap, F as behaviorSchema, I as behaviorSignals, L as clearBehaviors, M as resolveRefInJson, N as parseNodePath, O as Node, P as Behavior, R as getBehavior, S as createSaveStore, T as resolveOrderGroups, V as Signal, _ as SaveSlots, a as serializeNode, b as restoreBehaviors, d as computeViewport, f as resolveViewport, g as resolveConstants, h as isConstRef, i as Scene, j as nodeRefWarnings, m as CONST_REF_KEY, n as loadScene, o as SCENE_FORMAT, p as SceneTree, v as behaviorsWithoutSave, w as effectiveOrder, x as savesWithoutUid, y as captureBehaviors, z as registerBehavior } from "./loader-KZESOZmE.js";
|
|
2
|
+
import { A as UiBanner, B as isAudioContextAvailable, D as EffectLog, E as LogManager, F as T_PREFIX, G as SFX_PRESET_NAMES, H as spatialGain, I as suggestLocale, J as MusicManager, K as sfxDuration, M as UiText, N as BASE_LOCALE, O as HudLayer, P as Localization, Q as AudioBuses, R as translationKey, S as qualityEnvironment, T as suggestQuality, U as spatialPan, V as ROLLOFF_MODELS, W as SFX_PRESETS, X as crossfadeGains, Y as WebAudioMusicBackend, Z as fadeGain, _ as AudioPlayer, a as UiMuteToggle, b as Settings, c as UiRenderScaleSelect, d as UiToggle, f as UiVolumeSlider, g as UiDialogue, h as UiButton, i as UiLanguageSelect, j as UiBar, l as UiSelect, m as Respawn, n as UiFrameCapSelect, o as UiPanel, p as Timer, q as synthSfx, r as UiImage, s as UiQualitySelect, t as registerCoreNodes, u as UiSlider, v as Engine, w as readDeviceHints, z as SfxEngine } from "./register-Cs53CY7s.js";
|
|
3
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
4
4
|
import { t as Rng } from "./rng-DP-SR7eg.js";
|
|
5
5
|
import { n as jsonEquals, r as jsonKind, t as jsonClone } from "./json-CwwhxQgb.js";
|
|
6
6
|
import { a as getNodeSignals, c as mergeStaticSignals, i as getNodeSchema, l as registerNode, n as clearRegistry, o as getNodeType, r as createNode, u as registeredTypes } from "./registry-CF70EArN.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { i as joystickVector, n as attachTouchControls, t as TouchControls } from "./touch-DEAmqGdf.js";
|
|
8
|
+
import { n as startRecording, r as auditScene, t as replay } from "./replay-BkpDV51O.js";
|
|
9
|
+
import { i as parseDrive, n as logReport, r as logText, t as resolveRendering } from "./rendering-options-ByAdi8PF.js";
|
|
9
10
|
import { t as createNoise2D } from "./noise-CGUMx44x.js";
|
|
10
11
|
import { a as PARTICLE_PRESETS, i as ParticleSim, n as resolveFrames, o as PARTICLE_PRESET_NAMES, s as applyParticlePreset, t as resolveAnimation } from "./sprite-animation-CY-mrr1L.js";
|
|
11
|
-
import { a as preloadUrls, i as preloadSceneAssets, n as newUid, o as findPath, r as assetUrls, s as gridFromRows, t as VERSION } from "./src-
|
|
12
|
-
import { t as duplicateNode } from "./duplicate-
|
|
12
|
+
import { a as preloadUrls, i as preloadSceneAssets, n as newUid, o as findPath, r as assetUrls, s as gridFromRows, t as VERSION } from "./src-BLlsGJW9.js";
|
|
13
|
+
import { t as duplicateNode } from "./duplicate-B8QnX2-r.js";
|
|
13
14
|
export { AudioBuses, AudioPlayer, BASE_LOCALE, Behavior, CONST_REF_KEY, EffectLog, Engine, HudLayer, IncantoError, InputMap, Localization, LogManager, MusicManager, Node, ORDER_GROUP_BASE, PARTICLE_PRESETS, PARTICLE_PRESET_NAMES, ParticleSim, ROLLOFF_MODELS, Respawn, Rng, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, SaveSlots, Scene, SceneTree, Settings, SfxEngine, Signal, T_PREFIX, Timer, TouchControls, UiBanner, UiBar, UiButton, UiDialogue, UiFrameCapSelect, UiImage, UiLanguageSelect, UiMuteToggle, UiPanel, UiQualitySelect, UiRenderScaleSelect, UiSelect, UiSlider, UiText, UiToggle, UiVolumeSlider, VERSION, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, auditScene, behaviorSchema, behaviorSignals, behaviorsWithoutSave, captureBehaviors, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, createSaveStore, crossfadeGains, describeRefProblem, duplicateNode, effectiveOrder, fadeGain, findPath, getBehavior, getNodeSchema, getNodeSignals, getNodeType, gridFromRows, isAudioContextAvailable, isConstRef, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, logReport, logText, mergeStaticSignals, newUid, nodeRefWarnings, parseDrive, parseNodePath, preloadSceneAssets, preloadUrls, qualityEnvironment, readDeviceHints, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, replay, resolveAnimation, resolveConstants, resolveFrames, resolveOrderGroups, resolveRefInJson, resolveRendering, resolveViewport, restoreBehaviors, savesWithoutUid, serializeNode, sfxDuration, spatialGain, spatialPan, startRecording, suggestLocale, suggestQuality, synthSfx, translationKey };
|
|
@@ -20,6 +20,17 @@ var Signal = class {
|
|
|
20
20
|
disconnectAll() {
|
|
21
21
|
this.connections.length = 0;
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* How many listeners are attached.
|
|
25
|
+
*
|
|
26
|
+
* A leak is a SLOPE, and a game had no way to see one: every check in this
|
|
27
|
+
* engine is a snapshot. A handler connected per frame and never disconnected,
|
|
28
|
+
* or a `once` that armed and never fired, accumulates invisibly and is
|
|
29
|
+
* perfectly healthy for the first thirty seconds of every playthrough.
|
|
30
|
+
*/
|
|
31
|
+
get count() {
|
|
32
|
+
return this.connections.length;
|
|
33
|
+
}
|
|
23
34
|
emit(...args) {
|
|
24
35
|
const snapshot = [...this.connections];
|
|
25
36
|
for (const conn of snapshot) {
|
|
@@ -652,6 +663,16 @@ var Node = class {
|
|
|
652
663
|
off(signal, fn) {
|
|
653
664
|
this._signals.get(signal)?.disconnect(fn);
|
|
654
665
|
}
|
|
666
|
+
/**
|
|
667
|
+
* How many listeners this node has on `signal` — 0 for one nobody watches.
|
|
668
|
+
*
|
|
669
|
+
* The engine's own leak test reads it, and so can a game: a handler connected
|
|
670
|
+
* every frame and never disconnected is invisible to every other check here,
|
|
671
|
+
* because every other check is a snapshot and a leak is a slope.
|
|
672
|
+
*/
|
|
673
|
+
listenerCount(signal) {
|
|
674
|
+
return this._signals.get(signal)?.count ?? 0;
|
|
675
|
+
}
|
|
655
676
|
emit(signal, ...args) {
|
|
656
677
|
this.assertDeclared(signal);
|
|
657
678
|
this._signals.get(signal)?.emit(...args);
|
|
@@ -1674,6 +1695,7 @@ const FREED_KEY = "#freed";
|
|
|
1674
1695
|
function captureBehaviors(root, source) {
|
|
1675
1696
|
const state = {};
|
|
1676
1697
|
const unaddressable = [];
|
|
1698
|
+
const unrecordable = [];
|
|
1677
1699
|
let saveable = 0;
|
|
1678
1700
|
const walk = (node) => {
|
|
1679
1701
|
const behavior = node.behavior;
|
|
@@ -1698,12 +1720,45 @@ function captureBehaviors(root, source) {
|
|
|
1698
1720
|
mark(root);
|
|
1699
1721
|
const freed = authoredUids(source).filter((uid) => !alive.has(uid));
|
|
1700
1722
|
if (freed.length > 0) state[FREED_KEY] = freed;
|
|
1723
|
+
unrecordable.push(...goneWithoutUid(source, root));
|
|
1701
1724
|
}
|
|
1702
1725
|
return {
|
|
1703
1726
|
state,
|
|
1704
1727
|
unaddressable,
|
|
1705
|
-
saveable
|
|
1728
|
+
saveable,
|
|
1729
|
+
unrecordable
|
|
1730
|
+
};
|
|
1731
|
+
}
|
|
1732
|
+
/**
|
|
1733
|
+
* Authored nodes this run CONSUMED that the ledger cannot record.
|
|
1734
|
+
*
|
|
1735
|
+
* `#freed` is authored uids minus alive uids, so a node with no uid can never
|
|
1736
|
+
* enter it — it is silently omitted, and on load it comes back. Every collected
|
|
1737
|
+
* coin in a scene without uids returns, while the score that counted it does
|
|
1738
|
+
* not, and nothing says a word.
|
|
1739
|
+
*
|
|
1740
|
+
* A uid is the only stable handle (it survives a rename and a reparent), so the
|
|
1741
|
+
* engine must not guess a replacement — a saved PATH would point at whatever
|
|
1742
|
+
* node moved into that slot after the next edit. What it can do is notice, by
|
|
1743
|
+
* walking the authored tree and asking the live one whether each node is still
|
|
1744
|
+
* there. Reported, not repaired.
|
|
1745
|
+
*/
|
|
1746
|
+
function goneWithoutUid(source, root) {
|
|
1747
|
+
const out = [];
|
|
1748
|
+
const walk = (node, path) => {
|
|
1749
|
+
if (typeof node !== "object" || node === null) return;
|
|
1750
|
+
const n = node;
|
|
1751
|
+
const name = typeof n.name === "string" ? n.name : "";
|
|
1752
|
+
const here = path === "" ? name : `${path}/${name}`;
|
|
1753
|
+
if (!(typeof n.uid === "string" && n.uid !== "") && here !== "" && path !== "") {
|
|
1754
|
+
const live = root.getNodeOrNull(here.slice(here.indexOf("/") + 1));
|
|
1755
|
+
const leaving = live ? live.tree?._isQueuedFree(live) ?? false : false;
|
|
1756
|
+
if (!live || leaving) out.push(here);
|
|
1757
|
+
}
|
|
1758
|
+
if (Array.isArray(n.children)) for (const child of n.children) walk(child, here);
|
|
1706
1759
|
};
|
|
1760
|
+
walk(source.root, "");
|
|
1761
|
+
return out;
|
|
1707
1762
|
}
|
|
1708
1763
|
/** Every uid the scene FILE declares, in document order. */
|
|
1709
1764
|
function authoredUids(source) {
|
|
@@ -3004,6 +3059,7 @@ function buildNodeJson(json, opts) {
|
|
|
3004
3059
|
return buildNode(json, opts, [], "", void 0);
|
|
3005
3060
|
}
|
|
3006
3061
|
function validateHeader(json) {
|
|
3062
|
+
rejectUnknownKeys(json, SCENE_KEYS, "the scene", "");
|
|
3007
3063
|
if (json.format !== 1) throw new IncantoError("BAD_FORMAT", `Unsupported scene format ${JSON.stringify(json.format)}; this engine reads format 1.`);
|
|
3008
3064
|
if (json.type !== "scene") throw new IncantoError("BAD_FORMAT", `Expected "type": "scene", got ${JSON.stringify(json.type)}.`);
|
|
3009
3065
|
if (typeof json.name !== "string" || json.name === "") throw new IncantoError("BAD_FORMAT", "Scene \"name\" must be a non-empty string.");
|
|
@@ -3042,9 +3098,100 @@ function withNodeContext(e, path, nj) {
|
|
|
3042
3098
|
});
|
|
3043
3099
|
return e;
|
|
3044
3100
|
}
|
|
3101
|
+
/**
|
|
3102
|
+
* Every key a node object may carry.
|
|
3103
|
+
*
|
|
3104
|
+
* The node object was an OPEN dictionary sitting between a dozen closed ones —
|
|
3105
|
+
* `props`, `environment`, `material`, `network` and the environment's own
|
|
3106
|
+
* sub-blocks all reject an unknown key with a did-you-mean. A node accepted
|
|
3107
|
+
* anything, and the one that costs an author a day is `connections`:
|
|
3108
|
+
*
|
|
3109
|
+
* ```jsonc
|
|
3110
|
+
* { "name": "Room", "type": "Node3D",
|
|
3111
|
+
* "connections": [ … ], // <- silently nothing
|
|
3112
|
+
* "children": [ … ] }
|
|
3113
|
+
* ```
|
|
3114
|
+
*
|
|
3115
|
+
* `wireConnections` is reached from the scene header and from a sub-scene's own
|
|
3116
|
+
* header, and NOTHING reads `nodeJson.connections`. Measured on two scenes
|
|
3117
|
+
* differing only in which brace the same wire sits inside:
|
|
3118
|
+
*
|
|
3119
|
+
* ```
|
|
3120
|
+
* inside Dst.n=0 validateScene.ok=true auditWarnings=0
|
|
3121
|
+
* beside Dst.n=1 validateScene.ok=true auditWarnings=0
|
|
3122
|
+
* none Dst.n=0 validateScene.ok=true auditWarnings=0
|
|
3123
|
+
* ```
|
|
3124
|
+
*
|
|
3125
|
+
* The misplaced wire is bit-for-bit indistinguishable from deleting it. A
|
|
3126
|
+
* builder moved 28 of them into `root`, got `1/1 scene(s) valid` with zero
|
|
3127
|
+
* warnings, and found out sixty seconds later from `incanto-playtest`.
|
|
3128
|
+
*/
|
|
3129
|
+
const NODE_KEYS = [
|
|
3130
|
+
"name",
|
|
3131
|
+
"uid",
|
|
3132
|
+
"type",
|
|
3133
|
+
"instance",
|
|
3134
|
+
"overrides",
|
|
3135
|
+
"groups",
|
|
3136
|
+
"tags",
|
|
3137
|
+
"props",
|
|
3138
|
+
"script",
|
|
3139
|
+
"network",
|
|
3140
|
+
"children"
|
|
3141
|
+
];
|
|
3142
|
+
/** Every key the scene HEADER may carry — closed for the same reason. */
|
|
3143
|
+
const SCENE_KEYS = [
|
|
3144
|
+
"format",
|
|
3145
|
+
"type",
|
|
3146
|
+
"name",
|
|
3147
|
+
"dimension",
|
|
3148
|
+
"viewport",
|
|
3149
|
+
"assets",
|
|
3150
|
+
"constants",
|
|
3151
|
+
"orderGroups",
|
|
3152
|
+
"input",
|
|
3153
|
+
"multiplayer",
|
|
3154
|
+
"environment",
|
|
3155
|
+
"strings",
|
|
3156
|
+
"physics",
|
|
3157
|
+
"root",
|
|
3158
|
+
"connections"
|
|
3159
|
+
];
|
|
3160
|
+
/** Closest known key by edit distance — only a genuinely близкая one. */
|
|
3161
|
+
function nearestKey(key, known) {
|
|
3162
|
+
const lower = key.toLowerCase();
|
|
3163
|
+
let best = null;
|
|
3164
|
+
let bestScore = 3;
|
|
3165
|
+
for (const candidate of known) {
|
|
3166
|
+
let d = 0;
|
|
3167
|
+
const a = lower;
|
|
3168
|
+
const b = candidate.toLowerCase();
|
|
3169
|
+
const rows = Array.from({ length: a.length + 1 }, (_, i) => [i, ...Array(b.length).fill(0)]);
|
|
3170
|
+
for (let j = 0; j <= b.length; j++) rows[0][j] = j;
|
|
3171
|
+
for (let i = 1; i <= a.length; i++) for (let j = 1; j <= b.length; j++) {
|
|
3172
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
3173
|
+
rows[i][j] = Math.min(rows[i - 1][j] + 1, rows[i][j - 1] + 1, rows[i - 1][j - 1] + cost);
|
|
3174
|
+
}
|
|
3175
|
+
d = rows[a.length][b.length];
|
|
3176
|
+
if (d < bestScore) {
|
|
3177
|
+
bestScore = d;
|
|
3178
|
+
best = candidate;
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
return best;
|
|
3182
|
+
}
|
|
3183
|
+
/** Reject a key the shape does not have, the way `props` already does. */
|
|
3184
|
+
function rejectUnknownKeys(obj, known, what, where) {
|
|
3185
|
+
for (const key of Object.keys(obj)) {
|
|
3186
|
+
if (known.includes(key)) continue;
|
|
3187
|
+
const guess = nearestKey(key, known);
|
|
3188
|
+
throw new IncantoError("BAD_FORMAT", `Unknown key "${key}" on ${what}${where ? ` '${where}'` : ""}.` + (guess ? ` Did you mean "${guess}"?` : "") + ` Valid keys: [${known.join(", ")}].` + (key === "connections" ? " Connections live on the SCENE, beside \"root\" — a node cannot carry them, and one placed here does exactly nothing." : ""), { validOptions: [...known] });
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3045
3191
|
function buildNode(nj, opts, stack, parentPath, constants, bands = []) {
|
|
3046
3192
|
const path = `${parentPath}/${nj.name}`;
|
|
3047
3193
|
try {
|
|
3194
|
+
rejectUnknownKeys(nj, NODE_KEYS, "node", nj.name ?? "");
|
|
3048
3195
|
if (nj.instance !== void 0 && nj.type !== void 0) throw new IncantoError("BAD_FORMAT", `Node '${nj.name}' declares both "type" and "instance" — they are mutually exclusive.`);
|
|
3049
3196
|
if (nj.instance !== void 0) return buildInstance(nj, opts, stack, path, constants, bands);
|
|
3050
3197
|
if (nj.type === void 0) throw new IncantoError("BAD_FORMAT", `Node '${nj.name}' must declare either "type" or "instance".`);
|
package/dist/net.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { d as PropSchema, jt as Node } from "./behavior-
|
|
2
|
-
import { a as NetworkManagerOptions, c as LocalGameServer, d as ServerClass, f as createLocalGameServer, g as Unsubscribe, h as NetworkTransport, i as NetworkManager, l as LocalGameServerOptions, m as LoopbackTransport, n as SplitScreenPlayer, o as applySyncPatch, p as LoopbackHub, r as createSplitScreen, s as CollectionOptions, t as SplitScreenOptions, u as LocalGameServerTransport } from "./split-screen-
|
|
1
|
+
import { d as PropSchema, jt as Node } from "./behavior-ZnfzKDo3.js";
|
|
2
|
+
import { a as NetworkManagerOptions, c as LocalGameServer, d as ServerClass, f as createLocalGameServer, g as Unsubscribe, h as NetworkTransport, i as NetworkManager, l as LocalGameServerOptions, m as LoopbackTransport, n as SplitScreenPlayer, o as applySyncPatch, p as LoopbackHub, r as createSplitScreen, s as CollectionOptions, t as SplitScreenOptions, u as LocalGameServerTransport } from "./split-screen-DD5eYIgW.js";
|
|
3
3
|
|
|
4
4
|
//#region src/net/agent8.d.ts
|
|
5
5
|
/**
|
package/dist/net.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { n as createAgent8Server } from "./agent8-CvsfVskX.js";
|
|
2
|
-
import { a as NetworkManager, d as createLocalGameServer, f as LoopbackHub, l as LocalGameServer, n as registerNodesNet, o as applySyncPatch, p as LoopbackTransport, r as NetworkSpawner, t as createSplitScreen, u as LocalGameServerTransport } from "./split-screen-
|
|
2
|
+
import { a as NetworkManager, d as createLocalGameServer, f as LoopbackHub, l as LocalGameServer, n as registerNodesNet, o as applySyncPatch, p as LoopbackTransport, r as NetworkSpawner, t as createSplitScreen, u as LocalGameServerTransport } from "./split-screen-DS8RaUV_.js";
|
|
3
3
|
export { LocalGameServer, LocalGameServerTransport, LoopbackHub, LoopbackTransport, NetworkManager, NetworkSpawner, applySyncPatch, createAgent8Server, createLocalGameServer, createSplitScreen, registerNodesNet };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { k as diagnose } from "./loader-
|
|
2
|
+
import { k as diagnose } from "./loader-KZESOZmE.js";
|
|
3
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
4
|
-
import { _ as RigidBody2D, b as validateCollider2D, g as PhysicsBody2D, h as CharacterBody2D, m as Area2D, p as Joint2D, y as Node2D } from "./register-
|
|
4
|
+
import { _ as RigidBody2D, b as validateCollider2D, g as PhysicsBody2D, h as CharacterBody2D, m as Area2D, p as Joint2D, y as Node2D } from "./register-vIxU3Xbv.js";
|
|
5
5
|
import { n as registerDebugSource } from "./debug-draw-BM3DsvtT.js";
|
|
6
|
-
import { t as withoutRapierInitNoise } from "./quiet-rapier-
|
|
6
|
+
import { n as jointContacts, t as withoutRapierInitNoise } from "./quiet-rapier-C6fW4zcW.js";
|
|
7
7
|
//#region src/2d/physics/physics-2d.ts
|
|
8
8
|
var physics_2d_exports = /* @__PURE__ */ __exportAll({
|
|
9
9
|
Physics2D: () => Physics2D,
|
|
@@ -66,6 +66,7 @@ var Physics2D = class {
|
|
|
66
66
|
this.unregisterDebug = registerDebugSource(this);
|
|
67
67
|
this.disconnect = engine.fixedUpdated.connect((dt) => this.step(dt));
|
|
68
68
|
this.disconnectScene = engine.sceneChanged.connect(() => {
|
|
69
|
+
this.pruneOverlaps();
|
|
69
70
|
if (engine.scene) this.syncBodies();
|
|
70
71
|
});
|
|
71
72
|
engine.physics = this;
|
|
@@ -119,6 +120,8 @@ var Physics2D = class {
|
|
|
119
120
|
const b = this.byColliderHandle.get(h2);
|
|
120
121
|
if (!a || !b) return;
|
|
121
122
|
const sig = started ? "triggerEnter" : "triggerExit";
|
|
123
|
+
this.trackOverlap(a, b, started);
|
|
124
|
+
this.trackOverlap(b, a, started);
|
|
122
125
|
try {
|
|
123
126
|
a.emit(sig, b);
|
|
124
127
|
b.emit(sig, a);
|
|
@@ -284,9 +287,58 @@ var Physics2D = class {
|
|
|
284
287
|
else if (joint.type === "rope") data = R.JointData.rope(length, a1, a2);
|
|
285
288
|
else if (joint.type === "spring") data = R.JointData.spring(length, joint.stiffness, joint.damping, a1, a2);
|
|
286
289
|
else data = R.JointData.revolute(a1, a2);
|
|
287
|
-
|
|
290
|
+
const made = this.world.createImpulseJoint(data, ea.body, eb.body, true);
|
|
291
|
+
made.setContactsEnabled(jointContacts(joint.type, joint.collide));
|
|
292
|
+
this.joints.set(joint, made);
|
|
288
293
|
}
|
|
289
294
|
}
|
|
295
|
+
/** Who is currently inside each sensor/body, maintained from the event drain. */
|
|
296
|
+
overlaps = /* @__PURE__ */ new Map();
|
|
297
|
+
trackOverlap(self, other, started) {
|
|
298
|
+
let set = this.overlaps.get(self);
|
|
299
|
+
if (started) {
|
|
300
|
+
if (!set) {
|
|
301
|
+
set = /* @__PURE__ */ new Set();
|
|
302
|
+
this.overlaps.set(self, set);
|
|
303
|
+
}
|
|
304
|
+
set.add(other);
|
|
305
|
+
} else if (set) {
|
|
306
|
+
set.delete(other);
|
|
307
|
+
if (set.size === 0) this.overlaps.delete(self);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Forget every overlap whose node has left the tree.
|
|
312
|
+
*
|
|
313
|
+
* The lazy drop inside `overlapping()` only cleans the SETS of a key someone
|
|
314
|
+
* asks about, and only when they ask. This clears the KEYS, which is where the
|
|
315
|
+
* dead nodes were pinned.
|
|
316
|
+
*/
|
|
317
|
+
/** Drop one node from the overlap map, both as a key and as a member. */
|
|
318
|
+
forgetOverlaps(node) {
|
|
319
|
+
this.overlaps.delete(node);
|
|
320
|
+
for (const [self, others] of this.overlaps) {
|
|
321
|
+
others.delete(node);
|
|
322
|
+
if (others.size === 0) this.overlaps.delete(self);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
pruneOverlaps() {
|
|
326
|
+
for (const [self, others] of this.overlaps) {
|
|
327
|
+
if (self.tree === null) {
|
|
328
|
+
this.overlaps.delete(self);
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
for (const other of others) if (other.tree === null) others.delete(other);
|
|
332
|
+
if (others.size === 0) this.overlaps.delete(self);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
/** The bodies currently overlapping `node`. Freed nodes are dropped on read. */
|
|
336
|
+
overlapping(node) {
|
|
337
|
+
const set = this.overlaps.get(node);
|
|
338
|
+
if (!set) return [];
|
|
339
|
+
for (const other of set) if (other.tree === null) set.delete(other);
|
|
340
|
+
return [...set];
|
|
341
|
+
}
|
|
290
342
|
/**
|
|
291
343
|
* World-space raycast in PIXELS (y-down). `exclude` skips that body — a
|
|
292
344
|
* shooter probing from inside its own collider needs it. Sensors (Area2D)
|
|
@@ -400,6 +452,7 @@ var Physics2D = class {
|
|
|
400
452
|
this.byColliderHandle.delete(e.collider.handle);
|
|
401
453
|
this.world.removeRigidBody(e.body);
|
|
402
454
|
this.entries.delete(node);
|
|
455
|
+
this.forgetOverlaps(node);
|
|
403
456
|
node._physics = null;
|
|
404
457
|
}
|
|
405
458
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { k as diagnose } from "./loader-
|
|
2
|
+
import { k as diagnose } from "./loader-KZESOZmE.js";
|
|
3
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
4
|
-
import {
|
|
4
|
+
import { H as CharacterBody3D, K as Node3D, U as PhysicsBody3D, V as Area3D, W as RigidBody3D, q as validateCollider3D } from "./gameplay-CGSv-9XR.js";
|
|
5
5
|
import { n as registerDebugSource } from "./debug-draw-BM3DsvtT.js";
|
|
6
|
-
import { t as withoutRapierInitNoise } from "./quiet-rapier-
|
|
7
|
-
import { A as Terrain3D, F as InstancedMesh3D, L as buildMeshGeometry, P as Joint3D } from "./environment-presets-
|
|
6
|
+
import { n as jointContacts, t as withoutRapierInitNoise } from "./quiet-rapier-C6fW4zcW.js";
|
|
7
|
+
import { A as Terrain3D, F as InstancedMesh3D, L as buildMeshGeometry, P as Joint3D } from "./environment-presets-C-5g2pMh.js";
|
|
8
8
|
import { Euler, Matrix4, Quaternion, Vector3 } from "three";
|
|
9
9
|
//#region src/3d/physics/collider-lines.ts
|
|
10
10
|
/**
|
|
@@ -262,6 +262,7 @@ var Physics3D = class {
|
|
|
262
262
|
this.unregisterDebug = registerDebugSource(this);
|
|
263
263
|
this.disconnect = engine.fixedUpdated.connect((dt) => this.step(dt));
|
|
264
264
|
this.disconnectScene = engine.sceneChanged.connect(() => {
|
|
265
|
+
this.pruneOverlaps();
|
|
265
266
|
if (engine.scene) this.syncBodies();
|
|
266
267
|
});
|
|
267
268
|
engine.physics = this;
|
|
@@ -328,6 +329,11 @@ var Physics3D = class {
|
|
|
328
329
|
nv[0] = lv.x;
|
|
329
330
|
nv[1] = lv.y;
|
|
330
331
|
nv[2] = lv.z;
|
|
332
|
+
const av = e.body.angvel();
|
|
333
|
+
const na = node.angularVelocity;
|
|
334
|
+
na[0] = av.x;
|
|
335
|
+
na[1] = av.y;
|
|
336
|
+
na[2] = av.z;
|
|
331
337
|
e.lastV[0] = lv.x;
|
|
332
338
|
e.lastV[1] = lv.y;
|
|
333
339
|
e.lastV[2] = lv.z;
|
|
@@ -342,6 +348,8 @@ var Physics3D = class {
|
|
|
342
348
|
const b = this.byColliderHandle.get(h2);
|
|
343
349
|
if (!a || !b) return;
|
|
344
350
|
const sig = started ? "triggerEnter" : "triggerExit";
|
|
351
|
+
this.trackOverlap(a, b, started);
|
|
352
|
+
this.trackOverlap(b, a, started);
|
|
345
353
|
try {
|
|
346
354
|
a.emit(sig, b);
|
|
347
355
|
b.emit(sig, a);
|
|
@@ -602,6 +610,7 @@ var Physics3D = class {
|
|
|
602
610
|
this.byColliderHandle.delete(e.collider.handle);
|
|
603
611
|
this.world.removeRigidBody(e.body);
|
|
604
612
|
this.entries.delete(node);
|
|
613
|
+
this.forgetOverlaps(node);
|
|
605
614
|
node._physics = null;
|
|
606
615
|
node._interpPrev = node._interpCurr = null;
|
|
607
616
|
}
|
|
@@ -635,6 +644,11 @@ var Physics3D = class {
|
|
|
635
644
|
if (node instanceof RigidBody3D) {
|
|
636
645
|
desc.setGravityScale(node.gravityScale);
|
|
637
646
|
desc.setLinvel(node.linearVelocity[0] ?? 0, node.linearVelocity[1] ?? 0, node.linearVelocity[2] ?? 0);
|
|
647
|
+
desc.setAngvel({
|
|
648
|
+
x: node.angularVelocity[0] ?? 0,
|
|
649
|
+
y: node.angularVelocity[1] ?? 0,
|
|
650
|
+
z: node.angularVelocity[2] ?? 0
|
|
651
|
+
});
|
|
638
652
|
if (node.fixedRotation) desc.lockRotations();
|
|
639
653
|
}
|
|
640
654
|
const body = this.world.createRigidBody(desc);
|
|
@@ -791,9 +805,64 @@ var Physics3D = class {
|
|
|
791
805
|
else if (joint.type === "rope") data = R.JointData.rope(length, a1, a2);
|
|
792
806
|
else if (joint.type === "spring") data = R.JointData.spring(length, joint.stiffness, joint.damping, a1, a2);
|
|
793
807
|
else data = R.JointData.spherical(a1, a2);
|
|
794
|
-
|
|
808
|
+
const made = this.world.createImpulseJoint(data, ea.body, eb.body, true);
|
|
809
|
+
made.setContactsEnabled(jointContacts(joint.type, joint.collide));
|
|
810
|
+
this.joints.set(joint, made);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
/** Who is currently inside each sensor/body, maintained from the event drain. */
|
|
814
|
+
overlaps = /* @__PURE__ */ new Map();
|
|
815
|
+
trackOverlap(self, other, started) {
|
|
816
|
+
let set = this.overlaps.get(self);
|
|
817
|
+
if (started) {
|
|
818
|
+
if (!set) {
|
|
819
|
+
set = /* @__PURE__ */ new Set();
|
|
820
|
+
this.overlaps.set(self, set);
|
|
821
|
+
}
|
|
822
|
+
set.add(other);
|
|
823
|
+
} else if (set) {
|
|
824
|
+
set.delete(other);
|
|
825
|
+
if (set.size === 0) this.overlaps.delete(self);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* Forget every overlap whose node has left the tree.
|
|
830
|
+
*
|
|
831
|
+
* The lazy drop inside `overlapping()` only cleans the SETS of a key someone
|
|
832
|
+
* asks about, and only when they ask. This clears the KEYS, which is where the
|
|
833
|
+
* dead nodes were pinned.
|
|
834
|
+
*/
|
|
835
|
+
/** Drop one node from the overlap map, both as a key and as a member. */
|
|
836
|
+
forgetOverlaps(node) {
|
|
837
|
+
this.overlaps.delete(node);
|
|
838
|
+
for (const [self, others] of this.overlaps) {
|
|
839
|
+
others.delete(node);
|
|
840
|
+
if (others.size === 0) this.overlaps.delete(self);
|
|
795
841
|
}
|
|
796
842
|
}
|
|
843
|
+
pruneOverlaps() {
|
|
844
|
+
for (const [self, others] of this.overlaps) {
|
|
845
|
+
if (self.tree === null) {
|
|
846
|
+
this.overlaps.delete(self);
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
for (const other of others) if (other.tree === null) others.delete(other);
|
|
850
|
+
if (others.size === 0) this.overlaps.delete(self);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* The bodies currently overlapping `node` — the standing answer behind
|
|
855
|
+
* `triggerEnter`/`triggerExit`.
|
|
856
|
+
*
|
|
857
|
+
* Freed nodes are dropped on read rather than tracked, so a hazard that
|
|
858
|
+
* despawns inside a plate cannot leave a ghost holding a door open.
|
|
859
|
+
*/
|
|
860
|
+
overlapping(node) {
|
|
861
|
+
const set = this.overlaps.get(node);
|
|
862
|
+
if (!set) return [];
|
|
863
|
+
for (const other of set) if (other.tree === null) set.delete(other);
|
|
864
|
+
return [...set];
|
|
865
|
+
}
|
|
797
866
|
/**
|
|
798
867
|
* World-space raycast (meters). `exclude` skips that body's collider —
|
|
799
868
|
* ground probes from inside a capsule need it. `opts.staticOnly` hits ONLY the
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
//#region src/core/joint-contacts.ts
|
|
2
|
+
/**
|
|
3
|
+
* Should these two jointed bodies collide with each other?
|
|
4
|
+
*
|
|
5
|
+
* `null` means "decide from the type": a PIVOT wants its bodies overlapping and
|
|
6
|
+
* must not let the contact solver fight the joint; a TETHER anchors something to
|
|
7
|
+
* the world and that something still has to rest on it.
|
|
8
|
+
*/
|
|
9
|
+
function jointContacts(type, collide) {
|
|
10
|
+
if (collide !== null) return collide;
|
|
11
|
+
return type === "rope" || type === "spring";
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
1
14
|
//#region src/core/quiet-rapier.ts
|
|
2
15
|
/**
|
|
3
16
|
* Swallow one wrong warning that rapier prints about itself.
|
|
@@ -43,4 +56,4 @@ async function withoutRapierInitNoise(boot, out = console) {
|
|
|
43
56
|
}
|
|
44
57
|
}
|
|
45
58
|
//#endregion
|
|
46
|
-
export { withoutRapierInitNoise as t };
|
|
59
|
+
export { jointContacts as n, withoutRapierInitNoise as t };
|
package/dist/react.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { P as Scene, b as Engine, n as BehaviorCtor } from "./behavior-
|
|
1
|
+
import { P as Scene, b as Engine, n as BehaviorCtor } from "./behavior-ZnfzKDo3.js";
|
|
2
2
|
import { c as JsonValue } from "./schema-CFeioQRE.js";
|
|
3
3
|
import { CSSProperties, ReactNode } from "react";
|
|
4
4
|
|
package/dist/react.js
CHANGED
|
@@ -156,7 +156,7 @@ function IncantoCanvas(props) {
|
|
|
156
156
|
pointer: latest.pointer,
|
|
157
157
|
...keyboard !== void 0 ? { keyboard } : {}
|
|
158
158
|
};
|
|
159
|
-
const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-
|
|
159
|
+
const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-FAXdHta3.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-DMetd3vR.js").then((n) => n.n)).createGame2D(o)))(opts);
|
|
160
160
|
if (disposed) {
|
|
161
161
|
next.dispose();
|
|
162
162
|
return;
|