@jgengine/core 0.18.0 → 0.19.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/CHANGELOG.md +265 -2
- package/dist/ai/decisionGraph.d.ts +96 -0
- package/dist/ai/decisionGraph.js +212 -0
- package/dist/ai/groupAssist.d.ts +10 -0
- package/dist/ai/groupAssist.js +11 -0
- package/dist/ai/mobBrainGraph.d.ts +3 -0
- package/dist/ai/mobBrainGraph.js +25 -0
- package/dist/ai/pursuit.d.ts +1 -1
- package/dist/ai/pursuit.js +1 -1
- package/dist/ai/tacticalQueries.d.ts +30 -0
- package/dist/ai/tacticalQueries.js +45 -0
- package/dist/ai/threat.d.ts +8 -0
- package/dist/ai/threat.js +10 -0
- package/dist/anim/animGraph.d.ts +155 -0
- package/dist/anim/animGraph.js +414 -0
- package/dist/anim/boneTexture.d.ts +55 -0
- package/dist/anim/boneTexture.js +43 -0
- package/dist/anim/footPlacement.d.ts +57 -0
- package/dist/anim/footPlacement.js +67 -0
- package/dist/anim/ikSolver.d.ts +51 -0
- package/dist/anim/ikSolver.js +124 -0
- package/dist/anim/locomotionGraph.d.ts +35 -0
- package/dist/anim/locomotionGraph.js +62 -0
- package/dist/assets/spriteAtlas.d.ts +21 -0
- package/dist/assets/spriteAtlas.js +103 -0
- package/dist/audio/audioFalloff.d.ts +12 -0
- package/dist/audio/doppler.d.ts +21 -0
- package/dist/audio/doppler.js +32 -0
- package/dist/audio/engineLayers.d.ts +83 -0
- package/dist/audio/engineLayers.js +115 -0
- package/dist/combat/abilityKit.d.ts +1 -0
- package/dist/combat/abilityKit.js +1 -0
- package/dist/combat/death.d.ts +7 -0
- package/dist/combat/shotOrigin.d.ts +6 -0
- package/dist/combat/shotOrigin.js +6 -0
- package/dist/combat/weaponFeedback.d.ts +36 -0
- package/dist/combat/weaponFeedback.js +42 -0
- package/dist/combat/weaponHandling.d.ts +161 -0
- package/dist/combat/weaponHandling.js +202 -0
- package/dist/combat/weaponPresentation.d.ts +115 -0
- package/dist/combat/weaponPresentation.js +96 -0
- package/dist/devtools/warnOnce.d.ts +8 -0
- package/dist/devtools/warnOnce.js +18 -0
- package/dist/economy/currency.d.ts +16 -2
- package/dist/economy/currency.js +55 -14
- package/dist/economy/wallet.d.ts +5 -4
- package/dist/economy/wallet.js +8 -4
- package/dist/editor/commandApply.js +1 -0
- package/dist/editor/commands.d.ts +4 -1
- package/dist/editor/document.d.ts +2 -1
- package/dist/editor/document.js +92 -1
- package/dist/editor/environment.d.ts +3 -0
- package/dist/editor/environment.js +24 -0
- package/dist/editor/index.d.ts +4 -2
- package/dist/editor/index.js +1 -1
- package/dist/editor/types.d.ts +20 -0
- package/dist/faction/reputation.d.ts +6 -0
- package/dist/faction/reputation.js +11 -0
- package/dist/game/cameraConfig.d.ts +59 -1
- package/dist/game/chat.d.ts +22 -1
- package/dist/game/chat.js +48 -19
- package/dist/game/controlGate.d.ts +6 -0
- package/dist/game/controlGate.js +30 -1
- package/dist/game/defineGame.d.ts +10 -1
- package/dist/game/events.d.ts +14 -1
- package/dist/game/lootTable.d.ts +4 -0
- package/dist/game/lootTable.js +16 -0
- package/dist/game/pixelPerfect.d.ts +25 -0
- package/dist/game/pixelPerfect.js +29 -0
- package/dist/game/playableGame.d.ts +167 -6
- package/dist/game/systemRuntime.js +6 -1
- package/dist/game/toasts.d.ts +8 -0
- package/dist/game/toasts.js +7 -0
- package/dist/game/vfxInstance.d.ts +9 -0
- package/dist/game/vfxInstance.js +12 -1
- package/dist/game/viewports.d.ts +43 -0
- package/dist/game/viewports.js +46 -0
- package/dist/game/worldItem.d.ts +5 -1
- package/dist/game/worldItem.js +0 -1
- package/dist/input/actionBindings.d.ts +17 -2
- package/dist/input/actionBindings.js +24 -11
- package/dist/input/actionContexts.d.ts +45 -0
- package/dist/input/actionContexts.js +113 -0
- package/dist/input/axisInput.d.ts +4 -0
- package/dist/input/axisInput.js +4 -0
- package/dist/input/axisShaper.d.ts +73 -0
- package/dist/input/axisShaper.js +105 -0
- package/dist/input/gamepadModel.d.ts +79 -0
- package/dist/input/gamepadModel.js +108 -0
- package/dist/input/haptics.d.ts +50 -0
- package/dist/input/haptics.js +74 -0
- package/dist/input/inputBuffer.d.ts +40 -0
- package/dist/input/inputBuffer.js +59 -0
- package/dist/input/lookChannel.d.ts +10 -0
- package/dist/input/lookChannel.js +6 -0
- package/dist/input/pointer.d.ts +2 -0
- package/dist/input/pointer.js +4 -0
- package/dist/item/affix.d.ts +6 -0
- package/dist/item/affix.js +6 -0
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +196 -1
- package/dist/movement/characterController.d.ts +76 -0
- package/dist/movement/characterController.js +216 -0
- package/dist/movement/freeFlight.d.ts +119 -0
- package/dist/movement/freeFlight.js +294 -0
- package/dist/movement/glideModel.d.ts +3 -0
- package/dist/movement/glideModel.js +6 -0
- package/dist/movement/grappleSwing.d.ts +2 -0
- package/dist/movement/grappleSwing.js +5 -1
- package/dist/movement/movementModel.d.ts +39 -0
- package/dist/movement/movementModel.js +62 -19
- package/dist/movement/movementProbe.d.ts +51 -0
- package/dist/movement/movementProbe.js +119 -0
- package/dist/movement/playerMovement.d.ts +30 -3
- package/dist/movement/playerMovement.js +304 -7
- package/dist/movement/serverStep.d.ts +5 -0
- package/dist/movement/serverStep.js +6 -0
- package/dist/movement/solidObstacles.d.ts +39 -8
- package/dist/movement/solidObstacles.js +71 -6
- package/dist/movement/voxelController.js +7 -7
- package/dist/multiplayer/poseSyncGate.d.ts +3 -1
- package/dist/multiplayer/poseSyncGate.js +9 -1
- package/dist/multiplayer/presenceContract.d.ts +6 -0
- package/dist/multiplayer/presenceModel.d.ts +2 -0
- package/dist/multiplayer/presenceModel.js +11 -0
- package/dist/nav/navFromEnvironment.d.ts +9 -3
- package/dist/nav/navFromEnvironment.js +18 -12
- package/dist/nav/navMesh.d.ts +74 -0
- package/dist/nav/navMesh.js +570 -0
- package/dist/physics/aircraftDynamics.d.ts +390 -0
- package/dist/physics/aircraftDynamics.js +704 -0
- package/dist/physics/boatDynamics.d.ts +116 -0
- package/dist/physics/boatDynamics.js +160 -0
- package/dist/physics/drivableVehicle.d.ts +22 -8
- package/dist/physics/drivableVehicle.js +4 -2
- package/dist/physics/flightDynamics.d.ts +29 -4
- package/dist/physics/flightDynamics.js +28 -5
- package/dist/physics/handlingProbe.d.ts +249 -0
- package/dist/physics/handlingProbe.js +606 -0
- package/dist/physics/kinematicVehicle.d.ts +37 -2
- package/dist/physics/kinematicVehicle.js +15 -0
- package/dist/physics/physicsBackend.d.ts +203 -0
- package/dist/physics/physicsBackend.js +49 -0
- package/dist/physics/physicsBackendConformance.d.ts +27 -0
- package/dist/physics/physicsBackendConformance.js +151 -0
- package/dist/physics/physicsWorldBackend.d.ts +45 -0
- package/dist/physics/physicsWorldBackend.js +518 -0
- package/dist/physics/vehicleBackendLink.d.ts +49 -0
- package/dist/physics/vehicleBackendLink.js +102 -0
- package/dist/physics/vehicleDynamics.d.ts +374 -0
- package/dist/physics/vehicleDynamics.js +737 -0
- package/dist/physics/worldColliders.d.ts +13 -0
- package/dist/physics/worldColliders.js +128 -0
- package/dist/puzzle/cellGrid.d.ts +15 -9
- package/dist/puzzle/cellGrid.js +13 -9
- package/dist/random/rng.d.ts +12 -1
- package/dist/random/rng.js +20 -3
- package/dist/render/environment.d.ts +23 -0
- package/dist/render/environment.js +1 -0
- package/dist/render/lookPreset.d.ts +19 -6
- package/dist/render/lookPreset.js +56 -4
- package/dist/render/postProcessing.d.ts +40 -1
- package/dist/render/postProcessing.js +1 -1
- package/dist/render/sprite2d.d.ts +38 -0
- package/dist/render/sprite2d.js +40 -0
- package/dist/runtime/adapter.d.ts +20 -1
- package/dist/runtime/adapter.js +8 -1
- package/dist/runtime/cameraDirector.d.ts +17 -3
- package/dist/runtime/cameraDirector.js +25 -0
- package/dist/runtime/commandInput.d.ts +52 -0
- package/dist/runtime/commandInput.js +71 -0
- package/dist/runtime/commandRunner.d.ts +39 -7
- package/dist/runtime/commandRunner.js +35 -5
- package/dist/runtime/context/combat.d.ts +1 -0
- package/dist/runtime/context/combat.js +2 -1
- package/dist/runtime/context/deathLoot.d.ts +2 -0
- package/dist/runtime/context/deathLoot.js +1 -0
- package/dist/runtime/gameContext.js +18 -1
- package/dist/runtime/gameContextTypes.d.ts +33 -7
- package/dist/runtime/gameRuntime.d.ts +23 -5
- package/dist/runtime/gameRuntime.js +21 -4
- package/dist/runtime/headlessRunner.d.ts +10 -0
- package/dist/runtime/headlessRunner.js +26 -8
- package/dist/runtime/hostPolicy.d.ts +3 -2
- package/dist/runtime/hostPolicy.js +8 -10
- package/dist/runtime/hostedGameRunner.js +34 -6
- package/dist/runtime/hostedWorldSession.d.ts +12 -16
- package/dist/runtime/hostedWorldSession.js +29 -6
- package/dist/runtime/hostedWorldStore.d.ts +20 -0
- package/dist/runtime/hostedWorldStore.js +1 -0
- package/dist/runtime/inputRecorder.d.ts +30 -0
- package/dist/runtime/inputRecorder.js +59 -0
- package/dist/runtime/inputSnapshot.d.ts +14 -0
- package/dist/runtime/inputSnapshot.js +11 -0
- package/dist/runtime/localPlayers.d.ts +73 -0
- package/dist/runtime/localPlayers.js +119 -0
- package/dist/runtime/poseInterpolation.d.ts +36 -0
- package/dist/runtime/poseInterpolation.js +91 -0
- package/dist/runtime/prediction.d.ts +45 -0
- package/dist/runtime/prediction.js +62 -0
- package/dist/runtime/simContext.d.ts +45 -0
- package/dist/runtime/simContext.js +65 -0
- package/dist/runtime/simLoop.d.ts +52 -0
- package/dist/runtime/simLoop.js +72 -0
- package/dist/runtime/simSnapshot.d.ts +32 -0
- package/dist/runtime/simSnapshot.js +60 -0
- package/dist/runtime/snapshot.d.ts +8 -0
- package/dist/runtime/snapshotBuffer.d.ts +33 -0
- package/dist/runtime/snapshotBuffer.js +58 -0
- package/dist/runtime/transport.d.ts +41 -2
- package/dist/runtime/transport.js +7 -1
- package/dist/runtime/worldChunks.d.ts +2 -0
- package/dist/runtime/worldChunks.js +14 -0
- package/dist/runtime/worldProjection.d.ts +9 -0
- package/dist/runtime/worldProjection.js +17 -1
- package/dist/runtime/worldSnapshot.d.ts +6 -0
- package/dist/runtime/worldSnapshot.js +3 -0
- package/dist/scene/behaviorRuntime.d.ts +29 -1
- package/dist/scene/behaviorRuntime.js +71 -4
- package/dist/scene/behaviors.d.ts +12 -1
- package/dist/scene/builtinSceneKinds.js +10 -0
- package/dist/scene/sceneKinds.d.ts +9 -0
- package/dist/scene/vehicleSeat.d.ts +5 -1
- package/dist/scene/vehicleSeat.js +5 -1
- package/dist/sensor/concealment.d.ts +6 -0
- package/dist/sensor/concealment.js +8 -0
- package/dist/sensor/freezeMonitor.d.ts +6 -0
- package/dist/sensor/freezeMonitor.js +8 -0
- package/dist/sensor/perception.d.ts +70 -0
- package/dist/sensor/perception.js +189 -0
- package/dist/sensor/recordingBuffer.d.ts +3 -0
- package/dist/sensor/recordingBuffer.js +8 -0
- package/dist/session/roundState.d.ts +4 -0
- package/dist/session/roundState.js +11 -0
- package/dist/settings/frameRateLimit.d.ts +16 -0
- package/dist/settings/frameRateLimit.js +37 -0
- package/dist/settings/graphicsProfile.d.ts +21 -0
- package/dist/settings/graphicsProfile.js +48 -0
- package/dist/settings/graphicsSettings.d.ts +40 -0
- package/dist/settings/graphicsSettings.js +81 -0
- package/dist/settings/settingsModel.d.ts +9 -1
- package/dist/settings/settingsModel.js +10 -0
- package/dist/stats/eventMeter.d.ts +1 -1
- package/dist/stats/eventMeter.js +1 -1
- package/dist/time/accrueSince.d.ts +10 -0
- package/dist/time/accrueSince.js +9 -0
- package/dist/time/rateWindow.d.ts +16 -0
- package/dist/time/rateWindow.js +14 -0
- package/dist/time/serverTick.d.ts +21 -3
- package/dist/time/serverTick.js +14 -13
- package/dist/vfx/feedbackMixer.d.ts +61 -0
- package/dist/vfx/feedbackMixer.js +106 -0
- package/dist/vfx/particleDirector.d.ts +68 -0
- package/dist/vfx/particleDirector.js +81 -0
- package/dist/visibility/simulationCulling.d.ts +8 -1
- package/dist/visibility/simulationCulling.js +7 -0
- package/dist/visibility/spatialIndex.d.ts +21 -0
- package/dist/visibility/spatialIndex.js +16 -0
- package/dist/world/authoredSolids.d.ts +46 -0
- package/dist/world/authoredSolids.js +75 -0
- package/dist/world/buildingGenerator.js +2 -2
- package/dist/world/buildingKit.d.ts +3 -1
- package/dist/world/buildings.d.ts +46 -2
- package/dist/world/buildings.js +53 -3
- package/dist/world/cityContent.js +103 -25
- package/dist/world/cityKind.d.ts +139 -0
- package/dist/world/cityKind.js +367 -16
- package/dist/world/features.d.ts +56 -3
- package/dist/world/features.js +13 -2
- package/dist/world/footprintGrid.d.ts +7 -0
- package/dist/world/footprintGrid.js +16 -0
- package/dist/world/interiors.d.ts +1 -0
- package/dist/world/interiors.js +5 -3
- package/dist/world/lod.d.ts +6 -0
- package/dist/world/lod.js +8 -0
- package/dist/world/placement.d.ts +7 -0
- package/dist/world/placement.js +2 -0
- package/dist/world/placementController.d.ts +1 -1
- package/dist/world/placementController.js +7 -0
- package/dist/world/raceCircuit.d.ts +168 -0
- package/dist/world/raceCircuit.js +797 -0
- package/dist/world/terraform.d.ts +8 -0
- package/dist/world/terraform.js +7 -0
- package/dist/world/terrain.d.ts +5 -1
- package/dist/world/terrain.js +5 -0
- package/dist/world/territory.d.ts +97 -0
- package/dist/world/territory.js +182 -0
- package/dist/world/walls.d.ts +7 -0
- package/dist/world/walls.js +7 -0
- package/dist/world/worldSolids.d.ts +84 -0
- package/dist/world/worldSolids.js +240 -0
- package/dist/world.d.ts +5 -3
- package/dist/world.js +4 -2
- package/package.json +1 -1
- package/skills/ce-handoff/SKILL.md +2 -2
- package/skills/game-design/SKILL.md +11 -1
- package/skills/jgengine/SKILL.md +9 -5
- package/skills/jgengine/api.md +205 -101
- package/skills/jgengine/capabilities.md +56 -0
- package/skills/jgengine/recipes/minimal-game.md +3 -0
- package/skills/jgengine-combat/SKILL.md +5 -0
- package/skills/jgengine-combat/api.md +138 -103
- package/skills/jgengine-combat/capabilities.md +29 -1
- package/skills/jgengine-combat/recipes/weapon-feel.md +50 -0
- package/skills/jgengine-gameplay/SKILL.md +10 -0
- package/skills/jgengine-gameplay/api.md +501 -453
- package/skills/jgengine-gameplay/capabilities.md +36 -0
- package/skills/jgengine-gameplay/reference-systems.md +4 -0
- package/skills/jgengine-world/SKILL.md +21 -3
- package/skills/jgengine-world/api.md +1172 -625
- package/skills/jgengine-world/capabilities.md +229 -3
- package/skills/jgengine-world/recipes/character-animation.md +39 -0
- package/skills/jgengine-world/recipes/controls.md +44 -0
- package/skills/jgengine-world/recipes/flight-feel.md +76 -0
- package/skills/jgengine-world/recipes/npc-senses.md +43 -0
- package/skills/jgengine-world/recipes/vehicle-feel.md +85 -0
- package/skills/jgengine-world/reference.md +31 -2
- package/skills/jgengine-world/visibility.md +112 -0
|
@@ -3,6 +3,7 @@ import type { AvoidZone } from "../world/geometry.js";
|
|
|
3
3
|
import { type ClearanceOptions } from "../world/scatterRegion.js";
|
|
4
4
|
import type { TerraformSnapshot } from "../world/terraform.js";
|
|
5
5
|
import type { EditorDocument, EditorEnvironment } from "./types.js";
|
|
6
|
+
import type { LightingConfig } from "../game/playableGame.js";
|
|
6
7
|
/**
|
|
7
8
|
* World-authoring convergence (#1018, epic #1006 Phase 4): derive the **coordinate/placement**
|
|
8
9
|
* content of an `environment()` world from the scene document, so a game with an authored document
|
|
@@ -46,6 +47,8 @@ export declare function skyFromDocument(doc: EditorDocument): SkyEnvironmentConf
|
|
|
46
47
|
* @internal
|
|
47
48
|
*/
|
|
48
49
|
export declare function skyConfigFromEnvironment(env: EditorEnvironment): SkyEnvironmentConfig;
|
|
50
|
+
/** Converts authored `light` markers into the runtime lighting contract. */
|
|
51
|
+
export declare function lightingFromDocument(doc: EditorDocument): LightingConfig | undefined;
|
|
49
52
|
/**
|
|
50
53
|
* The origin-centered terrain footprint that covers every authored object in a document, padded and
|
|
51
54
|
* floored to `minBounds`. An empty document yields exactly `minBounds`. Because `environment()`
|
|
@@ -25,9 +25,33 @@ export function skyConfigFromEnvironment(env) {
|
|
|
25
25
|
...(env.zenithColor === undefined ? {} : { zenithColor: env.zenithColor }),
|
|
26
26
|
...(env.sunIntensity === undefined ? {} : { sunIntensity: env.sunIntensity }),
|
|
27
27
|
...(env.ambientIntensity === undefined ? {} : { ambientIntensity: env.ambientIntensity }),
|
|
28
|
+
...(env.sunAzimuth === undefined && env.sunElevation === undefined
|
|
29
|
+
? {}
|
|
30
|
+
: {
|
|
31
|
+
sun: {
|
|
32
|
+
...(env.sunAzimuth === undefined ? {} : { azimuth: env.sunAzimuth }),
|
|
33
|
+
...(env.sunElevation === undefined ? {} : { elevation: env.sunElevation }),
|
|
34
|
+
},
|
|
35
|
+
}),
|
|
28
36
|
...(env.fog === undefined ? {} : { fog: { ...env.fog } }),
|
|
29
37
|
};
|
|
30
38
|
}
|
|
39
|
+
/** Converts authored `light` markers into the runtime lighting contract. */
|
|
40
|
+
export function lightingFromDocument(doc) {
|
|
41
|
+
const points = [];
|
|
42
|
+
const spots = [];
|
|
43
|
+
for (const marker of doc.markers) {
|
|
44
|
+
if (marker.kind !== "light" || marker.position === undefined)
|
|
45
|
+
continue;
|
|
46
|
+
const meta = marker.meta ?? {};
|
|
47
|
+
const base = { position: [marker.position.x, marker.position.y, marker.position.z], color: typeof meta.color === "string" ? meta.color : "#ffd6a0", intensity: typeof meta.intensity === "number" ? meta.intensity : 2, distance: typeof meta.range === "number" ? meta.range : 12, castShadow: meta.castShadow === true };
|
|
48
|
+
if (meta.type === "spot")
|
|
49
|
+
spots.push({ ...base, angle: typeof meta.angle === "number" ? meta.angle : Math.PI / 6 });
|
|
50
|
+
else
|
|
51
|
+
points.push(base);
|
|
52
|
+
}
|
|
53
|
+
return points.length === 0 && spots.length === 0 ? undefined : { point: points, spot: spots };
|
|
54
|
+
}
|
|
31
55
|
/**
|
|
32
56
|
* The origin-centered terrain footprint that covers every authored object in a document, padded and
|
|
33
57
|
* floored to `minBounds`. An empty document yields exactly `minBounds`. Because `environment()`
|
package/dist/editor/index.d.ts
CHANGED
|
@@ -5,7 +5,9 @@ export type { EditorTerrain } from "./types.js";
|
|
|
5
5
|
/** A baked top-down minimap (PNG data URI + world bounds) persisted on the scene document. */
|
|
6
6
|
export type { EditorMinimapBake } from "./types.js";
|
|
7
7
|
/** Scene-document sky/fog/lighting bag edited by the lighting workspace. */
|
|
8
|
-
export type { EditorEnvironment, EditorFogConfig, EditorSkyPreset } from "./types.js";
|
|
8
|
+
export type { EditorEnvironment, EditorFogConfig, EditorSkyPreset, PointLightingConfig } from "./types.js";
|
|
9
|
+
/** Declarative HDRI, cube, or gradient environment source used by the shell. */
|
|
10
|
+
export type { EnvironmentSource } from "../render/environment.js";
|
|
9
11
|
/** Per-kind show/hide flags for the editor's layer panel. */
|
|
10
12
|
export type { EditorKindVisibility } from "./types.js";
|
|
11
13
|
/** The four placeable-object collections a prefab fragment or clipboard fragment carries. */
|
|
@@ -62,7 +64,7 @@ export { WORLD_BASE_SHARD_ID, WORLD_MANIFEST_KIND, decodeWorldManifest, exportWo
|
|
|
62
64
|
export type { DecodeWorldManifestResult, SplitWorldOptions, SplitWorldResult, WorldGrid, WorldManifest, WorldManifestShard, WorldQuery, WorldShardBounds, WorldShardResidency, WorldShardResolver, WorldStreamingConfig, } from "./world.js";
|
|
63
65
|
/** Result of {@link decodeEditorDocument}: a typed document, or every diagnostic collected while decoding it. */
|
|
64
66
|
export type { DecodeEditorDocumentResult } from "./document.js";
|
|
65
|
-
export { environmentContentFromDocument, skyConfigFromEnvironment, skyFromDocument, terrainBoundsFromDocument, } from "./environment.js";
|
|
67
|
+
export { environmentContentFromDocument, lightingFromDocument, skyConfigFromEnvironment, skyFromDocument, terrainBoundsFromDocument, } from "./environment.js";
|
|
66
68
|
/** Coordinate/placement content of an `environment()` world derived from the scene document. */
|
|
67
69
|
export type { EnvironmentContent, EnvironmentContentOptions } from "./environment.js";
|
|
68
70
|
export { bakeMinimapFromDocument, documentBakeZones } from "./minimap.js";
|
package/dist/editor/index.js
CHANGED
|
@@ -3,7 +3,7 @@ export { ENTITY_CATALOG_ID, entityDefinitionSchema, entityEntryFromCatalog, reso
|
|
|
3
3
|
export { WELL_KNOWN_MARKER_KINDS, WELL_KNOWN_PATH_KINDS, WELL_KNOWN_VOLUME_KINDS, } from "./types.js";
|
|
4
4
|
export { applyEditorDocumentOverlay, cloneEditorDocument, collectDescendants, createEmptyEditorDocument, createPrefabFragment, decodeEditorDocument, editorChildren, editorDocumentBounds, editorDocumentExtras, editorDocumentSize, editorMarkerPosition, editorMarkerXZ, editorParentOf, editorRoots, exportEditorDocumentJson, extractEditorFragment, findEditorCatalog, findEditorCatalogEntry, findEditorCollection, findEditorMarker, findEditorNote, findEditorPath, findEditorPrefab, findEditorVolume, importEditorDocumentJson, isEditorObjectCollectionLocked, isEditorObjectHidden, isEditorObjectLocked, isEditorObjectSelfLocked, listEditorKinds, mergeEditorDocuments, normalizeEditorLayers, requireEditorMarker, seedEditorCatalogs, wouldCreateCycle, } from "./document.js";
|
|
5
5
|
export { WORLD_BASE_SHARD_ID, WORLD_MANIFEST_KIND, decodeWorldManifest, exportWorldManifestJson, loadWorldDocument, selectWorldShards, shardMatchesQuery, singleShardWorldManifest, splitEditorDocumentIntoShards, } from "./world.js";
|
|
6
|
-
export { environmentContentFromDocument, skyConfigFromEnvironment, skyFromDocument, terrainBoundsFromDocument, } from "./environment.js";
|
|
6
|
+
export { environmentContentFromDocument, lightingFromDocument, skyConfigFromEnvironment, skyFromDocument, terrainBoundsFromDocument, } from "./environment.js";
|
|
7
7
|
export { bakeMinimapFromDocument, documentBakeZones } from "./minimap.js";
|
|
8
8
|
export { createWorldStreamer, shardDistance } from "./streamer.js";
|
|
9
9
|
export { applyDirectiveOverlay, materializeDirective, materializeDirectives, materializePopulationDirective, materializeScatterDirective, resolveDirectiveFootprint, } from "./directives.js";
|
package/dist/editor/types.d.ts
CHANGED
|
@@ -3,6 +3,10 @@ import type { EditorUiDocument } from "../ui/hudDocument.js";
|
|
|
3
3
|
import type { MinimapBakeBounds } from "../world/minimapBake.js";
|
|
4
4
|
import type { TerraformSnapshot } from "../world/terraform.js";
|
|
5
5
|
import type { EditorGridLayer } from "./grid.js";
|
|
6
|
+
import type { EnvironmentSource } from "../render/environment.js";
|
|
7
|
+
import type { PointLightingConfig } from "../game/playableGame.js";
|
|
8
|
+
/** Runtime point-light shape reused by authored editor light markers. */
|
|
9
|
+
export type { PointLightingConfig } from "../game/playableGame.js";
|
|
6
10
|
export type { EditorUiDocument, EditorUiPanelLayout, HudResizeAxes, HudPanelTypeDef } from "../ui/hudDocument.js";
|
|
7
11
|
/** A world-space point used across editor markers, volumes, and paths. */
|
|
8
12
|
export type EditorVec3 = {
|
|
@@ -177,6 +181,12 @@ export interface EditorMinimapBake {
|
|
|
177
181
|
/** World-space rectangle the baked image spans. */
|
|
178
182
|
bounds: MinimapBakeBounds;
|
|
179
183
|
}
|
|
184
|
+
/** A named baked artifact produced by an editor or build tool. */
|
|
185
|
+
export interface EditorBake {
|
|
186
|
+
kind: "nav" | "minimap";
|
|
187
|
+
id: string;
|
|
188
|
+
data: unknown;
|
|
189
|
+
}
|
|
180
190
|
/** Named sky look stored on the scene document; matches runtime `SkyEnvironmentConfig.preset`. */
|
|
181
191
|
export type EditorSkyPreset = "day" | "dusk" | "night";
|
|
182
192
|
/**
|
|
@@ -197,6 +207,10 @@ export interface EditorFogConfig {
|
|
|
197
207
|
* @capability editor-environment persist sky/fog/lighting knobs on the scene document
|
|
198
208
|
*/
|
|
199
209
|
export interface EditorEnvironment {
|
|
210
|
+
/** Optional runtime environment map source; absent keeps the procedural gradient. */
|
|
211
|
+
source?: EnvironmentSource;
|
|
212
|
+
/** Authored local point lights; the runtime clamps these to its bounded light budget. */
|
|
213
|
+
pointLights?: readonly PointLightingConfig[];
|
|
200
214
|
/** Fixed sky look when `timeOfDay` is off (or no clock is available). */
|
|
201
215
|
preset?: EditorSkyPreset;
|
|
202
216
|
/** Drive sun/sky from the world clock's day fraction instead of the fixed `preset`. */
|
|
@@ -205,6 +219,10 @@ export interface EditorEnvironment {
|
|
|
205
219
|
zenithColor?: string;
|
|
206
220
|
sunIntensity?: number;
|
|
207
221
|
ambientIntensity?: number;
|
|
222
|
+
/** Sun compass bearing in degrees (0 = -Z, 90 = +X); runtime `SkySunConfig.azimuth`. */
|
|
223
|
+
sunAzimuth?: number;
|
|
224
|
+
/** Sun height above the horizon in degrees; runtime `SkySunConfig.elevation`. */
|
|
225
|
+
sunElevation?: number;
|
|
208
226
|
fog?: EditorFogConfig;
|
|
209
227
|
}
|
|
210
228
|
/** The full authored scene: every marker, volume, path, note, and sculpted terrain for a game. */
|
|
@@ -246,6 +264,8 @@ export interface EditorDocument {
|
|
|
246
264
|
* until baked, so old docs and unbaked games load unchanged.
|
|
247
265
|
*/
|
|
248
266
|
minimap?: EditorMinimapBake;
|
|
267
|
+
/** Named baked artifacts; `minimap` remains as a compatible legacy alias. */
|
|
268
|
+
bakes?: EditorBake[];
|
|
249
269
|
/**
|
|
250
270
|
* Scene-document sky/fog/lighting (#1110). Absent until authored in the lighting workspace or
|
|
251
271
|
* seeded via `editorLayers`, so old docs load unchanged and world.ts can keep a fallback sky.
|
|
@@ -17,6 +17,10 @@ export interface ReputationLedgerConfig {
|
|
|
17
17
|
min?: number;
|
|
18
18
|
max?: number;
|
|
19
19
|
}
|
|
20
|
+
/** Plain JSON standings of a {@link ReputationLedger}: stored values only, `initial` stays config. */
|
|
21
|
+
export interface ReputationLedgerState {
|
|
22
|
+
records: Record<string, Record<string, number>>;
|
|
23
|
+
}
|
|
20
24
|
export interface ReputationLedger {
|
|
21
25
|
standing(actorId: string, factionId: string): number;
|
|
22
26
|
hasStanding(actorId: string, factionId: string): boolean;
|
|
@@ -26,6 +30,8 @@ export interface ReputationLedger {
|
|
|
26
30
|
relation(actorId: string, factionId: string): FactionRelation;
|
|
27
31
|
standings(actorId: string): Record<string, number>;
|
|
28
32
|
reset(actorId: string, factionId?: string): void;
|
|
33
|
+
snapshot(): ReputationLedgerState;
|
|
34
|
+
restore(next: ReputationLedgerState): void;
|
|
29
35
|
}
|
|
30
36
|
export declare function createReputationLedger(config?: ReputationLedgerConfig): ReputationLedger;
|
|
31
37
|
export interface EffectiveRelationInput {
|
|
@@ -77,6 +77,17 @@ export function createReputationLedger(config = {}) {
|
|
|
77
77
|
else
|
|
78
78
|
records.get(actorId)?.delete(factionId);
|
|
79
79
|
},
|
|
80
|
+
snapshot() {
|
|
81
|
+
const out = {};
|
|
82
|
+
for (const [actorId, row] of records)
|
|
83
|
+
out[actorId] = Object.fromEntries(row);
|
|
84
|
+
return { records: out };
|
|
85
|
+
},
|
|
86
|
+
restore(next) {
|
|
87
|
+
records.clear();
|
|
88
|
+
for (const [actorId, row] of Object.entries(next.records))
|
|
89
|
+
records.set(actorId, new Map(Object.entries(row)));
|
|
90
|
+
},
|
|
80
91
|
};
|
|
81
92
|
}
|
|
82
93
|
export function effectiveRelation(input) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { WeaponHandlingFrame } from "../combat/weaponHandling.js";
|
|
2
|
+
import type { WeaponPresentationTuning } from "../combat/weaponPresentation.js";
|
|
1
3
|
export interface CameraFollowState {
|
|
2
4
|
entityId: string;
|
|
3
5
|
target: {
|
|
@@ -12,6 +14,11 @@ export interface CameraFollowState {
|
|
|
12
14
|
};
|
|
13
15
|
distance: number;
|
|
14
16
|
}
|
|
17
|
+
/** What a camera rig reads about the held weapon each frame; see `GameCameraConfig.weapon`. */
|
|
18
|
+
export interface CameraWeaponView {
|
|
19
|
+
handling: WeaponHandlingFrame;
|
|
20
|
+
presentation?: WeaponPresentationTuning;
|
|
21
|
+
}
|
|
15
22
|
export interface FirstPersonCameraConfig {
|
|
16
23
|
/** Camera height above the followed entity's origin. Default 1.6. */
|
|
17
24
|
eyeHeight?: number;
|
|
@@ -45,6 +52,12 @@ export interface FirstPersonCameraConfig {
|
|
|
45
52
|
export type CameraRigKind = "orbit" | "first" | "topDown" | "rts" | "shoulder" | "lockOn" | "chase" | "observer" | "turntable" | "sideScroll" | "inspection" | "none";
|
|
46
53
|
/** Canvas camera projection. "orthographic" renders a flat 2D-style view (side-scrollers, falling-block puzzles) — pair with `rig: "sideScroll"`; default "perspective". */
|
|
47
54
|
export type CameraProjection = "perspective" | "orthographic";
|
|
55
|
+
/** Pixel-art orthographic camera tuning. `pixelsPerUnit` is the authored pixel density. */
|
|
56
|
+
export interface PixelPerfectCameraConfig {
|
|
57
|
+
pixelsPerUnit: number;
|
|
58
|
+
/** Increase density in whole-number steps to use the largest scale that fits the viewport. */
|
|
59
|
+
integerScale?: boolean;
|
|
60
|
+
}
|
|
48
61
|
/** Fixed lateral 2.5D follow (side-on platformer cam): the camera sits perpendicular to the travel axis, tracks the followed entity, and never reads player look input. */
|
|
49
62
|
export interface SideScrollCameraConfig {
|
|
50
63
|
/** World axis the action travels along; the camera watches from the perpendicular side. Default "x". */
|
|
@@ -164,11 +177,47 @@ export interface ChaseCameraConfig {
|
|
|
164
177
|
lookHeight?: number;
|
|
165
178
|
/** Spring-arm damping (higher = stiffer, less lag). Default 6. */
|
|
166
179
|
springDamping?: number;
|
|
167
|
-
/**
|
|
180
|
+
/**
|
|
181
|
+
* Speed→FOV curve: FOV lerps from `base` to `max` as speed climbs to `speedForMax`. Speed is the
|
|
182
|
+
* followed entity's sim `velocity` when it publishes one. `response` (default 6) eases the FOV
|
|
183
|
+
* toward the curve with `1-exp(-response*dt)`; `Infinity` applies it unsmoothed.
|
|
184
|
+
*/
|
|
168
185
|
fov?: {
|
|
169
186
|
base?: number;
|
|
170
187
|
max?: number;
|
|
171
188
|
speedForMax?: number;
|
|
189
|
+
response?: number;
|
|
190
|
+
};
|
|
191
|
+
/** Pull the boom back by up to `extra` world units as speed climbs to `speedForMax` (default the FOV curve's `speedForMax`). */
|
|
192
|
+
distanceBySpeed?: {
|
|
193
|
+
extra: number;
|
|
194
|
+
speedForMax?: number;
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* Tilt the boom with the target's body pitch (`rotationX`) so the camera stays behind it on
|
|
198
|
+
* slopes and ramps. `blend` (default 1) is the fraction of the pitch followed, clamped to `max`
|
|
199
|
+
* radians (default 0.6) and eased by `response` (default 5). Omit the block to keep a level boom.
|
|
200
|
+
*/
|
|
201
|
+
pitchFollow?: {
|
|
202
|
+
blend?: number;
|
|
203
|
+
max?: number;
|
|
204
|
+
response?: number;
|
|
205
|
+
};
|
|
206
|
+
/** How `ctx.camera.kickFov(degrees)` impulses feel: the kick decays at `decay` per second (default 7) and the summed kick is clamped to `max` degrees (default 20). */
|
|
207
|
+
fovKick?: {
|
|
208
|
+
decay?: number;
|
|
209
|
+
max?: number;
|
|
210
|
+
};
|
|
211
|
+
/** Input action that, while held, swings the chase view to look back at what is behind the target. */
|
|
212
|
+
lookBackAction?: string;
|
|
213
|
+
/**
|
|
214
|
+
* Pull the boom in past scene colliders, objects, terrain and walls via `ctx.scene.raycast`
|
|
215
|
+
* (entities never block). `radius` (default 0.3) keeps the lens off the surface; `minDistance`
|
|
216
|
+
* (default 0.8) is the closest the camera gets to the look pivot. Default on; `false` disables.
|
|
217
|
+
*/
|
|
218
|
+
collision?: boolean | {
|
|
219
|
+
radius?: number;
|
|
220
|
+
minDistance?: number;
|
|
172
221
|
};
|
|
173
222
|
/** Procedural shake amplitude per unit of speed (adds to the trauma channel). Default 0.0. */
|
|
174
223
|
shakePerSpeed?: number;
|
|
@@ -378,6 +427,8 @@ export interface GameCameraConfig {
|
|
|
378
427
|
rig?: CameraRigKind;
|
|
379
428
|
/** Canvas camera projection. "orthographic" renders a flat 2D-style view (side-scrollers, falling-block puzzles) — pair with `rig: "sideScroll"`; default perspective. */
|
|
380
429
|
projection?: CameraProjection;
|
|
430
|
+
/** Pixel-perfect orthographic rendering; ignored for perspective cameras. */
|
|
431
|
+
pixelPerfect?: PixelPerfectCameraConfig;
|
|
381
432
|
/** Render frustum overrides applied to the canvas camera. `far` defaults to 300 — raise it for worlds whose content spans more than a few hundred units, or distant scenery silently clips. `zoom` is the orthographic zoom in canvas pixels per world unit, read only when `projection` is "orthographic"; default 50. */
|
|
382
433
|
frustum?: {
|
|
383
434
|
fov?: number;
|
|
@@ -438,6 +489,13 @@ export interface GameCameraConfig {
|
|
|
438
489
|
followEntityId?: string | null;
|
|
439
490
|
/** Fired each frame after cameraFollow lock is applied. */
|
|
440
491
|
onCameraFollow?: (state: CameraFollowState) => void;
|
|
492
|
+
/**
|
|
493
|
+
* The followed entity's held weapon, read every frame by the `first` and `shoulder` rigs. Return the
|
|
494
|
+
* weapon's `createWeaponHandling` frame and its presentation tuning, or `null` for none. First person
|
|
495
|
+
* poses the viewmodel from it (offset, ADS pose, viewmodel FOV, sway, bob, kick) and adds the recoil to
|
|
496
|
+
* the look; over-the-shoulder takes its ADS blend from `adsProgress` and adds the same recoil.
|
|
497
|
+
*/
|
|
498
|
+
weapon?: (entityId: string) => CameraWeaponView | null;
|
|
441
499
|
rotateSpeed?: number;
|
|
442
500
|
zoomSpeed?: number;
|
|
443
501
|
dampingFactor?: number;
|
package/dist/game/chat.d.ts
CHANGED
|
@@ -30,11 +30,18 @@ export type ChatSendResult = {
|
|
|
30
30
|
export interface ChatSnapshot {
|
|
31
31
|
messages: Record<string, ChatMessage[]>;
|
|
32
32
|
counter: number;
|
|
33
|
+
rateWindows?: Record<string, Record<string, number[]>>;
|
|
33
34
|
}
|
|
34
35
|
export interface Chat {
|
|
35
36
|
register(def: ChatChannelDef): void;
|
|
36
37
|
channels(): ChatChannelDef[];
|
|
38
|
+
send(fromUserId: string, body: string): ChatSendResult;
|
|
37
39
|
send(fromUserId: string, channelId: string, body: string): ChatSendResult;
|
|
40
|
+
recent(options?: {
|
|
41
|
+
limit?: number;
|
|
42
|
+
channelId?: string;
|
|
43
|
+
viewerUserId?: string;
|
|
44
|
+
}): ChatMessage[];
|
|
38
45
|
whisper(fromUserId: string, toUserId: string, body: string): ChatSendResult;
|
|
39
46
|
history(channelId: string, options?: {
|
|
40
47
|
limit?: number;
|
|
@@ -57,12 +64,26 @@ export interface ChatDeps {
|
|
|
57
64
|
}
|
|
58
65
|
export declare const DEFAULT_CHAT_RATE_LIMIT: ChatRateLimit;
|
|
59
66
|
export declare const DEFAULT_CHAT_HISTORY_LIMIT = 100;
|
|
60
|
-
export declare const DEFAULT_CHAT_BODY_LENGTH =
|
|
67
|
+
export declare const DEFAULT_CHAT_BODY_LENGTH = 240;
|
|
61
68
|
export declare const DEFAULT_PROXIMITY_CHAT_RADIUS = 20;
|
|
62
69
|
export declare const WHISPER_CHANNEL_PREFIX = "whisper:";
|
|
63
70
|
export declare function whisperChannelId(a: string, b: string): string;
|
|
64
71
|
export interface ChatRateLimiter {
|
|
65
72
|
allow(key: string, atMs: number): boolean;
|
|
73
|
+
snapshot(): Record<string, number[]>;
|
|
74
|
+
restore(state: Record<string, number[]>): void;
|
|
66
75
|
}
|
|
67
76
|
export declare function createChatRateLimiter(limit: ChatRateLimit): ChatRateLimiter;
|
|
68
77
|
export declare function createChat(deps: ChatDeps): Chat;
|
|
78
|
+
/** Sanitized chat text or a displayable validation failure. */
|
|
79
|
+
export type ChatValidation = {
|
|
80
|
+
ok: true;
|
|
81
|
+
text: string;
|
|
82
|
+
} | {
|
|
83
|
+
ok: false;
|
|
84
|
+
reason: string;
|
|
85
|
+
};
|
|
86
|
+
/** Strip control characters and enforce the shared chat message policy before storage or broadcast. */
|
|
87
|
+
export declare function validateChatMessage(value: unknown, options?: {
|
|
88
|
+
maxLength?: number;
|
|
89
|
+
}): ChatValidation;
|
package/dist/game/chat.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { decideRateWindow } from "../time/rateWindow.js";
|
|
2
|
+
export const DEFAULT_CHAT_RATE_LIMIT = { count: 1, perMs: 2_000 };
|
|
2
3
|
export const DEFAULT_CHAT_HISTORY_LIMIT = 100;
|
|
3
|
-
export const DEFAULT_CHAT_BODY_LENGTH =
|
|
4
|
+
export const DEFAULT_CHAT_BODY_LENGTH = 240;
|
|
4
5
|
export const DEFAULT_PROXIMITY_CHAT_RADIUS = 20;
|
|
5
6
|
export const WHISPER_CHANNEL_PREFIX = "whisper:";
|
|
6
7
|
export function whisperChannelId(a, b) {
|
|
@@ -11,15 +12,17 @@ export function createChatRateLimiter(limit) {
|
|
|
11
12
|
const windows = new Map();
|
|
12
13
|
return {
|
|
13
14
|
allow(key, atMs) {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
const result = decideRateWindow(windows.get(key) ?? [], atMs, { windowMs: limit.perMs, max: limit.count });
|
|
16
|
+
windows.set(key, result.timestamps);
|
|
17
|
+
return result.allowed;
|
|
18
|
+
},
|
|
19
|
+
snapshot() {
|
|
20
|
+
return Object.fromEntries(Array.from(windows, ([key, stamps]) => [key, [...stamps]]));
|
|
21
|
+
},
|
|
22
|
+
restore(state) {
|
|
23
|
+
windows.clear();
|
|
24
|
+
for (const [key, stamps] of Object.entries(state))
|
|
25
|
+
windows.set(key, [...stamps]);
|
|
23
26
|
},
|
|
24
27
|
};
|
|
25
28
|
}
|
|
@@ -93,11 +96,10 @@ export function createChat(deps) {
|
|
|
93
96
|
messagesByChannel.set(channelId, ring);
|
|
94
97
|
}
|
|
95
98
|
function sendResolved(fromUserId, channelId, body, def) {
|
|
96
|
-
const
|
|
97
|
-
if (
|
|
98
|
-
return { reason:
|
|
99
|
-
|
|
100
|
-
return { reason: "message too long" };
|
|
99
|
+
const validation = validateChatMessage(body, { maxLength: maxBodyLength });
|
|
100
|
+
if (!validation.ok)
|
|
101
|
+
return { reason: validation.reason };
|
|
102
|
+
const trimmed = validation.text;
|
|
101
103
|
let filteredBody = trimmed;
|
|
102
104
|
if (filter !== undefined) {
|
|
103
105
|
const filtered = filter.apply(trimmed);
|
|
@@ -136,10 +138,18 @@ export function createChat(deps) {
|
|
|
136
138
|
channels() {
|
|
137
139
|
return Array.from(channelDefs.values(), (def) => ({ ...def }));
|
|
138
140
|
},
|
|
139
|
-
send(fromUserId,
|
|
141
|
+
send(fromUserId, channelOrBody, suppliedBody) {
|
|
142
|
+
const channelId = suppliedBody === undefined ? "global" : channelOrBody;
|
|
143
|
+
const body = suppliedBody ?? channelOrBody;
|
|
140
144
|
const def = channelDefs.get(channelId) ?? null;
|
|
141
145
|
return sendResolved(fromUserId, channelId, body, def);
|
|
142
146
|
},
|
|
147
|
+
recent(options) {
|
|
148
|
+
return this.history(options?.channelId ?? "global", {
|
|
149
|
+
limit: Math.max(0, Math.min(100, Math.floor(options?.limit ?? 50) || 0)),
|
|
150
|
+
viewerUserId: options?.viewerUserId,
|
|
151
|
+
});
|
|
152
|
+
},
|
|
143
153
|
whisper(fromUserId, toUserId, body) {
|
|
144
154
|
if (fromUserId === toUserId)
|
|
145
155
|
return { reason: "cannot whisper yourself" };
|
|
@@ -153,14 +163,15 @@ export function createChat(deps) {
|
|
|
153
163
|
? ring
|
|
154
164
|
: ring.filter((message) => !hasBlocked(viewerUserId, message.fromUserId));
|
|
155
165
|
const limit = options?.limit;
|
|
156
|
-
return limit === undefined ? visible.slice() : visible.slice(-limit);
|
|
166
|
+
return limit === undefined ? visible.slice() : limit <= 0 ? [] : visible.slice(-limit);
|
|
157
167
|
},
|
|
158
168
|
snapshot() {
|
|
159
169
|
const messages = {};
|
|
160
170
|
for (const [channelId, ring] of messagesByChannel) {
|
|
161
171
|
messages[channelId] = ring.map((message) => ({ ...message }));
|
|
162
172
|
}
|
|
163
|
-
|
|
173
|
+
const rateWindows = Object.fromEntries(Array.from(limiters, ([id, limiter]) => [id, limiter.snapshot()]));
|
|
174
|
+
return { messages, counter, rateWindows };
|
|
164
175
|
},
|
|
165
176
|
hydrate(data) {
|
|
166
177
|
messagesByChannel.clear();
|
|
@@ -168,6 +179,24 @@ export function createChat(deps) {
|
|
|
168
179
|
messagesByChannel.set(channelId, ring.map((message) => ({ ...message })));
|
|
169
180
|
}
|
|
170
181
|
counter = data.counter;
|
|
182
|
+
limiters.clear();
|
|
183
|
+
for (const [channelId, state] of Object.entries(data.rateWindows ?? {})) {
|
|
184
|
+
limiterFor(channelId, channelDefs.get(channelId)?.rateLimit ?? defaultRateLimit).restore(state);
|
|
185
|
+
}
|
|
171
186
|
},
|
|
172
187
|
};
|
|
173
188
|
}
|
|
189
|
+
/** Strip control characters and enforce the shared chat message policy before storage or broadcast. */
|
|
190
|
+
export function validateChatMessage(value, options = {}) {
|
|
191
|
+
const maxLength = options.maxLength ?? DEFAULT_CHAT_BODY_LENGTH;
|
|
192
|
+
if (!Number.isSafeInteger(maxLength) || maxLength < 1)
|
|
193
|
+
throw new RangeError("Invalid chat length limit");
|
|
194
|
+
if (typeof value !== "string")
|
|
195
|
+
return { ok: false, reason: "invalid message" };
|
|
196
|
+
const text = value.replace(/[\u0000-\u001f\u007f-\u009f]/g, "").trim();
|
|
197
|
+
if (!text.length)
|
|
198
|
+
return { ok: false, reason: "empty message" };
|
|
199
|
+
if (text.length > maxLength)
|
|
200
|
+
return { ok: false, reason: "message too long" };
|
|
201
|
+
return { ok: true, text };
|
|
202
|
+
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { GameContext } from "../runtime/gameContext.js";
|
|
2
|
+
import { type ActionContextStack } from "../input/actionContexts.js";
|
|
3
|
+
import type { ActionCodesMap } from "../input/actionBindings.js";
|
|
2
4
|
export declare const PLAY_CONTROLS_STORE_KEY = "jg.playControls";
|
|
5
|
+
/** Returns the context stack associated with a game context. */
|
|
6
|
+
export declare function actionContextStack(ctx: GameContext): ActionContextStack;
|
|
7
|
+
/** Applies active contexts to a base action map for shell input tracking. */
|
|
8
|
+
export declare function activeActionCodes(ctx: GameContext, base: ActionCodesMap): ActionCodesMap;
|
|
3
9
|
export declare function setPlayControlsActive(ctx: GameContext, active: boolean): void;
|
|
4
10
|
export declare function playControlsActive(ctx: GameContext): boolean;
|
package/dist/game/controlGate.js
CHANGED
|
@@ -1,7 +1,36 @@
|
|
|
1
|
+
import { createActionContextStack } from "../input/actionContexts.js";
|
|
1
2
|
export const PLAY_CONTROLS_STORE_KEY = "jg.playControls";
|
|
3
|
+
const ACTION_CONTEXTS = new WeakMap();
|
|
4
|
+
function contextsFor(ctx) {
|
|
5
|
+
let stack = ACTION_CONTEXTS.get(ctx);
|
|
6
|
+
if (stack === undefined) {
|
|
7
|
+
stack = createActionContextStack();
|
|
8
|
+
ACTION_CONTEXTS.set(ctx, stack);
|
|
9
|
+
}
|
|
10
|
+
return stack;
|
|
11
|
+
}
|
|
12
|
+
/** Returns the context stack associated with a game context. */
|
|
13
|
+
export function actionContextStack(ctx) {
|
|
14
|
+
return contextsFor(ctx);
|
|
15
|
+
}
|
|
16
|
+
/** Applies active contexts to a base action map for shell input tracking. */
|
|
17
|
+
export function activeActionCodes(ctx, base) {
|
|
18
|
+
const stack = contextsFor(ctx);
|
|
19
|
+
const layered = createActionContextStack();
|
|
20
|
+
layered.push({ id: "__jg_base_actions", codes: base, passthrough: true });
|
|
21
|
+
for (const context of stack.snapshot().contexts)
|
|
22
|
+
layered.push(context);
|
|
23
|
+
return layered.active();
|
|
24
|
+
}
|
|
2
25
|
export function setPlayControlsActive(ctx, active) {
|
|
26
|
+
const stack = contextsFor(ctx);
|
|
27
|
+
if (active)
|
|
28
|
+
stack.pop("menu");
|
|
29
|
+
else
|
|
30
|
+
stack.push({ id: "menu", codes: {}, passthrough: false });
|
|
3
31
|
ctx.game.store.set(PLAY_CONTROLS_STORE_KEY, active);
|
|
4
32
|
}
|
|
5
33
|
export function playControlsActive(ctx) {
|
|
6
|
-
return ctx.game.store.get(PLAY_CONTROLS_STORE_KEY) !== false
|
|
34
|
+
return ctx.game.store.get(PLAY_CONTROLS_STORE_KEY) !== false &&
|
|
35
|
+
!actionContextStack(ctx).snapshot().contexts.some((context) => context.id === "menu");
|
|
7
36
|
}
|
|
@@ -12,7 +12,10 @@ import { type AssetCatalog, type ModelAssetRef } from "../scene/assetCatalog.js"
|
|
|
12
12
|
import { type EntityStore } from "../scene/entityStore.js";
|
|
13
13
|
import type { StoreHandle } from "../store/defineStore.js";
|
|
14
14
|
import type { TimeConfig } from "../time/simClock.js";
|
|
15
|
+
import type { SimulationConfig } from "../runtime/simLoop.js";
|
|
15
16
|
import type { WorldFeature } from "../world/features.js";
|
|
17
|
+
import type { PhysicsBackend } from "../physics/physicsBackend.js";
|
|
18
|
+
import type { CharacterControllerConfig } from "../movement/characterController.js";
|
|
16
19
|
/** Tunes offline whole-world save (`defineGameDefinition({ persist })`). Offline games already autosave to `localStorage` by default — pass this only to tune it (or `persist: false` to opt out). Defaults: continuous `autosave` to `localStorage`, one slot, no version. */
|
|
17
20
|
export interface PersistConfig {
|
|
18
21
|
/** `"autosave"` (default) writes on a trailing timer while the world changes — at most once per `autosaveMs`, so a never-idle world (AI, physics, day/night) still persists; `"manual"` writes only on `ctx.game.save.checkpoint()` (save points / quest triggers). */
|
|
@@ -24,10 +27,14 @@ export interface PersistConfig {
|
|
|
24
27
|
/** Autosave debounce in ms (autosave mode). Default 3000. */
|
|
25
28
|
autosaveMs?: number;
|
|
26
29
|
}
|
|
27
|
-
/** World gravity and jump tuning, plus
|
|
30
|
+
/** World gravity and jump tuning, plus the optional rigid-body backend and character capsule. */
|
|
28
31
|
export interface PhysicsConfig {
|
|
29
32
|
gravity?: number;
|
|
30
33
|
jumpVelocity?: number;
|
|
34
|
+
/** Physics solver used by the shell movement controller and the context's physics stage. */
|
|
35
|
+
backend?: PhysicsBackend;
|
|
36
|
+
/** Capsule dimensions for players using the configured physics backend. */
|
|
37
|
+
controller?: CharacterControllerConfig;
|
|
31
38
|
/** Opt placed scene objects into projectile raycasts as blocking cover AABBs. Default false — enabling changes where shots land. */
|
|
32
39
|
projectileObstacles?: boolean;
|
|
33
40
|
}
|
|
@@ -180,6 +187,8 @@ export interface GameDefinition<TAssetRef extends ModelAssetRef = ModelAssetRef,
|
|
|
180
187
|
physics?: PhysicsConfig;
|
|
181
188
|
/** Simulation clock: real→game time scale, selectable speeds, calendar. Exposed as `ctx.time`; the shell feeds its scaled dt to `loop.onTick`. */
|
|
182
189
|
time?: TimeConfig;
|
|
190
|
+
/** How the simulation is stepped — a fixed `hz` with render interpolation, or `"variable"` (default) once per frame. Exposed as `ctx.sim`. */
|
|
191
|
+
simulation?: SimulationConfig;
|
|
183
192
|
/** Per-action ring-buffer capacity for `ctx.game.feed`. Default 20. */
|
|
184
193
|
feed?: GameFeedOptions;
|
|
185
194
|
inventories?: Record<string, InventoryDeclaration>;
|
package/dist/game/events.d.ts
CHANGED
|
@@ -12,11 +12,17 @@ export interface EntityDiedEvent {
|
|
|
12
12
|
position: [number, number, number];
|
|
13
13
|
serverId?: string;
|
|
14
14
|
}
|
|
15
|
-
/** Request that an entity's rig play a one-shot animation clip bound to `event` in its `animation.oneShots` (e.g. an "attack" swing); the shell resolves the clip and plays it once over the locomotion state. */
|
|
15
|
+
/** Request that an entity's rig play a one-shot animation clip bound to `event` in its `animation.oneShots` (e.g. an "attack" swing); the shell resolves the clip and plays it once over the locomotion state. With an `animation.graph`, `event` arms the trigger of that name. */
|
|
16
16
|
export interface EntityAnimationEvent {
|
|
17
17
|
instanceId: string;
|
|
18
18
|
event: string;
|
|
19
19
|
}
|
|
20
|
+
/** A named clip moment (`AnimGraph.events`) the rig just crossed — foot plant, hit frame, reload point. Audio and combat listen by `name`. */
|
|
21
|
+
export interface AnimationClipEvent {
|
|
22
|
+
instanceId: string;
|
|
23
|
+
name: string;
|
|
24
|
+
clip: string;
|
|
25
|
+
}
|
|
20
26
|
/**
|
|
21
27
|
* One granted reward: an inventory `item` id or a `currency` id, plus how many. Named because every
|
|
22
28
|
* consumer of `loot.granted` needs it — a toast renderer, a loot log, a session summary — and each
|
|
@@ -230,6 +236,12 @@ export interface AudioLoopSetEvent {
|
|
|
230
236
|
rate?: number;
|
|
231
237
|
gain?: number;
|
|
232
238
|
at?: readonly [number, number, number];
|
|
239
|
+
/** Emitter velocity (world units/s) for doppler on sounds that declare `doppler`. */
|
|
240
|
+
velocity?: readonly [number, number, number];
|
|
241
|
+
/** Lowpass cutoff in Hz. */
|
|
242
|
+
lowpass?: number;
|
|
243
|
+
/** Highpass cutoff in Hz. */
|
|
244
|
+
highpass?: number;
|
|
233
245
|
}
|
|
234
246
|
/** Stop and dispose the retained loop `id`; an unknown `id` is ignored (#1051). */
|
|
235
247
|
export interface AudioLoopStopEvent {
|
|
@@ -269,6 +281,7 @@ export interface GameEventMap {
|
|
|
269
281
|
"audio.loopSet": AudioLoopSetEvent;
|
|
270
282
|
"audio.loopStop": AudioLoopStopEvent;
|
|
271
283
|
"entity.animation": EntityAnimationEvent;
|
|
284
|
+
"animation.event": AnimationClipEvent;
|
|
272
285
|
}
|
|
273
286
|
export type GameEventHandler<TPayload> = (payload: TPayload) => void;
|
|
274
287
|
export interface GameEvents<TMap extends GameEventMap = GameEventMap> {
|
package/dist/game/lootTable.d.ts
CHANGED
|
@@ -38,6 +38,10 @@ export interface LootRegistry {
|
|
|
38
38
|
register(def: LootTableDef): void;
|
|
39
39
|
has(id: string): boolean;
|
|
40
40
|
roll(id: string, rng?: () => number): Drop[];
|
|
41
|
+
/** Registered tables in registration order; plain JSON unless an entry uses `generate`. */
|
|
42
|
+
snapshot(): LootTableDef[];
|
|
43
|
+
/** Replaces every registered table with `next`, validating each. */
|
|
44
|
+
restore(next: readonly LootTableDef[]): void;
|
|
41
45
|
}
|
|
42
46
|
/** Options for {@link createLootRegistry} — inject a default RNG so bare `roll(id)` uses the world stream. */
|
|
43
47
|
export interface LootRegistryOptions {
|
package/dist/game/lootTable.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { resolveRng } from "../random/resolveRng.js";
|
|
2
|
+
function copyDef(def) {
|
|
3
|
+
return {
|
|
4
|
+
...def,
|
|
5
|
+
entries: def.entries.map((entry) => ({ ...entry, count: typeof entry.count === "number" ? entry.count : [entry.count[0], entry.count[1]] })),
|
|
6
|
+
};
|
|
7
|
+
}
|
|
2
8
|
function assertValidEntry(entry, mode) {
|
|
3
9
|
const kinds = [entry.item !== undefined, entry.currency !== undefined, entry.generate !== undefined].filter(Boolean).length;
|
|
4
10
|
if (kinds !== 1) {
|
|
@@ -88,6 +94,16 @@ export function createLootRegistry(options = {}) {
|
|
|
88
94
|
}
|
|
89
95
|
return drops;
|
|
90
96
|
},
|
|
97
|
+
snapshot() {
|
|
98
|
+
return [...tables.values()].map(copyDef);
|
|
99
|
+
},
|
|
100
|
+
restore(next) {
|
|
101
|
+
for (const def of next)
|
|
102
|
+
assertValidDef(def);
|
|
103
|
+
tables.clear();
|
|
104
|
+
for (const def of next)
|
|
105
|
+
tables.set(def.id, copyDef(def));
|
|
106
|
+
},
|
|
91
107
|
};
|
|
92
108
|
}
|
|
93
109
|
/**
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Viewport dimensions in device-independent canvas pixels. */
|
|
2
|
+
export interface PixelPerfectViewport {
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
}
|
|
6
|
+
/** The orthographic bounds and scale derived from a pixel-perfect camera config. */
|
|
7
|
+
export interface PixelPerfectFrustum {
|
|
8
|
+
left: number;
|
|
9
|
+
right: number;
|
|
10
|
+
top: number;
|
|
11
|
+
bottom: number;
|
|
12
|
+
zoom: number;
|
|
13
|
+
scale: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Computes an orthographic frustum whose world units map to the requested pixel density.
|
|
17
|
+
* Integer scaling chooses the largest whole-number density multiplier available in the viewport.
|
|
18
|
+
*/
|
|
19
|
+
export declare function pixelPerfectFrustum(viewport: PixelPerfectViewport, pixelsPerUnit: number, integerScale?: boolean): PixelPerfectFrustum;
|
|
20
|
+
/** Snaps a camera position to the authored pixel grid without changing its orientation. */
|
|
21
|
+
export declare function snapPixelPerfectPosition<T extends {
|
|
22
|
+
x: number;
|
|
23
|
+
y: number;
|
|
24
|
+
z: number;
|
|
25
|
+
}>(position: T, pixelsPerUnit: number): T;
|