@jgengine/core 0.18.1 → 0.21.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 +504 -7
- package/dist/ai/decisionGraph.d.ts +50 -4
- package/dist/ai/decisionGraph.js +159 -23
- package/dist/ai/flock.d.ts +1 -1
- package/dist/ai/flock.js +7 -4
- package/dist/ai/groupAssist.d.ts +10 -0
- package/dist/ai/groupAssist.js +11 -0
- package/dist/ai/pursuit.d.ts +1 -1
- package/dist/ai/pursuit.js +1 -1
- package/dist/ai/threat.d.ts +8 -0
- package/dist/ai/threat.js +10 -0
- package/dist/anim/animGraph.d.ts +43 -1
- package/dist/anim/animGraph.js +326 -10
- 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/locomotionGraph.d.ts +18 -1
- package/dist/anim/locomotionGraph.js +42 -3
- package/dist/audio/audioFalloff.d.ts +6 -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/audio/voiceAllocator.d.ts +46 -0
- package/dist/audio/voiceAllocator.js +76 -0
- package/dist/combat/abilityKit.d.ts +20 -0
- package/dist/combat/abilityKit.js +41 -4
- package/dist/combat/death.d.ts +11 -3
- package/dist/combat/death.js +3 -3
- package/dist/combat/magazine.d.ts +9 -0
- package/dist/combat/magazine.js +24 -2
- package/dist/combat/projectiles.d.ts +94 -2
- package/dist/combat/projectiles.js +282 -61
- package/dist/combat/regenShield.d.ts +14 -0
- package/dist/combat/regenShield.js +23 -7
- 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/crafting/recipeCatalog.d.ts +16 -0
- package/dist/crafting/recipeCatalog.js +55 -0
- package/dist/devtools/warnOnce.d.ts +8 -0
- package/dist/devtools/warnOnce.js +18 -0
- package/dist/economy/auctionBook.d.ts +1 -1
- package/dist/economy/auctionBook.js +11 -1
- package/dist/economy/listingBook.d.ts +1 -1
- package/dist/economy/listingBook.js +11 -1
- package/dist/economy/resourceLedger.js +3 -3
- package/dist/economy/shopStock.js +7 -6
- package/dist/economy/techTree.js +7 -6
- package/dist/economy/unlockPoints.js +3 -6
- package/dist/editor/commandApply.js +89 -16
- package/dist/editor/commands.d.ts +38 -1
- package/dist/editor/commands.js +320 -3
- package/dist/editor/creatorStorage.d.ts +59 -0
- package/dist/editor/creatorStorage.js +164 -0
- package/dist/editor/document.d.ts +4 -0
- package/dist/editor/document.js +119 -18
- package/dist/editor/environment.js +8 -0
- package/dist/editor/index.d.ts +2 -0
- package/dist/editor/liveSync.d.ts +4 -1
- package/dist/editor/liveSync.js +26 -10
- package/dist/editor/materialAuthoring.d.ts +14 -0
- package/dist/editor/materialAuthoring.js +38 -0
- package/dist/editor/modelNodeVisibility.d.ts +11 -0
- package/dist/editor/modelNodeVisibility.js +29 -0
- package/dist/editor/movementCatalog.d.ts +90 -0
- package/dist/editor/movementCatalog.js +144 -0
- package/dist/editor/simulation.d.ts +60 -0
- package/dist/editor/simulation.js +202 -0
- package/dist/editor/staticPrefab.d.ts +17 -0
- package/dist/editor/staticPrefab.js +51 -0
- package/dist/editor/types.d.ts +15 -0
- package/dist/faction/reputation.d.ts +6 -0
- package/dist/faction/reputation.js +11 -0
- package/dist/game/cameraConfig.d.ts +54 -2
- package/dist/game/clipRoles.d.ts +2 -1
- package/dist/game/clipRoles.js +7 -3
- package/dist/game/contentValidation.d.ts +69 -0
- package/dist/game/contentValidation.js +118 -0
- package/dist/game/controlGate.d.ts +8 -0
- package/dist/game/controlGate.js +43 -6
- package/dist/game/defineGame.d.ts +32 -3
- package/dist/game/dialogueGraph.d.ts +20 -0
- package/dist/game/dialogueGraph.js +107 -0
- package/dist/game/events.d.ts +11 -0
- package/dist/game/keyValueStore.d.ts +17 -4
- package/dist/game/keyValueStore.js +35 -23
- package/dist/game/lootModifiers.d.ts +17 -0
- package/dist/game/lootModifiers.js +26 -0
- package/dist/game/lootTable.d.ts +4 -0
- package/dist/game/lootTable.js +16 -0
- package/dist/game/narrativeAuthoring.d.ts +67 -0
- package/dist/game/narrativeAuthoring.js +122 -0
- package/dist/game/playableGame.d.ts +134 -35
- package/dist/game/quest.d.ts +15 -0
- package/dist/game/quest.js +209 -63
- package/dist/game/questCatalog.d.ts +50 -0
- package/dist/game/questCatalog.js +205 -0
- package/dist/game/saveStore.d.ts +6 -5
- package/dist/game/saveStore.js +57 -57
- package/dist/game/systemRuntime.d.ts +2 -2
- package/dist/game/systemRuntime.js +84 -33
- 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/gameplay.d.ts +3 -0
- package/dist/gameplay.js +3 -0
- package/dist/input/actionBindings.d.ts +15 -1
- package/dist/input/actionBindings.js +14 -3
- package/dist/input/actionContexts.d.ts +25 -1
- package/dist/input/actionContexts.js +82 -25
- 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 +36 -2
- package/dist/input/gamepadModel.js +69 -30
- 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/interaction/promptRegistry.d.ts +5 -4
- package/dist/interaction/promptRegistry.js +8 -2
- package/dist/inventory/inventoryModel.js +9 -6
- package/dist/inventory/shapedGrid.d.ts +48 -9
- package/dist/inventory/shapedGrid.js +69 -10
- package/dist/item/affix.d.ts +6 -0
- package/dist/item/affix.js +6 -0
- package/dist/item/durability.js +4 -0
- package/dist/item/itemInstanceRegistry.d.ts +32 -1
- package/dist/item/itemInstanceRegistry.js +111 -7
- package/dist/item/itemgen.d.ts +1 -1
- package/dist/item/itemgen.js +1 -1
- package/dist/item/modularItem.js +7 -1
- package/dist/material/appearanceSignals.d.ts +53 -0
- package/dist/material/appearanceSignals.js +47 -0
- package/dist/material/hairCards.d.ts +47 -0
- package/dist/material/hairCards.js +136 -0
- package/dist/material/materialAsset.d.ts +162 -0
- package/dist/material/materialAsset.js +382 -0
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +444 -1
- package/dist/movement/characterController.d.ts +5 -2
- package/dist/movement/characterController.js +50 -23
- package/dist/movement/freeFlight.d.ts +2 -2
- package/dist/movement/freeFlight.js +3 -3
- 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 +63 -1
- package/dist/movement/movementModel.js +136 -26
- package/dist/movement/movementProbe.d.ts +51 -0
- package/dist/movement/movementProbe.js +119 -0
- package/dist/movement/playerMovement.d.ts +48 -2
- package/dist/movement/playerMovement.js +493 -87
- package/dist/movement/poseState.d.ts +2 -0
- package/dist/movement/poseState.js +29 -1
- package/dist/movement/solidObstacles.d.ts +11 -1
- package/dist/movement/solidObstacles.js +48 -3
- package/dist/movement/terrainGrade.d.ts +13 -0
- package/dist/movement/terrainGrade.js +33 -0
- package/dist/movement/voxelController.d.ts +2 -2
- package/dist/movement/voxelController.js +10 -10
- package/dist/nav/navFromEnvironment.d.ts +9 -3
- package/dist/nav/navFromEnvironment.js +18 -12
- package/dist/nav/navGrid.d.ts +19 -1
- package/dist/nav/navGrid.js +26 -10
- package/dist/nav/navMesh.d.ts +43 -2
- package/dist/nav/navMesh.js +534 -128
- package/dist/nav/solidRoute.d.ts +50 -0
- package/dist/nav/solidRoute.js +144 -0
- package/dist/physics/aircraftDynamics.d.ts +390 -0
- package/dist/physics/aircraftDynamics.js +704 -0
- package/dist/physics/ballisticSweep.d.ts +10 -3
- package/dist/physics/ballisticSweep.js +53 -29
- 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 +21 -4
- package/dist/physics/flightDynamics.js +28 -5
- package/dist/physics/forceVolume.d.ts +41 -0
- package/dist/physics/forceVolume.js +88 -0
- package/dist/physics/handlingProbe.d.ts +249 -0
- package/dist/physics/handlingProbe.js +606 -0
- package/dist/physics/kinematicVehicle.d.ts +40 -5
- package/dist/physics/kinematicVehicle.js +17 -2
- package/dist/physics/physicsBackend.d.ts +12 -0
- package/dist/physics/physicsBackendConformance.js +34 -0
- package/dist/physics/physicsWorldBackend.d.ts +1 -0
- package/dist/physics/physicsWorldBackend.js +52 -3
- package/dist/physics/spatialGrid.d.ts +1 -1
- package/dist/physics/spatialGrid.js +8 -9
- package/dist/physics/vehicleBackendLink.d.ts +49 -0
- package/dist/physics/vehicleBackendLink.js +102 -0
- package/dist/physics/vehicleDynamics.d.ts +375 -0
- package/dist/physics/vehicleDynamics.js +737 -0
- package/dist/physics/vehicleObstacles.d.ts +3 -2
- package/dist/physics/vehicleObstacles.js +29 -5
- package/dist/physics/worldColliders.d.ts +1 -1
- package/dist/physics/worldColliders.js +35 -3
- package/dist/puzzle/cellGrid.d.ts +15 -9
- package/dist/puzzle/cellGrid.js +13 -9
- package/dist/render/lookPreset.d.ts +9 -2
- package/dist/render/lookPreset.js +29 -2
- package/dist/render/postProcessing.d.ts +30 -0
- package/dist/render/postProcessing.js +1 -1
- package/dist/runtime/cameraDirector.d.ts +54 -3
- package/dist/runtime/cameraDirector.js +187 -0
- package/dist/runtime/commandDispatch.d.ts +14 -0
- package/dist/runtime/commandDispatch.js +19 -0
- package/dist/runtime/commandInput.d.ts +37 -0
- package/dist/runtime/commandInput.js +58 -0
- package/dist/runtime/commandRunner.d.ts +27 -0
- package/dist/runtime/commandRunner.js +31 -3
- package/dist/runtime/context/combat.d.ts +12 -4
- package/dist/runtime/context/combat.js +179 -11
- package/dist/runtime/context/deathLoot.d.ts +11 -8
- package/dist/runtime/context/deathLoot.js +17 -16
- package/dist/runtime/context/scene.d.ts +2 -0
- package/dist/runtime/context/scene.js +13 -21
- package/dist/runtime/descriptors/baseline.js +34 -3
- package/dist/runtime/descriptors/features.d.ts +2 -1
- package/dist/runtime/descriptors/features.js +18 -0
- package/dist/runtime/gameContext.js +86 -13
- package/dist/runtime/gameContextTypes.d.ts +33 -3
- package/dist/runtime/gameRuntime.d.ts +3 -1
- package/dist/runtime/gameRuntime.js +4 -0
- package/dist/runtime/headlessRunner.d.ts +3 -1
- package/dist/runtime/headlessRunner.js +11 -11
- package/dist/runtime/hostedGameRunner.d.ts +24 -1
- package/dist/runtime/hostedGameRunner.js +156 -10
- package/dist/runtime/hostedWorldSession.d.ts +17 -5
- package/dist/runtime/hostedWorldSession.js +154 -16
- package/dist/runtime/hostedWorldStore.d.ts +2 -2
- package/dist/runtime/inputRecorder.d.ts +1 -1
- package/dist/runtime/inputRecorder.js +21 -1
- package/dist/runtime/inputSnapshot.d.ts +22 -4
- package/dist/runtime/inputSnapshot.js +41 -3
- package/dist/runtime/localPlayers.d.ts +73 -0
- package/dist/runtime/localPlayers.js +119 -0
- package/dist/runtime/runtimeSave.d.ts +10 -6
- package/dist/runtime/runtimeSave.js +11 -5
- package/dist/runtime/simContext.d.ts +8 -3
- package/dist/runtime/simContext.js +7 -3
- package/dist/runtime/simSnapshot.js +2 -0
- package/dist/runtime/transport.d.ts +9 -0
- package/dist/runtime/transport.js +7 -1
- package/dist/runtime/worldReplication.d.ts +2 -0
- package/dist/runtime/worldReplication.js +1 -1
- package/dist/scene/assetCatalog.d.ts +2 -0
- package/dist/scene/behaviorRuntime.d.ts +38 -7
- package/dist/scene/behaviorRuntime.js +293 -30
- package/dist/scene/behaviors.d.ts +35 -2
- package/dist/scene/behaviors.js +29 -0
- package/dist/scene/entityStore.d.ts +14 -3
- package/dist/scene/entityStore.js +45 -9
- package/dist/scene/mount.d.ts +9 -0
- package/dist/scene/mount.js +35 -0
- package/dist/scene/possession.d.ts +2 -0
- package/dist/scene/possession.js +21 -1
- package/dist/scene/sceneKinds.d.ts +9 -0
- package/dist/scene/sceneRaycast.d.ts +7 -0
- package/dist/scene/sceneRaycast.js +55 -0
- package/dist/scene/spatial.d.ts +20 -1
- package/dist/scene/spatial.js +94 -2
- package/dist/scene/vehicleSeat.d.ts +41 -3
- package/dist/scene/vehicleSeat.js +94 -3
- 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 +11 -1
- package/dist/sensor/perception.js +57 -10
- 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 +2 -0
- package/dist/settings/graphicsProfile.js +12 -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/accumulatorMeter.d.ts +10 -0
- package/dist/stats/accumulatorMeter.js +6 -0
- package/dist/stats/eventMeter.d.ts +10 -3
- package/dist/stats/eventMeter.js +6 -3
- package/dist/store/defineStore.d.ts +3 -1
- package/dist/store/defineStore.js +3 -1
- package/dist/store/observableKeyedStore.d.ts +1 -1
- package/dist/store/observableKeyedStore.js +8 -6
- package/dist/time/simClock.d.ts +6 -2
- package/dist/time/simClock.js +5 -0
- package/dist/vfx/feedbackMixer.d.ts +61 -0
- package/dist/vfx/feedbackMixer.js +106 -0
- package/dist/vfx/particleDirector.d.ts +58 -6
- package/dist/vfx/particleDirector.js +113 -22
- package/dist/vfx/particles.d.ts +68 -5
- package/dist/vfx/particles.js +284 -32
- package/dist/visibility/assetStreaming.d.ts +21 -3
- package/dist/visibility/assetStreaming.js +108 -24
- package/dist/visibility/settings.d.ts +8 -2
- package/dist/visibility/settings.js +3 -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/visibility/visibilitySystem.js +1 -0
- package/dist/work/expedition.d.ts +110 -0
- package/dist/work/expedition.js +195 -0
- package/dist/work/staffedStation.d.ts +29 -0
- package/dist/work/staffedStation.js +27 -0
- package/dist/world/authoredAnimation.d.ts +38 -0
- package/dist/world/authoredAnimation.js +99 -0
- package/dist/world/authoredObjects.d.ts +24 -4
- package/dist/world/authoredObjects.js +43 -16
- package/dist/world/authoredSimulation.d.ts +74 -0
- package/dist/world/authoredSimulation.js +302 -0
- package/dist/world/authoredSolids.d.ts +46 -0
- package/dist/world/authoredSolids.js +75 -0
- package/dist/world/authoredWeather.d.ts +120 -0
- package/dist/world/authoredWeather.js +314 -0
- package/dist/world/buildingGenerator.js +2 -2
- package/dist/world/buildingKit.d.ts +11 -4
- package/dist/world/buildingKit.js +21 -8
- package/dist/world/buildings.d.ts +34 -2
- package/dist/world/buildings.js +11 -1
- package/dist/world/cityKind.d.ts +7 -0
- package/dist/world/cityKind.js +52 -1
- package/dist/world/environmentForces.d.ts +21 -0
- package/dist/world/environmentForces.js +42 -0
- package/dist/world/features.d.ts +32 -5
- package/dist/world/features.js +6 -0
- package/dist/world/footprintGrid.d.ts +62 -0
- package/dist/world/footprintGrid.js +217 -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/pathTerrain.d.ts +1 -1
- package/dist/world/terraform.d.ts +9 -1
- package/dist/world/terraform.js +53 -18
- package/dist/world/walls.d.ts +7 -0
- package/dist/world/walls.js +7 -0
- package/dist/world/weather.d.ts +17 -0
- package/dist/world/weather.js +116 -20
- package/dist/world/worldSolids.d.ts +95 -0
- package/dist/world/worldSolids.js +351 -0
- package/dist/world.d.ts +6 -6
- package/dist/world.js +5 -5
- package/package.json +1 -1
- package/skills/ce-handoff/SKILL.md +2 -2
- package/skills/game-design/SKILL.md +2 -2
- package/skills/jgengine/SKILL.md +28 -6
- package/skills/jgengine/api.md +139 -99
- package/skills/jgengine/capabilities.md +28 -0
- package/skills/jgengine/recipes/minimal-game.md +10 -2
- package/skills/jgengine-combat/SKILL.md +13 -2
- package/skills/jgengine-combat/api.md +150 -103
- package/skills/jgengine-combat/capabilities.md +29 -1
- package/skills/jgengine-combat/recipes/portable-runtime-state.md +60 -6
- package/skills/jgengine-combat/recipes/portable-weapon-plumbing.md +37 -3
- package/skills/jgengine-combat/recipes/weapon-feel.md +50 -0
- package/skills/jgengine-combat/reference.md +24 -2
- package/skills/jgengine-gameplay/SKILL.md +63 -0
- package/skills/jgengine-gameplay/api.md +583 -463
- package/skills/jgengine-gameplay/capabilities.md +92 -0
- package/skills/jgengine-gameplay/recipes/device-save-status.md +46 -0
- package/skills/jgengine-gameplay/recipes/expeditions.md +108 -0
- package/skills/jgengine-gameplay/recipes/narrative-authoring.md +76 -0
- package/skills/jgengine-gameplay/recipes/paged-item-storage.md +101 -0
- package/skills/jgengine-gameplay/recipes/quest-authoring.md +63 -0
- package/skills/jgengine-gameplay/recipes/shaped-inventory.md +50 -0
- package/skills/jgengine-gameplay/reference-content-validation.md +74 -0
- package/skills/jgengine-gameplay/reference-systems.md +123 -0
- package/skills/jgengine-world/SKILL.md +25 -4
- package/skills/jgengine-world/api.md +1045 -655
- package/skills/jgengine-world/capabilities.md +246 -9
- package/skills/jgengine-world/recipes/character-animation.md +62 -0
- package/skills/jgengine-world/recipes/controls.md +48 -0
- package/skills/jgengine-world/recipes/flight-feel.md +76 -0
- package/skills/jgengine-world/recipes/npc-senses.md +55 -0
- package/skills/jgengine-world/recipes/vehicle-feel.md +87 -0
- package/skills/jgengine-world/reference.md +180 -4
- package/skills/jgengine-world/visibility.md +139 -0
package/dist/game/saveStore.js
CHANGED
|
@@ -1,47 +1,45 @@
|
|
|
1
|
-
import { defaultKeyValueStorage } from "./keyValueStore.js";
|
|
2
|
-
function readSafe(storage, key) {
|
|
3
|
-
if (storage === null)
|
|
4
|
-
return null;
|
|
5
|
-
try {
|
|
6
|
-
return storage.getItem(key);
|
|
7
|
-
}
|
|
8
|
-
catch {
|
|
9
|
-
return null;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
1
|
+
import { defaultKeyValueStorage, reportStorageFailure } from "./keyValueStore.js";
|
|
12
2
|
/**
|
|
13
3
|
* A {@link SaveBackend} over a synchronous {@link KeyValueStorage} — the
|
|
14
4
|
* browser's `localStorage` by default (offline, on-device saves), a test stub,
|
|
15
|
-
* or `null` for
|
|
16
|
-
*
|
|
5
|
+
* or `null` for a no-op backend. Default fallback ignores failures; `errorMode: "throw"`
|
|
6
|
+
* rejects failed operations and unavailable storage, so SaveStore status/onError can report them.
|
|
7
|
+
* Use {@link memorySaveBackend} for deliberate ephemeral persistence.
|
|
17
8
|
*
|
|
18
9
|
* @capability local-save persist a game to on-device localStorage (offline)
|
|
19
10
|
*/
|
|
20
|
-
export function localSaveBackend(storage) {
|
|
21
|
-
const
|
|
11
|
+
export function localSaveBackend(storage, policy = {}) {
|
|
12
|
+
const resolve = () => {
|
|
13
|
+
const store = storage === undefined ? defaultKeyValueStorage((error) => { throw error; }) : storage;
|
|
14
|
+
if (store === null)
|
|
15
|
+
throw new Error("Local storage is unavailable");
|
|
16
|
+
return store;
|
|
17
|
+
};
|
|
22
18
|
return {
|
|
23
|
-
read
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// storage rejected the write; the store keeps the value in memory
|
|
31
|
-
}
|
|
19
|
+
async read(key) {
|
|
20
|
+
try {
|
|
21
|
+
return resolve().getItem(key);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
reportStorageFailure(policy, { operation: "read", key, error });
|
|
25
|
+
return null;
|
|
32
26
|
}
|
|
33
|
-
return Promise.resolve();
|
|
34
27
|
},
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
28
|
+
async write(key, value) {
|
|
29
|
+
try {
|
|
30
|
+
resolve().setItem(key, value);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
reportStorageFailure(policy, { operation: "write", key, error });
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
async remove(key) {
|
|
37
|
+
try {
|
|
38
|
+
resolve().removeItem(key);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
reportStorageFailure(policy, { operation: "remove", key, error });
|
|
43
42
|
}
|
|
44
|
-
return Promise.resolve();
|
|
45
43
|
},
|
|
46
44
|
};
|
|
47
45
|
}
|
|
@@ -143,10 +141,10 @@ export function createSaveStore(config) {
|
|
|
143
141
|
return parsed;
|
|
144
142
|
}
|
|
145
143
|
async function readSlots() {
|
|
144
|
+
const raw = await backend.read(indexKey());
|
|
145
|
+
if (raw === null)
|
|
146
|
+
return [];
|
|
146
147
|
try {
|
|
147
|
-
const raw = await backend.read(indexKey());
|
|
148
|
-
if (raw === null)
|
|
149
|
-
return [];
|
|
150
148
|
const parsed = JSON.parse(raw);
|
|
151
149
|
return Array.isArray(parsed) ? parsed.filter((entry) => typeof entry === "string") : [];
|
|
152
150
|
}
|
|
@@ -155,26 +153,16 @@ export function createSaveStore(config) {
|
|
|
155
153
|
}
|
|
156
154
|
}
|
|
157
155
|
async function rememberSlot(slot) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
await backend.write(indexKey(), JSON.stringify([...list, slot]));
|
|
163
|
-
}
|
|
164
|
-
catch (error) {
|
|
165
|
-
reportError(error);
|
|
166
|
-
}
|
|
156
|
+
const list = await readSlots();
|
|
157
|
+
if (list.includes(slot))
|
|
158
|
+
return;
|
|
159
|
+
await backend.write(indexKey(), JSON.stringify([...list, slot]));
|
|
167
160
|
}
|
|
168
161
|
async function forgetSlot(slot) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
await backend.write(indexKey(), JSON.stringify(list.filter((entry) => entry !== slot)));
|
|
174
|
-
}
|
|
175
|
-
catch (error) {
|
|
176
|
-
reportError(error);
|
|
177
|
-
}
|
|
162
|
+
const list = await readSlots();
|
|
163
|
+
if (!list.includes(slot))
|
|
164
|
+
return;
|
|
165
|
+
await backend.write(indexKey(), JSON.stringify(list.filter((entry) => entry !== slot)));
|
|
178
166
|
}
|
|
179
167
|
function clearTimer() {
|
|
180
168
|
if (pendingTimer !== null) {
|
|
@@ -243,7 +231,10 @@ export function createSaveStore(config) {
|
|
|
243
231
|
firstDirtyAt = null;
|
|
244
232
|
setStatus("idle");
|
|
245
233
|
emit();
|
|
246
|
-
void rememberSlot(currentSlot)
|
|
234
|
+
void rememberSlot(currentSlot).catch((error) => {
|
|
235
|
+
setStatus("error");
|
|
236
|
+
reportError(error);
|
|
237
|
+
});
|
|
247
238
|
return current;
|
|
248
239
|
}
|
|
249
240
|
catch (error) {
|
|
@@ -298,7 +289,16 @@ export function createSaveStore(config) {
|
|
|
298
289
|
firstDirtyAt = null;
|
|
299
290
|
return load();
|
|
300
291
|
},
|
|
301
|
-
slots
|
|
292
|
+
async slots() {
|
|
293
|
+
try {
|
|
294
|
+
return await readSlots();
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
setStatus("error");
|
|
298
|
+
reportError(error);
|
|
299
|
+
return [];
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
302
|
subscribe(listener) {
|
|
303
303
|
listeners.add(listener);
|
|
304
304
|
return () => listeners.delete(listener);
|
|
@@ -15,14 +15,14 @@ export interface InstalledSystems {
|
|
|
15
15
|
tick(ctx: GameContext, dt: number): void;
|
|
16
16
|
/** Invoke every system's `reset` (scenario/run wipe). */
|
|
17
17
|
reset(ctx: GameContext): void;
|
|
18
|
-
/**
|
|
18
|
+
/** Retire once in reverse order; finish cleanup before rethrowing the first error. */
|
|
19
19
|
dispose(ctx: GameContext): void;
|
|
20
20
|
/** Run one manual system by id (no-op if missing or not manual). */
|
|
21
21
|
runManual(ctx: GameContext, id: string, dt?: number): void;
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* Install systems on a live context: `create` → bind events → register save/replicate → `start`.
|
|
25
|
-
* Call once per world boot
|
|
25
|
+
* Call once per world boot. Failed installation retires entered systems and listeners, then rethrows the startup error.
|
|
26
26
|
* @internal
|
|
27
27
|
*/
|
|
28
28
|
export declare function installSystems(ctx: GameContext, systems: readonly SystemDefinition[], options?: CompileSystemScheduleOptions & {
|
|
@@ -7,7 +7,7 @@ function resolveModule(spec, ctx) {
|
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
9
|
* Install systems on a live context: `create` → bind events → register save/replicate → `start`.
|
|
10
|
-
* Call once per world boot
|
|
10
|
+
* Call once per world boot. Failed installation retires entered systems and listeners, then rethrows the startup error.
|
|
11
11
|
* @internal
|
|
12
12
|
*/
|
|
13
13
|
export function installSystems(ctx, systems, options) {
|
|
@@ -16,29 +16,41 @@ export function installSystems(ctx, systems, options) {
|
|
|
16
16
|
const unsubs = [];
|
|
17
17
|
const intervalAcc = new Map();
|
|
18
18
|
const fixedAcc = new Map();
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
19
|
+
const acquired = [];
|
|
20
|
+
let disposed = false;
|
|
21
|
+
try {
|
|
22
|
+
for (const system of list) {
|
|
23
|
+
acquired.push(system);
|
|
24
|
+
system.create?.(ctx);
|
|
25
|
+
}
|
|
26
|
+
for (const system of list) {
|
|
27
|
+
if (system.events !== undefined) {
|
|
28
|
+
for (const [name, handler] of Object.entries(system.events)) {
|
|
29
|
+
if (handler === undefined)
|
|
30
|
+
continue;
|
|
31
|
+
const off = ctx.game.events.on(name, ((event) => {
|
|
32
|
+
handler(ctx, event);
|
|
33
|
+
}));
|
|
34
|
+
unsubs.push(off);
|
|
35
|
+
}
|
|
31
36
|
}
|
|
37
|
+
const save = resolveModule(system.save, ctx);
|
|
38
|
+
if (save !== undefined)
|
|
39
|
+
options?.modules?.registerSave(save);
|
|
40
|
+
const replicate = resolveModule(system.replicate, ctx);
|
|
41
|
+
if (replicate !== undefined)
|
|
42
|
+
options?.modules?.registerReplicate(replicate);
|
|
43
|
+
}
|
|
44
|
+
for (const system of list) {
|
|
45
|
+
system.start?.(ctx);
|
|
32
46
|
}
|
|
33
|
-
const save = resolveModule(system.save, ctx);
|
|
34
|
-
if (save !== undefined)
|
|
35
|
-
options?.modules?.registerSave(save);
|
|
36
|
-
const replicate = resolveModule(system.replicate, ctx);
|
|
37
|
-
if (replicate !== undefined)
|
|
38
|
-
options?.modules?.registerReplicate(replicate);
|
|
39
47
|
}
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
catch (error) {
|
|
49
|
+
try {
|
|
50
|
+
dispose(ctx);
|
|
51
|
+
}
|
|
52
|
+
catch { /* The startup error remains the first failure. */ }
|
|
53
|
+
throw error;
|
|
42
54
|
}
|
|
43
55
|
for (const group of schedule.fixed) {
|
|
44
56
|
fixedAcc.set(group.rate, 0);
|
|
@@ -52,7 +64,7 @@ export function installSystems(ctx, systems, options) {
|
|
|
52
64
|
}
|
|
53
65
|
}
|
|
54
66
|
function tick(ctx, dt) {
|
|
55
|
-
if (!(dt > 0))
|
|
67
|
+
if (disposed || !(dt > 0))
|
|
56
68
|
return;
|
|
57
69
|
for (const group of schedule.fixed) {
|
|
58
70
|
const step = 1 / group.rate;
|
|
@@ -79,6 +91,8 @@ export function installSystems(ctx, systems, options) {
|
|
|
79
91
|
}
|
|
80
92
|
}
|
|
81
93
|
function reset(ctx) {
|
|
94
|
+
if (disposed)
|
|
95
|
+
return;
|
|
82
96
|
for (const system of list)
|
|
83
97
|
system.reset?.(ctx);
|
|
84
98
|
for (const group of schedule.fixed)
|
|
@@ -87,14 +101,33 @@ export function installSystems(ctx, systems, options) {
|
|
|
87
101
|
intervalAcc.set(interval.id, 0);
|
|
88
102
|
}
|
|
89
103
|
function dispose(ctx) {
|
|
90
|
-
|
|
91
|
-
|
|
104
|
+
if (disposed)
|
|
105
|
+
return;
|
|
106
|
+
disposed = true;
|
|
107
|
+
let failure;
|
|
108
|
+
for (const off of unsubs) {
|
|
109
|
+
try {
|
|
110
|
+
off();
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
failure ??= { error };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
92
116
|
unsubs.length = 0;
|
|
93
|
-
for (let i =
|
|
94
|
-
|
|
117
|
+
for (let i = acquired.length - 1; i >= 0; i -= 1) {
|
|
118
|
+
try {
|
|
119
|
+
acquired[i]?.dispose?.(ctx);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
failure ??= { error };
|
|
123
|
+
}
|
|
95
124
|
}
|
|
125
|
+
if (failure !== undefined)
|
|
126
|
+
throw failure.error;
|
|
96
127
|
}
|
|
97
128
|
function runManual(ctx, id, dt = 0) {
|
|
129
|
+
if (disposed)
|
|
130
|
+
return;
|
|
98
131
|
if (!schedule.manual.includes(id))
|
|
99
132
|
return;
|
|
100
133
|
schedule.systemsById.get(id)?.update?.(ctx, dt);
|
|
@@ -119,17 +152,18 @@ export function composeGameLoop(systems, loop, options) {
|
|
|
119
152
|
if (systems === undefined || systems.length === 0) {
|
|
120
153
|
return loop ?? {};
|
|
121
154
|
}
|
|
122
|
-
|
|
155
|
+
const retired = new WeakSet();
|
|
123
156
|
const loopBefore = options?.loopBeforeSystems === true;
|
|
124
157
|
return {
|
|
125
158
|
onInit(ctx) {
|
|
159
|
+
retired.delete(ctx);
|
|
126
160
|
const modules = ctx.game.registerSave !== undefined && ctx.game.registerReplicate !== undefined
|
|
127
161
|
? {
|
|
128
162
|
registerSave: (m) => ctx.game.registerSave(m),
|
|
129
163
|
registerReplicate: (m) => ctx.game.registerReplicate(m),
|
|
130
164
|
}
|
|
131
165
|
: undefined;
|
|
132
|
-
installed = installSystems(ctx, systems, { ...options, modules });
|
|
166
|
+
const installed = installSystems(ctx, systems, { ...options, modules });
|
|
133
167
|
installedByCtx.set(ctx, installed);
|
|
134
168
|
loop?.onInit?.(ctx);
|
|
135
169
|
},
|
|
@@ -139,10 +173,10 @@ export function composeGameLoop(systems, loop, options) {
|
|
|
139
173
|
onTick(ctx, dt) {
|
|
140
174
|
if (loopBefore) {
|
|
141
175
|
loop?.onTick?.(ctx, dt);
|
|
142
|
-
|
|
176
|
+
installedByCtx.get(ctx)?.tick(ctx, dt);
|
|
143
177
|
}
|
|
144
178
|
else {
|
|
145
|
-
|
|
179
|
+
installedByCtx.get(ctx)?.tick(ctx, dt);
|
|
146
180
|
loop?.onTick?.(ctx, dt);
|
|
147
181
|
}
|
|
148
182
|
},
|
|
@@ -150,13 +184,30 @@ export function composeGameLoop(systems, loop, options) {
|
|
|
150
184
|
loop?.onPlayerLeave?.(ctx, player);
|
|
151
185
|
},
|
|
152
186
|
onReset(ctx) {
|
|
153
|
-
|
|
187
|
+
installedByCtx.get(ctx)?.reset(ctx);
|
|
154
188
|
loop?.onReset?.(ctx);
|
|
155
189
|
},
|
|
156
190
|
onDispose(ctx) {
|
|
157
|
-
|
|
191
|
+
if (retired.has(ctx))
|
|
192
|
+
return;
|
|
193
|
+
retired.add(ctx);
|
|
194
|
+
const installed = installedByCtx.get(ctx);
|
|
158
195
|
installedByCtx.delete(ctx);
|
|
159
|
-
|
|
196
|
+
let failure;
|
|
197
|
+
try {
|
|
198
|
+
installed?.dispose(ctx);
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
failure = { error };
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
loop?.onDispose?.(ctx);
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
failure ??= { error };
|
|
208
|
+
}
|
|
209
|
+
if (failure !== undefined)
|
|
210
|
+
throw failure.error;
|
|
160
211
|
},
|
|
161
212
|
};
|
|
162
213
|
}
|
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) {
|
package/dist/gameplay.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ export { medalFor, rankLeaderboard, type RankLeaderboardOptions, type RankableRo
|
|
|
31
31
|
export { createLevelSequence, type LevelSequence } from "./game/levelSequence.js";
|
|
32
32
|
export { createLoadouts, type LoadoutDef } from "./game/loadout.js";
|
|
33
33
|
export { evaluateLootFilter, lootFilter, type LootFilterRule } from "./game/lootFilter.js";
|
|
34
|
+
export { timeScaledRarity, type TimeScaledRarityOptions } from "./game/lootModifiers.js";
|
|
34
35
|
export { createLootPipeline, defineLootPipeline, type LootDropProvenance, type LootModifier, type LootPipeline, type LootPipelineDef, type LootPipelineDeps, type LootPlanEntry, type LootResolution, type LootResolveContext, type LootRollPlan, type LootStage, type LootStageKind, type LootStageStatus, type LootStageTrace, } from "./game/lootPipeline.js";
|
|
35
36
|
export { createLootRegistry, lootTable, type Drop, type LootTableDef } from "./game/lootTable.js";
|
|
36
37
|
export { resolveOneShotClip } from "./game/modelAnimation.js";
|
|
@@ -100,5 +101,7 @@ export { createMultiRegionHealth, type MultiRegionHealth } from "./survival/regi
|
|
|
100
101
|
export { createCommitController } from "./turn/commit.js";
|
|
101
102
|
export { createIntentBoard } from "./turn/intent.js";
|
|
102
103
|
export { createTurnLoop, type TurnLoop } from "./turn/turnLoop.js";
|
|
104
|
+
export { dispatchExpedition, recallExpedition, returnExpeditionNow, settleExpedition, type ExpeditionConfig, type ExpeditionDispatch, type ExpeditionEvent, type ExpeditionHazard, type ExpeditionState, type ExpeditionSupplyRule, } from "./work/expedition.js";
|
|
103
105
|
export { activeJobs, cancelJob, createWorkQueue, enqueue, fifoOrdering, jobById, jobProgress, pauseJob, priorityOrdering, queueSize, queuedJobs, resumeJob, tick, type CancelResult, type EnqueueOptions, type EnqueueResult, type Job, type JobId, type JobOrdering, type JobStatus, type JobValidation, type TickResult, type WorkQueueConfig, type WorkQueueEvent, type WorkQueueState, } from "./work/jobQueue.js";
|
|
106
|
+
export { stationOutputRate, type StaffedStation, type StationRateTuning, type StationWorker, } from "./work/staffedStation.js";
|
|
104
107
|
export { unitTrainingConfig, type ResourceCost, type TrainableUnitDef, type UnitReservation, type UnitSpawnOrder, type UnitTrainingOptions, type UnitTrainingSpec, } from "./work/unitTraining.js";
|
package/dist/gameplay.js
CHANGED
|
@@ -31,6 +31,7 @@ export { medalFor, rankLeaderboard, } from "./game/leaderboardRank.js";
|
|
|
31
31
|
export { createLevelSequence } from "./game/levelSequence.js";
|
|
32
32
|
export { createLoadouts } from "./game/loadout.js";
|
|
33
33
|
export { evaluateLootFilter, lootFilter } from "./game/lootFilter.js";
|
|
34
|
+
export { timeScaledRarity } from "./game/lootModifiers.js";
|
|
34
35
|
export { createLootPipeline, defineLootPipeline, } from "./game/lootPipeline.js";
|
|
35
36
|
export { createLootRegistry, lootTable } from "./game/lootTable.js";
|
|
36
37
|
export { resolveOneShotClip } from "./game/modelAnimation.js";
|
|
@@ -100,5 +101,7 @@ export { createMultiRegionHealth } from "./survival/regionHealth.js";
|
|
|
100
101
|
export { createCommitController } from "./turn/commit.js";
|
|
101
102
|
export { createIntentBoard } from "./turn/intent.js";
|
|
102
103
|
export { createTurnLoop } from "./turn/turnLoop.js";
|
|
104
|
+
export { dispatchExpedition, recallExpedition, returnExpeditionNow, settleExpedition, } from "./work/expedition.js";
|
|
103
105
|
export { activeJobs, cancelJob, createWorkQueue, enqueue, fifoOrdering, jobById, jobProgress, pauseJob, priorityOrdering, queueSize, queuedJobs, resumeJob, tick, } from "./work/jobQueue.js";
|
|
106
|
+
export { stationOutputRate, } from "./work/staffedStation.js";
|
|
104
107
|
export { unitTrainingConfig, } from "./work/unitTraining.js";
|
|
@@ -34,7 +34,13 @@ export type ActionCodes<TCode extends string = string> = readonly TCode[] | {
|
|
|
34
34
|
toggle?: readonly TCode[];
|
|
35
35
|
repeatMs?: number;
|
|
36
36
|
};
|
|
37
|
-
/**
|
|
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
|
+
*/
|
|
38
44
|
export type ActionCodesMap<TAction extends string = string, TCode extends string = string> = Record<TAction, ActionCodes<TCode>>;
|
|
39
45
|
/** @internal */
|
|
40
46
|
export declare function actionRepeatMs(codes: ActionCodes | undefined): number | undefined;
|
|
@@ -82,6 +88,14 @@ export interface ActionStateTracker<TAction extends string> {
|
|
|
82
88
|
wasPressed(action: TAction): boolean;
|
|
83
89
|
endFrame(): void;
|
|
84
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[];
|
|
85
99
|
}
|
|
86
100
|
/** @internal */
|
|
87
101
|
export declare function createActionStateTracker<TAction extends string, TCode extends string = string>(map: ActionStateBindingMap<TAction, TCode>): ActionStateTracker<TAction>;
|
|
@@ -179,9 +179,9 @@ function resolveActionBindingModes(config) {
|
|
|
179
179
|
}
|
|
180
180
|
/** @internal */
|
|
181
181
|
export function createActionStateTracker(map) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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()]));
|
|
185
185
|
const toggledActions = new Set();
|
|
186
186
|
const pressedThisFrame = new Set();
|
|
187
187
|
const activeCodes = new Set();
|
|
@@ -240,5 +240,16 @@ export function createActionStateTracker(map) {
|
|
|
240
240
|
pressedThisFrame.clear();
|
|
241
241
|
activeCodes.clear();
|
|
242
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,
|
|
243
254
|
};
|
|
244
255
|
}
|