@jgengine/core 0.9.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 +34 -3
- 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 +19 -1
- package/dist/ai/spawnDirector.js +53 -26
- package/dist/ai/spawnPoint.d.ts +7 -0
- package/dist/ai/spawnPoint.js +7 -0
- 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/audio/audioFalloff.d.ts +5 -2
- package/dist/audio/music.d.ts +53 -0
- package/dist/audio/music.js +34 -0
- package/dist/audio/synth.d.ts +60 -0
- package/dist/audio/synth.js +8 -0
- package/dist/authoring.d.ts +15 -0
- package/dist/authoring.js +15 -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 +47 -0
- package/dist/combat/dotField.js +52 -0
- 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 +7 -2
- package/dist/combat/projectiles.js +48 -17
- package/dist/combat/receivedDamage.d.ts +117 -0
- package/dist/combat/receivedDamage.js +92 -0
- package/dist/combat/regenShield.d.ts +39 -0
- package/dist/combat/regenShield.js +49 -0
- 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 +30 -1
- package/dist/combat/shotOrigin.js +70 -2
- 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 +248 -0
- package/dist/editor/commands.js +865 -0
- package/dist/editor/document.d.ts +186 -0
- package/dist/editor/document.js +884 -0
- 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 +97 -0
- package/dist/editor/index.js +10 -0
- 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 +175 -0
- package/dist/editor/types.js +29 -0
- 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 +30 -0
- package/dist/game/connectedPlayers.js +21 -0
- package/dist/game/cosmetics.d.ts +9 -0
- package/dist/game/cosmetics.js +16 -0
- package/dist/game/defineGame.d.ts +141 -2
- package/dist/game/defineGame.js +39 -1
- 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 +50 -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/keyValueStore.d.ts +25 -0
- package/dist/game/keyValueStore.js +61 -0
- package/dist/game/leaderboard.d.ts +5 -0
- package/dist/game/leaderboard.js +5 -0
- package/dist/game/levelSequence.d.ts +24 -2
- package/dist/game/levelSequence.js +51 -2
- 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 +21 -0
- package/dist/game/lootTable.js +15 -4
- package/dist/game/modelAnimation.d.ts +8 -0
- package/dist/game/modelAnimation.js +18 -0
- 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 +160 -12
- 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 +5 -5
- 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 -7
- package/dist/index.js +5 -7
- package/dist/input/actionBindings.d.ts +24 -5
- package/dist/input/actionBindings.js +23 -5
- package/dist/input/axisInput.d.ts +13 -1
- package/dist/input/axisInput.js +22 -1
- 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/contextMenu.d.ts +1 -0
- package/dist/interaction/contextMenu.js +1 -0
- package/dist/interaction/lockpick.d.ts +61 -0
- package/dist/interaction/lockpick.js +168 -0
- package/dist/interaction/proximityPrompt.d.ts +5 -1
- package/dist/interaction/proximityPrompt.js +5 -3
- 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 +36 -1
- 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 +38 -7
- package/dist/movement/movementModel.js +54 -13
- package/dist/movement/playerMovement.d.ts +41 -0
- package/dist/movement/playerMovement.js +236 -0
- 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 +24 -0
- package/dist/random/rng.js +30 -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 +76 -0
- package/dist/render/postProcessing.js +21 -0
- 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 +58 -0
- package/dist/runtime/adapter.js +54 -5
- 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 +163 -19
- package/dist/runtime/gameContext.js +385 -339
- 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 +54 -0
- package/dist/runtime/hostedGameRunner.js +85 -0
- package/dist/runtime/hostedWorldSession.d.ts +73 -0
- package/dist/runtime/hostedWorldSession.js +64 -0
- package/dist/runtime/inputSnapshot.d.ts +21 -3
- package/dist/runtime/inputSnapshot.js +8 -2
- package/dist/runtime/motionIntents.d.ts +12 -1
- package/dist/runtime/motionIntents.js +26 -1
- 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 +62 -0
- package/dist/runtime/worldChannel.js +114 -0
- package/dist/runtime/worldMirror.d.ts +36 -0
- package/dist/runtime/worldMirror.js +44 -0
- package/dist/runtime/worldProjection.d.ts +47 -0
- package/dist/runtime/worldProjection.js +52 -0
- package/dist/runtime/worldReplication.d.ts +69 -0
- package/dist/runtime/worldReplication.js +217 -0
- package/dist/runtime/worldSnapshot.d.ts +44 -0
- package/dist/runtime/worldSnapshot.js +30 -0
- package/dist/scene/assetCatalog.d.ts +22 -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 +18 -0
- package/dist/scene/colliders.js +61 -1
- package/dist/scene/entityStats.d.ts +13 -0
- package/dist/scene/entityStats.js +30 -0
- package/dist/scene/entityStore.d.ts +41 -1
- package/dist/scene/entityStore.js +75 -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 +9 -0
- package/dist/store/observableKeyedStore.js +43 -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 +6 -4
- package/dist/tactics/fallingGrid.js +6 -4
- 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 +144 -0
- package/dist/ui/gameLayout.js +148 -0
- 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/orientation.d.ts +39 -0
- package/dist/ui/orientation.js +49 -0
- package/dist/ui/selectionModel.d.ts +92 -0
- package/dist/ui/selectionModel.js +88 -0
- package/dist/ui/swingTimer.d.ts +42 -0
- package/dist/ui/swingTimer.js +38 -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/visibility/spatialIndex.js +34 -20
- 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/environmentSummary.d.ts +11 -2
- package/dist/world/environmentSummary.js +11 -1
- package/dist/world/features.d.ts +244 -7
- package/dist/world/features.js +92 -4
- 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 +55 -0
- package/dist/world/roads.js +184 -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/streets.d.ts +80 -0
- package/dist/world/streets.js +144 -0
- package/dist/world/terraform.d.ts +213 -1
- package/dist/world/terraform.js +655 -20
- package/dist/world/terrain.d.ts +138 -12
- package/dist/world/terrain.js +346 -28
- 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 -4983
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, zero-dependency classification of a model-asset fetch before it reaches
|
|
3
|
+
* a GLTF parser. Runtime loaders hand the raw response (status, content type,
|
|
4
|
+
* and the first bytes) to {@link classifyAssetResponse} so a missing or
|
|
5
|
+
* mis-served file surfaces as an actionable diagnostic naming the broken asset
|
|
6
|
+
* contract, instead of the opaque "Unexpected token < in JSON" parse error a
|
|
7
|
+
* dev-server HTML fallback otherwise produces. No `fetch`, DOM, or three.js
|
|
8
|
+
* dependency lives here — callers supply the already-read bytes.
|
|
9
|
+
*/
|
|
10
|
+
/** GLB little-endian magic — the ASCII "glTF" at byte 0 of a binary glTF container. @internal */
|
|
11
|
+
export const GLB_MAGIC_ASCII = "glTF";
|
|
12
|
+
function startsWithAscii(bytes, ascii) {
|
|
13
|
+
if (bytes.length < ascii.length)
|
|
14
|
+
return false;
|
|
15
|
+
for (let i = 0; i < ascii.length; i += 1) {
|
|
16
|
+
if (bytes[i] !== ascii.charCodeAt(i))
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
/** Skip a UTF-8 BOM and leading ASCII whitespace, returning the index of the first meaningful byte. */
|
|
22
|
+
function firstNonBlank(bytes) {
|
|
23
|
+
let i = 0;
|
|
24
|
+
if (bytes.length >= 3 && bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf)
|
|
25
|
+
i = 3;
|
|
26
|
+
while (i < bytes.length) {
|
|
27
|
+
const b = bytes[i];
|
|
28
|
+
// space, tab, CR, LF
|
|
29
|
+
if (b === 0x20 || b === 0x09 || b === 0x0d || b === 0x0a)
|
|
30
|
+
i += 1;
|
|
31
|
+
else
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
return i;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Classify the leading bytes of a response body by their format signature, with
|
|
38
|
+
* no HTTP context. Recognizes GLB (`glTF` magic), glTF-JSON (a leading `{`), an
|
|
39
|
+
* HTML fallback page (`<!doctype`, `<html`, or a bare `<`), and a few common
|
|
40
|
+
* non-model binaries (PNG, JPEG, ZIP) so they can be reported as unsupported
|
|
41
|
+
* rather than corrupt.
|
|
42
|
+
*/
|
|
43
|
+
export function readByteSignature(bytes) {
|
|
44
|
+
if (bytes.length === 0)
|
|
45
|
+
return "empty";
|
|
46
|
+
if (startsWithAscii(bytes, GLB_MAGIC_ASCII))
|
|
47
|
+
return "glb";
|
|
48
|
+
if (bytes.length >= 8 && bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4e && bytes[3] === 0x47) {
|
|
49
|
+
return "png";
|
|
50
|
+
}
|
|
51
|
+
if (bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff)
|
|
52
|
+
return "jpeg";
|
|
53
|
+
if (bytes.length >= 4 && bytes[0] === 0x50 && bytes[1] === 0x4b && bytes[2] === 0x03 && bytes[3] === 0x04) {
|
|
54
|
+
return "zip";
|
|
55
|
+
}
|
|
56
|
+
const start = firstNonBlank(bytes);
|
|
57
|
+
const head = bytes[start];
|
|
58
|
+
if (head === undefined)
|
|
59
|
+
return "empty";
|
|
60
|
+
// '<' — an HTML document served where a model was expected.
|
|
61
|
+
if (head === 0x3c)
|
|
62
|
+
return "html";
|
|
63
|
+
// '{' — glTF-JSON (the non-binary .gltf form) is a parseable model.
|
|
64
|
+
if (head === 0x7b)
|
|
65
|
+
return "gltf-json";
|
|
66
|
+
return "unknown";
|
|
67
|
+
}
|
|
68
|
+
function describe(logicalId, url) {
|
|
69
|
+
return logicalId === undefined ? `"${url}"` : `"${logicalId}" (${url})`;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Turn a {@link AssetResponseProbe} into an {@link AssetLoadDiagnosis}. HTTP
|
|
73
|
+
* status is checked first (a `>= 400` or `0` status is `missing`), then the
|
|
74
|
+
* `Content-Type` and byte signature decide between an HTML fallback, a
|
|
75
|
+
* parseable model (`ok`), an unsupported-but-recognizable format, and otherwise
|
|
76
|
+
* corrupt bytes. The returned `message` always names the asset and URL and, for
|
|
77
|
+
* failures, points at the likely fix (provision the pack, check the serving path).
|
|
78
|
+
*
|
|
79
|
+
* @capability asset-load-diagnostics classify a model fetch (missing / HTML fallback / corrupt / unsupported) before parsing
|
|
80
|
+
*/
|
|
81
|
+
export function classifyAssetResponse(probe) {
|
|
82
|
+
const { url, logicalId, status, statusText, contentType, bytes } = probe;
|
|
83
|
+
const signature = bytes === undefined ? "empty" : readByteSignature(bytes);
|
|
84
|
+
const who = describe(logicalId, url);
|
|
85
|
+
const finish = (kind, message) => ({
|
|
86
|
+
kind,
|
|
87
|
+
ok: kind === "ok",
|
|
88
|
+
url,
|
|
89
|
+
...(logicalId === undefined ? {} : { logicalId }),
|
|
90
|
+
byteSignature: signature,
|
|
91
|
+
message,
|
|
92
|
+
});
|
|
93
|
+
if (status !== undefined && (status === 0 || status >= 400)) {
|
|
94
|
+
const suffix = statusText === undefined || statusText.length === 0 ? "" : ` ${statusText}`;
|
|
95
|
+
return finish("missing", `Asset ${who} did not load: HTTP ${status}${suffix}. The file is not being served — provision the pack (e.g. \`assets pull <source>\`) or check the model path.`);
|
|
96
|
+
}
|
|
97
|
+
const htmlContentType = contentType !== undefined && contentType.toLowerCase().includes("text/html");
|
|
98
|
+
if (signature === "html" || (htmlContentType && signature !== "glb" && signature !== "gltf-json")) {
|
|
99
|
+
return finish("html", `Asset ${who} resolved to an HTML page, not a model — the dev server returned its fallback document because the file is missing. Provision the pack (\`assets pull <source>\`) so the bytes exist under the served path.`);
|
|
100
|
+
}
|
|
101
|
+
if (signature === "glb" || signature === "gltf-json")
|
|
102
|
+
return finish("ok", `Asset ${who} loaded (${signature}).`);
|
|
103
|
+
if (signature === "png" || signature === "jpeg" || signature === "zip") {
|
|
104
|
+
return finish("unsupported", `Asset ${who} is a ${signature} file, not a GLB/glTF model — the reference points at the wrong file or the wrong loader is being used.`);
|
|
105
|
+
}
|
|
106
|
+
if (signature === "empty") {
|
|
107
|
+
return finish("corrupt", `Asset ${who} returned an empty body — the file exists but has no bytes. Re-provision the pack or remove the reference.`);
|
|
108
|
+
}
|
|
109
|
+
return finish("corrupt", `Asset ${who} is not a recognizable GLB/glTF model (leading bytes do not match any known signature). The file is likely truncated or corrupt — re-provision the pack.`);
|
|
110
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The generator-asset seam: register a parametric asset generator (`generate(params, seed) → parts`)
|
|
3
|
+
* so a slider-driven prop (a bookcase with width/shelves/book-density, a building with floors/bays)
|
|
4
|
+
* lives in the catalog alongside static GLBs. A placed instance stores only `{ assetId, params, seed }`
|
|
5
|
+
* — the geometry is re-resolved at runtime, never baked. Schema drives the inspector via #809. The
|
|
6
|
+
* generic seam is engine; concrete generators (building, bookcase) are adopters registered from
|
|
7
|
+
* game/example code — the building generator is the one engine adopter, as a proof it composes.
|
|
8
|
+
*
|
|
9
|
+
* @capability asset-generator seeded parametric geometry registered as a placeable asset
|
|
10
|
+
*/
|
|
11
|
+
import { type ParamSchema, type ParsedParams } from "./sceneKinds.js";
|
|
12
|
+
/** One generated primitive part — a box/panel placed in the asset's local space. */
|
|
13
|
+
export interface GeneratedPart {
|
|
14
|
+
id: string;
|
|
15
|
+
/** Optional semantic tag (shelf, board, book, wall, window …) for materials/inspection. */
|
|
16
|
+
kind?: string;
|
|
17
|
+
/** Local-space center. */
|
|
18
|
+
position: readonly [number, number, number];
|
|
19
|
+
/** Full box dimensions (w, h, d) in meters. */
|
|
20
|
+
size: readonly [number, number, number];
|
|
21
|
+
rotationY?: number;
|
|
22
|
+
/** Hex color; the renderer falls back to a neutral material when absent. */
|
|
23
|
+
color?: string;
|
|
24
|
+
}
|
|
25
|
+
/** A resolved generator asset: its parts plus the overall local-space bounds (min/max corners). */
|
|
26
|
+
export interface GeneratedAsset {
|
|
27
|
+
parts: GeneratedPart[];
|
|
28
|
+
bounds: {
|
|
29
|
+
min: readonly [number, number, number];
|
|
30
|
+
max: readonly [number, number, number];
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* The declared local-space direction the asset's "front" faces (a bookcase's open/book face, a
|
|
34
|
+
* building's entrance) — so a camera rig or placement tool can orient it without hand-tuned
|
|
35
|
+
* `rotationY`. Omit to use the convention default, +Z (`DEFAULT_FORWARD` in `./facing`); every
|
|
36
|
+
* generator should build its front toward +Z unless it declares otherwise.
|
|
37
|
+
*/
|
|
38
|
+
forward?: readonly [number, number, number];
|
|
39
|
+
}
|
|
40
|
+
/** A registered asset generator — schema drives the inspector; `generate` is a pure seeded function. */
|
|
41
|
+
export interface AssetGeneratorDefinition {
|
|
42
|
+
id: string;
|
|
43
|
+
label: string;
|
|
44
|
+
schema: ParamSchema;
|
|
45
|
+
/** Deterministic geometry from validated params + a seed string. Same inputs → same parts. */
|
|
46
|
+
generate: (params: ParsedParams, seed: string) => GeneratedAsset;
|
|
47
|
+
}
|
|
48
|
+
/** Register a parametric asset generator. Idempotent per id (last wins); call at module load. */
|
|
49
|
+
export declare function registerAssetGenerator(definition: AssetGeneratorDefinition): void;
|
|
50
|
+
/** The registered generator for an id, or undefined. @internal */
|
|
51
|
+
export declare function getAssetGenerator(id: string): AssetGeneratorDefinition | undefined;
|
|
52
|
+
/** Every registered generator, in registration order. @internal */
|
|
53
|
+
export declare function listAssetGenerators(): AssetGeneratorDefinition[];
|
|
54
|
+
/**
|
|
55
|
+
* Compute bounds from parts (each part is an axis-aligned box at its center) — a helper generators
|
|
56
|
+
* return so callers can frame/ground the asset without re-deriving it.
|
|
57
|
+
*/
|
|
58
|
+
export declare function partsBounds(parts: readonly GeneratedPart[]): GeneratedAsset["bounds"];
|
|
59
|
+
/**
|
|
60
|
+
* Re-resolve a placed generator instance from its stored `meta` (`{ assetId, seed, ...params }`): look
|
|
61
|
+
* up the generator, parse its params off `meta` against the schema, and generate. Returns null when
|
|
62
|
+
* `assetId` names no registered generator. This is what a runtime renderer calls per placed instance.
|
|
63
|
+
* @internal
|
|
64
|
+
*/
|
|
65
|
+
export declare function resolveGeneratorAsset(meta: Record<string, unknown> | undefined): GeneratedAsset | null;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The generator-asset seam: register a parametric asset generator (`generate(params, seed) → parts`)
|
|
3
|
+
* so a slider-driven prop (a bookcase with width/shelves/book-density, a building with floors/bays)
|
|
4
|
+
* lives in the catalog alongside static GLBs. A placed instance stores only `{ assetId, params, seed }`
|
|
5
|
+
* — the geometry is re-resolved at runtime, never baked. Schema drives the inspector via #809. The
|
|
6
|
+
* generic seam is engine; concrete generators (building, bookcase) are adopters registered from
|
|
7
|
+
* game/example code — the building generator is the one engine adopter, as a proof it composes.
|
|
8
|
+
*
|
|
9
|
+
* @capability asset-generator seeded parametric geometry registered as a placeable asset
|
|
10
|
+
*/
|
|
11
|
+
import { parseParams } from "./sceneKinds.js";
|
|
12
|
+
const generators = new Map();
|
|
13
|
+
/** Register a parametric asset generator. Idempotent per id (last wins); call at module load. */
|
|
14
|
+
export function registerAssetGenerator(definition) {
|
|
15
|
+
generators.set(definition.id, definition);
|
|
16
|
+
}
|
|
17
|
+
/** The registered generator for an id, or undefined. @internal */
|
|
18
|
+
export function getAssetGenerator(id) {
|
|
19
|
+
return generators.get(id);
|
|
20
|
+
}
|
|
21
|
+
/** Every registered generator, in registration order. @internal */
|
|
22
|
+
export function listAssetGenerators() {
|
|
23
|
+
return [...generators.values()];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Compute bounds from parts (each part is an axis-aligned box at its center) — a helper generators
|
|
27
|
+
* return so callers can frame/ground the asset without re-deriving it.
|
|
28
|
+
*/
|
|
29
|
+
export function partsBounds(parts) {
|
|
30
|
+
if (parts.length === 0)
|
|
31
|
+
return { min: [0, 0, 0], max: [0, 0, 0] };
|
|
32
|
+
let minX = Infinity;
|
|
33
|
+
let minY = Infinity;
|
|
34
|
+
let minZ = Infinity;
|
|
35
|
+
let maxX = -Infinity;
|
|
36
|
+
let maxY = -Infinity;
|
|
37
|
+
let maxZ = -Infinity;
|
|
38
|
+
for (const part of parts) {
|
|
39
|
+
minX = Math.min(minX, part.position[0] - part.size[0] / 2);
|
|
40
|
+
minY = Math.min(minY, part.position[1] - part.size[1] / 2);
|
|
41
|
+
minZ = Math.min(minZ, part.position[2] - part.size[2] / 2);
|
|
42
|
+
maxX = Math.max(maxX, part.position[0] + part.size[0] / 2);
|
|
43
|
+
maxY = Math.max(maxY, part.position[1] + part.size[1] / 2);
|
|
44
|
+
maxZ = Math.max(maxZ, part.position[2] + part.size[2] / 2);
|
|
45
|
+
}
|
|
46
|
+
return { min: [minX, minY, minZ], max: [maxX, maxY, maxZ] };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Re-resolve a placed generator instance from its stored `meta` (`{ assetId, seed, ...params }`): look
|
|
50
|
+
* up the generator, parse its params off `meta` against the schema, and generate. Returns null when
|
|
51
|
+
* `assetId` names no registered generator. This is what a runtime renderer calls per placed instance.
|
|
52
|
+
* @internal
|
|
53
|
+
*/
|
|
54
|
+
export function resolveGeneratorAsset(meta) {
|
|
55
|
+
const assetId = typeof meta?.["assetId"] === "string" ? meta["assetId"] : undefined;
|
|
56
|
+
if (assetId === undefined)
|
|
57
|
+
return null;
|
|
58
|
+
const generator = generators.get(assetId);
|
|
59
|
+
if (generator === undefined)
|
|
60
|
+
return null;
|
|
61
|
+
const seed = typeof meta?.["seed"] === "string" ? meta["seed"] : assetId;
|
|
62
|
+
const params = parseParams(generator.schema, meta);
|
|
63
|
+
return generator.generate(params, seed);
|
|
64
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import type { Aabb, Footprint, Vec2 } from "../world/geometry.js";
|
|
2
|
+
/**
|
|
3
|
+
* Wrap `degrees` into `[0, 360)` — the canonical heading range every policy and conversion here works
|
|
4
|
+
* in. Non-finite input collapses to `0` so bad metadata never leaks a `NaN` heading downstream.
|
|
5
|
+
* @capability asset-heading normalize a placement heading into the engine's degree convention
|
|
6
|
+
*/
|
|
7
|
+
export declare function normalizeDegrees(degrees: number): number;
|
|
8
|
+
/**
|
|
9
|
+
* Convert a compass heading (degrees, `0` = engine north, increasing clockwise) into the Three.js
|
|
10
|
+
* Y-rotation (radians, wrapped to `(-PI, PI]`) a renderer applies. Sign matches
|
|
11
|
+
* `world/placementController.quarterTurnsToRotationY`, so heading `90` equals one clockwise quarter
|
|
12
|
+
* turn. This is the one place degrees cross into the private radian space Three.js requires.
|
|
13
|
+
* @capability asset-heading turn a north-relative degree heading into a Three.js Y-rotation
|
|
14
|
+
*/
|
|
15
|
+
export declare function headingToRotationY(headingDegrees: number): number;
|
|
16
|
+
/**
|
|
17
|
+
* Inverse of {@link headingToRotationY}: recover the `[0, 360)` compass heading from a Three.js
|
|
18
|
+
* Y-rotation. Lets a gizmo or inspector present radians as the degrees authors edit.
|
|
19
|
+
* @capability asset-heading read a Three.js Y-rotation back as a north-relative degree heading
|
|
20
|
+
*/
|
|
21
|
+
export declare function rotationYToHeading(rotationY: number): number;
|
|
22
|
+
/** Bridge the legacy 0–3 quarter-turn placement model onto degrees (each turn is 90 degrees clockwise). @internal */
|
|
23
|
+
export declare function quarterTurnsToHeading(quarterTurns: number): number;
|
|
24
|
+
/**
|
|
25
|
+
* A per-asset/per-tool rotation constraint, authored as data instead of hardcoded in build code:
|
|
26
|
+
* `locked` pins every placement to one heading, `snap` quantizes to an increment (15, 45, …), and
|
|
27
|
+
* `free` allows any heading. Serializable — this is the shape stored on a catalog entry or a tool.
|
|
28
|
+
*/
|
|
29
|
+
export type PlacementRotationPolicy = {
|
|
30
|
+
readonly mode: "locked";
|
|
31
|
+
readonly degrees?: number;
|
|
32
|
+
} | {
|
|
33
|
+
readonly mode: "snap";
|
|
34
|
+
readonly snapDegrees: number;
|
|
35
|
+
} | {
|
|
36
|
+
readonly mode: "free";
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Quantize `degrees` to the nearest multiple of `increment` (both in the `[0, 360)` convention). A
|
|
40
|
+
* non-positive increment is treated as free rotation and only normalizes. The math behind a
|
|
41
|
+
* `snap`-mode {@link PlacementRotationPolicy}.
|
|
42
|
+
* @capability placement-rotation snap a heading to a fixed degree increment
|
|
43
|
+
*/
|
|
44
|
+
export declare function snapHeading(degrees: number, increment: number): number;
|
|
45
|
+
/**
|
|
46
|
+
* Resolve a raw drag/scroll heading through a {@link PlacementRotationPolicy} into the heading a
|
|
47
|
+
* placement should actually commit: `locked` ignores the input for its fixed heading, `snap` quantizes,
|
|
48
|
+
* `free` (or an absent policy) normalizes. Deterministic — the single interpreter preview, editor
|
|
49
|
+
* placement, and runtime spawning share instead of each re-implementing the modes.
|
|
50
|
+
* @capability placement-rotation apply a rotation policy to a candidate placement heading
|
|
51
|
+
*/
|
|
52
|
+
export declare function applyRotationPolicy(degrees: number, policy: PlacementRotationPolicy | undefined): number;
|
|
53
|
+
/**
|
|
54
|
+
* Where a model's pivot sits within its footprint: `center` (origin at the footprint center),
|
|
55
|
+
* `corner` (origin at the min-X/min-Z corner), or a normalized `{ x, z }` fraction in `[-0.5, 0.5]`.
|
|
56
|
+
*/
|
|
57
|
+
export type AssetAnchor = "center" | "corner" | {
|
|
58
|
+
readonly x: number;
|
|
59
|
+
readonly z: number;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Catalog-level asset-space metadata: how a model is authored relative to the engine, owned by the
|
|
63
|
+
* catalog entry rather than re-derived per game. Every field is optional so an unmeasured asset stays
|
|
64
|
+
* valid; {@link resolveFacingRotationY}/{@link assetUnitScale}/{@link resolveAnchorOffset} supply the
|
|
65
|
+
* documented defaults.
|
|
66
|
+
*/
|
|
67
|
+
export interface AssetSpace {
|
|
68
|
+
/** Compass heading (degrees, `0` = north) the model's front visually points at `rotationY = 0`. Default `0`. */
|
|
69
|
+
forwardDegrees?: number;
|
|
70
|
+
/** Multiply source-space lengths by this to reach engine meters (a kit's native tile size correction). Default `1`. */
|
|
71
|
+
unitScale?: number;
|
|
72
|
+
/** Authored footprint in engine meters — the grid/placement extent, independent of the raw mesh bounds. */
|
|
73
|
+
footprint?: Footprint;
|
|
74
|
+
/** Authored grid cell size in engine meters this asset snaps to. */
|
|
75
|
+
gridSize?: number;
|
|
76
|
+
/** Where the model origin sits within {@link footprint}. Default `center`. */
|
|
77
|
+
anchor?: AssetAnchor;
|
|
78
|
+
/** Optional asset-local placement bounds (XZ) a tool clamps a drop position into. */
|
|
79
|
+
bounds?: Aabb;
|
|
80
|
+
/** Default rotation constraint when this asset is placed. Absent means free rotation. */
|
|
81
|
+
rotation?: PlacementRotationPolicy;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The Three.js Y-rotation that makes a model whose front is authored at `forwardDegrees` visually point
|
|
85
|
+
* `headingDegrees` (engine north = `0`). This is the catalog-owned replacement for per-game corrective
|
|
86
|
+
* yaw: a model authored facing south (`forwardDegrees: 180`) placed toward north resolves to `Math.PI`.
|
|
87
|
+
* @capability asset-facing resolve a placement heading into a Three.js Y-rotation using canonical forward
|
|
88
|
+
*/
|
|
89
|
+
export declare function resolveFacingRotationY(headingDegrees: number, space?: Pick<AssetSpace, "forwardDegrees">): number;
|
|
90
|
+
/** The effective, guarded unit scale for an asset — a finite positive `unitScale`, else `1`. @internal */
|
|
91
|
+
export declare function assetUnitScale(space?: Pick<AssetSpace, "unitScale">): number;
|
|
92
|
+
/**
|
|
93
|
+
* Convert a source-space length to engine meters through the asset's {@link AssetSpace.unitScale} — the
|
|
94
|
+
* data-owned replacement for per-game scale constants (a native ~4-unit kit tile down to a 1-unit grid).
|
|
95
|
+
* @capability asset-units convert a source-space length to engine meters via catalog unit scale
|
|
96
|
+
*/
|
|
97
|
+
export declare function toEngineUnits(sourceValue: number, space?: Pick<AssetSpace, "unitScale">): number;
|
|
98
|
+
/**
|
|
99
|
+
* The pivot's offset from the footprint center, in engine meters, implied by {@link AssetSpace.anchor}
|
|
100
|
+
* and {@link AssetSpace.footprint}. `center` yields `[0, 0]`; `corner` yields the min corner; a
|
|
101
|
+
* normalized `{ x, z }` scales by the footprint. Subtract it to seat a footprint centered on a point.
|
|
102
|
+
* @capability asset-anchor resolve a model's pivot offset from its footprint center
|
|
103
|
+
*/
|
|
104
|
+
export declare function resolveAnchorOffset(space?: Pick<AssetSpace, "anchor" | "footprint">): Vec2;
|
|
105
|
+
/**
|
|
106
|
+
* The axis-aligned footprint extent after rotating a rectangular {@link Footprint} by `headingDegrees`
|
|
107
|
+
* — footprint-aware placement: a 90-degree turn swaps width/depth, and arbitrary headings grow the
|
|
108
|
+
* bounding box so overlap and snap checks see the space the rotated asset truly occupies.
|
|
109
|
+
* @capability asset-footprint rotate a rectangular footprint into its axis-aligned extent
|
|
110
|
+
*/
|
|
111
|
+
export declare function rotatedFootprint(footprint: Footprint, headingDegrees: number): Footprint;
|
|
112
|
+
/** One problem found by {@link validateAssetSpace}: which metadata field and why it is invalid. */
|
|
113
|
+
export interface AssetSpaceIssue {
|
|
114
|
+
field: string;
|
|
115
|
+
message: string;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Report contradictory or malformed asset-space metadata so catalog generation can reject it before it
|
|
119
|
+
* reaches a tool: non-finite headings, non-positive scales/sizes, inverted bounds, unknown anchors, and
|
|
120
|
+
* ill-formed rotation policies (a `snap` with a non-positive increment, an unknown `mode`). An empty
|
|
121
|
+
* array means valid. Validation lives here so tools consume resolved metadata instead of reinterpreting.
|
|
122
|
+
* @capability asset-space validate catalog asset-space metadata during index generation
|
|
123
|
+
*/
|
|
124
|
+
export declare function validateAssetSpace(raw: unknown): AssetSpaceIssue[];
|
|
125
|
+
/**
|
|
126
|
+
* Parse an untrusted rotation policy (from a scene document or catalog JSON) into a valid
|
|
127
|
+
* {@link PlacementRotationPolicy}, defaulting to `free` when absent or malformed. The lenient companion
|
|
128
|
+
* to {@link validateAssetSpace} for the read path.
|
|
129
|
+
* @capability placement-rotation parse a serialized rotation policy, defaulting to free
|
|
130
|
+
*/
|
|
131
|
+
export declare function parseRotationPolicy(raw: unknown): PlacementRotationPolicy;
|
|
132
|
+
/**
|
|
133
|
+
* Parse untrusted JSON into an {@link AssetSpace}, dropping malformed fields, or `undefined` when
|
|
134
|
+
* nothing valid remains — the round-trippable read path for serialized catalog/scene metadata.
|
|
135
|
+
* @capability asset-space parse serialized asset-space metadata into a validated shape
|
|
136
|
+
*/
|
|
137
|
+
export declare function parseAssetSpace(raw: unknown): AssetSpace | undefined;
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset-space metadata + placement rotation policy: the catalog-owned description of how a model sits
|
|
3
|
+
* in its source space and how a placement tool may rotate it. Games author scenes against engine north
|
|
4
|
+
* (heading `0` degrees, increasing clockwise) instead of transplanting per-game yaw offsets and magic
|
|
5
|
+
* scales; the catalog entry ({@link "scene/assetCatalog".ModelAssetRef}) is the upstream owner of the
|
|
6
|
+
* correction. Public APIs speak degrees; the only radians here are the private Three.js Y-rotation this
|
|
7
|
+
* module converts to, matching `world/placementController`'s `quarterTurnsToRotationY` sign so a
|
|
8
|
+
* quarter turn still equals 90 degrees clockwise. Pure and serializable — no three.js, no allocation of
|
|
9
|
+
* shared state — so preview, placement, and runtime spawning share one interpretation.
|
|
10
|
+
*/
|
|
11
|
+
const DEG2RAD = Math.PI / 180;
|
|
12
|
+
const RAD2DEG = 180 / Math.PI;
|
|
13
|
+
const FULL_TURN = 360;
|
|
14
|
+
function clamp(value, min, max) {
|
|
15
|
+
return value < min ? min : value > max ? max : value;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Wrap `degrees` into `[0, 360)` — the canonical heading range every policy and conversion here works
|
|
19
|
+
* in. Non-finite input collapses to `0` so bad metadata never leaks a `NaN` heading downstream.
|
|
20
|
+
* @capability asset-heading normalize a placement heading into the engine's degree convention
|
|
21
|
+
*/
|
|
22
|
+
export function normalizeDegrees(degrees) {
|
|
23
|
+
if (!Number.isFinite(degrees))
|
|
24
|
+
return 0;
|
|
25
|
+
const wrapped = degrees % FULL_TURN;
|
|
26
|
+
return wrapped < 0 ? wrapped + FULL_TURN : wrapped;
|
|
27
|
+
}
|
|
28
|
+
function wrapRadians(radians) {
|
|
29
|
+
let r = radians % (2 * Math.PI);
|
|
30
|
+
if (r <= -Math.PI)
|
|
31
|
+
r += 2 * Math.PI;
|
|
32
|
+
if (r > Math.PI)
|
|
33
|
+
r -= 2 * Math.PI;
|
|
34
|
+
return r + 0; // normalize -0 to 0
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Convert a compass heading (degrees, `0` = engine north, increasing clockwise) into the Three.js
|
|
38
|
+
* Y-rotation (radians, wrapped to `(-PI, PI]`) a renderer applies. Sign matches
|
|
39
|
+
* `world/placementController.quarterTurnsToRotationY`, so heading `90` equals one clockwise quarter
|
|
40
|
+
* turn. This is the one place degrees cross into the private radian space Three.js requires.
|
|
41
|
+
* @capability asset-heading turn a north-relative degree heading into a Three.js Y-rotation
|
|
42
|
+
*/
|
|
43
|
+
export function headingToRotationY(headingDegrees) {
|
|
44
|
+
return wrapRadians(-normalizeDegrees(headingDegrees) * DEG2RAD);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Inverse of {@link headingToRotationY}: recover the `[0, 360)` compass heading from a Three.js
|
|
48
|
+
* Y-rotation. Lets a gizmo or inspector present radians as the degrees authors edit.
|
|
49
|
+
* @capability asset-heading read a Three.js Y-rotation back as a north-relative degree heading
|
|
50
|
+
*/
|
|
51
|
+
export function rotationYToHeading(rotationY) {
|
|
52
|
+
return normalizeDegrees(-rotationY * RAD2DEG);
|
|
53
|
+
}
|
|
54
|
+
/** Bridge the legacy 0–3 quarter-turn placement model onto degrees (each turn is 90 degrees clockwise). @internal */
|
|
55
|
+
export function quarterTurnsToHeading(quarterTurns) {
|
|
56
|
+
return normalizeDegrees(Math.round(quarterTurns) * 90);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Quantize `degrees` to the nearest multiple of `increment` (both in the `[0, 360)` convention). A
|
|
60
|
+
* non-positive increment is treated as free rotation and only normalizes. The math behind a
|
|
61
|
+
* `snap`-mode {@link PlacementRotationPolicy}.
|
|
62
|
+
* @capability placement-rotation snap a heading to a fixed degree increment
|
|
63
|
+
*/
|
|
64
|
+
export function snapHeading(degrees, increment) {
|
|
65
|
+
if (!(increment > 0))
|
|
66
|
+
return normalizeDegrees(degrees);
|
|
67
|
+
return normalizeDegrees(Math.round(normalizeDegrees(degrees) / increment) * increment);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Resolve a raw drag/scroll heading through a {@link PlacementRotationPolicy} into the heading a
|
|
71
|
+
* placement should actually commit: `locked` ignores the input for its fixed heading, `snap` quantizes,
|
|
72
|
+
* `free` (or an absent policy) normalizes. Deterministic — the single interpreter preview, editor
|
|
73
|
+
* placement, and runtime spawning share instead of each re-implementing the modes.
|
|
74
|
+
* @capability placement-rotation apply a rotation policy to a candidate placement heading
|
|
75
|
+
*/
|
|
76
|
+
export function applyRotationPolicy(degrees, policy) {
|
|
77
|
+
if (policy === undefined || policy.mode === "free")
|
|
78
|
+
return normalizeDegrees(degrees);
|
|
79
|
+
if (policy.mode === "locked")
|
|
80
|
+
return normalizeDegrees(policy.degrees ?? 0);
|
|
81
|
+
return snapHeading(degrees, policy.snapDegrees);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The Three.js Y-rotation that makes a model whose front is authored at `forwardDegrees` visually point
|
|
85
|
+
* `headingDegrees` (engine north = `0`). This is the catalog-owned replacement for per-game corrective
|
|
86
|
+
* yaw: a model authored facing south (`forwardDegrees: 180`) placed toward north resolves to `Math.PI`.
|
|
87
|
+
* @capability asset-facing resolve a placement heading into a Three.js Y-rotation using canonical forward
|
|
88
|
+
*/
|
|
89
|
+
export function resolveFacingRotationY(headingDegrees, space) {
|
|
90
|
+
return headingToRotationY(headingDegrees - (space?.forwardDegrees ?? 0));
|
|
91
|
+
}
|
|
92
|
+
/** The effective, guarded unit scale for an asset — a finite positive `unitScale`, else `1`. @internal */
|
|
93
|
+
export function assetUnitScale(space) {
|
|
94
|
+
const s = space?.unitScale;
|
|
95
|
+
return typeof s === "number" && Number.isFinite(s) && s > 0 ? s : 1;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Convert a source-space length to engine meters through the asset's {@link AssetSpace.unitScale} — the
|
|
99
|
+
* data-owned replacement for per-game scale constants (a native ~4-unit kit tile down to a 1-unit grid).
|
|
100
|
+
* @capability asset-units convert a source-space length to engine meters via catalog unit scale
|
|
101
|
+
*/
|
|
102
|
+
export function toEngineUnits(sourceValue, space) {
|
|
103
|
+
return sourceValue * assetUnitScale(space);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The pivot's offset from the footprint center, in engine meters, implied by {@link AssetSpace.anchor}
|
|
107
|
+
* and {@link AssetSpace.footprint}. `center` yields `[0, 0]`; `corner` yields the min corner; a
|
|
108
|
+
* normalized `{ x, z }` scales by the footprint. Subtract it to seat a footprint centered on a point.
|
|
109
|
+
* @capability asset-anchor resolve a model's pivot offset from its footprint center
|
|
110
|
+
*/
|
|
111
|
+
export function resolveAnchorOffset(space) {
|
|
112
|
+
const w = space?.footprint?.w ?? 0;
|
|
113
|
+
const d = space?.footprint?.d ?? 0;
|
|
114
|
+
const anchor = space?.anchor ?? "center";
|
|
115
|
+
let fx = 0;
|
|
116
|
+
let fz = 0;
|
|
117
|
+
if (anchor === "corner") {
|
|
118
|
+
fx = -0.5;
|
|
119
|
+
fz = -0.5;
|
|
120
|
+
}
|
|
121
|
+
else if (typeof anchor === "object") {
|
|
122
|
+
fx = clamp(anchor.x, -0.5, 0.5);
|
|
123
|
+
fz = clamp(anchor.z, -0.5, 0.5);
|
|
124
|
+
}
|
|
125
|
+
return [fx * w, fz * d];
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The axis-aligned footprint extent after rotating a rectangular {@link Footprint} by `headingDegrees`
|
|
129
|
+
* — footprint-aware placement: a 90-degree turn swaps width/depth, and arbitrary headings grow the
|
|
130
|
+
* bounding box so overlap and snap checks see the space the rotated asset truly occupies.
|
|
131
|
+
* @capability asset-footprint rotate a rectangular footprint into its axis-aligned extent
|
|
132
|
+
*/
|
|
133
|
+
export function rotatedFootprint(footprint, headingDegrees) {
|
|
134
|
+
const rad = normalizeDegrees(headingDegrees) * DEG2RAD;
|
|
135
|
+
const cos = Math.abs(Math.cos(rad));
|
|
136
|
+
const sin = Math.abs(Math.sin(rad));
|
|
137
|
+
return {
|
|
138
|
+
w: footprint.w * cos + footprint.d * sin,
|
|
139
|
+
d: footprint.w * sin + footprint.d * cos,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function isRecord(value) {
|
|
143
|
+
return typeof value === "object" && value !== null;
|
|
144
|
+
}
|
|
145
|
+
function finitePositive(value) {
|
|
146
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
147
|
+
}
|
|
148
|
+
function validateRotationPolicy(raw) {
|
|
149
|
+
if (!isRecord(raw))
|
|
150
|
+
return [{ field: "mode", message: "expected an object" }];
|
|
151
|
+
if (raw.mode === "free")
|
|
152
|
+
return [];
|
|
153
|
+
if (raw.mode === "locked") {
|
|
154
|
+
return raw.degrees !== undefined && (typeof raw.degrees !== "number" || !Number.isFinite(raw.degrees))
|
|
155
|
+
? [{ field: "degrees", message: "expected a finite number" }]
|
|
156
|
+
: [];
|
|
157
|
+
}
|
|
158
|
+
if (raw.mode === "snap") {
|
|
159
|
+
return finitePositive(raw.snapDegrees) ? [] : [{ field: "snapDegrees", message: "expected a positive number" }];
|
|
160
|
+
}
|
|
161
|
+
return [{ field: "mode", message: "expected 'locked', 'snap', or 'free'" }];
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Report contradictory or malformed asset-space metadata so catalog generation can reject it before it
|
|
165
|
+
* reaches a tool: non-finite headings, non-positive scales/sizes, inverted bounds, unknown anchors, and
|
|
166
|
+
* ill-formed rotation policies (a `snap` with a non-positive increment, an unknown `mode`). An empty
|
|
167
|
+
* array means valid. Validation lives here so tools consume resolved metadata instead of reinterpreting.
|
|
168
|
+
* @capability asset-space validate catalog asset-space metadata during index generation
|
|
169
|
+
*/
|
|
170
|
+
export function validateAssetSpace(raw) {
|
|
171
|
+
const issues = [];
|
|
172
|
+
if (!isRecord(raw)) {
|
|
173
|
+
issues.push({ field: "", message: "expected an object" });
|
|
174
|
+
return issues;
|
|
175
|
+
}
|
|
176
|
+
if (raw.forwardDegrees !== undefined && (typeof raw.forwardDegrees !== "number" || !Number.isFinite(raw.forwardDegrees))) {
|
|
177
|
+
issues.push({ field: "forwardDegrees", message: "expected a finite number" });
|
|
178
|
+
}
|
|
179
|
+
if (raw.unitScale !== undefined && !finitePositive(raw.unitScale)) {
|
|
180
|
+
issues.push({ field: "unitScale", message: "expected a positive number" });
|
|
181
|
+
}
|
|
182
|
+
if (raw.gridSize !== undefined && !finitePositive(raw.gridSize)) {
|
|
183
|
+
issues.push({ field: "gridSize", message: "expected a positive number" });
|
|
184
|
+
}
|
|
185
|
+
if (raw.footprint !== undefined) {
|
|
186
|
+
const fp = raw.footprint;
|
|
187
|
+
if (!isRecord(fp) || !finitePositive(fp.w) || !finitePositive(fp.d)) {
|
|
188
|
+
issues.push({ field: "footprint", message: "expected { w > 0, d > 0 }" });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (raw.anchor !== undefined) {
|
|
192
|
+
const a = raw.anchor;
|
|
193
|
+
const named = a === "center" || a === "corner";
|
|
194
|
+
const explicit = isRecord(a) && typeof a.x === "number" && typeof a.z === "number";
|
|
195
|
+
if (!named && !explicit)
|
|
196
|
+
issues.push({ field: "anchor", message: "expected 'center', 'corner', or { x, z }" });
|
|
197
|
+
}
|
|
198
|
+
if (raw.bounds !== undefined) {
|
|
199
|
+
const b = raw.bounds;
|
|
200
|
+
if (!isRecord(b) || typeof b.minX !== "number" || typeof b.maxX !== "number" || typeof b.minZ !== "number" || typeof b.maxZ !== "number") {
|
|
201
|
+
issues.push({ field: "bounds", message: "expected an Aabb" });
|
|
202
|
+
}
|
|
203
|
+
else if (b.maxX < b.minX || b.maxZ < b.minZ) {
|
|
204
|
+
issues.push({ field: "bounds", message: "max must be >= min" });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (raw.rotation !== undefined) {
|
|
208
|
+
issues.push(...validateRotationPolicy(raw.rotation).map((m) => ({ field: `rotation.${m.field}`, message: m.message })));
|
|
209
|
+
}
|
|
210
|
+
return issues;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Parse an untrusted rotation policy (from a scene document or catalog JSON) into a valid
|
|
214
|
+
* {@link PlacementRotationPolicy}, defaulting to `free` when absent or malformed. The lenient companion
|
|
215
|
+
* to {@link validateAssetSpace} for the read path.
|
|
216
|
+
* @capability placement-rotation parse a serialized rotation policy, defaulting to free
|
|
217
|
+
*/
|
|
218
|
+
export function parseRotationPolicy(raw) {
|
|
219
|
+
if (isRecord(raw)) {
|
|
220
|
+
if (raw.mode === "locked") {
|
|
221
|
+
return typeof raw.degrees === "number" && Number.isFinite(raw.degrees)
|
|
222
|
+
? { mode: "locked", degrees: normalizeDegrees(raw.degrees) }
|
|
223
|
+
: { mode: "locked" };
|
|
224
|
+
}
|
|
225
|
+
if (raw.mode === "snap" && finitePositive(raw.snapDegrees))
|
|
226
|
+
return { mode: "snap", snapDegrees: raw.snapDegrees };
|
|
227
|
+
}
|
|
228
|
+
return { mode: "free" };
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Parse untrusted JSON into an {@link AssetSpace}, dropping malformed fields, or `undefined` when
|
|
232
|
+
* nothing valid remains — the round-trippable read path for serialized catalog/scene metadata.
|
|
233
|
+
* @capability asset-space parse serialized asset-space metadata into a validated shape
|
|
234
|
+
*/
|
|
235
|
+
export function parseAssetSpace(raw) {
|
|
236
|
+
if (!isRecord(raw))
|
|
237
|
+
return undefined;
|
|
238
|
+
const out = {};
|
|
239
|
+
if (typeof raw.forwardDegrees === "number" && Number.isFinite(raw.forwardDegrees))
|
|
240
|
+
out.forwardDegrees = normalizeDegrees(raw.forwardDegrees);
|
|
241
|
+
if (finitePositive(raw.unitScale))
|
|
242
|
+
out.unitScale = raw.unitScale;
|
|
243
|
+
if (finitePositive(raw.gridSize))
|
|
244
|
+
out.gridSize = raw.gridSize;
|
|
245
|
+
if (isRecord(raw.footprint) && finitePositive(raw.footprint.w) && finitePositive(raw.footprint.d)) {
|
|
246
|
+
out.footprint = { w: raw.footprint.w, d: raw.footprint.d };
|
|
247
|
+
}
|
|
248
|
+
if (raw.anchor === "center" || raw.anchor === "corner")
|
|
249
|
+
out.anchor = raw.anchor;
|
|
250
|
+
else if (isRecord(raw.anchor) && typeof raw.anchor.x === "number" && typeof raw.anchor.z === "number") {
|
|
251
|
+
out.anchor = { x: raw.anchor.x, z: raw.anchor.z };
|
|
252
|
+
}
|
|
253
|
+
if (isRecord(raw.bounds) && typeof raw.bounds.minX === "number" && typeof raw.bounds.maxX === "number" && typeof raw.bounds.minZ === "number" && typeof raw.bounds.maxZ === "number") {
|
|
254
|
+
out.bounds = { minX: raw.bounds.minX, maxX: raw.bounds.maxX, minZ: raw.bounds.minZ, maxZ: raw.bounds.maxZ };
|
|
255
|
+
}
|
|
256
|
+
if (isRecord(raw.rotation))
|
|
257
|
+
out.rotation = parseRotationPolicy(raw.rotation);
|
|
258
|
+
return Object.keys(out).length === 0 ? undefined : out;
|
|
259
|
+
}
|