@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
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Computes an orthographic frustum whose world units map to the requested pixel density.
|
|
3
|
+
* Integer scaling chooses the largest whole-number density multiplier available in the viewport.
|
|
4
|
+
*/
|
|
5
|
+
export function pixelPerfectFrustum(viewport, pixelsPerUnit, integerScale = false) {
|
|
6
|
+
const width = Math.max(1, viewport.width);
|
|
7
|
+
const height = Math.max(1, viewport.height);
|
|
8
|
+
const density = Math.max(1e-6, pixelsPerUnit);
|
|
9
|
+
const scale = integerScale ? Math.max(1, Math.floor(Math.min(width, height) / density)) : 1;
|
|
10
|
+
const zoom = density * scale;
|
|
11
|
+
return {
|
|
12
|
+
left: -width / (2 * zoom),
|
|
13
|
+
right: width / (2 * zoom),
|
|
14
|
+
top: height / (2 * zoom),
|
|
15
|
+
bottom: -height / (2 * zoom),
|
|
16
|
+
zoom,
|
|
17
|
+
scale,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Snaps a camera position to the authored pixel grid without changing its orientation. */
|
|
21
|
+
export function snapPixelPerfectPosition(position, pixelsPerUnit) {
|
|
22
|
+
const density = Math.max(1e-6, pixelsPerUnit);
|
|
23
|
+
return {
|
|
24
|
+
...position,
|
|
25
|
+
x: Math.round(position.x * density) / density,
|
|
26
|
+
y: Math.round(position.y * density) / density,
|
|
27
|
+
z: Math.round(position.z * density) / density,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -1,14 +1,22 @@
|
|
|
1
|
+
import type { AnimGraph } from "../anim/animGraph.js";
|
|
1
2
|
import type { AudioBusDef, SoundDef } from "../audio/audioFalloff.js";
|
|
2
3
|
import type { MusicTheme } from "../audio/music.js";
|
|
3
4
|
import type { EditorCatalogDefinition, EditorDocument } from "../editor/types.js";
|
|
4
5
|
import type { PostProcessingConfig } from "../render/postProcessing.js";
|
|
6
|
+
import type { EnvironmentSource } from "../render/environment.js";
|
|
7
|
+
import type { SpriteAtlas } from "../assets/spriteAtlas.js";
|
|
5
8
|
import type { LookPreset } from "../render/lookPreset.js";
|
|
9
|
+
import type { GamepadFeelConfig } from "../input/gamepadModel.js";
|
|
10
|
+
import type { LocalPlayersConfig } from "../runtime/localPlayers.js";
|
|
6
11
|
import type { TouchControlsConfig } from "../input/touchScheme.js";
|
|
7
12
|
import type { GameSettingsConfig } from "../settings/settingsModel.js";
|
|
13
|
+
import type { GraphicsProfile } from "../settings/graphicsProfile.js";
|
|
8
14
|
import type { GameOrientation } from "../ui/orientation.js";
|
|
9
15
|
import type { HudPlatform, HudViewportConfig } from "../ui/hudScale.js";
|
|
10
16
|
import type { PositionedPrompt } from "../interaction/proximityPrompt.js";
|
|
17
|
+
import type { ChaseCameraTuning } from "../runtime/cameraDirector.js";
|
|
11
18
|
import type { CatalogEntityRole, GameContext, GameContextContent } from "../runtime/gameContext.js";
|
|
19
|
+
import type { FreeFlightBindings } from "../movement/freeFlight.js";
|
|
12
20
|
import type { ModelDims } from "../scene/assetCatalog.js";
|
|
13
21
|
import type { PartMotionParams, PartRole } from "./partAnimation.js";
|
|
14
22
|
import type { CollisionMeshData } from "../scene/collisionMesh.js";
|
|
@@ -16,14 +24,21 @@ import type { SkyEnvironmentConfig } from "../world/features.js";
|
|
|
16
24
|
import type { VisibilityConfig } from "../visibility/config.js";
|
|
17
25
|
import type { GameDefinition, GameLoop } from "./defineGame.js";
|
|
18
26
|
import type { GameCameraConfig } from "./cameraConfig.js";
|
|
27
|
+
import type { ViewportsConfig } from "./viewports.js";
|
|
19
28
|
import type { LootFilterRule } from "./lootFilter.js";
|
|
20
29
|
import type { RarityStyle } from "./worldItem.js";
|
|
21
|
-
export type { CameraFollowState, CameraKeyframe, CameraProjection, CameraRigKind, CameraShakeConfig, ChaseCameraConfig, ChaseView, CinematicCameraConfig, FirstPersonCameraConfig, GameCameraConfig, InspectionCameraConfig, InspectionZoomAnchor, LockOnCameraConfig, ObserverCameraConfig, PlayerFovConfig, RtsCameraConfig, ShoulderCameraConfig, SideScrollCameraConfig, TopDownCameraConfig, TurntableCameraConfig, } from "./cameraConfig.js";
|
|
30
|
+
export type { CameraFollowState, CameraKeyframe, CameraProjection, CameraRigKind, CameraWeaponView, CameraShakeConfig, ChaseCameraConfig, ChaseView, CinematicCameraConfig, FirstPersonCameraConfig, GameCameraConfig, InspectionCameraConfig, InspectionZoomAnchor, LockOnCameraConfig, ObserverCameraConfig, PlayerFovConfig, RtsCameraConfig, ShoulderCameraConfig, SideScrollCameraConfig, TopDownCameraConfig, TurntableCameraConfig, } from "./cameraConfig.js";
|
|
22
31
|
export { CAMERA_FRUSTUM_DEFAULTS } from "./cameraConfig.js";
|
|
32
|
+
/**
|
|
33
|
+
* Mouse/touch pointer bindings for `defineGame({ pointer })`.
|
|
34
|
+
*
|
|
35
|
+
* @capability pointer-commands click an entity or the ground to run a command (click-to-target, click-to-move, grab loot, marquee select)
|
|
36
|
+
*/
|
|
23
37
|
export interface PointerConfig {
|
|
24
38
|
/**
|
|
25
|
-
* Left-click
|
|
26
|
-
*
|
|
39
|
+
* Left-click runs this command with `{ point, entity, object }`; `entity` is the clicked entity id or
|
|
40
|
+
* null, so the same command covers click-to-move, ground-target and click-to-target
|
|
41
|
+
* (`ctx.scene.entity.setTarget`). Suppresses the default left-click hotbar fire.
|
|
27
42
|
*/
|
|
28
43
|
moveCommand?: string;
|
|
29
44
|
/** Enable left-drag marquee + single-click box-select of entities (RTS unit command). */
|
|
@@ -52,6 +67,11 @@ export interface EntitySpriteConfig {
|
|
|
52
67
|
width: number;
|
|
53
68
|
height: number;
|
|
54
69
|
y: number;
|
|
70
|
+
/** Optional atlas-backed animation. The atlas image supersedes `url` when present. */
|
|
71
|
+
clip?: {
|
|
72
|
+
atlas: SpriteAtlas;
|
|
73
|
+
animation: string;
|
|
74
|
+
};
|
|
55
75
|
}
|
|
56
76
|
export interface WorldItemRenderConfig {
|
|
57
77
|
/** Baseline rarity→color/beam/label render binding (#32); the game's rarity palette. */
|
|
@@ -119,6 +139,14 @@ export interface ModelAnimationConfig {
|
|
|
119
139
|
* `string[]` picks a random variant per trigger. `death` clamps on its final frame instead of returning.
|
|
120
140
|
*/
|
|
121
141
|
oneShots?: Record<string, string | readonly string[]>;
|
|
142
|
+
/**
|
|
143
|
+
* A full animation graph (`@jgengine/core/anim/animGraph`) — layered state machine with blend trees, data
|
|
144
|
+
* transitions, triggers, and clip events. Takes over from `states`/`oneShots` when set: the shell feeds the
|
|
145
|
+
* entity's smoothed ground speed as the `speed` parameter plus anything under the entity blackboard key
|
|
146
|
+
* `ANIM_PARAMS_KEY`, arms triggers from `entity.animation` (`hit` and `death` from their combat events), and
|
|
147
|
+
* emits `animation.event` for clip events. `locomotionGraph()` builds the default idle/walk/run graph.
|
|
148
|
+
*/
|
|
149
|
+
graph?: AnimGraph;
|
|
122
150
|
}
|
|
123
151
|
/**
|
|
124
152
|
* Real PBR map URLs (e.g. `buildMaterialCatalog(...).resolve(id)!.maps` from `@jgengine/assets`)
|
|
@@ -130,8 +158,11 @@ export interface ModelMaterialMaps {
|
|
|
130
158
|
normal?: string;
|
|
131
159
|
roughness?: string;
|
|
132
160
|
ao?: string;
|
|
161
|
+
metalness?: string;
|
|
162
|
+
emissive?: string;
|
|
163
|
+
height?: string;
|
|
133
164
|
}
|
|
134
|
-
/** Per-entity PBR material override (#151.3) applied to every
|
|
165
|
+
/** Per-entity PBR material override (#151.3) applied to every standard or physical material in the model's cloned scene graph. */
|
|
135
166
|
export interface ModelMaterialOverride {
|
|
136
167
|
color?: string;
|
|
137
168
|
metalness?: number;
|
|
@@ -203,6 +234,8 @@ export interface ModelConfig {
|
|
|
203
234
|
collisionMesh?: CollisionMeshData;
|
|
204
235
|
/** Per-entity PBR tint/finish override (#151.3); cloned onto each `MeshStandardMaterial` in the model so shared GLTF caches stay untouched. */
|
|
205
236
|
material?: ModelMaterialOverride;
|
|
237
|
+
/** Shadow participation of every mesh in the model. Default `"both"` (casts and receives); `"none"` for foliage cards, decals, or viewmodels that must never shadow the world. */
|
|
238
|
+
shadows?: "cast" | "receive" | "both" | "none";
|
|
206
239
|
/**
|
|
207
240
|
* Plays a GLTF animation clip on the model when the source has any (skinned or not).
|
|
208
241
|
* A model resolved from a catalog id whose index carries `clips` animates automatically —
|
|
@@ -212,6 +245,14 @@ export interface ModelConfig {
|
|
|
212
245
|
* rig's bind pose.
|
|
213
246
|
*/
|
|
214
247
|
animation?: ModelAnimationConfig | "auto" | "none";
|
|
248
|
+
/**
|
|
249
|
+
* Runtime inverse kinematics for a rigged model, applied after the animation mixer each frame.
|
|
250
|
+
* Feet follow slopes and steps while keeping each clip's swing lift, soles never sink below the
|
|
251
|
+
* ground, the pelvis drops so the lower leg can reach, and the correction fades out while airborne.
|
|
252
|
+
* `"auto"` finds the legs by bone name (`UpLeg`, `UpperLeg`, `Thigh`); bone names resolve on the
|
|
253
|
+
* loaded scene.
|
|
254
|
+
*/
|
|
255
|
+
ik?: "auto" | ModelIkConfig;
|
|
215
256
|
/** Props/weapons parented to named bones on this model's rig; each follows its bone through animation. */
|
|
216
257
|
attachments?: readonly ModelAttachment[];
|
|
217
258
|
/** Static kit-of-parts pieces stacked at fixed local offsets — no bone/rig involved. Use this for a compound entity assembled from several modular meshes (a castle keep from base + mid + roof pieces); use `attachments` for props parented to an animated rig's bones. Tag parts with a `role` to procedurally animate a rig-less character composition. */
|
|
@@ -219,6 +260,25 @@ export interface ModelConfig {
|
|
|
219
260
|
/** Tuning for the procedural part-motion driver when any part carries a `role`; omit for defaults. */
|
|
220
261
|
partMotion?: PartMotionParams;
|
|
221
262
|
}
|
|
263
|
+
/** Bone names and limits for {@link ModelConfig.ik}. */
|
|
264
|
+
export interface ModelIkConfig {
|
|
265
|
+
/** Thigh → shin → foot chains; omit to find them by bone name. `[]` turns foot IK off. */
|
|
266
|
+
feet?: readonly {
|
|
267
|
+
root: string;
|
|
268
|
+
mid: string;
|
|
269
|
+
tip: string;
|
|
270
|
+
}[];
|
|
271
|
+
/** Bone shifted down when a foot has to reach lower ground; defaults to the first thigh's parent. */
|
|
272
|
+
pelvis?: string;
|
|
273
|
+
/** Largest foot or pelvis correction, as a fraction of leg length. Default `0.4`. */
|
|
274
|
+
maxAdjust?: number;
|
|
275
|
+
/** Tilt planted feet to the ground normal, `0..1`. Default `1`. */
|
|
276
|
+
alignToGround?: number;
|
|
277
|
+
/** Turns this bone toward the camera. */
|
|
278
|
+
lookAt?: {
|
|
279
|
+
bone: string;
|
|
280
|
+
};
|
|
281
|
+
}
|
|
222
282
|
export interface ObjectStyle {
|
|
223
283
|
color?: string;
|
|
224
284
|
opacity?: number;
|
|
@@ -255,10 +315,29 @@ export interface HemisphereLightingConfig {
|
|
|
255
315
|
groundColor?: string;
|
|
256
316
|
intensity?: number;
|
|
257
317
|
}
|
|
318
|
+
/** A bounded dynamic point light in world space. */
|
|
319
|
+
export interface PointLightingConfig {
|
|
320
|
+
color?: string;
|
|
321
|
+
intensity?: number;
|
|
322
|
+
position: readonly [number, number, number];
|
|
323
|
+
distance?: number;
|
|
324
|
+
decay?: number;
|
|
325
|
+
castShadow?: boolean;
|
|
326
|
+
}
|
|
327
|
+
/** A bounded dynamic spot light in world space. */
|
|
328
|
+
export interface SpotLightingConfig extends PointLightingConfig {
|
|
329
|
+
target?: readonly [number, number, number];
|
|
330
|
+
angle?: number;
|
|
331
|
+
penumbra?: number;
|
|
332
|
+
}
|
|
258
333
|
/** Declarative lighting replacing the shell's hardcoded ambient/directional default (#207.5); mounts regardless of world kind, only when supplied. */
|
|
259
334
|
export interface LightingConfig {
|
|
260
335
|
ambient?: AmbientLightingConfig;
|
|
261
336
|
directional?: readonly DirectionalLightingConfig[];
|
|
337
|
+
point?: readonly PointLightingConfig[];
|
|
338
|
+
spot?: readonly SpotLightingConfig[];
|
|
339
|
+
/** Maximum dynamic point + spot lights mounted by the shell. Default 8. */
|
|
340
|
+
maxDynamicLights?: number;
|
|
262
341
|
hemisphere?: HemisphereLightingConfig;
|
|
263
342
|
}
|
|
264
343
|
export interface BackdropFogConfig {
|
|
@@ -274,6 +353,62 @@ export interface BackdropConfig {
|
|
|
274
353
|
sky?: SkyEnvironmentConfig;
|
|
275
354
|
fog?: BackdropFogConfig;
|
|
276
355
|
}
|
|
356
|
+
/** Free-flight families moved into the walk controller so one seam covers ground + air — creative/spectator/noclip/hover are weightless, aircraft, rotorcraft and rockets use `physics/aircraftDynamics`. */
|
|
357
|
+
export interface FlightConfig {
|
|
358
|
+
/** Which free-flight family to use when flight is active. Default "creative". */
|
|
359
|
+
mode?: "creative" | "spectator" | "noclip" | "hover";
|
|
360
|
+
/** Horizontal base speed (units/s). Default 8. */
|
|
361
|
+
speed?: number;
|
|
362
|
+
/** Vertical base speed for ascend/descend. Default = `speed`. */
|
|
363
|
+
verticalSpeed?: number;
|
|
364
|
+
/** Sprint multiplier while shift is held. Default 1.8. */
|
|
365
|
+
sprintMultiplier?: number;
|
|
366
|
+
/** Lerp rate toward target velocity. Default 20. */
|
|
367
|
+
acceleration?: number;
|
|
368
|
+
/** Downward acceleration for hover mode. Default 0 (weightless). */
|
|
369
|
+
gravity?: number;
|
|
370
|
+
/** Upward thrust while ascending in hover. Default 32. */
|
|
371
|
+
thrust?: number;
|
|
372
|
+
/** When true, W moves along the look pitch (6DOF spectator). Default false. */
|
|
373
|
+
alignWithLook?: boolean;
|
|
374
|
+
/** Whether flight slides against world solids. Default true for creative/hover, false for spectator/noclip. */
|
|
375
|
+
collide?: boolean;
|
|
376
|
+
/** Axis bindings for this flight — which actions drive each axis. Unlisted axes keep the defaults above. */
|
|
377
|
+
bindings?: FreeFlightBindings;
|
|
378
|
+
/** Camera POV while this flight is active — chase tuning overlay applied on mount, cleared on dismount. */
|
|
379
|
+
camera?: ChaseCameraTuning | null;
|
|
380
|
+
/** Gate flight behind live game state — creative toggle, stamina check, etc. `false` stays walking. Default true when `flight` is present. */
|
|
381
|
+
canFly?: (ctx: GameContext) => boolean;
|
|
382
|
+
}
|
|
383
|
+
/** Feel knobs for the built-in walk controller; see {@link PlayerMovementConfig.feel}. */
|
|
384
|
+
export interface MovementFeelConfig {
|
|
385
|
+
/** Ground velocity response, 1/s (default 26). Lower drifts into and out of motion; higher is snappy. */
|
|
386
|
+
groundAcceleration?: number;
|
|
387
|
+
/** Air velocity response, 1/s (default 12). `0` commits to the jump arc; high values steer freely in the air. */
|
|
388
|
+
airAcceleration?: number;
|
|
389
|
+
/** Stopping response on the ground with no input, 1/s (default 18). Lower slides like ice. */
|
|
390
|
+
groundFriction?: number;
|
|
391
|
+
/** Sprint speed as a multiple of walk speed (default 2.25). */
|
|
392
|
+
runMultiplier?: number;
|
|
393
|
+
/** Crouch speed as a multiple of walk speed (default 0.45). */
|
|
394
|
+
crouchMultiplier?: number;
|
|
395
|
+
/** A jump pressed up to this many ms before landing fires on landing (default 0). */
|
|
396
|
+
jumpBufferMs?: number;
|
|
397
|
+
/** A jump pressed up to this many ms after walking off a ledge still fires (default 0). */
|
|
398
|
+
coyoteMs?: number;
|
|
399
|
+
/** Rising speed kept when jump is released early, 0..1 (default 1: no cut). Lower makes a tap a hop. */
|
|
400
|
+
jumpCutFactor?: number;
|
|
401
|
+
/** Gravity multiplier near the peak, while vertical speed is under `apexSpeed` (default 1). Below 1 hangs. */
|
|
402
|
+
apexGravityScale?: number;
|
|
403
|
+
/** Vertical speed under which `apexGravityScale` applies, m/s (default 1.5). */
|
|
404
|
+
apexSpeed?: number;
|
|
405
|
+
/** Gravity multiplier while falling (default 1). Above 1 drops faster than it rose. */
|
|
406
|
+
fallGravityScale?: number;
|
|
407
|
+
/** After landing, jumps wait and walk speed ramps back from `landingSpeedScale` over this many ms (default 0). */
|
|
408
|
+
landingRecoveryMs?: number;
|
|
409
|
+
/** Walk speed share right after landing when `landingRecoveryMs` is set (default 0.5). */
|
|
410
|
+
landingSpeedScale?: number;
|
|
411
|
+
}
|
|
277
412
|
/** Movement-control levers for the shell-driven local player walk controller. */
|
|
278
413
|
export interface PlayerMovementConfig {
|
|
279
414
|
/** "free" (default) moves camera-relative across the plane; "axis" locks travel to one world axis; "grid" snaps each committed position to cell centers. */
|
|
@@ -298,6 +433,13 @@ export interface PlayerMovementConfig {
|
|
|
298
433
|
canSprint?: (ctx: GameContext) => boolean;
|
|
299
434
|
/** Fraction of walk speed while backpedalling (holding `moveBack`). Overrides the engine default (0.65). */
|
|
300
435
|
backpedalMult?: number;
|
|
436
|
+
/**
|
|
437
|
+
* Walk-controller feel. Acceleration and friction are exponential response rates (1/s): higher reaches
|
|
438
|
+
* the target velocity or stops sooner. Omitted fields keep the engine defaults, so nothing changes until a
|
|
439
|
+
* game sets one. The jump fields apply to the kinematic walk; a `physics.backend` capsule or `collision.voxel`
|
|
440
|
+
* body jumps on the press frame with plain gravity. Tune against `measureMovement` (`@jgengine/core/movement/movementProbe`).
|
|
441
|
+
*/
|
|
442
|
+
feel?: MovementFeelConfig;
|
|
301
443
|
/** Radians/second the rendered body rotates toward its movement heading (shortest arc), so strafing/backpedalling read as a turn rather than an instant flip; also the rate the internally-integrated `turnLeft`/`turnRight` heading turns when the shell doesn't own yaw. Unset = body facing snaps instantly (no change to existing feel). */
|
|
302
444
|
turnSpeed?: number;
|
|
303
445
|
/** On-foot swimming when the terrain declares a `waterLevel` and the player is submerged: caps speed and floats them at the surface. `true` uses defaults; default off. */
|
|
@@ -308,6 +450,15 @@ export interface PlayerMovementConfig {
|
|
|
308
450
|
slopeSlide?: {
|
|
309
451
|
maxClimbSlope?: number;
|
|
310
452
|
} | boolean;
|
|
453
|
+
/**
|
|
454
|
+
* Free-flight — creative/spectator/noclip/hover — folded into the same walk controller so a game
|
|
455
|
+
* doesn't hand-roll a second movement loop to get Minecraft-like flight. `true` uses creative defaults;
|
|
456
|
+
* an object tunes speed/vertical/sprint/collide/canFly. Horizontal is always yaw-relative strafe
|
|
457
|
+
* (A = left, D = right — never roll/bank), vertical is Space/Ctrl, and aircraft, rotorcraft and rockets
|
|
458
|
+
* use `physics/aircraftDynamics`. The shell drives this automatically when present; `canFly` toggles
|
|
459
|
+
* between walking and flying per tick without rebuilding the config.
|
|
460
|
+
*/
|
|
461
|
+
flight?: FlightConfig | boolean;
|
|
311
462
|
}
|
|
312
463
|
/** One frame's movement resolution handed to `PlayerMovementConfig.beforeCommit`. */
|
|
313
464
|
export interface MovementCommitFrame {
|
|
@@ -412,7 +563,7 @@ export interface PlayableGame<TUi = unknown, TWorldOverlay = unknown, TRenderEnt
|
|
|
412
563
|
/** Optional canvas-layer VFX component (e.g. traveling projectiles); receives `{ ctx }` (#542) so overlay VFX read live engine state without a separate hook or a module-global workaround. */
|
|
413
564
|
WorldOverlay?: TOverlay;
|
|
414
565
|
/** Replaces the default demo backdrop (ground + grid + rocks) with the game's own scene — ground, sky, structures. Camera, input, HUD, entity rendering, and the loop stay shell-provided; supply your world without forking the shell. When unset and `game.world` is an `environment()` descriptor, the shell auto-renders that world here — no manual wiring needed. */
|
|
415
|
-
environment?: TWorldOverlay;
|
|
566
|
+
environment?: TWorldOverlay | EnvironmentSource;
|
|
416
567
|
/** The game's authored scene document (`editor.scene.json`, normalized). When set, `defineGame` always mounts `AuthoredScene` over it (draped paths, scatter, studios, placed catalog props) — any `WorldOverlay` renders alongside it as VFX only — and the embedded editor opens it as its default layers: the zero-wiring path from document to play and edit modes. */
|
|
417
568
|
editorLayers?: EditorDocument;
|
|
418
569
|
/** Game-exported gameplay catalog definitions (schemas + defaults). `GameHost` forwards them to the summoned editor's Data panel; no per-game editor bootstrap needed. */
|
|
@@ -441,10 +592,18 @@ export interface PlayableGame<TUi = unknown, TWorldOverlay = unknown, TRenderEnt
|
|
|
441
592
|
capture?: GameCaptureConfig;
|
|
442
593
|
/** Cast/receive shadows across the scene (R3F Canvas shadow pass). Default true. */
|
|
443
594
|
shadows?: boolean;
|
|
595
|
+
/** Per-quality renderer budgets; omitted tiers use {@link DEFAULT_GRAPHICS_PROFILES}. */
|
|
596
|
+
graphics?: Partial<Record<"low" | "medium" | "high", Partial<GraphicsProfile>>>;
|
|
444
597
|
/** Pointer-driven input: click-to-move, box-select, right-click verbs, cursor aim (#22/#30/#31). */
|
|
445
598
|
pointer?: PointerConfig;
|
|
446
599
|
/** Touch controls on coarse-pointer devices. Unset derives a scheme from `input` (virtual joystick for movement actions, on-screen buttons for the rest); a config refines it with gestures and curated buttons; `false` opts out. */
|
|
447
600
|
touch?: TouchControlsConfig | false;
|
|
601
|
+
/** Gamepad stick deadzone, response curve and trigger deadzone the shell's pad poll applies before publishing analog values. Unset keeps the shell defaults (axial `0.12`/`0.95`, linear). */
|
|
602
|
+
gamepad?: GamepadFeelConfig;
|
|
603
|
+
/** Local seats on this screen for couch co-op. With `maxSlots > 1` each extra pad hot-joins its own seat on its first button press, the shell calls `loop.onNewPlayer(ctx, { userId, isNew: true })` for it, and its input lands on `localPlayers(ctx).local(slotId).input` instead of `ctx.input`. Unset keeps one seat: every device drives `ctx.player`. */
|
|
604
|
+
localPlayers?: LocalPlayersConfig;
|
|
605
|
+
/** Split-screen for local seats (`localPlayers`): one viewport per joined seat, each following its own entity. The primary seat keeps the main camera rig; post-processing is skipped while more than one viewport is drawn. */
|
|
606
|
+
viewports?: ViewportsConfig;
|
|
448
607
|
/** Phone orientation contract. Legacy `"landscape"`/`"portrait"` stays advisory (a dismissible rotate hint). The object form `{ mobile: "landscape-required" }` is strict — the shell shows an engine-owned rotate screen and blocks gameplay until the device is turned. See `GameOrientation`. */
|
|
449
608
|
orientation?: GameOrientation;
|
|
450
609
|
/** Where the game is meant to be played. Default `["web", "mobile"]` — design-resolution HUD fit is on for every game: `HudCanvas` auto-scales from `hudFit.designSize` down to the live viewport, so the desktop layout shrinks to fit a phone instead of overflowing it. Declare `["web"]` to opt a desktop-only game out (compact displays fall back to the legacy fixed 0.85 zoom). */
|
|
@@ -499,10 +658,12 @@ export interface PlayableGame<TUi = unknown, TWorldOverlay = unknown, TRenderEnt
|
|
|
499
658
|
collision?: VoxelCollisionConfig;
|
|
500
659
|
/** Movement-control levers (axis/grid constraints, object collision, pre-commit hook) for the shell-driven walk controller. */
|
|
501
660
|
movement?: PlayerMovementConfig;
|
|
502
|
-
/**
|
|
661
|
+
/** Graphics look profile (#1680). Unset/`"neutral"` keeps the output unstyled; `"photoreal"` and `"toon"` select authored presentation profiles, while `"cinematic"` and `"flat"` remain compatibility aliases. Explicit `lighting`/`backdrop`/`postProcessing` always wins. */
|
|
503
662
|
look?: LookPreset;
|
|
504
663
|
/** Declarative ambient/directional/hemisphere lighting (#207.5); replaces the shell's hardcoded default lights when present, regardless of world kind. */
|
|
505
664
|
lighting?: LightingConfig;
|
|
665
|
+
/** Image-based environment source; omit to use the procedural gradient probe. */
|
|
666
|
+
environmentSource?: EnvironmentSource;
|
|
506
667
|
/** Generic background/sky/fog (#207.6), applied for any world kind including a custom `environment` component. */
|
|
507
668
|
backdrop?: BackdropConfig;
|
|
508
669
|
/** Declarative post-processing chain (AO/bloom/tone-map/grade). When set, the shell mounts an EffectComposer and owns the render; absent leaves the renderer drawing directly (unchanged). */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { compileSystemSchedule, } from "./systemSchedule.js";
|
|
2
|
+
const MAX_FIXED_SUBSTEPS = 8;
|
|
2
3
|
function resolveModule(spec, ctx) {
|
|
3
4
|
if (spec === undefined)
|
|
4
5
|
return undefined;
|
|
@@ -56,10 +57,14 @@ export function installSystems(ctx, systems, options) {
|
|
|
56
57
|
for (const group of schedule.fixed) {
|
|
57
58
|
const step = 1 / group.rate;
|
|
58
59
|
let acc = (fixedAcc.get(group.rate) ?? 0) + dt;
|
|
59
|
-
|
|
60
|
+
let substeps = 0;
|
|
61
|
+
while (acc >= step && substeps < MAX_FIXED_SUBSTEPS) {
|
|
60
62
|
runIds(ctx, group.order, step);
|
|
61
63
|
acc -= step;
|
|
64
|
+
substeps += 1;
|
|
62
65
|
}
|
|
66
|
+
if (acc >= step)
|
|
67
|
+
acc %= step;
|
|
63
68
|
fixedAcc.set(group.rate, acc);
|
|
64
69
|
}
|
|
65
70
|
runIds(ctx, schedule.frameOrder, dt);
|
package/dist/game/toasts.d.ts
CHANGED
|
@@ -15,6 +15,11 @@ export interface Toast<T = string> {
|
|
|
15
15
|
export declare function appendToast<T>(toasts: readonly Toast<T>[], toast: Toast<T>, cap: number): readonly Toast<T>[];
|
|
16
16
|
/** Drop every toast whose `expiresAt` is at or before `now`. Returns the same array when nothing expired. */
|
|
17
17
|
export declare function pruneToasts<T>(toasts: readonly Toast<T>[], now: number): readonly Toast<T>[];
|
|
18
|
+
/** Plain JSON state of a {@link ToastQueue}: live toasts plus the id counter. */
|
|
19
|
+
export interface ToastQueueState<T = string> {
|
|
20
|
+
toasts: Toast<T>[];
|
|
21
|
+
counter: number;
|
|
22
|
+
}
|
|
18
23
|
/** Stateful transient-toast list with a size cap and time-to-live eviction. */
|
|
19
24
|
export interface ToastQueue<T = string> {
|
|
20
25
|
/** Raise a toast at `now` living `ttlSeconds` (falling back to the queue default); returns it. */
|
|
@@ -25,6 +30,9 @@ export interface ToastQueue<T = string> {
|
|
|
25
30
|
list(): readonly Toast<T>[];
|
|
26
31
|
/** Remove all toasts. */
|
|
27
32
|
clear(): void;
|
|
33
|
+
/** Copies the queue; each toast's `body` is shared, since the queue never mutates it. */
|
|
34
|
+
snapshot(): ToastQueueState<T>;
|
|
35
|
+
restore(next: ToastQueueState<T>): void;
|
|
28
36
|
}
|
|
29
37
|
/** Options for {@link createToastQueue}. */
|
|
30
38
|
export interface ToastQueueOptions {
|
package/dist/game/toasts.js
CHANGED
|
@@ -40,5 +40,12 @@ export function createToastQueue(options = {}) {
|
|
|
40
40
|
clear() {
|
|
41
41
|
toasts = [];
|
|
42
42
|
},
|
|
43
|
+
snapshot() {
|
|
44
|
+
return { toasts: toasts.map((toast) => ({ ...toast })), counter };
|
|
45
|
+
},
|
|
46
|
+
restore(next) {
|
|
47
|
+
toasts = next.toasts.map((toast) => ({ ...toast }));
|
|
48
|
+
counter = next.counter;
|
|
49
|
+
},
|
|
43
50
|
};
|
|
44
51
|
}
|
|
@@ -115,6 +115,15 @@ export interface VfxInstanceStore {
|
|
|
115
115
|
tick(nowMs?: number): number;
|
|
116
116
|
/** Dispose every instance (scene change / reset), emitting a `stop` op for each. */
|
|
117
117
|
clear(): void;
|
|
118
|
+
/** Plain JSON copy of every live instance plus the id counter. */
|
|
119
|
+
snapshot(): VfxInstanceStoreState;
|
|
120
|
+
/** Replaces the live set: emits `stop` for each current instance, then `upsert` for each restored one. */
|
|
121
|
+
restore(next: VfxInstanceStoreState): void;
|
|
122
|
+
}
|
|
123
|
+
/** Plain JSON state of a {@link VfxInstanceStore}. */
|
|
124
|
+
export interface VfxInstanceStoreState {
|
|
125
|
+
instances: VfxInstanceState[];
|
|
126
|
+
seq: number;
|
|
118
127
|
}
|
|
119
128
|
/** Options for {@link createVfxInstanceStore}. */
|
|
120
129
|
export interface VfxInstanceStoreOptions {
|
package/dist/game/vfxInstance.js
CHANGED
|
@@ -92,5 +92,16 @@ export function createVfxInstanceStore(options = {}) {
|
|
|
92
92
|
for (const id of [...instances.keys()])
|
|
93
93
|
stop(id);
|
|
94
94
|
}
|
|
95
|
-
|
|
95
|
+
function snapshot() {
|
|
96
|
+
return { instances: structuredClone([...instances.values()]), seq };
|
|
97
|
+
}
|
|
98
|
+
function restore(next) {
|
|
99
|
+
clear();
|
|
100
|
+
seq = next.seq;
|
|
101
|
+
for (const state of structuredClone(next.instances)) {
|
|
102
|
+
instances.set(state.id, state);
|
|
103
|
+
emit({ op: "upsert", id: state.id, instance: state });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return { upsert, update, stop, get, list, count, tick, clear, snapshot, restore };
|
|
96
107
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ChaseCameraConfig, TopDownCameraConfig } from "./cameraConfig.js";
|
|
2
|
+
/** A screen region as fractions of the canvas, `[x, y, width, height]` in `0..1` with the origin at the top left. */
|
|
3
|
+
export type ViewportRect = readonly [number, number, number, number];
|
|
4
|
+
/** The follow camera an extra viewport runs; the primary seat keeps the game's main camera rig. */
|
|
5
|
+
export interface ViewportCameraConfig {
|
|
6
|
+
/** Default: `"topDown"` when the game's main rig is top-down, otherwise `"chase"`. */
|
|
7
|
+
rig?: "chase" | "topDown";
|
|
8
|
+
chase?: ChaseCameraConfig;
|
|
9
|
+
topDown?: TopDownCameraConfig;
|
|
10
|
+
}
|
|
11
|
+
/** One viewport: which local seat it shows and where. */
|
|
12
|
+
export interface ViewportDef {
|
|
13
|
+
/** Local seat id (`"slot:0"`, `"slot:1"`, …) from `localPlayers(ctx)`. */
|
|
14
|
+
slot: string;
|
|
15
|
+
rect: ViewportRect;
|
|
16
|
+
camera?: ViewportCameraConfig;
|
|
17
|
+
}
|
|
18
|
+
/** `defineGame({ viewports })`: split-screen layout for local seats. */
|
|
19
|
+
export interface ViewportsConfig {
|
|
20
|
+
/** `"auto"` (default) splits the screen evenly across the seats currently joined; a list pins explicit rects. */
|
|
21
|
+
layout?: "auto" | readonly ViewportDef[];
|
|
22
|
+
/** Two-seat split: `"vertical"` puts them side by side (default), `"horizontal"` stacks them. */
|
|
23
|
+
split?: "vertical" | "horizontal";
|
|
24
|
+
/** Follow camera for seats whose viewport has none. */
|
|
25
|
+
camera?: ViewportCameraConfig;
|
|
26
|
+
}
|
|
27
|
+
/** A viewport in canvas pixels with a bottom-left origin, ready for `gl.setViewport`/`setScissor`. */
|
|
28
|
+
export interface ViewportPixels {
|
|
29
|
+
x: number;
|
|
30
|
+
y: number;
|
|
31
|
+
width: number;
|
|
32
|
+
height: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Even split-screen rects for seats in order: one fills the screen, two split side by side (or stacked),
|
|
36
|
+
* three put the first on a full-width top half, four make a 2×2 grid, more fill a grid row by row.
|
|
37
|
+
* @capability split-screen Split the screen into one viewport per local seat, each with its own follow camera.
|
|
38
|
+
*/
|
|
39
|
+
export declare function splitViewports(slots: readonly string[], split?: "vertical" | "horizontal"): ViewportDef[];
|
|
40
|
+
/** The viewports to draw for the seats currently joined: the explicit layout filtered to joined seats, or an even split. */
|
|
41
|
+
export declare function resolveViewports(config: ViewportsConfig | undefined, slots: readonly string[]): ViewportDef[];
|
|
42
|
+
/** Convert a top-left fractional rect into whole canvas pixels with a bottom-left origin; neighbours share edges exactly. */
|
|
43
|
+
export declare function viewportPixels(rect: ViewportRect, width: number, height: number): ViewportPixels;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
function gridRects(count) {
|
|
2
|
+
const columns = Math.ceil(Math.sqrt(count));
|
|
3
|
+
const rows = Math.ceil(count / columns);
|
|
4
|
+
const rects = [];
|
|
5
|
+
for (let index = 0; index < count; index += 1) {
|
|
6
|
+
const row = Math.floor(index / columns);
|
|
7
|
+
const inRow = row === rows - 1 ? count - row * columns : columns;
|
|
8
|
+
const column = index - row * columns;
|
|
9
|
+
rects.push([column / inRow, row / rows, 1 / inRow, 1 / rows]);
|
|
10
|
+
}
|
|
11
|
+
return rects;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Even split-screen rects for seats in order: one fills the screen, two split side by side (or stacked),
|
|
15
|
+
* three put the first on a full-width top half, four make a 2×2 grid, more fill a grid row by row.
|
|
16
|
+
* @capability split-screen Split the screen into one viewport per local seat, each with its own follow camera.
|
|
17
|
+
*/
|
|
18
|
+
export function splitViewports(slots, split = "vertical") {
|
|
19
|
+
const count = slots.length;
|
|
20
|
+
let rects;
|
|
21
|
+
if (count <= 1)
|
|
22
|
+
rects = [[0, 0, 1, 1]];
|
|
23
|
+
else if (count === 2) {
|
|
24
|
+
rects = split === "vertical" ? [[0, 0, 0.5, 1], [0.5, 0, 0.5, 1]] : [[0, 0, 1, 0.5], [0, 0.5, 1, 0.5]];
|
|
25
|
+
}
|
|
26
|
+
else if (count === 3)
|
|
27
|
+
rects = [[0, 0, 1, 0.5], [0, 0.5, 0.5, 0.5], [0.5, 0.5, 0.5, 0.5]];
|
|
28
|
+
else
|
|
29
|
+
rects = gridRects(count);
|
|
30
|
+
return slots.map((slot, index) => ({ slot, rect: rects[index] }));
|
|
31
|
+
}
|
|
32
|
+
/** The viewports to draw for the seats currently joined: the explicit layout filtered to joined seats, or an even split. */
|
|
33
|
+
export function resolveViewports(config, slots) {
|
|
34
|
+
const layout = config?.layout ?? "auto";
|
|
35
|
+
if (layout === "auto")
|
|
36
|
+
return splitViewports(slots, config?.split);
|
|
37
|
+
return layout.filter((def) => slots.includes(def.slot));
|
|
38
|
+
}
|
|
39
|
+
/** Convert a top-left fractional rect into whole canvas pixels with a bottom-left origin; neighbours share edges exactly. */
|
|
40
|
+
export function viewportPixels(rect, width, height) {
|
|
41
|
+
const left = Math.round(rect[0] * width);
|
|
42
|
+
const right = Math.round((rect[0] + rect[2]) * width);
|
|
43
|
+
const top = Math.round(rect[1] * height);
|
|
44
|
+
const bottom = Math.round((rect[1] + rect[3]) * height);
|
|
45
|
+
return { x: left, y: height - bottom, width: Math.max(1, right - left), height: Math.max(1, bottom - top) };
|
|
46
|
+
}
|
package/dist/game/worldItem.d.ts
CHANGED
|
@@ -62,7 +62,6 @@ export declare function selectNearestWorldItem(candidates: readonly {
|
|
|
62
62
|
/**
|
|
63
63
|
* Spawn and track pickup-able items scattered in the world, including drops that scatter on death.
|
|
64
64
|
*
|
|
65
|
-
* @capability world-drops spawn pickup-able items in the world, including death drops
|
|
66
65
|
* @internal
|
|
67
66
|
*/
|
|
68
67
|
export declare function createWorldItemStore(deps: WorldItemStoreDeps): WorldItemStore;
|
|
@@ -87,6 +86,11 @@ export interface ResolvedDeathDrops {
|
|
|
87
86
|
* @internal
|
|
88
87
|
*/
|
|
89
88
|
export declare function resolveDeathDrops(drops: readonly Drop[], options: ResolveDeathDropsOptions): ResolvedDeathDrops;
|
|
89
|
+
/**
|
|
90
|
+
* Per-rarity look of a dropped item: beam, color and label. Set via `defineGame({ worldItem: { rarityStyle } })`.
|
|
91
|
+
*
|
|
92
|
+
* @capability loot-beams rarity-colored loot beams and labels on dropped items via defineGame({ worldItem: { rarityStyle, beamHeight } })
|
|
93
|
+
*/
|
|
90
94
|
export interface RarityStyle {
|
|
91
95
|
color?: string;
|
|
92
96
|
beam?: boolean;
|
package/dist/game/worldItem.js
CHANGED
|
@@ -39,7 +39,6 @@ export function selectNearestWorldItem(candidates, from, radius) {
|
|
|
39
39
|
/**
|
|
40
40
|
* Spawn and track pickup-able items scattered in the world, including drops that scatter on death.
|
|
41
41
|
*
|
|
42
|
-
* @capability world-drops spawn pickup-able items in the world, including death drops
|
|
43
42
|
* @internal
|
|
44
43
|
*/
|
|
45
44
|
export function createWorldItemStore(deps) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type GamepadGlyphName, type GamepadGlyphSet } from "./gamepadModel.js";
|
|
1
2
|
/**
|
|
2
3
|
* Action-binding model: games bind semantic actions ("jump", "interact") to
|
|
3
4
|
* physical control codes; capture layers resolve raw events through this map
|
|
@@ -33,7 +34,13 @@ export type ActionCodes<TCode extends string = string> = readonly TCode[] | {
|
|
|
33
34
|
toggle?: readonly TCode[];
|
|
34
35
|
repeatMs?: number;
|
|
35
36
|
};
|
|
36
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Maps each game action name to the input codes (hold/toggle keys, repeat rate) that trigger it. In a
|
|
39
|
+
* `defineGame({ input })`, pressing an action runs the same-named command with `{ yaw, pitch, aim }`;
|
|
40
|
+
* `repeatMs` re-fires it while held (automatic weapons).
|
|
41
|
+
*
|
|
42
|
+
* @capability fire-input bind a key or mouse button to a named action that runs the same-named command with the aim; repeatMs auto-fires while held
|
|
43
|
+
*/
|
|
37
44
|
export type ActionCodesMap<TAction extends string = string, TCode extends string = string> = Record<TAction, ActionCodes<TCode>>;
|
|
38
45
|
/** @internal */
|
|
39
46
|
export declare function actionRepeatMs(codes: ActionCodes | undefined): number | undefined;
|
|
@@ -69,7 +76,7 @@ export declare function resolveActionCommand(action: string, has: (command: stri
|
|
|
69
76
|
/** Short display label for a raw key/button code (e.g. `"KeyW"` → `"W"`).
|
|
70
77
|
* @internal
|
|
71
78
|
*/
|
|
72
|
-
export declare function bindingLabel(code: string): string;
|
|
79
|
+
export declare function bindingLabel(code: string, glyphSet?: GamepadGlyphName | GamepadGlyphSet): string;
|
|
73
80
|
/** Short display label for an action's first bound code, or null when unbound.
|
|
74
81
|
* @internal
|
|
75
82
|
*/
|
|
@@ -81,6 +88,14 @@ export interface ActionStateTracker<TAction extends string> {
|
|
|
81
88
|
wasPressed(action: TAction): boolean;
|
|
82
89
|
endFrame(): void;
|
|
83
90
|
reset(): void;
|
|
91
|
+
/**
|
|
92
|
+
* Swap the binding map without dropping what is physically held: a key still down counts toward
|
|
93
|
+
* whatever action it maps to now, with no fresh press edge. Toggle state survives for actions that
|
|
94
|
+
* come back later.
|
|
95
|
+
*/
|
|
96
|
+
rebind(map: ActionStateBindingMap<TAction, string>): void;
|
|
97
|
+
/** The action names in the current binding map. */
|
|
98
|
+
actions(): readonly TAction[];
|
|
84
99
|
}
|
|
85
100
|
/** @internal */
|
|
86
101
|
export declare function createActionStateTracker<TAction extends string, TCode extends string = string>(map: ActionStateBindingMap<TAction, TCode>): ActionStateTracker<TAction>;
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Action-binding model: games bind semantic actions ("jump", "interact") to
|
|
3
|
-
* physical control codes; capture layers resolve raw events through this map
|
|
4
|
-
* so gameplay code never sees keycodes. Control codes are plain strings, so
|
|
5
|
-
* the same model serves keyboard codes, mouse buttons, touch controls, or
|
|
6
|
-
* gamepad inputs.
|
|
7
|
-
*/
|
|
1
|
+
import { GAMEPAD_GLYPH_SETS } from "./gamepadModel.js";
|
|
8
2
|
/** @internal */
|
|
9
3
|
export function bindingMatches(code, binding) {
|
|
10
4
|
return code === binding.primary || code === binding.secondary;
|
|
@@ -142,7 +136,15 @@ const CODE_LABELS = {
|
|
|
142
136
|
/** Short display label for a raw key/button code (e.g. `"KeyW"` → `"W"`).
|
|
143
137
|
* @internal
|
|
144
138
|
*/
|
|
145
|
-
export function bindingLabel(code) {
|
|
139
|
+
export function bindingLabel(code, glyphSet = "generic") {
|
|
140
|
+
const padButton = /^pad:(\d+)$/.exec(code);
|
|
141
|
+
if (padButton !== null) {
|
|
142
|
+
const set = typeof glyphSet === "string" ? GAMEPAD_GLYPH_SETS[glyphSet] : glyphSet;
|
|
143
|
+
return set.buttons[Number(padButton[1])] ?? `Button ${padButton[1]}`;
|
|
144
|
+
}
|
|
145
|
+
const padAxis = /^padaxis:(\d+)([+-])$/.exec(code);
|
|
146
|
+
if (padAxis !== null)
|
|
147
|
+
return `Axis ${padAxis[1]}${padAxis[2]}`;
|
|
146
148
|
const direct = CODE_LABELS[code];
|
|
147
149
|
if (direct !== undefined)
|
|
148
150
|
return direct;
|
|
@@ -177,9 +179,9 @@ function resolveActionBindingModes(config) {
|
|
|
177
179
|
}
|
|
178
180
|
/** @internal */
|
|
179
181
|
export function createActionStateTracker(map) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
182
|
+
let actions = Object.keys(map);
|
|
183
|
+
let modesByAction = new Map(actions.map((action) => [action, resolveActionBindingModes(map[action])]));
|
|
184
|
+
let heldCodesByAction = new Map(actions.map((action) => [action, new Set()]));
|
|
183
185
|
const toggledActions = new Set();
|
|
184
186
|
const pressedThisFrame = new Set();
|
|
185
187
|
const activeCodes = new Set();
|
|
@@ -238,5 +240,16 @@ export function createActionStateTracker(map) {
|
|
|
238
240
|
pressedThisFrame.clear();
|
|
239
241
|
activeCodes.clear();
|
|
240
242
|
},
|
|
243
|
+
rebind(next) {
|
|
244
|
+
actions = Object.keys(next);
|
|
245
|
+
modesByAction = new Map(actions.map((action) => [action, resolveActionBindingModes(next[action])]));
|
|
246
|
+
heldCodesByAction = new Map(actions.map((action) => [action, new Set()]));
|
|
247
|
+
for (const code of activeCodes) {
|
|
248
|
+
const holdAction = findAction(code, (modes) => modes.hold);
|
|
249
|
+
if (holdAction !== null)
|
|
250
|
+
heldCodesByAction.get(holdAction).add(code);
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
actions: () => actions,
|
|
241
254
|
};
|
|
242
255
|
}
|