@jgengine/core 0.10.0 → 0.11.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 +13 -2
- package/LICENSE +203 -661
- package/README.md +1 -1
- package/dist/ai/crowd.d.ts +55 -0
- package/dist/ai/crowd.js +114 -0
- package/dist/ai/flock.d.ts +5 -2
- package/dist/ai/flock.js +5 -2
- package/dist/ai/groupAssist.d.ts +1 -0
- package/dist/ai/groupAssist.js +1 -0
- package/dist/ai/heatSystem.d.ts +76 -0
- package/dist/ai/heatSystem.js +97 -0
- package/dist/ai/interestScheduler.d.ts +107 -0
- package/dist/ai/interestScheduler.js +118 -0
- package/dist/ai/jobBoard.d.ts +1 -0
- package/dist/ai/jobBoard.js +1 -0
- package/dist/ai/laneSelect.d.ts +5 -2
- package/dist/ai/laneSelect.js +5 -2
- package/dist/ai/mobBrain.d.ts +1 -0
- package/dist/ai/mobBrain.js +1 -0
- package/dist/ai/spawnDirector.d.ts +7 -2
- package/dist/ai/spawnDirector.js +18 -15
- package/dist/ai/spawnPoint.d.ts +3 -1
- package/dist/ai/spawnPoint.js +3 -1
- package/dist/ai/targetAcquisition.d.ts +84 -0
- package/dist/ai/targetAcquisition.js +92 -0
- package/dist/anim/easing.d.ts +19 -0
- package/dist/anim/easing.js +19 -0
- package/dist/anim/oscillator.d.ts +12 -0
- package/dist/anim/oscillator.js +25 -0
- package/dist/area/areaEffectField.d.ts +127 -0
- package/dist/area/areaEffectField.js +175 -0
- package/dist/area/stackPolicy.d.ts +47 -0
- package/dist/area/stackPolicy.js +99 -0
- package/dist/authoring.d.ts +1 -0
- package/dist/authoring.js +1 -0
- package/dist/board/laneBoard.d.ts +13 -0
- package/dist/board/laneBoard.js +13 -0
- package/dist/board/timelineBoard.d.ts +10 -0
- package/dist/board/timelineBoard.js +10 -0
- package/dist/combat/abilityKit.d.ts +18 -0
- package/dist/combat/abilityKit.js +24 -9
- package/dist/combat/animationState.d.ts +5 -0
- package/dist/combat/animationState.js +5 -0
- package/dist/combat/breakMeters.d.ts +10 -0
- package/dist/combat/breakMeters.js +10 -0
- package/dist/combat/castRunner.d.ts +5 -0
- package/dist/combat/castRunner.js +5 -0
- package/dist/combat/comboPoints.d.ts +5 -0
- package/dist/combat/comboPoints.js +5 -0
- package/dist/combat/comboString.d.ts +5 -0
- package/dist/combat/comboString.js +5 -0
- package/dist/combat/damageInterceptors.d.ts +192 -0
- package/dist/combat/damageInterceptors.js +251 -0
- package/dist/combat/damageMatchup.d.ts +68 -0
- package/dist/combat/damageMatchup.js +42 -0
- package/dist/combat/damageResolution.d.ts +63 -0
- package/dist/combat/damageResolution.js +50 -0
- package/dist/combat/death.d.ts +7 -1
- package/dist/combat/death.js +5 -0
- package/dist/combat/deathReason.d.ts +14 -0
- package/dist/combat/deathReason.js +1 -0
- package/dist/combat/defensiveWindow.d.ts +5 -0
- package/dist/combat/defensiveWindow.js +5 -0
- package/dist/combat/dotField.d.ts +5 -1
- package/dist/combat/dotField.js +5 -1
- package/dist/combat/downed.d.ts +5 -0
- package/dist/combat/downed.js +5 -0
- package/dist/combat/effects.d.ts +5 -0
- package/dist/combat/effects.js +5 -0
- package/dist/combat/encounterSequence.d.ts +258 -0
- package/dist/combat/encounterSequence.js +426 -0
- package/dist/combat/hitReaction.d.ts +57 -1
- package/dist/combat/hitReaction.js +45 -5
- package/dist/combat/magazine.d.ts +54 -0
- package/dist/combat/magazine.js +96 -0
- package/dist/combat/projectiles.d.ts +5 -0
- package/dist/combat/projectiles.js +32 -14
- package/dist/combat/receivedDamage.d.ts +117 -0
- package/dist/combat/receivedDamage.js +92 -0
- package/dist/combat/regenShield.d.ts +5 -1
- package/dist/combat/regenShield.js +5 -1
- package/dist/combat/renderCues.d.ts +51 -0
- package/dist/combat/renderCues.js +44 -0
- package/dist/combat/resourcePool.d.ts +5 -0
- package/dist/combat/resourcePool.js +5 -0
- package/dist/combat/rewardAllocation.d.ts +121 -0
- package/dist/combat/rewardAllocation.js +251 -0
- package/dist/combat/shotOrigin.d.ts +18 -1
- package/dist/combat/shotOrigin.js +39 -0
- package/dist/combat/statusApplication.d.ts +96 -0
- package/dist/combat/statusApplication.js +81 -0
- package/dist/combat/telegraph.d.ts +11 -2
- package/dist/combat/telegraph.js +11 -2
- package/dist/combat/thresholdCrossings.d.ts +85 -0
- package/dist/combat/thresholdCrossings.js +116 -0
- package/dist/combat.d.ts +34 -0
- package/dist/combat.js +34 -0
- package/dist/commands/commandRegistry.d.ts +11 -0
- package/dist/commands/commandRegistry.js +9 -2
- package/dist/crafting/crop.d.ts +18 -0
- package/dist/crafting/crop.js +18 -0
- package/dist/crafting/production.d.ts +5 -0
- package/dist/crafting/production.js +5 -0
- package/dist/devtools/rewriteTunables.d.ts +17 -0
- package/dist/devtools/rewriteTunables.js +276 -0
- package/dist/devtools/saveEndpoint.d.ts +46 -0
- package/dist/devtools/saveEndpoint.js +48 -0
- package/dist/devtools/transformTunables.d.ts +3 -0
- package/dist/devtools/transformTunables.js +3 -0
- package/dist/devtools/tunableSchema.d.ts +31 -0
- package/dist/devtools/tunableSchema.js +31 -0
- package/dist/economy/currency.d.ts +12 -1
- package/dist/economy/currency.js +12 -1
- package/dist/economy/listingBook.d.ts +91 -0
- package/dist/economy/listingBook.js +144 -0
- package/dist/economy/resourceLedger.d.ts +222 -0
- package/dist/economy/resourceLedger.js +353 -0
- package/dist/economy/sharedWallet.d.ts +14 -0
- package/dist/economy/sharedWallet.js +14 -0
- package/dist/economy/techTree.d.ts +12 -0
- package/dist/economy/techTree.js +12 -0
- package/dist/economy/wallet.d.ts +26 -2
- package/dist/economy/wallet.js +35 -6
- package/dist/editor/commands.d.ts +190 -7
- package/dist/editor/commands.js +727 -55
- package/dist/editor/document.d.ts +159 -13
- package/dist/editor/document.js +750 -19
- package/dist/editor/grid.d.ts +229 -0
- package/dist/editor/grid.js +366 -0
- package/dist/editor/gridAdapters.d.ts +80 -0
- package/dist/editor/gridAdapters.js +134 -0
- package/dist/editor/index.d.ts +73 -6
- package/dist/editor/index.js +6 -2
- package/dist/editor/liveSync.d.ts +127 -0
- package/dist/editor/liveSync.js +249 -0
- package/dist/editor/runtimeInspector.d.ts +88 -0
- package/dist/editor/runtimeInspector.js +239 -0
- package/dist/editor/types.d.ts +107 -1
- package/dist/faction/reputation.d.ts +5 -0
- package/dist/faction/reputation.js +5 -0
- package/dist/format/distance.d.ts +17 -0
- package/dist/format/distance.js +18 -0
- package/dist/format/duration.d.ts +27 -0
- package/dist/format/duration.js +45 -0
- package/dist/format/speed.d.ts +17 -0
- package/dist/format/speed.js +25 -0
- package/dist/game/connectedPlayers.d.ts +6 -4
- package/dist/game/connectedPlayers.js +2 -2
- package/dist/game/cosmetics.d.ts +9 -0
- package/dist/game/cosmetics.js +16 -0
- package/dist/game/defineGame.d.ts +100 -2
- package/dist/game/defineGame.js +39 -2
- package/dist/game/defineSystem.d.ts +88 -0
- package/dist/game/defineSystem.js +45 -0
- package/dist/game/dialogue.d.ts +33 -0
- package/dist/game/dialogue.js +21 -0
- package/dist/game/events.d.ts +35 -13
- package/dist/game/events.js +5 -0
- package/dist/game/feed.d.ts +33 -0
- package/dist/game/feed.js +35 -2
- package/dist/game/gamePhase.d.ts +11 -0
- package/dist/game/gamePhase.js +16 -0
- package/dist/game/leaderboard.d.ts +5 -0
- package/dist/game/leaderboard.js +5 -0
- package/dist/game/loadout.d.ts +5 -0
- package/dist/game/loadout.js +5 -0
- package/dist/game/lootFilter.d.ts +2 -0
- package/dist/game/lootFilter.js +2 -0
- package/dist/game/lootPipeline.d.ts +164 -0
- package/dist/game/lootPipeline.js +216 -0
- package/dist/game/lootTable.d.ts +19 -2
- package/dist/game/lootTable.js +15 -5
- package/dist/game/objectives.d.ts +43 -0
- package/dist/game/objectives.js +36 -0
- package/dist/game/paramLayers.d.ts +148 -0
- package/dist/game/paramLayers.js +185 -0
- package/dist/game/ping.d.ts +5 -0
- package/dist/game/ping.js +5 -0
- package/dist/game/playableGame.d.ts +92 -7
- package/dist/game/quest.d.ts +8 -0
- package/dist/game/quest.js +29 -0
- package/dist/game/race.d.ts +141 -0
- package/dist/game/race.js +169 -0
- package/dist/game/recordBook.d.ts +3 -1
- package/dist/game/recordBook.js +4 -1
- package/dist/game/ruleSelection.d.ts +75 -0
- package/dist/game/ruleSelection.js +127 -0
- package/dist/game/runDraft.d.ts +5 -0
- package/dist/game/runDraft.js +5 -0
- package/dist/game/saveStore.d.ts +111 -0
- package/dist/game/saveStore.js +312 -0
- package/dist/game/snapshotHistory.d.ts +67 -0
- package/dist/game/snapshotHistory.js +95 -0
- package/dist/game/social.d.ts +38 -1
- package/dist/game/social.js +71 -0
- package/dist/game/spawnPoints.d.ts +5 -0
- package/dist/game/spawnPoints.js +5 -0
- package/dist/game/systemRuntime.d.ts +50 -0
- package/dist/game/systemRuntime.js +157 -0
- package/dist/game/systemSchedule.d.ts +72 -0
- package/dist/game/systemSchedule.js +242 -0
- package/dist/game/toasts.d.ts +43 -0
- package/dist/game/toasts.js +44 -0
- package/dist/game/trade.d.ts +10 -0
- package/dist/game/trade.js +10 -0
- package/dist/game/unlocks.d.ts +8 -0
- package/dist/game/unlocks.js +16 -0
- package/dist/game/vfxInstance.d.ts +138 -0
- package/dist/game/vfxInstance.js +96 -0
- package/dist/game/worldItem.d.ts +17 -4
- package/dist/game/worldItem.js +17 -4
- package/dist/gameplay.d.ts +91 -0
- package/dist/gameplay.js +91 -0
- package/dist/index.d.ts +5 -14
- package/dist/index.js +5 -14
- package/dist/input/actionBindings.d.ts +23 -6
- package/dist/input/actionBindings.js +23 -6
- package/dist/input/axisInput.d.ts +7 -2
- package/dist/input/axisInput.js +7 -2
- package/dist/input/controlGroups.d.ts +67 -0
- package/dist/input/controlGroups.js +41 -0
- package/dist/input/touchScheme.d.ts +82 -6
- package/dist/input/touchScheme.js +123 -10
- package/dist/interaction/lockpick.d.ts +61 -0
- package/dist/interaction/lockpick.js +168 -0
- package/dist/interaction/proximityPrompt.d.ts +2 -0
- package/dist/interaction/proximityPrompt.js +2 -0
- package/dist/interaction/qte.d.ts +5 -0
- package/dist/interaction/qte.js +5 -0
- package/dist/interaction/skillCheck.d.ts +5 -0
- package/dist/interaction/skillCheck.js +5 -0
- package/dist/inventory/inventoryModel.d.ts +12 -0
- package/dist/inventory/inventoryModel.js +12 -0
- package/dist/inventory/shapedGrid.d.ts +16 -0
- package/dist/inventory/shapedGrid.js +16 -0
- package/dist/inventory/slotModel.d.ts +10 -0
- package/dist/inventory/slotModel.js +10 -0
- package/dist/item/durability.d.ts +5 -0
- package/dist/item/durability.js +5 -0
- package/dist/item/itemIdentity.d.ts +171 -0
- package/dist/item/itemIdentity.js +138 -0
- package/dist/item/itemInstanceRegistry.d.ts +32 -0
- package/dist/item/itemInstanceRegistry.js +34 -0
- package/dist/item/itemgen.d.ts +163 -0
- package/dist/item/itemgen.js +151 -0
- package/dist/item/modularItem.d.ts +5 -0
- package/dist/item/modularItem.js +5 -0
- package/dist/item/use.d.ts +5 -0
- package/dist/item/use.js +5 -0
- package/dist/item/useBehavior.d.ts +142 -0
- package/dist/item/useBehavior.js +105 -0
- package/dist/item/weapon.d.ts +5 -0
- package/dist/item/weapon.js +5 -0
- package/dist/math/scalar.d.ts +25 -0
- package/dist/math/scalar.js +47 -0
- package/dist/meta/changelog.d.ts +4 -1
- package/dist/meta/changelog.js +16 -2
- package/dist/movement/avoidance.d.ts +45 -0
- package/dist/movement/avoidance.js +123 -0
- package/dist/movement/cameraRig.d.ts +8 -0
- package/dist/movement/cameraRig.js +8 -0
- package/dist/movement/dash.d.ts +11 -0
- package/dist/movement/dash.js +11 -0
- package/dist/movement/formation.d.ts +135 -0
- package/dist/movement/formation.js +194 -0
- package/dist/movement/glideModel.d.ts +5 -0
- package/dist/movement/glideModel.js +5 -0
- package/dist/movement/grappleSwing.d.ts +5 -0
- package/dist/movement/grappleSwing.js +5 -0
- package/dist/movement/leaderTrail.d.ts +5 -0
- package/dist/movement/leaderTrail.js +5 -0
- package/dist/movement/movementModel.d.ts +35 -8
- package/dist/movement/movementModel.js +42 -14
- package/dist/movement/playerMovement.js +65 -10
- package/dist/movement/poseState.d.ts +13 -0
- package/dist/movement/poseState.js +16 -0
- package/dist/movement/steering.d.ts +8 -0
- package/dist/movement/steering.js +18 -0
- package/dist/multiplayer/combatSnapshot.d.ts +3 -0
- package/dist/multiplayer/combatSnapshot.js +3 -0
- package/dist/multiplayer/lagCompensation.d.ts +5 -0
- package/dist/multiplayer/lagCompensation.js +5 -0
- package/dist/multiplayer/presenceModel.d.ts +9 -1
- package/dist/multiplayer/presenceModel.js +9 -1
- package/dist/multiplayer/simultaneousCommit.d.ts +3 -0
- package/dist/multiplayer/simultaneousCommit.js +3 -0
- package/dist/multiplayer.d.ts +11 -0
- package/dist/multiplayer.js +11 -0
- package/dist/nav/pathFollow.d.ts +57 -0
- package/dist/nav/pathFollow.js +109 -0
- package/dist/orders/orderKinds.d.ts +156 -0
- package/dist/orders/orderKinds.js +197 -0
- package/dist/orders/orderQueue.d.ts +208 -0
- package/dist/orders/orderQueue.js +211 -0
- package/dist/physics/drivableVehicle.d.ts +29 -0
- package/dist/physics/drivableVehicle.js +16 -0
- package/dist/physics/kinematicVehicle.d.ts +21 -1
- package/dist/physics/kinematicVehicle.js +23 -8
- package/dist/physics/physicsWorld.d.ts +24 -12
- package/dist/physics/physicsWorld.js +11 -5
- package/dist/physics/radialImpulse.d.ts +25 -0
- package/dist/physics/radialImpulse.js +27 -0
- package/dist/procedural.d.ts +3 -0
- package/dist/procedural.js +3 -0
- package/dist/progression/statGraph.d.ts +116 -0
- package/dist/progression/statGraph.js +318 -0
- package/dist/puzzle/cellGrid.d.ts +9 -0
- package/dist/puzzle/cellGrid.js +9 -0
- package/dist/puzzle/fallingPiece.d.ts +9 -0
- package/dist/puzzle/fallingPiece.js +9 -0
- package/dist/puzzle/nonogram.d.ts +39 -0
- package/dist/puzzle/nonogram.js +136 -0
- package/dist/random/nameGen.d.ts +5 -0
- package/dist/random/nameGen.js +5 -0
- package/dist/random/pick.d.ts +8 -0
- package/dist/random/pick.js +31 -0
- package/dist/random/rng.d.ts +22 -0
- package/dist/random/rng.js +28 -8
- package/dist/random/seedLink.d.ts +4 -0
- package/dist/random/seedLink.js +4 -0
- package/dist/relation/keyedValues.d.ts +54 -0
- package/dist/relation/keyedValues.js +115 -0
- package/dist/relation/thresholds.d.ts +62 -0
- package/dist/relation/thresholds.js +62 -0
- package/dist/render/color.d.ts +12 -0
- package/dist/render/color.js +26 -0
- package/dist/render/lookPreset.d.ts +41 -0
- package/dist/render/lookPreset.js +29 -0
- package/dist/render/postProcessing.d.ts +20 -0
- package/dist/render/postProcessing.js +21 -1
- package/dist/rules/predicate.d.ts +53 -0
- package/dist/rules/predicate.js +64 -0
- package/dist/rules/ruleEffects.d.ts +38 -0
- package/dist/rules/ruleEffects.js +37 -0
- package/dist/rules/triggeredRules.d.ts +160 -0
- package/dist/rules/triggeredRules.js +241 -0
- package/dist/runtime/adapter.d.ts +42 -3
- package/dist/runtime/adapter.js +44 -1
- package/dist/runtime/cameraDirector.d.ts +1 -0
- package/dist/runtime/cameraDirector.js +1 -0
- package/dist/runtime/commandRunner.d.ts +1 -0
- package/dist/runtime/commandRunner.js +1 -0
- package/dist/runtime/context/combatFx.d.ts +23 -0
- package/dist/runtime/context/combatFx.js +161 -0
- package/dist/runtime/context/registries.d.ts +13 -0
- package/dist/runtime/context/registries.js +84 -0
- package/dist/runtime/context/worldItems.d.ts +21 -0
- package/dist/runtime/context/worldItems.js +37 -0
- package/dist/runtime/descriptors/baseline.d.ts +48 -0
- package/dist/runtime/descriptors/baseline.js +134 -0
- package/dist/runtime/descriptors/features.d.ts +74 -0
- package/dist/runtime/descriptors/features.js +281 -0
- package/dist/runtime/gameContext.d.ts +114 -13
- package/dist/runtime/gameContext.js +303 -395
- package/dist/runtime/gameRuntime.d.ts +1 -0
- package/dist/runtime/gameRuntime.js +1 -0
- package/dist/runtime/headlessRunner.d.ts +60 -0
- package/dist/runtime/headlessRunner.js +44 -0
- package/dist/runtime/hostPersistence.d.ts +12 -0
- package/dist/runtime/hostPersistence.js +12 -0
- package/dist/runtime/hostedGameRunner.d.ts +8 -3
- package/dist/runtime/hostedGameRunner.js +26 -3
- package/dist/runtime/hostedWorldSession.d.ts +12 -4
- package/dist/runtime/hostedWorldSession.js +11 -4
- package/dist/runtime/inputSnapshot.d.ts +8 -3
- package/dist/runtime/inputSnapshot.js +6 -2
- package/dist/runtime/motionIntents.d.ts +11 -2
- package/dist/runtime/motionIntents.js +16 -2
- package/dist/runtime/perContext.d.ts +15 -0
- package/dist/runtime/perContext.js +21 -0
- package/dist/runtime/runtimeSave.d.ts +79 -0
- package/dist/runtime/runtimeSave.js +105 -0
- package/dist/runtime/save.d.ts +4 -0
- package/dist/runtime/save.js +4 -0
- package/dist/runtime/snapshot.d.ts +7 -0
- package/dist/runtime/snapshot.js +7 -0
- package/dist/runtime/visibility.d.ts +2 -1
- package/dist/runtime/visibility.js +2 -1
- package/dist/runtime/worldChannel.d.ts +7 -3
- package/dist/runtime/worldChannel.js +51 -6
- package/dist/runtime/worldMirror.d.ts +14 -2
- package/dist/runtime/worldMirror.js +15 -2
- package/dist/runtime/worldProjection.d.ts +47 -0
- package/dist/runtime/worldProjection.js +52 -0
- package/dist/runtime/worldReplication.d.ts +30 -7
- package/dist/runtime/worldReplication.js +41 -9
- package/dist/runtime/worldSnapshot.d.ts +30 -3
- package/dist/runtime/worldSnapshot.js +20 -5
- package/dist/scene/assetCatalog.d.ts +21 -0
- package/dist/scene/assetCatalog.js +4 -0
- package/dist/scene/assetDiagnostics.d.ts +69 -0
- package/dist/scene/assetDiagnostics.js +110 -0
- package/dist/scene/assetGenerator.d.ts +65 -0
- package/dist/scene/assetGenerator.js +64 -0
- package/dist/scene/assetSpace.d.ts +137 -0
- package/dist/scene/assetSpace.js +259 -0
- package/dist/scene/authoredTriggers.d.ts +169 -0
- package/dist/scene/authoredTriggers.js +340 -0
- package/dist/scene/behaviorRuntime.d.ts +75 -0
- package/dist/scene/behaviorRuntime.js +217 -0
- package/dist/scene/behaviors.d.ts +14 -2
- package/dist/scene/behaviors.js +7 -2
- package/dist/scene/bodyBind.d.ts +60 -0
- package/dist/scene/bodyBind.js +51 -0
- package/dist/scene/builtinSceneKinds.d.ts +5 -0
- package/dist/scene/builtinSceneKinds.js +62 -0
- package/dist/scene/colliders.d.ts +13 -1
- package/dist/scene/colliders.js +37 -1
- package/dist/scene/entityStats.d.ts +11 -2
- package/dist/scene/entityStats.js +11 -2
- package/dist/scene/entityStore.d.ts +12 -1
- package/dist/scene/entityStore.js +15 -0
- package/dist/scene/facing.d.ts +18 -0
- package/dist/scene/facing.js +26 -0
- package/dist/scene/modelSockets.d.ts +33 -0
- package/dist/scene/modelSockets.js +29 -0
- package/dist/scene/objectQuery.d.ts +2 -0
- package/dist/scene/objectQuery.js +2 -0
- package/dist/scene/objectStore.d.ts +4 -0
- package/dist/scene/objectStore.js +6 -0
- package/dist/scene/possession.d.ts +8 -0
- package/dist/scene/possession.js +14 -0
- package/dist/scene/roster.d.ts +3 -0
- package/dist/scene/roster.js +12 -0
- package/dist/scene/sceneKinds.d.ts +240 -0
- package/dist/scene/sceneKinds.js +214 -0
- package/dist/scene/sceneOwnership.d.ts +169 -0
- package/dist/scene/sceneOwnership.js +179 -0
- package/dist/scene/selectionBookmarks.d.ts +93 -0
- package/dist/scene/selectionBookmarks.js +122 -0
- package/dist/scene/vehicleSeat.d.ts +68 -0
- package/dist/scene/vehicleSeat.js +59 -0
- package/dist/sensor/concealment.d.ts +3 -0
- package/dist/sensor/concealment.js +3 -0
- package/dist/sensor/freezeMonitor.d.ts +1 -0
- package/dist/sensor/freezeMonitor.js +1 -0
- package/dist/sensor/frustumSensor.d.ts +7 -3
- package/dist/sensor/frustumSensor.js +7 -3
- package/dist/sensor/hiddenStateProbe.d.ts +5 -2
- package/dist/sensor/hiddenStateProbe.js +5 -2
- package/dist/sensor/recordingBuffer.d.ts +2 -1
- package/dist/sensor/recordingBuffer.js +2 -1
- package/dist/sensor/replayLoop.d.ts +6 -2
- package/dist/sensor/replayLoop.js +6 -2
- package/dist/sensor/revealQuery.d.ts +2 -1
- package/dist/sensor/revealQuery.js +2 -1
- package/dist/sensor/visionCone.d.ts +10 -3
- package/dist/sensor/visionCone.js +10 -3
- package/dist/session/contestedChannel.d.ts +1 -0
- package/dist/session/contestedChannel.js +1 -0
- package/dist/session/extraction.d.ts +1 -0
- package/dist/session/extraction.js +1 -0
- package/dist/session/roles.d.ts +6 -0
- package/dist/session/roles.js +6 -0
- package/dist/session/roundState.d.ts +8 -0
- package/dist/session/roundState.js +2 -0
- package/dist/settings/settingsModel.d.ts +1 -0
- package/dist/settings/settingsModel.js +1 -0
- package/dist/stats/accumulatorMeter.d.ts +6 -0
- package/dist/stats/accumulatorMeter.js +6 -0
- package/dist/stats/eventMeter.d.ts +5 -0
- package/dist/stats/eventMeter.js +5 -0
- package/dist/stats/rollCheck.d.ts +5 -0
- package/dist/stats/rollCheck.js +5 -0
- package/dist/stats/statModifiers.d.ts +5 -0
- package/dist/stats/statModifiers.js +5 -0
- package/dist/store/defineKeyedStore.d.ts +38 -0
- package/dist/store/defineKeyedStore.js +42 -0
- package/dist/store/defineStore.d.ts +33 -0
- package/dist/store/defineStore.js +35 -0
- package/dist/store/observableKeyedStore.d.ts +5 -0
- package/dist/store/observableKeyedStore.js +34 -2
- package/dist/survival/decayMeter.d.ts +55 -0
- package/dist/survival/decayMeter.js +130 -55
- package/dist/survival/moodle.d.ts +2 -0
- package/dist/survival/regionHealth.d.ts +6 -0
- package/dist/survival/regionHealth.js +5 -0
- package/dist/tactics/fallingGrid.d.ts +4 -2
- package/dist/tactics/fallingGrid.js +4 -2
- package/dist/tactics/predictiveQuery.d.ts +3 -0
- package/dist/tactics/predictiveQuery.js +3 -0
- package/dist/tactics/snapshot.d.ts +2 -0
- package/dist/tactics/snapshot.js +2 -0
- package/dist/tactics/surface.d.ts +1 -0
- package/dist/tactics/surface.js +1 -0
- package/dist/tactics/tacticalGrid.d.ts +1 -0
- package/dist/tactics/tacticalGrid.js +1 -0
- package/dist/time/beatClock.d.ts +14 -4
- package/dist/time/beatClock.js +14 -4
- package/dist/time/calendarClock.d.ts +3 -0
- package/dist/time/calendarClock.js +3 -0
- package/dist/time/idleProgress.d.ts +4 -0
- package/dist/time/idleProgress.js +4 -0
- package/dist/time/simClock.d.ts +2 -0
- package/dist/time/simClock.js +20 -5
- package/dist/ui/actionModel.d.ts +110 -0
- package/dist/ui/actionModel.js +159 -0
- package/dist/ui/gameLayout.d.ts +30 -11
- package/dist/ui/gameLayout.js +30 -11
- package/dist/ui/hudDocument.d.ts +114 -0
- package/dist/ui/hudDocument.js +220 -0
- package/dist/ui/hudLayout.d.ts +33 -1
- package/dist/ui/hudLayout.js +204 -17
- package/dist/ui/selectionModel.d.ts +92 -0
- package/dist/ui/selectionModel.js +88 -0
- package/dist/ui/tooltipModel.d.ts +63 -0
- package/dist/ui/tooltipModel.js +82 -0
- package/dist/ui.d.ts +15 -0
- package/dist/ui.js +15 -0
- package/dist/visibility/bounds.d.ts +8 -1
- package/dist/visibility/bounds.js +8 -1
- package/dist/visibility/frustum.d.ts +12 -3
- package/dist/visibility/frustum.js +12 -3
- package/dist/work/jobQueue.d.ts +201 -0
- package/dist/work/jobQueue.js +253 -0
- package/dist/work/unitTraining.d.ts +58 -0
- package/dist/work/unitTraining.js +52 -0
- package/dist/world/authoredObjects.d.ts +58 -0
- package/dist/world/authoredObjects.js +69 -0
- package/dist/world/buildingGenerator.d.ts +18 -0
- package/dist/world/buildingGenerator.js +62 -0
- package/dist/world/buildings.d.ts +5 -0
- package/dist/world/buildings.js +5 -0
- package/dist/world/catenary.d.ts +22 -0
- package/dist/world/catenary.js +69 -0
- package/dist/world/features.d.ts +115 -7
- package/dist/world/features.js +56 -5
- package/dist/world/footprintGrid.d.ts +59 -0
- package/dist/world/footprintGrid.js +141 -0
- package/dist/world/geometry.d.ts +64 -0
- package/dist/world/geometry.js +94 -0
- package/dist/world/grassKind.d.ts +51 -0
- package/dist/world/grassKind.js +101 -0
- package/dist/world/pathInstances.d.ts +27 -0
- package/dist/world/pathInstances.js +27 -0
- package/dist/world/pathTerrain.d.ts +80 -0
- package/dist/world/pathTerrain.js +142 -0
- package/dist/world/placeAsset.d.ts +81 -0
- package/dist/world/placeAsset.js +77 -0
- package/dist/world/placement.d.ts +4 -0
- package/dist/world/placement.js +4 -0
- package/dist/world/placementController.d.ts +6 -0
- package/dist/world/placementController.js +6 -0
- package/dist/world/polyline.d.ts +34 -0
- package/dist/world/polyline.js +81 -0
- package/dist/world/roads.d.ts +8 -0
- package/dist/world/roads.js +38 -0
- package/dist/world/scatterRegion.d.ts +164 -0
- package/dist/world/scatterRegion.js +409 -0
- package/dist/world/sceneShapes.d.ts +43 -0
- package/dist/world/sceneShapes.js +1 -0
- package/dist/world/segment.d.ts +27 -0
- package/dist/world/segment.js +43 -0
- package/dist/world/soilKind.d.ts +48 -0
- package/dist/world/soilKind.js +92 -0
- package/dist/world/spatialSample.d.ts +241 -0
- package/dist/world/spatialSample.js +465 -0
- package/dist/world/terraform.d.ts +213 -1
- package/dist/world/terraform.js +655 -20
- package/dist/world/terrain.d.ts +119 -20
- package/dist/world/terrain.js +247 -41
- package/dist/world/terrainGuides.d.ts +182 -0
- package/dist/world/terrainGuides.js +343 -0
- package/dist/world/vec2.d.ts +81 -0
- package/dist/world/vec2.js +128 -0
- package/dist/world/vec3.d.ts +49 -0
- package/dist/world/vec3.js +76 -0
- package/dist/world/vegetation.d.ts +71 -0
- package/dist/world/vegetation.js +151 -0
- package/dist/world/visibilityField.d.ts +154 -0
- package/dist/world/visibilityField.js +271 -0
- package/dist/world/volumetricClouds.d.ts +52 -0
- package/dist/world/volumetricClouds.js +43 -0
- package/dist/world/walls.d.ts +9 -0
- package/dist/world/walls.js +9 -0
- package/dist/world/waterKind.d.ts +55 -0
- package/dist/world/waterKind.js +99 -0
- package/dist/world.d.ts +132 -0
- package/dist/world.js +132 -0
- package/package.json +32 -8
- package/skills/jgengine/SKILL.md +51 -0
- package/skills/jgengine/api.md +434 -0
- package/skills/jgengine/capabilities.md +29 -0
- package/skills/jgengine/reference-cartridge.md +72 -0
- package/skills/jgengine-combat/SKILL.md +34 -0
- package/skills/jgengine-combat/api.md +549 -0
- package/skills/jgengine-combat/capabilities.md +169 -0
- package/skills/jgengine-combat/reference.md +98 -0
- package/skills/jgengine-gameplay/SKILL.md +39 -0
- package/skills/jgengine-gameplay/api.md +1794 -0
- package/skills/jgengine-gameplay/capabilities.md +378 -0
- package/skills/jgengine-gameplay/reference-systems.md +60 -0
- package/skills/jgengine-world/SKILL.md +38 -0
- package/skills/jgengine-world/api.md +2652 -0
- package/skills/jgengine-world/capabilities.md +370 -0
- package/skills/jgengine-world/reference.md +222 -0
- package/dist/cartridge/runtime.d.ts +0 -15
- package/dist/cartridge/runtime.js +0 -556
- package/dist/cartridge/spec.d.ts +0 -188
- package/dist/cartridge/spec.js +0 -19
- package/dist/cartridge/validate.d.ts +0 -2
- package/dist/cartridge/validate.js +0 -78
- package/dist/store/engineStore.d.ts +0 -22
- package/dist/store/engineStore.js +0 -50
- package/dist/store/features.d.ts +0 -10
- package/dist/store/features.js +0 -23
- package/llms.txt +0 -5389
package/dist/editor/document.js
CHANGED
|
@@ -1,4 +1,33 @@
|
|
|
1
|
-
|
|
1
|
+
import { cloneEditorUiDocument, decodeEditorUiDocument } from "../ui/hudDocument.js";
|
|
2
|
+
import { migrateTerrainSnapshot } from "../world/terraform.js";
|
|
3
|
+
import { cloneGridLayer, migrateGridLayer, } from "./grid.js";
|
|
4
|
+
function migrateGrids(grids) {
|
|
5
|
+
return grids === undefined ? undefined : grids.map(migrateGridLayer);
|
|
6
|
+
}
|
|
7
|
+
function upsertGrids(base, overlay) {
|
|
8
|
+
if (base === undefined && overlay === undefined)
|
|
9
|
+
return undefined;
|
|
10
|
+
const byId = new Map();
|
|
11
|
+
for (const layer of base ?? [])
|
|
12
|
+
byId.set(layer.id, cloneGridLayer(layer));
|
|
13
|
+
for (const layer of overlay ?? [])
|
|
14
|
+
byId.set(layer.id, cloneGridLayer(layer));
|
|
15
|
+
return [...byId.values()];
|
|
16
|
+
}
|
|
17
|
+
/** Spread-ready non-placeable fields preserved across document rebuilds. @internal */
|
|
18
|
+
export function editorDocumentExtras(doc) {
|
|
19
|
+
return {
|
|
20
|
+
prefabs: doc.prefabs,
|
|
21
|
+
collections: doc.collections,
|
|
22
|
+
catalogs: doc.catalogs,
|
|
23
|
+
...(doc.grids === undefined ? {} : { grids: doc.grids }),
|
|
24
|
+
...(doc.terrain === undefined ? {} : { terrain: doc.terrain }),
|
|
25
|
+
...(doc.ui === undefined ? {} : { ui: doc.ui }),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** Builds a fresh, empty editor document to start authoring a scene from scratch.
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
2
31
|
export function createEmptyEditorDocument() {
|
|
3
32
|
return {
|
|
4
33
|
version: 1,
|
|
@@ -6,10 +35,32 @@ export function createEmptyEditorDocument() {
|
|
|
6
35
|
volumes: [],
|
|
7
36
|
paths: [],
|
|
8
37
|
annotations: [],
|
|
38
|
+
prefabs: [],
|
|
39
|
+
collections: [],
|
|
40
|
+
catalogs: [],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function cloneCatalogEntry(entry) {
|
|
44
|
+
return {
|
|
45
|
+
id: entry.id,
|
|
46
|
+
...(entry.label === undefined ? {} : { label: entry.label }),
|
|
47
|
+
...(entry.meta === undefined ? {} : { meta: { ...entry.meta } }),
|
|
9
48
|
};
|
|
10
49
|
}
|
|
11
|
-
|
|
50
|
+
function cloneCatalogs(catalogs) {
|
|
51
|
+
return (catalogs ?? []).map((catalog) => ({
|
|
52
|
+
id: catalog.id,
|
|
53
|
+
entries: catalog.entries.map(cloneCatalogEntry),
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Deep-copies an editor document so edits never mutate the source. The terrain snapshot is
|
|
58
|
+
* shared by reference: sculpt commands replace it wholesale (copy-on-write), never mutate it in
|
|
59
|
+
* place, so history snapshots stay cheap even on large heightfields.
|
|
60
|
+
* @internal
|
|
61
|
+
*/
|
|
12
62
|
export function cloneEditorDocument(doc) {
|
|
63
|
+
const ui = cloneEditorUiDocument(doc.ui);
|
|
13
64
|
return {
|
|
14
65
|
version: 1,
|
|
15
66
|
markers: doc.markers.map((marker) => ({
|
|
@@ -33,52 +84,255 @@ export function cloneEditorDocument(doc) {
|
|
|
33
84
|
position: { ...note.position },
|
|
34
85
|
...(note.meta === undefined ? {} : { meta: { ...note.meta } }),
|
|
35
86
|
})),
|
|
87
|
+
prefabs: doc.prefabs.map((prefab) => ({
|
|
88
|
+
...prefab,
|
|
89
|
+
fragment: {
|
|
90
|
+
markers: prefab.fragment.markers.map((marker) => ({ ...marker, position: { ...marker.position } })),
|
|
91
|
+
volumes: prefab.fragment.volumes.map((volume) => ({ ...volume, center: { ...volume.center } })),
|
|
92
|
+
paths: prefab.fragment.paths.map((path) => ({ ...path, points: path.points.map((point) => ({ ...point })) })),
|
|
93
|
+
annotations: prefab.fragment.annotations.map((note) => ({ ...note, position: { ...note.position } })),
|
|
94
|
+
},
|
|
95
|
+
})),
|
|
96
|
+
collections: doc.collections.map((collection) => ({ ...collection, memberIds: [...collection.memberIds] })),
|
|
97
|
+
catalogs: cloneCatalogs(doc.catalogs),
|
|
98
|
+
...(doc.grids === undefined ? {} : { grids: doc.grids.map(cloneGridLayer) }),
|
|
99
|
+
...(doc.terrain === undefined ? {} : { terrain: doc.terrain }),
|
|
100
|
+
...(ui === undefined ? {} : { ui }),
|
|
36
101
|
};
|
|
37
102
|
}
|
|
38
103
|
function asArray(value) {
|
|
39
104
|
return value === undefined ? [] : [...value];
|
|
40
105
|
}
|
|
41
|
-
|
|
106
|
+
function upsertCatalogEntries(base, overlay) {
|
|
107
|
+
return upsertById(base.map(cloneCatalogEntry), overlay.map(cloneCatalogEntry));
|
|
108
|
+
}
|
|
109
|
+
function upsertCatalogs(base, overlay) {
|
|
110
|
+
const byId = new Map((base ?? []).map((catalog) => [catalog.id, { id: catalog.id, entries: catalog.entries.map(cloneCatalogEntry) }]));
|
|
111
|
+
for (const catalog of overlay ?? []) {
|
|
112
|
+
const existing = byId.get(catalog.id);
|
|
113
|
+
byId.set(catalog.id, {
|
|
114
|
+
id: catalog.id,
|
|
115
|
+
entries: existing === undefined ? catalog.entries.map(cloneCatalogEntry) : upsertCatalogEntries(existing.entries, catalog.entries),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return [...byId.values()];
|
|
119
|
+
}
|
|
120
|
+
/** Resolves a game's `editorLayers` export — document, partial data, or factory — into a full document.
|
|
121
|
+
* @internal
|
|
122
|
+
*/
|
|
42
123
|
export function normalizeEditorLayers(input) {
|
|
43
124
|
if (input === undefined || input === null)
|
|
44
125
|
return createEmptyEditorDocument();
|
|
45
126
|
const resolved = typeof input === "function" ? input() : input;
|
|
127
|
+
const ui = cloneEditorUiDocument(resolved.ui);
|
|
46
128
|
return {
|
|
47
129
|
version: 1,
|
|
48
130
|
markers: asArray(resolved.markers),
|
|
49
131
|
volumes: asArray(resolved.volumes),
|
|
50
132
|
paths: asArray(resolved.paths),
|
|
51
133
|
annotations: asArray(resolved.annotations),
|
|
134
|
+
prefabs: asArray(resolved.prefabs),
|
|
135
|
+
collections: asArray(resolved.collections),
|
|
136
|
+
catalogs: cloneCatalogs(resolved.catalogs),
|
|
137
|
+
...(migrateGrids(resolved.grids) === undefined ? {} : { grids: migrateGrids(resolved.grids) }),
|
|
138
|
+
...(resolved.terrain === undefined ? {} : { terrain: migrateTerrainSnapshot(resolved.terrain) }),
|
|
139
|
+
...(ui === undefined ? {} : { ui }),
|
|
52
140
|
};
|
|
53
141
|
}
|
|
54
|
-
/**
|
|
142
|
+
/**
|
|
143
|
+
* Reserves `id` in `taken` and returns it unchanged when free; otherwise mints the next free
|
|
144
|
+
* `<id>_copy`/`<id>_copyN` suffix and reserves that instead. The one place object ids are made
|
|
145
|
+
* unique across a document's markers/volumes/paths/annotations as a single global namespace, so a
|
|
146
|
+
* merged-in or imported object can never silently shadow an existing object in another collection.
|
|
147
|
+
* @internal
|
|
148
|
+
*/
|
|
149
|
+
export function ensureUniqueEditorId(id, taken) {
|
|
150
|
+
if (!taken.has(id)) {
|
|
151
|
+
taken.add(id);
|
|
152
|
+
return id;
|
|
153
|
+
}
|
|
154
|
+
let candidate = `${id}_copy`;
|
|
155
|
+
let n = 2;
|
|
156
|
+
while (taken.has(candidate)) {
|
|
157
|
+
candidate = `${id}_copy${n}`;
|
|
158
|
+
n += 1;
|
|
159
|
+
}
|
|
160
|
+
taken.add(candidate);
|
|
161
|
+
return candidate;
|
|
162
|
+
}
|
|
163
|
+
/** Combines multiple editor documents' markers, volumes, paths, notes, prefabs, and collections
|
|
164
|
+
* into one, re-idding any placeable object whose id collides with one already merged in — across
|
|
165
|
+
* all four collections, since selection/parenting/removal treat those ids as one document-global
|
|
166
|
+
* namespace.
|
|
167
|
+
* @internal
|
|
168
|
+
*/
|
|
55
169
|
export function mergeEditorDocuments(...docs) {
|
|
56
170
|
const out = createEmptyEditorDocument();
|
|
171
|
+
const taken = new Set();
|
|
172
|
+
const remap = (item) => {
|
|
173
|
+
const id = ensureUniqueEditorId(item.id, taken);
|
|
174
|
+
return id === item.id ? item : { ...item, id };
|
|
175
|
+
};
|
|
57
176
|
for (const doc of docs) {
|
|
58
|
-
out.markers.push(...doc.markers);
|
|
59
|
-
out.volumes.push(...doc.volumes);
|
|
60
|
-
out.paths.push(...doc.paths);
|
|
61
|
-
out.annotations.push(...doc.annotations);
|
|
177
|
+
out.markers.push(...doc.markers.map(remap));
|
|
178
|
+
out.volumes.push(...doc.volumes.map(remap));
|
|
179
|
+
out.paths.push(...doc.paths.map(remap));
|
|
180
|
+
out.annotations.push(...doc.annotations.map(remap));
|
|
181
|
+
out.prefabs.push(...doc.prefabs);
|
|
182
|
+
out.collections.push(...doc.collections);
|
|
183
|
+
out.catalogs = upsertCatalogs(out.catalogs, doc.catalogs);
|
|
184
|
+
const mergedGrids = upsertGrids(out.grids, doc.grids);
|
|
185
|
+
if (mergedGrids !== undefined)
|
|
186
|
+
out.grids = mergedGrids;
|
|
187
|
+
if (doc.terrain !== undefined)
|
|
188
|
+
out.terrain = doc.terrain;
|
|
189
|
+
if (doc.ui !== undefined) {
|
|
190
|
+
out.ui = {
|
|
191
|
+
panels: {
|
|
192
|
+
...(out.ui?.panels ?? {}),
|
|
193
|
+
...Object.fromEntries(Object.entries(doc.ui.panels).map(([id, panel]) => [id, { ...panel }])),
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
62
197
|
}
|
|
63
198
|
return out;
|
|
64
199
|
}
|
|
65
|
-
/**
|
|
200
|
+
/** Extracts the subset of a document matching the given ids — the clipboard fragment for copy/paste.
|
|
201
|
+
* @internal
|
|
202
|
+
*/
|
|
203
|
+
export function extractEditorFragment(doc, ids) {
|
|
204
|
+
const wanted = new Set(ids);
|
|
205
|
+
return cloneEditorDocument({
|
|
206
|
+
version: 1,
|
|
207
|
+
markers: doc.markers.filter((marker) => wanted.has(marker.id)),
|
|
208
|
+
volumes: doc.volumes.filter((volume) => wanted.has(volume.id)),
|
|
209
|
+
paths: doc.paths.filter((path) => wanted.has(path.id)),
|
|
210
|
+
annotations: doc.annotations.filter((note) => wanted.has(note.id)),
|
|
211
|
+
prefabs: [],
|
|
212
|
+
collections: [],
|
|
213
|
+
catalogs: [],
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
/** Counts every object in a document across markers, volumes, paths, and notes.
|
|
217
|
+
* @internal
|
|
218
|
+
*/
|
|
219
|
+
export function editorDocumentSize(doc) {
|
|
220
|
+
return doc.markers.length + doc.volumes.length + doc.paths.length + doc.annotations.length;
|
|
221
|
+
}
|
|
222
|
+
/** Looks up a prefab by id in an editor document.
|
|
223
|
+
* @internal
|
|
224
|
+
*/
|
|
225
|
+
export function findEditorPrefab(doc, id) {
|
|
226
|
+
return doc.prefabs.find((prefab) => prefab.id === id);
|
|
227
|
+
}
|
|
228
|
+
/** Looks up a named collection / selection set by id in an editor document.
|
|
229
|
+
* @internal
|
|
230
|
+
*/
|
|
231
|
+
export function findEditorCollection(doc, id) {
|
|
232
|
+
return doc.collections.find((collection) => collection.id === id);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Looks up a gameplay data catalog by id on the scene document.
|
|
236
|
+
* @capability editor-catalogs Find a persisted catalog bag by id.
|
|
237
|
+
*/
|
|
238
|
+
export function findEditorCatalog(doc, id) {
|
|
239
|
+
return doc.catalogs.find((catalog) => catalog.id === id);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Looks up one entry inside a gameplay data catalog.
|
|
243
|
+
* @capability editor-catalogs Find a catalog entry's meta bag by catalog + entry id.
|
|
244
|
+
*/
|
|
245
|
+
export function findEditorCatalogEntry(doc, catalogId, entryId) {
|
|
246
|
+
return findEditorCatalog(doc, catalogId)?.entries.find((entry) => entry.id === entryId);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Seeds default catalog rows from game-exported definitions into a document: missing catalogs and
|
|
250
|
+
* missing entries are filled from the definition; existing document values win (overlay already applied).
|
|
251
|
+
* @capability editor-catalogs Seed scene-document catalogs from game-exported schemas.
|
|
252
|
+
*/
|
|
253
|
+
export function seedEditorCatalogs(doc, definitions) {
|
|
254
|
+
if (definitions.length === 0)
|
|
255
|
+
return doc;
|
|
256
|
+
const next = cloneCatalogs(doc.catalogs);
|
|
257
|
+
for (const definition of definitions) {
|
|
258
|
+
const existingIndex = next.findIndex((catalog) => catalog.id === definition.id);
|
|
259
|
+
const existing = existingIndex >= 0 ? next[existingIndex] : undefined;
|
|
260
|
+
const byId = new Map((existing?.entries ?? []).map((entry) => [entry.id, cloneCatalogEntry(entry)]));
|
|
261
|
+
for (const entry of definition.entries) {
|
|
262
|
+
if (!byId.has(entry.id))
|
|
263
|
+
byId.set(entry.id, cloneCatalogEntry(entry));
|
|
264
|
+
}
|
|
265
|
+
const catalog = {
|
|
266
|
+
id: definition.id,
|
|
267
|
+
entries: [...byId.values()],
|
|
268
|
+
};
|
|
269
|
+
if (existingIndex >= 0)
|
|
270
|
+
next[existingIndex] = catalog;
|
|
271
|
+
else
|
|
272
|
+
next.push(catalog);
|
|
273
|
+
}
|
|
274
|
+
return { ...doc, catalogs: next };
|
|
275
|
+
}
|
|
276
|
+
/** True when an object id is a member of any locked collection — blocks move/delete on it.
|
|
277
|
+
* @internal
|
|
278
|
+
*/
|
|
279
|
+
export function isEditorObjectLocked(doc, id) {
|
|
280
|
+
return doc.collections.some((collection) => collection.locked === true && collection.memberIds.includes(id));
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Extracts the selected ids into a prefab fragment centered on their own bounds centroid, so the
|
|
284
|
+
* same prefab reinserts consistently regardless of where in the scene (or which game) it lands.
|
|
285
|
+
* @internal
|
|
286
|
+
*/
|
|
287
|
+
export function createPrefabFragment(doc, ids) {
|
|
288
|
+
const extracted = extractEditorFragment(doc, ids);
|
|
289
|
+
const bounds = editorDocumentBounds(extracted);
|
|
290
|
+
const origin = bounds === null
|
|
291
|
+
? { x: 0, y: 0, z: 0 }
|
|
292
|
+
: {
|
|
293
|
+
x: (bounds.min.x + bounds.max.x) / 2,
|
|
294
|
+
y: (bounds.min.y + bounds.max.y) / 2,
|
|
295
|
+
z: (bounds.min.z + bounds.max.z) / 2,
|
|
296
|
+
};
|
|
297
|
+
const shift = (point) => ({
|
|
298
|
+
x: point.x - origin.x,
|
|
299
|
+
y: point.y - origin.y,
|
|
300
|
+
z: point.z - origin.z,
|
|
301
|
+
});
|
|
302
|
+
return {
|
|
303
|
+
markers: extracted.markers.map((marker) => ({ ...marker, position: shift(marker.position) })),
|
|
304
|
+
volumes: extracted.volumes.map((volume) => ({ ...volume, center: shift(volume.center) })),
|
|
305
|
+
paths: extracted.paths.map((path) => ({ ...path, points: path.points.map(shift) })),
|
|
306
|
+
annotations: extracted.annotations.map((note) => ({ ...note, position: shift(note.position) })),
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
/** Looks up a marker by id in an editor document.
|
|
310
|
+
* @internal
|
|
311
|
+
*/
|
|
66
312
|
export function findEditorMarker(doc, id) {
|
|
67
313
|
return doc.markers.find((marker) => marker.id === id);
|
|
68
314
|
}
|
|
69
|
-
/** Looks up a volume by id in an editor document.
|
|
315
|
+
/** Looks up a volume by id in an editor document.
|
|
316
|
+
* @internal
|
|
317
|
+
*/
|
|
70
318
|
export function findEditorVolume(doc, id) {
|
|
71
319
|
return doc.volumes.find((volume) => volume.id === id);
|
|
72
320
|
}
|
|
73
|
-
/** Looks up a path by id in an editor document.
|
|
321
|
+
/** Looks up a path by id in an editor document.
|
|
322
|
+
* @internal
|
|
323
|
+
*/
|
|
74
324
|
export function findEditorPath(doc, id) {
|
|
75
325
|
return doc.paths.find((path) => path.id === id);
|
|
76
326
|
}
|
|
77
|
-
/** Looks up an annotation note by id in an editor document.
|
|
327
|
+
/** Looks up an annotation note by id in an editor document.
|
|
328
|
+
* @internal
|
|
329
|
+
*/
|
|
78
330
|
export function findEditorNote(doc, id) {
|
|
79
331
|
return doc.annotations.find((note) => note.id === id);
|
|
80
332
|
}
|
|
81
|
-
/** Lists the distinct marker, volume, and path kinds authored in a document.
|
|
333
|
+
/** Lists the distinct marker, volume, and path kinds authored in a document.
|
|
334
|
+
* @internal
|
|
335
|
+
*/
|
|
82
336
|
export function listEditorKinds(doc) {
|
|
83
337
|
const markers = new Set();
|
|
84
338
|
const volumes = new Set();
|
|
@@ -95,19 +349,496 @@ export function listEditorKinds(doc) {
|
|
|
95
349
|
paths: [...paths].sort(),
|
|
96
350
|
};
|
|
97
351
|
}
|
|
98
|
-
|
|
352
|
+
function editorNodes(doc) {
|
|
353
|
+
return [...doc.markers, ...doc.volumes, ...doc.paths, ...doc.annotations];
|
|
354
|
+
}
|
|
355
|
+
/** The id of an object's parent, or undefined when it is a root (or unknown).
|
|
356
|
+
* @internal
|
|
357
|
+
*/
|
|
358
|
+
export function editorParentOf(doc, id) {
|
|
359
|
+
return editorNodes(doc).find((node) => node.id === id)?.parentId;
|
|
360
|
+
}
|
|
361
|
+
/** The direct child ids of an object (empty when it has none).
|
|
362
|
+
* @internal
|
|
363
|
+
*/
|
|
364
|
+
export function editorChildren(doc, parentId) {
|
|
365
|
+
return editorNodes(doc)
|
|
366
|
+
.filter((node) => node.parentId === parentId)
|
|
367
|
+
.map((node) => node.id);
|
|
368
|
+
}
|
|
369
|
+
/** Object ids with no parent (or whose parent no longer exists) — the roots of the hierarchy.
|
|
370
|
+
* @internal
|
|
371
|
+
*/
|
|
372
|
+
export function editorRoots(doc) {
|
|
373
|
+
const ids = new Set(editorNodes(doc).map((node) => node.id));
|
|
374
|
+
return editorNodes(doc)
|
|
375
|
+
.filter((node) => node.parentId === undefined || !ids.has(node.parentId))
|
|
376
|
+
.map((node) => node.id);
|
|
377
|
+
}
|
|
378
|
+
/** Every descendant id of the given ids (children, grandchildren, …), excluding the inputs.
|
|
379
|
+
* @internal
|
|
380
|
+
*/
|
|
381
|
+
export function collectDescendants(doc, ids) {
|
|
382
|
+
const childrenByParent = new Map();
|
|
383
|
+
for (const node of editorNodes(doc)) {
|
|
384
|
+
if (node.parentId === undefined)
|
|
385
|
+
continue;
|
|
386
|
+
const bucket = childrenByParent.get(node.parentId);
|
|
387
|
+
if (bucket === undefined)
|
|
388
|
+
childrenByParent.set(node.parentId, [node.id]);
|
|
389
|
+
else
|
|
390
|
+
bucket.push(node.id);
|
|
391
|
+
}
|
|
392
|
+
const out = new Set();
|
|
393
|
+
const stack = [...ids];
|
|
394
|
+
while (stack.length > 0) {
|
|
395
|
+
const current = stack.pop();
|
|
396
|
+
for (const child of childrenByParent.get(current) ?? []) {
|
|
397
|
+
if (out.has(child))
|
|
398
|
+
continue;
|
|
399
|
+
out.add(child);
|
|
400
|
+
stack.push(child);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return out;
|
|
404
|
+
}
|
|
405
|
+
/** True when parenting `id` under `parentId` would form a cycle (or parent itself to itself).
|
|
406
|
+
* @internal
|
|
407
|
+
*/
|
|
408
|
+
export function wouldCreateCycle(doc, id, parentId) {
|
|
409
|
+
if (parentId === null)
|
|
410
|
+
return false;
|
|
411
|
+
if (parentId === id)
|
|
412
|
+
return true;
|
|
413
|
+
return collectDescendants(doc, [id]).has(parentId);
|
|
414
|
+
}
|
|
415
|
+
/** Serializes an editor document to JSON text for saving or export.
|
|
416
|
+
* @internal
|
|
417
|
+
*/
|
|
99
418
|
export function exportEditorDocumentJson(doc, pretty = true) {
|
|
100
419
|
return JSON.stringify(doc, null, pretty ? 2 : undefined);
|
|
101
420
|
}
|
|
102
|
-
|
|
421
|
+
function isPlainObject(value) {
|
|
422
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
423
|
+
}
|
|
424
|
+
function decodeArray(value, path, decodeItem, errors) {
|
|
425
|
+
if (value === undefined)
|
|
426
|
+
return [];
|
|
427
|
+
if (!Array.isArray(value)) {
|
|
428
|
+
errors.push({ path, message: "expected an array" });
|
|
429
|
+
return [];
|
|
430
|
+
}
|
|
431
|
+
const out = [];
|
|
432
|
+
value.forEach((item, index) => {
|
|
433
|
+
const decoded = decodeItem(item, `${path}[${index}]`, errors);
|
|
434
|
+
if (decoded !== null)
|
|
435
|
+
out.push(decoded);
|
|
436
|
+
});
|
|
437
|
+
return out;
|
|
438
|
+
}
|
|
439
|
+
function decodeVec3(value, path, errors) {
|
|
440
|
+
if (!isPlainObject(value) || typeof value.x !== "number" || typeof value.y !== "number" || typeof value.z !== "number") {
|
|
441
|
+
errors.push({ path, message: "expected {x,y,z} numbers" });
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
return { x: value.x, y: value.y, z: value.z };
|
|
445
|
+
}
|
|
446
|
+
function decodeMeta(value, path, errors) {
|
|
447
|
+
if (value === undefined)
|
|
448
|
+
return undefined;
|
|
449
|
+
if (!isPlainObject(value)) {
|
|
450
|
+
errors.push({ path, message: "expected an object" });
|
|
451
|
+
return undefined;
|
|
452
|
+
}
|
|
453
|
+
return value;
|
|
454
|
+
}
|
|
455
|
+
function decodeMarker(item, path, errors) {
|
|
456
|
+
if (!isPlainObject(item)) {
|
|
457
|
+
errors.push({ path, message: "expected an object" });
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
if (typeof item.id !== "string")
|
|
461
|
+
errors.push({ path: `${path}.id`, message: "expected a string" });
|
|
462
|
+
if (typeof item.kind !== "string")
|
|
463
|
+
errors.push({ path: `${path}.kind`, message: "expected a string" });
|
|
464
|
+
const position = decodeVec3(item.position, `${path}.position`, errors);
|
|
465
|
+
if (typeof item.id !== "string" || typeof item.kind !== "string" || position === null)
|
|
466
|
+
return null;
|
|
467
|
+
const marker = { id: item.id, kind: item.kind, position };
|
|
468
|
+
if (typeof item.rotationY === "number")
|
|
469
|
+
marker.rotationY = item.rotationY;
|
|
470
|
+
if (typeof item.color === "string")
|
|
471
|
+
marker.color = item.color;
|
|
472
|
+
if (typeof item.label === "string")
|
|
473
|
+
marker.label = item.label;
|
|
474
|
+
if (typeof item.catalogId === "string")
|
|
475
|
+
marker.catalogId = item.catalogId;
|
|
476
|
+
if (typeof item.parentId === "string")
|
|
477
|
+
marker.parentId = item.parentId;
|
|
478
|
+
const meta = decodeMeta(item.meta, `${path}.meta`, errors);
|
|
479
|
+
if (meta !== undefined)
|
|
480
|
+
marker.meta = meta;
|
|
481
|
+
return marker;
|
|
482
|
+
}
|
|
483
|
+
const VOLUME_SHAPES = new Set(["sphere", "cylinder", "box"]);
|
|
484
|
+
function decodeVolume(item, path, errors) {
|
|
485
|
+
if (!isPlainObject(item)) {
|
|
486
|
+
errors.push({ path, message: "expected an object" });
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
if (typeof item.id !== "string")
|
|
490
|
+
errors.push({ path: `${path}.id`, message: "expected a string" });
|
|
491
|
+
if (typeof item.kind !== "string")
|
|
492
|
+
errors.push({ path: `${path}.kind`, message: "expected a string" });
|
|
493
|
+
const validShape = typeof item.shape === "string" && VOLUME_SHAPES.has(item.shape);
|
|
494
|
+
if (!validShape)
|
|
495
|
+
errors.push({ path: `${path}.shape`, message: "expected sphere | cylinder | box" });
|
|
496
|
+
const center = decodeVec3(item.center, `${path}.center`, errors);
|
|
497
|
+
if (typeof item.id !== "string" || typeof item.kind !== "string" || !validShape || center === null)
|
|
498
|
+
return null;
|
|
499
|
+
const volume = { id: item.id, kind: item.kind, shape: item.shape, center };
|
|
500
|
+
if (typeof item.radius === "number")
|
|
501
|
+
volume.radius = item.radius;
|
|
502
|
+
if (typeof item.height === "number")
|
|
503
|
+
volume.height = item.height;
|
|
504
|
+
if (item.halfExtents !== undefined) {
|
|
505
|
+
const halfExtents = decodeVec3(item.halfExtents, `${path}.halfExtents`, errors);
|
|
506
|
+
if (halfExtents !== null)
|
|
507
|
+
volume.halfExtents = halfExtents;
|
|
508
|
+
}
|
|
509
|
+
if (typeof item.color === "string")
|
|
510
|
+
volume.color = item.color;
|
|
511
|
+
if (typeof item.label === "string")
|
|
512
|
+
volume.label = item.label;
|
|
513
|
+
if (typeof item.parentId === "string")
|
|
514
|
+
volume.parentId = item.parentId;
|
|
515
|
+
const meta = decodeMeta(item.meta, `${path}.meta`, errors);
|
|
516
|
+
if (meta !== undefined)
|
|
517
|
+
volume.meta = meta;
|
|
518
|
+
return volume;
|
|
519
|
+
}
|
|
520
|
+
function decodePath(item, path, errors) {
|
|
521
|
+
if (!isPlainObject(item)) {
|
|
522
|
+
errors.push({ path, message: "expected an object" });
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
if (typeof item.id !== "string")
|
|
526
|
+
errors.push({ path: `${path}.id`, message: "expected a string" });
|
|
527
|
+
if (typeof item.kind !== "string")
|
|
528
|
+
errors.push({ path: `${path}.kind`, message: "expected a string" });
|
|
529
|
+
const validPoints = Array.isArray(item.points);
|
|
530
|
+
if (!validPoints)
|
|
531
|
+
errors.push({ path: `${path}.points`, message: "expected an array" });
|
|
532
|
+
const points = validPoints
|
|
533
|
+
? decodeArray(item.points, `${path}.points`, decodeVec3, errors)
|
|
534
|
+
: [];
|
|
535
|
+
if (typeof item.id !== "string" || typeof item.kind !== "string" || !validPoints)
|
|
536
|
+
return null;
|
|
537
|
+
const decodedPath = { id: item.id, kind: item.kind, points };
|
|
538
|
+
if (typeof item.width === "number")
|
|
539
|
+
decodedPath.width = item.width;
|
|
540
|
+
if (typeof item.color === "string")
|
|
541
|
+
decodedPath.color = item.color;
|
|
542
|
+
if (typeof item.label === "string")
|
|
543
|
+
decodedPath.label = item.label;
|
|
544
|
+
if (typeof item.parentId === "string")
|
|
545
|
+
decodedPath.parentId = item.parentId;
|
|
546
|
+
const meta = decodeMeta(item.meta, `${path}.meta`, errors);
|
|
547
|
+
if (meta !== undefined)
|
|
548
|
+
decodedPath.meta = meta;
|
|
549
|
+
return decodedPath;
|
|
550
|
+
}
|
|
551
|
+
function decodeNote(item, path, errors) {
|
|
552
|
+
if (!isPlainObject(item)) {
|
|
553
|
+
errors.push({ path, message: "expected an object" });
|
|
554
|
+
return null;
|
|
555
|
+
}
|
|
556
|
+
if (typeof item.id !== "string")
|
|
557
|
+
errors.push({ path: `${path}.id`, message: "expected a string" });
|
|
558
|
+
if (typeof item.text !== "string")
|
|
559
|
+
errors.push({ path: `${path}.text`, message: "expected a string" });
|
|
560
|
+
const position = decodeVec3(item.position, `${path}.position`, errors);
|
|
561
|
+
if (typeof item.id !== "string" || typeof item.text !== "string" || position === null)
|
|
562
|
+
return null;
|
|
563
|
+
const note = { id: item.id, text: item.text, position };
|
|
564
|
+
if (typeof item.color === "string")
|
|
565
|
+
note.color = item.color;
|
|
566
|
+
if (typeof item.parentId === "string")
|
|
567
|
+
note.parentId = item.parentId;
|
|
568
|
+
const meta = decodeMeta(item.meta, `${path}.meta`, errors);
|
|
569
|
+
if (meta !== undefined)
|
|
570
|
+
note.meta = meta;
|
|
571
|
+
return note;
|
|
572
|
+
}
|
|
573
|
+
function decodeFragmentContent(value, path, errors) {
|
|
574
|
+
if (!isPlainObject(value)) {
|
|
575
|
+
errors.push({ path, message: "expected an object" });
|
|
576
|
+
return null;
|
|
577
|
+
}
|
|
578
|
+
return {
|
|
579
|
+
markers: decodeArray(value.markers, `${path}.markers`, decodeMarker, errors),
|
|
580
|
+
volumes: decodeArray(value.volumes, `${path}.volumes`, decodeVolume, errors),
|
|
581
|
+
paths: decodeArray(value.paths, `${path}.paths`, decodePath, errors),
|
|
582
|
+
annotations: decodeArray(value.annotations, `${path}.annotations`, decodeNote, errors),
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
function decodePrefab(item, path, errors) {
|
|
586
|
+
if (!isPlainObject(item)) {
|
|
587
|
+
errors.push({ path, message: "expected an object" });
|
|
588
|
+
return null;
|
|
589
|
+
}
|
|
590
|
+
if (typeof item.id !== "string")
|
|
591
|
+
errors.push({ path: `${path}.id`, message: "expected a string" });
|
|
592
|
+
if (typeof item.name !== "string")
|
|
593
|
+
errors.push({ path: `${path}.name`, message: "expected a string" });
|
|
594
|
+
const fragment = decodeFragmentContent(item.fragment, `${path}.fragment`, errors);
|
|
595
|
+
if (typeof item.id !== "string" || typeof item.name !== "string" || fragment === null)
|
|
596
|
+
return null;
|
|
597
|
+
return { id: item.id, name: item.name, fragment };
|
|
598
|
+
}
|
|
599
|
+
function decodeCollection(item, path, errors) {
|
|
600
|
+
if (!isPlainObject(item)) {
|
|
601
|
+
errors.push({ path, message: "expected an object" });
|
|
602
|
+
return null;
|
|
603
|
+
}
|
|
604
|
+
if (typeof item.id !== "string")
|
|
605
|
+
errors.push({ path: `${path}.id`, message: "expected a string" });
|
|
606
|
+
if (typeof item.name !== "string")
|
|
607
|
+
errors.push({ path: `${path}.name`, message: "expected a string" });
|
|
608
|
+
const memberIds = [];
|
|
609
|
+
if (!Array.isArray(item.memberIds)) {
|
|
610
|
+
errors.push({ path: `${path}.memberIds`, message: "expected an array of strings" });
|
|
611
|
+
}
|
|
612
|
+
else {
|
|
613
|
+
item.memberIds.forEach((member, index) => {
|
|
614
|
+
if (typeof member === "string")
|
|
615
|
+
memberIds.push(member);
|
|
616
|
+
else
|
|
617
|
+
errors.push({ path: `${path}.memberIds[${index}]`, message: "expected a string" });
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
if (typeof item.id !== "string" || typeof item.name !== "string" || !Array.isArray(item.memberIds))
|
|
621
|
+
return null;
|
|
622
|
+
const collection = { id: item.id, name: item.name, memberIds };
|
|
623
|
+
if (typeof item.color === "string")
|
|
624
|
+
collection.color = item.color;
|
|
625
|
+
if (typeof item.locked === "boolean")
|
|
626
|
+
collection.locked = item.locked;
|
|
627
|
+
if (typeof item.visible === "boolean")
|
|
628
|
+
collection.visible = item.visible;
|
|
629
|
+
return collection;
|
|
630
|
+
}
|
|
631
|
+
function decodeCatalogEntry(item, path, errors) {
|
|
632
|
+
if (!isPlainObject(item)) {
|
|
633
|
+
errors.push({ path, message: "expected an object" });
|
|
634
|
+
return null;
|
|
635
|
+
}
|
|
636
|
+
if (typeof item.id !== "string") {
|
|
637
|
+
errors.push({ path: `${path}.id`, message: "expected a string" });
|
|
638
|
+
return null;
|
|
639
|
+
}
|
|
640
|
+
const entry = { id: item.id };
|
|
641
|
+
if (typeof item.label === "string")
|
|
642
|
+
entry.label = item.label;
|
|
643
|
+
const meta = decodeMeta(item.meta, `${path}.meta`, errors);
|
|
644
|
+
if (meta !== undefined)
|
|
645
|
+
entry.meta = meta;
|
|
646
|
+
return entry;
|
|
647
|
+
}
|
|
648
|
+
function decodeCatalog(item, path, errors) {
|
|
649
|
+
if (!isPlainObject(item)) {
|
|
650
|
+
errors.push({ path, message: "expected an object" });
|
|
651
|
+
return null;
|
|
652
|
+
}
|
|
653
|
+
if (typeof item.id !== "string") {
|
|
654
|
+
errors.push({ path: `${path}.id`, message: "expected a string" });
|
|
655
|
+
return null;
|
|
656
|
+
}
|
|
657
|
+
const entries = decodeArray(item.entries, `${path}.entries`, decodeCatalogEntry, errors);
|
|
658
|
+
return { id: item.id, entries };
|
|
659
|
+
}
|
|
660
|
+
const GRID_AXES = new Set(["xz", "xy"]);
|
|
661
|
+
function decodeGridPaletteEntry(item, path, errors) {
|
|
662
|
+
if (!isPlainObject(item)) {
|
|
663
|
+
errors.push({ path, message: "expected an object" });
|
|
664
|
+
return null;
|
|
665
|
+
}
|
|
666
|
+
if (typeof item.id !== "string") {
|
|
667
|
+
errors.push({ path: `${path}.id`, message: "expected a string" });
|
|
668
|
+
return null;
|
|
669
|
+
}
|
|
670
|
+
const entry = { id: item.id };
|
|
671
|
+
if (typeof item.label === "string")
|
|
672
|
+
entry.label = item.label;
|
|
673
|
+
if (typeof item.glyph === "string")
|
|
674
|
+
entry.glyph = item.glyph;
|
|
675
|
+
if (typeof item.color === "string")
|
|
676
|
+
entry.color = item.color;
|
|
677
|
+
const meta = decodeMeta(item.meta, `${path}.meta`, errors);
|
|
678
|
+
if (meta !== undefined)
|
|
679
|
+
entry.meta = meta;
|
|
680
|
+
return entry;
|
|
681
|
+
}
|
|
682
|
+
function decodeGridLayer(item, path, errors) {
|
|
683
|
+
if (!isPlainObject(item)) {
|
|
684
|
+
errors.push({ path, message: "expected an object" });
|
|
685
|
+
return null;
|
|
686
|
+
}
|
|
687
|
+
if (typeof item.id !== "string")
|
|
688
|
+
errors.push({ path: `${path}.id`, message: "expected a string" });
|
|
689
|
+
if (typeof item.kind !== "string")
|
|
690
|
+
errors.push({ path: `${path}.kind`, message: "expected a string" });
|
|
691
|
+
const origin = decodeVec3(item.origin, `${path}.origin`, errors);
|
|
692
|
+
if (typeof item.cellSize !== "number")
|
|
693
|
+
errors.push({ path: `${path}.cellSize`, message: "expected a number" });
|
|
694
|
+
if (typeof item.cols !== "number")
|
|
695
|
+
errors.push({ path: `${path}.cols`, message: "expected a number" });
|
|
696
|
+
if (typeof item.rows !== "number")
|
|
697
|
+
errors.push({ path: `${path}.rows`, message: "expected a number" });
|
|
698
|
+
const cellsValue = item.cells;
|
|
699
|
+
const validCells = isPlainObject(cellsValue);
|
|
700
|
+
if (!validCells)
|
|
701
|
+
errors.push({ path: `${path}.cells`, message: "expected an object" });
|
|
702
|
+
if (typeof item.id !== "string" ||
|
|
703
|
+
typeof item.kind !== "string" ||
|
|
704
|
+
origin === null ||
|
|
705
|
+
typeof item.cellSize !== "number" ||
|
|
706
|
+
typeof item.cols !== "number" ||
|
|
707
|
+
typeof item.rows !== "number" ||
|
|
708
|
+
!validCells) {
|
|
709
|
+
return null;
|
|
710
|
+
}
|
|
711
|
+
const cells = {};
|
|
712
|
+
for (const [key, value] of Object.entries(cellsValue)) {
|
|
713
|
+
if (typeof value !== "string") {
|
|
714
|
+
errors.push({ path: `${path}.cells.${key}`, message: "expected a string value id" });
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
cells[key] = value;
|
|
718
|
+
}
|
|
719
|
+
const layer = {
|
|
720
|
+
id: item.id,
|
|
721
|
+
kind: item.kind,
|
|
722
|
+
origin,
|
|
723
|
+
cellSize: item.cellSize,
|
|
724
|
+
cols: item.cols,
|
|
725
|
+
rows: item.rows,
|
|
726
|
+
cells,
|
|
727
|
+
};
|
|
728
|
+
if (typeof item.label === "string")
|
|
729
|
+
layer.label = item.label;
|
|
730
|
+
if (typeof item.axes === "string" && GRID_AXES.has(item.axes))
|
|
731
|
+
layer.axes = item.axes;
|
|
732
|
+
if (typeof item.empty === "string")
|
|
733
|
+
layer.empty = item.empty;
|
|
734
|
+
if (typeof item.visible === "boolean")
|
|
735
|
+
layer.visible = item.visible;
|
|
736
|
+
if (typeof item.parentId === "string")
|
|
737
|
+
layer.parentId = item.parentId;
|
|
738
|
+
if (typeof item.schemaVersion === "number")
|
|
739
|
+
layer.schemaVersion = item.schemaVersion;
|
|
740
|
+
if (Array.isArray(item.palette)) {
|
|
741
|
+
layer.palette = decodeArray(item.palette, `${path}.palette`, decodeGridPaletteEntry, errors);
|
|
742
|
+
}
|
|
743
|
+
const meta = decodeMeta(item.meta, `${path}.meta`, errors);
|
|
744
|
+
if (meta !== undefined)
|
|
745
|
+
layer.meta = meta;
|
|
746
|
+
return migrateGridLayer(layer);
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* The authoritative decoder/migrator for an editor document arriving from outside this process
|
|
750
|
+
* (disk, network, an agent's `import_document` RPC): validates every field with a path-specific
|
|
751
|
+
* diagnostic on failure instead of trusting the shape, then migrates forward — routes any embedded
|
|
752
|
+
* terrain through {@link migrateTerrainSnapshot} and normalizes `version` to the one this build
|
|
753
|
+
* understands. Every other loader in this module funnels through here.
|
|
754
|
+
* @internal
|
|
755
|
+
*/
|
|
756
|
+
export function decodeEditorDocument(raw) {
|
|
757
|
+
if (!isPlainObject(raw)) {
|
|
758
|
+
return { ok: false, errors: [{ path: "$", message: "editor document must be an object" }] };
|
|
759
|
+
}
|
|
760
|
+
const errors = [];
|
|
761
|
+
const markers = decodeArray(raw.markers, "$.markers", decodeMarker, errors);
|
|
762
|
+
const volumes = decodeArray(raw.volumes, "$.volumes", decodeVolume, errors);
|
|
763
|
+
const paths = decodeArray(raw.paths, "$.paths", decodePath, errors);
|
|
764
|
+
const annotations = decodeArray(raw.annotations, "$.annotations", decodeNote, errors);
|
|
765
|
+
const prefabs = decodeArray(raw.prefabs, "$.prefabs", decodePrefab, errors);
|
|
766
|
+
const collections = decodeArray(raw.collections, "$.collections", decodeCollection, errors);
|
|
767
|
+
const catalogs = decodeArray(raw.catalogs, "$.catalogs", decodeCatalog, errors);
|
|
768
|
+
const grids = raw.grids === undefined ? undefined : decodeArray(raw.grids, "$.grids", decodeGridLayer, errors);
|
|
769
|
+
if (raw.terrain !== undefined && !isPlainObject(raw.terrain)) {
|
|
770
|
+
errors.push({ path: "$.terrain", message: "expected an object" });
|
|
771
|
+
}
|
|
772
|
+
if (errors.length > 0)
|
|
773
|
+
return { ok: false, errors };
|
|
774
|
+
const terrain = raw.terrain === undefined ? undefined : migrateTerrainSnapshot(raw.terrain);
|
|
775
|
+
const ui = decodeEditorUiDocument(raw.ui);
|
|
776
|
+
return {
|
|
777
|
+
ok: true,
|
|
778
|
+
document: {
|
|
779
|
+
version: 1,
|
|
780
|
+
markers,
|
|
781
|
+
volumes,
|
|
782
|
+
paths,
|
|
783
|
+
annotations,
|
|
784
|
+
prefabs,
|
|
785
|
+
collections,
|
|
786
|
+
catalogs,
|
|
787
|
+
...(grids === undefined ? {} : { grids }),
|
|
788
|
+
...(terrain === undefined ? {} : { terrain }),
|
|
789
|
+
...(ui === undefined ? {} : { ui }),
|
|
790
|
+
},
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
/** Parses JSON text back into a normalized editor document, throwing a message that names every
|
|
794
|
+
* field that failed to decode when the shape is malformed.
|
|
795
|
+
* @internal
|
|
796
|
+
*/
|
|
103
797
|
export function importEditorDocumentJson(raw) {
|
|
104
798
|
const parsed = JSON.parse(raw);
|
|
105
|
-
|
|
106
|
-
|
|
799
|
+
const decoded = decodeEditorDocument(parsed);
|
|
800
|
+
if (!decoded.ok) {
|
|
801
|
+
throw new Error(`invalid editor document: ${decoded.errors.map((e) => `${e.path} ${e.message}`).join("; ")}`);
|
|
107
802
|
}
|
|
108
|
-
return
|
|
803
|
+
return decoded.document;
|
|
804
|
+
}
|
|
805
|
+
function upsertById(base, overlay) {
|
|
806
|
+
const overlayIds = new Set(overlay.map((item) => item.id));
|
|
807
|
+
return [...base.filter((item) => !overlayIds.has(item.id)), ...overlay];
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Applies a saved editor document on top of a game's derived layers: overlay objects replace
|
|
811
|
+
* same-id base objects and new overlay objects are appended, so editor saves win over source
|
|
812
|
+
* data until they are folded back in.
|
|
813
|
+
* @internal
|
|
814
|
+
*/
|
|
815
|
+
export function applyEditorDocumentOverlay(base, overlay) {
|
|
816
|
+
const terrain = overlay.terrain ?? base.terrain;
|
|
817
|
+
const ui = overlay.ui === undefined
|
|
818
|
+
? cloneEditorUiDocument(base.ui)
|
|
819
|
+
: {
|
|
820
|
+
panels: {
|
|
821
|
+
...(base.ui?.panels ?? {}),
|
|
822
|
+
...Object.fromEntries(Object.entries(overlay.ui.panels).map(([id, panel]) => [id, { ...panel }])),
|
|
823
|
+
},
|
|
824
|
+
};
|
|
825
|
+
return {
|
|
826
|
+
version: 1,
|
|
827
|
+
markers: upsertById(base.markers, overlay.markers),
|
|
828
|
+
volumes: upsertById(base.volumes, overlay.volumes),
|
|
829
|
+
paths: upsertById(base.paths, overlay.paths),
|
|
830
|
+
annotations: upsertById(base.annotations, overlay.annotations),
|
|
831
|
+
prefabs: upsertById(base.prefabs, overlay.prefabs),
|
|
832
|
+
collections: upsertById(base.collections, overlay.collections),
|
|
833
|
+
catalogs: upsertCatalogs(base.catalogs, overlay.catalogs),
|
|
834
|
+
...(upsertGrids(base.grids, overlay.grids) === undefined ? {} : { grids: upsertGrids(base.grids, overlay.grids) }),
|
|
835
|
+
...(terrain === undefined ? {} : { terrain }),
|
|
836
|
+
...(ui === undefined ? {} : { ui }),
|
|
837
|
+
};
|
|
109
838
|
}
|
|
110
|
-
/** Computes the world-space min/max bounds spanning every object in a document, or null if empty.
|
|
839
|
+
/** Computes the world-space min/max bounds spanning every object in a document, or null if empty.
|
|
840
|
+
* @internal
|
|
841
|
+
*/
|
|
111
842
|
export function editorDocumentBounds(doc) {
|
|
112
843
|
let minX = Infinity;
|
|
113
844
|
let minY = Infinity;
|