@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,409 @@
|
|
|
1
|
+
import { masksInfluence, } from "./geometry.js";
|
|
2
|
+
import { scatter } from "./scatter.js";
|
|
3
|
+
/** The editor path kind that marks a closed polyline as a foliage/scatter region. */
|
|
4
|
+
export const SCATTER_PATH_KIND = "scatter";
|
|
5
|
+
/** Defaults a bare scatter region fills with: sparse grass, lightly spaced. */
|
|
6
|
+
export const SCATTER_DEFAULTS = {
|
|
7
|
+
density: 0.15,
|
|
8
|
+
minSpacing: 1.5,
|
|
9
|
+
seed: "",
|
|
10
|
+
minScale: 0.8,
|
|
11
|
+
maxScale: 1.3,
|
|
12
|
+
minYaw: 0,
|
|
13
|
+
maxYaw: Math.PI * 2,
|
|
14
|
+
verticalOffset: 0,
|
|
15
|
+
alignToNormal: false,
|
|
16
|
+
maxSlope: 0,
|
|
17
|
+
minHeight: Number.NEGATIVE_INFINITY,
|
|
18
|
+
maxHeight: Number.POSITIVE_INFINITY,
|
|
19
|
+
edgeFalloff: 0,
|
|
20
|
+
jitter: 1,
|
|
21
|
+
palette: [{ item: "grass", weight: 1 }],
|
|
22
|
+
avoid: [],
|
|
23
|
+
autoAvoid: true,
|
|
24
|
+
};
|
|
25
|
+
/** Default clearance radius (m) for an auto-avoided gameplay marker with no explicit `meta.clearance`. */
|
|
26
|
+
export const DEFAULT_MARKER_CLEARANCE = 3.5;
|
|
27
|
+
/** Marker/volume kinds that repel scatter and flatten terrain by default (spawns, objectives, vendors). */
|
|
28
|
+
export const DEFAULT_CLEARANCE_KINDS = [
|
|
29
|
+
"player_spawn",
|
|
30
|
+
"spawn",
|
|
31
|
+
"boss",
|
|
32
|
+
"goal",
|
|
33
|
+
"keep",
|
|
34
|
+
"vendor",
|
|
35
|
+
"chest",
|
|
36
|
+
"travel",
|
|
37
|
+
];
|
|
38
|
+
function hash(text) {
|
|
39
|
+
let h = 2166136261 >>> 0;
|
|
40
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
41
|
+
h ^= text.charCodeAt(i);
|
|
42
|
+
h = Math.imul(h, 16777619);
|
|
43
|
+
}
|
|
44
|
+
return (h >>> 0) / 4294967296;
|
|
45
|
+
}
|
|
46
|
+
/** Ray-casting point-in-polygon test on the XZ plane. */
|
|
47
|
+
export function pointInPolygon(point, polygon) {
|
|
48
|
+
let inside = false;
|
|
49
|
+
const [px, pz] = point;
|
|
50
|
+
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i, i += 1) {
|
|
51
|
+
const [xi, zi] = polygon[i];
|
|
52
|
+
const [xj, zj] = polygon[j];
|
|
53
|
+
if (zi > pz !== zj > pz && px < ((xj - xi) * (pz - zi)) / (zj - zi) + xi)
|
|
54
|
+
inside = !inside;
|
|
55
|
+
}
|
|
56
|
+
return inside;
|
|
57
|
+
}
|
|
58
|
+
function distanceToSegment(px, pz, ax, az, bx, bz) {
|
|
59
|
+
const dx = bx - ax;
|
|
60
|
+
const dz = bz - az;
|
|
61
|
+
const lenSq = dx * dx + dz * dz || 1;
|
|
62
|
+
let t = ((px - ax) * dx + (pz - az) * dz) / lenSq;
|
|
63
|
+
t = t < 0 ? 0 : t > 1 ? 1 : t;
|
|
64
|
+
return Math.hypot(px - (ax + dx * t), pz - (az + dz * t));
|
|
65
|
+
}
|
|
66
|
+
/** Shortest distance from a point to a polygon's boundary. */
|
|
67
|
+
export function distanceToPolygonEdge(point, polygon) {
|
|
68
|
+
let best = Number.POSITIVE_INFINITY;
|
|
69
|
+
const [px, pz] = point;
|
|
70
|
+
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i, i += 1) {
|
|
71
|
+
const [xi, zi] = polygon[i];
|
|
72
|
+
const [xj, zj] = polygon[j];
|
|
73
|
+
best = Math.min(best, distanceToSegment(px, pz, xi, zi, xj, zj));
|
|
74
|
+
}
|
|
75
|
+
return best;
|
|
76
|
+
}
|
|
77
|
+
/** Axis-aligned bounds of a polygon, or null if it has no points. */
|
|
78
|
+
export function polygonBounds(polygon) {
|
|
79
|
+
if (polygon.length === 0)
|
|
80
|
+
return null;
|
|
81
|
+
let minX = Infinity;
|
|
82
|
+
let minZ = Infinity;
|
|
83
|
+
let maxX = -Infinity;
|
|
84
|
+
let maxZ = -Infinity;
|
|
85
|
+
for (const [x, z] of polygon) {
|
|
86
|
+
if (x < minX)
|
|
87
|
+
minX = x;
|
|
88
|
+
if (x > maxX)
|
|
89
|
+
maxX = x;
|
|
90
|
+
if (z < minZ)
|
|
91
|
+
minZ = z;
|
|
92
|
+
if (z > maxZ)
|
|
93
|
+
maxZ = z;
|
|
94
|
+
}
|
|
95
|
+
return { minX, minZ, maxX, maxZ };
|
|
96
|
+
}
|
|
97
|
+
/** Shoelace area of a polygon (always non-negative), in square meters. */
|
|
98
|
+
export function polygonArea(polygon) {
|
|
99
|
+
if (polygon.length < 3)
|
|
100
|
+
return 0;
|
|
101
|
+
let sum = 0;
|
|
102
|
+
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i, i += 1) {
|
|
103
|
+
const [xi, zi] = polygon[i];
|
|
104
|
+
const [xj, zj] = polygon[j];
|
|
105
|
+
sum += xj * zi - xi * zj;
|
|
106
|
+
}
|
|
107
|
+
return Math.abs(sum) / 2;
|
|
108
|
+
}
|
|
109
|
+
function pickWeighted(palette, roll) {
|
|
110
|
+
if (palette.length === 0)
|
|
111
|
+
return "grass";
|
|
112
|
+
const total = palette.reduce((sum, entry) => sum + Math.max(0, entry.weight), 0);
|
|
113
|
+
if (total <= 0)
|
|
114
|
+
return palette[0].item;
|
|
115
|
+
let acc = roll * total;
|
|
116
|
+
for (const entry of palette) {
|
|
117
|
+
acc -= Math.max(0, entry.weight);
|
|
118
|
+
if (acc <= 0)
|
|
119
|
+
return entry.item;
|
|
120
|
+
}
|
|
121
|
+
return palette[palette.length - 1].item;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Deterministic placements for one scatter region: scatter its polygon footprint at `density`
|
|
125
|
+
* items/m² (respecting `minSpacing`), clip to the polygon, thin near the edge, drop placements
|
|
126
|
+
* outside the slope/height mask, and derive item/scale/yaw from the region id + seed — so the same
|
|
127
|
+
* saved region always grows the same field. Grounds each instance on `terrain` when provided.
|
|
128
|
+
*/
|
|
129
|
+
export function resolveScatterRegion(region, terrain, avoid) {
|
|
130
|
+
const rules = region.rules;
|
|
131
|
+
const bounds = polygonBounds(region.polygon);
|
|
132
|
+
if (bounds === null || region.polygon.length < 3 || rules.density <= 0)
|
|
133
|
+
return [];
|
|
134
|
+
const ownDiscs = rules.avoid ?? [];
|
|
135
|
+
const masks = {
|
|
136
|
+
discs: avoid === undefined ? ownDiscs : ownDiscs.length === 0 ? avoid.discs : [...ownDiscs, ...avoid.discs],
|
|
137
|
+
corridors: avoid?.corridors ?? [],
|
|
138
|
+
};
|
|
139
|
+
const hasMasks = masks.discs.length > 0 || masks.corridors.length > 0;
|
|
140
|
+
const points = scatter({
|
|
141
|
+
area: bounds,
|
|
142
|
+
density: rules.density,
|
|
143
|
+
minDistance: rules.minSpacing,
|
|
144
|
+
jitter: rules.jitter,
|
|
145
|
+
seed: `${region.id}:${rules.seed}`,
|
|
146
|
+
});
|
|
147
|
+
const instances = [];
|
|
148
|
+
for (const point of points) {
|
|
149
|
+
const p = [point.x, point.z];
|
|
150
|
+
if (!pointInPolygon(p, region.polygon))
|
|
151
|
+
continue;
|
|
152
|
+
// Clearance masks repel scatter: hard reject in the solid core, thin probabilistically in the feather.
|
|
153
|
+
if (hasMasks) {
|
|
154
|
+
const influence = masksInfluence(point.x, point.z, masks);
|
|
155
|
+
if (influence >= 1)
|
|
156
|
+
continue;
|
|
157
|
+
if (influence > 0 && hash(`${region.id}:avoid:${point.index}`) < influence)
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (rules.edgeFalloff > 0) {
|
|
161
|
+
const edge = distanceToPolygonEdge(p, region.polygon);
|
|
162
|
+
if (edge < rules.edgeFalloff && hash(`${region.id}:edge:${point.index}`) > edge / rules.edgeFalloff)
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
let y = rules.verticalOffset;
|
|
166
|
+
let normal;
|
|
167
|
+
if (terrain !== undefined) {
|
|
168
|
+
const height = terrain.sampleHeight(point.x, point.z);
|
|
169
|
+
if (height < rules.minHeight || height > rules.maxHeight)
|
|
170
|
+
continue;
|
|
171
|
+
const n = terrain.sampleNormal(point.x, point.z);
|
|
172
|
+
if (rules.maxSlope > 0) {
|
|
173
|
+
const slope = Math.hypot(n[0], n[2]) / Math.max(n[1], 1e-9);
|
|
174
|
+
if (slope > rules.maxSlope)
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
y = height + rules.verticalOffset;
|
|
178
|
+
if (rules.alignToNormal)
|
|
179
|
+
normal = n;
|
|
180
|
+
}
|
|
181
|
+
const scaleRoll = hash(`${region.id}:${rules.seed}:scale:${point.index}`);
|
|
182
|
+
const yawRoll = hash(`${region.id}:${rules.seed}:yaw:${point.index}`);
|
|
183
|
+
const itemRoll = hash(`${region.id}:${rules.seed}:item:${point.index}`);
|
|
184
|
+
instances.push({
|
|
185
|
+
id: `${region.id}/${point.index}`,
|
|
186
|
+
regionId: region.id,
|
|
187
|
+
item: pickWeighted(rules.palette, itemRoll),
|
|
188
|
+
x: point.x,
|
|
189
|
+
y,
|
|
190
|
+
z: point.z,
|
|
191
|
+
rotationY: rules.minYaw + (rules.maxYaw - rules.minYaw) * yawRoll,
|
|
192
|
+
scale: rules.minScale + (rules.maxScale - rules.minScale) * scaleRoll,
|
|
193
|
+
...(normal === undefined ? {} : { normal }),
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
return instances;
|
|
197
|
+
}
|
|
198
|
+
function metaNumber(meta, key, fallback) {
|
|
199
|
+
const value = meta?.[key];
|
|
200
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
201
|
+
}
|
|
202
|
+
function metaString(meta, key, fallback) {
|
|
203
|
+
const value = meta?.[key];
|
|
204
|
+
return typeof value === "string" ? value : fallback;
|
|
205
|
+
}
|
|
206
|
+
function metaBool(meta, key, fallback) {
|
|
207
|
+
const value = meta?.[key];
|
|
208
|
+
return typeof value === "boolean" ? value : fallback;
|
|
209
|
+
}
|
|
210
|
+
/** Parses a scatter region's palette from meta: a weighted `palette` array, else a single `item`. */
|
|
211
|
+
export function readScatterPalette(meta) {
|
|
212
|
+
const raw = meta?.["palette"];
|
|
213
|
+
if (Array.isArray(raw)) {
|
|
214
|
+
const entries = raw
|
|
215
|
+
.filter((entry) => typeof entry === "object" && entry !== null)
|
|
216
|
+
.map((entry) => ({
|
|
217
|
+
item: typeof entry.item === "string" ? entry.item : "grass",
|
|
218
|
+
weight: typeof entry.weight === "number" && Number.isFinite(entry.weight) ? Math.max(0, entry.weight) : 1,
|
|
219
|
+
}))
|
|
220
|
+
.filter((entry) => entry.item.length > 0);
|
|
221
|
+
if (entries.length > 0)
|
|
222
|
+
return entries;
|
|
223
|
+
}
|
|
224
|
+
const single = metaString(meta, "item", "");
|
|
225
|
+
if (single.length > 0)
|
|
226
|
+
return [{ item: single, weight: 1 }];
|
|
227
|
+
return [...SCATTER_DEFAULTS.palette];
|
|
228
|
+
}
|
|
229
|
+
/** True when an editor path is a foliage/scatter region. */
|
|
230
|
+
export function isScatterPath(path) {
|
|
231
|
+
return path.kind === SCATTER_PATH_KIND;
|
|
232
|
+
}
|
|
233
|
+
/** The path's scatter rules with defaults filled in; null for non-scatter paths. */
|
|
234
|
+
export function readScatterRules(path) {
|
|
235
|
+
if (!isScatterPath(path))
|
|
236
|
+
return null;
|
|
237
|
+
const meta = path.meta;
|
|
238
|
+
return {
|
|
239
|
+
density: Math.max(0, metaNumber(meta, "density", SCATTER_DEFAULTS.density)),
|
|
240
|
+
minSpacing: Math.max(0, metaNumber(meta, "minSpacing", SCATTER_DEFAULTS.minSpacing)),
|
|
241
|
+
seed: metaString(meta, "seed", SCATTER_DEFAULTS.seed),
|
|
242
|
+
minScale: metaNumber(meta, "minScale", SCATTER_DEFAULTS.minScale),
|
|
243
|
+
maxScale: metaNumber(meta, "maxScale", SCATTER_DEFAULTS.maxScale),
|
|
244
|
+
minYaw: metaNumber(meta, "minYaw", SCATTER_DEFAULTS.minYaw),
|
|
245
|
+
maxYaw: metaNumber(meta, "maxYaw", SCATTER_DEFAULTS.maxYaw),
|
|
246
|
+
verticalOffset: metaNumber(meta, "verticalOffset", SCATTER_DEFAULTS.verticalOffset),
|
|
247
|
+
alignToNormal: metaBool(meta, "alignToNormal", SCATTER_DEFAULTS.alignToNormal),
|
|
248
|
+
maxSlope: Math.max(0, metaNumber(meta, "maxSlope", SCATTER_DEFAULTS.maxSlope)),
|
|
249
|
+
minHeight: metaNumber(meta, "minHeight", SCATTER_DEFAULTS.minHeight),
|
|
250
|
+
maxHeight: metaNumber(meta, "maxHeight", SCATTER_DEFAULTS.maxHeight),
|
|
251
|
+
edgeFalloff: Math.max(0, metaNumber(meta, "edgeFalloff", SCATTER_DEFAULTS.edgeFalloff)),
|
|
252
|
+
jitter: metaNumber(meta, "jitter", SCATTER_DEFAULTS.jitter),
|
|
253
|
+
palette: readScatterPalette(meta),
|
|
254
|
+
avoid: readAvoidZones(meta),
|
|
255
|
+
autoAvoid: metaBool(meta, "autoAvoid", SCATTER_DEFAULTS.autoAvoid),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Parses a region's manual clearance discs from `meta.avoid` (array of {x,z,radius,feather?}).
|
|
260
|
+
* @internal — used by `readScatterRules`; games author `avoid` through the region inspector.
|
|
261
|
+
*/
|
|
262
|
+
export function readAvoidZones(meta) {
|
|
263
|
+
const raw = meta?.["avoid"];
|
|
264
|
+
if (!Array.isArray(raw))
|
|
265
|
+
return [];
|
|
266
|
+
const zones = [];
|
|
267
|
+
for (const entry of raw) {
|
|
268
|
+
if (typeof entry !== "object" || entry === null)
|
|
269
|
+
continue;
|
|
270
|
+
const e = entry;
|
|
271
|
+
if (typeof e.x !== "number" || typeof e.z !== "number" || typeof e.radius !== "number")
|
|
272
|
+
continue;
|
|
273
|
+
zones.push({
|
|
274
|
+
x: e.x,
|
|
275
|
+
z: e.z,
|
|
276
|
+
radius: Math.max(0, e.radius),
|
|
277
|
+
...(typeof e.feather === "number" ? { feather: Math.max(0, e.feather) } : {}),
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
return zones;
|
|
281
|
+
}
|
|
282
|
+
/** Builds a resolvable {@link ScatterRegion} from a scatter path (XZ polygon + rules), or null. */
|
|
283
|
+
export function scatterRegionFromPath(path) {
|
|
284
|
+
const rules = readScatterRules(path);
|
|
285
|
+
if (rules === null || path.points.length < 3)
|
|
286
|
+
return null;
|
|
287
|
+
return {
|
|
288
|
+
id: path.id,
|
|
289
|
+
polygon: path.points.map((point) => [point.x, point.z]),
|
|
290
|
+
rules,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
/** Estimated placement count for a scatter path — density × polygon area, for a live UI readout. */
|
|
294
|
+
export function scatterRegionEstimate(path) {
|
|
295
|
+
const rules = readScatterRules(path);
|
|
296
|
+
if (rules === null)
|
|
297
|
+
return { area: 0, count: 0 };
|
|
298
|
+
const area = polygonArea(path.points.map((point) => [point.x, point.z]));
|
|
299
|
+
return { area, count: Math.floor(area * rules.density) };
|
|
300
|
+
}
|
|
301
|
+
/** Reads a gameplay object's clearance radius: explicit `meta.clearance`, else a kind default, else 0. */
|
|
302
|
+
function clearanceOf(kind, meta, kinds, defaultClearance) {
|
|
303
|
+
const explicit = meta?.["clearance"];
|
|
304
|
+
if (typeof explicit === "number" && Number.isFinite(explicit))
|
|
305
|
+
return Math.max(0, explicit);
|
|
306
|
+
return kinds.includes(kind) ? defaultClearance : 0;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Point-pad clearance **discs** from a document's markers/volumes — the terrain-flatten set (spawns,
|
|
310
|
+
* plots, POIs get a level pad). A marker/volume contributes a disc when it carries `meta.clearance`
|
|
311
|
+
* or its kind is in `kinds`. Paths are *not* included (they render draped, never flattened — see
|
|
312
|
+
* {@link clearanceMasksFrom} for their foliage corridor). Pass `ids`/`kinds` to scope it.
|
|
313
|
+
*/
|
|
314
|
+
export function clearanceZonesFrom(doc, options = {}) {
|
|
315
|
+
const kinds = options.ids !== undefined ? [] : options.kinds ?? DEFAULT_CLEARANCE_KINDS;
|
|
316
|
+
const idSet = options.ids === undefined ? null : new Set(options.ids);
|
|
317
|
+
const defaultClearance = options.defaultClearance ?? DEFAULT_MARKER_CLEARANCE;
|
|
318
|
+
const feather = options.feather ?? 2;
|
|
319
|
+
const zones = [];
|
|
320
|
+
const consider = (id, kind, x, z, meta, extra) => {
|
|
321
|
+
if (idSet !== null && !idSet.has(id))
|
|
322
|
+
return;
|
|
323
|
+
const clearance = idSet !== null ? defaultClearance : clearanceOf(kind, meta, kinds, defaultClearance);
|
|
324
|
+
if (clearance <= 0)
|
|
325
|
+
return;
|
|
326
|
+
zones.push({ x, z, radius: clearance + extra, feather });
|
|
327
|
+
};
|
|
328
|
+
for (const marker of doc.markers)
|
|
329
|
+
consider(marker.id, marker.kind, marker.position.x, marker.position.z, marker.meta, 0);
|
|
330
|
+
for (const volume of doc.volumes)
|
|
331
|
+
consider(volume.id, volume.kind, volume.center.x, volume.center.z, volume.meta, volume.radius ?? 0);
|
|
332
|
+
return zones;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* The full clearance region for scatter avoidance: point-pad {@link clearanceZonesFrom} **discs**
|
|
336
|
+
* plus clean-edged **corridors** along non-scatter paths (each `halfWidth` = `path.width/2 +
|
|
337
|
+
* clearance`). Corridors give a straight tree-line along a road instead of a scalloped disc chain —
|
|
338
|
+
* this is what `resolveScatter` auto-avoids. Set `includePaths: false` to drop the path corridors.
|
|
339
|
+
* @internal — `resolveScatter` builds these automatically; games rarely call it directly.
|
|
340
|
+
*/
|
|
341
|
+
export function clearanceMasksFrom(doc, options = {}) {
|
|
342
|
+
const kinds = options.ids !== undefined ? [] : options.kinds ?? DEFAULT_CLEARANCE_KINDS;
|
|
343
|
+
const idSet = options.ids === undefined ? null : new Set(options.ids);
|
|
344
|
+
const defaultClearance = options.defaultClearance ?? DEFAULT_MARKER_CLEARANCE;
|
|
345
|
+
const feather = options.feather ?? 2;
|
|
346
|
+
const includePaths = options.includePaths ?? true;
|
|
347
|
+
const corridors = [];
|
|
348
|
+
if (includePaths) {
|
|
349
|
+
for (const path of doc.paths) {
|
|
350
|
+
if (isScatterPath(path) || path.points.length < 2)
|
|
351
|
+
continue;
|
|
352
|
+
if (idSet !== null && !idSet.has(path.id))
|
|
353
|
+
continue;
|
|
354
|
+
const clearance = idSet !== null ? defaultClearance : clearanceOf(path.kind, path.meta, kinds, defaultClearance);
|
|
355
|
+
const explicit = typeof path.meta?.["clearance"] === "number";
|
|
356
|
+
if (clearance <= 0 && !explicit)
|
|
357
|
+
continue;
|
|
358
|
+
const half = (path.width ?? 4) / 2 + (clearance > 0 ? clearance : defaultClearance);
|
|
359
|
+
corridors.push({ points: path.points.map((point) => [point.x, point.z]), halfWidth: half, feather });
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return { discs: clearanceZonesFrom(doc, options), corridors };
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Every scatter region's placements across a document, grounded on `terrain` when provided. Regions
|
|
366
|
+
* honor clearance masks: their own manual `avoid` discs, plus (when the region's `autoAvoid` is on and
|
|
367
|
+
* `options.autoAvoid !== false`) the document-wide discs + path corridors from {@link clearanceMasksFrom}
|
|
368
|
+
* — so foliage auto-clears spawns, plots, and paths without hand-carving the polygon.
|
|
369
|
+
*/
|
|
370
|
+
export function resolveScatter(doc, terrain, options = {}) {
|
|
371
|
+
const baseDiscs = options.avoid ?? [];
|
|
372
|
+
const auto = options.autoAvoid === false ? { discs: [], corridors: [] } : clearanceMasksFrom(doc, options);
|
|
373
|
+
const out = [];
|
|
374
|
+
for (const path of doc.paths) {
|
|
375
|
+
const region = scatterRegionFromPath(path);
|
|
376
|
+
if (region === null)
|
|
377
|
+
continue;
|
|
378
|
+
const useAuto = region.rules.autoAvoid && options.autoAvoid !== false;
|
|
379
|
+
const masks = {
|
|
380
|
+
discs: useAuto ? [...baseDiscs, ...auto.discs] : baseDiscs,
|
|
381
|
+
corridors: useAuto ? auto.corridors : [],
|
|
382
|
+
};
|
|
383
|
+
out.push(...resolveScatterRegion(region, terrain, masks));
|
|
384
|
+
}
|
|
385
|
+
return out;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Buckets scatter instances into a uniform XZ grid of `chunkSize`-meter cells, so a dense region
|
|
389
|
+
* renders as many small draw units instead of one huge mesh — each chunk carries its own bounds and
|
|
390
|
+
* is frustum-culled independently, and offscreen chunks cost nothing. Empty chunks are omitted;
|
|
391
|
+
* order is deterministic (row-major by grid coordinate).
|
|
392
|
+
* @internal — the chunking behind the `InstancedScatter` renderer; not called directly by games.
|
|
393
|
+
*/
|
|
394
|
+
export function chunkScatterInstances(instances, chunkSize) {
|
|
395
|
+
const size = chunkSize > 0 ? chunkSize : 32;
|
|
396
|
+
const byKey = new Map();
|
|
397
|
+
for (const instance of instances) {
|
|
398
|
+
const gx = Math.floor(instance.x / size);
|
|
399
|
+
const gz = Math.floor(instance.z / size);
|
|
400
|
+
const key = `${gx}:${gz}`;
|
|
401
|
+
let chunk = byKey.get(key);
|
|
402
|
+
if (chunk === undefined) {
|
|
403
|
+
chunk = { key, min: [gx * size, gz * size], size, instances: [] };
|
|
404
|
+
byKey.set(key, chunk);
|
|
405
|
+
}
|
|
406
|
+
chunk.instances.push(instance);
|
|
407
|
+
}
|
|
408
|
+
return [...byKey.values()].sort((a, b) => a.key.localeCompare(b.key));
|
|
409
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** A world-space point — structurally compatible with the editor's `EditorVec3`. */
|
|
2
|
+
export interface ScenePoint3 {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
z: number;
|
|
6
|
+
}
|
|
7
|
+
/** The minimal polyline shape {@link resolveScatterRegion} et al. read; any `EditorPath` satisfies it. */
|
|
8
|
+
export interface ScenePathLike {
|
|
9
|
+
id: string;
|
|
10
|
+
kind: string;
|
|
11
|
+
points: readonly ScenePoint3[];
|
|
12
|
+
width?: number;
|
|
13
|
+
meta?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
/** The minimal point-object shape clearance reads from a document's markers; any `EditorMarker` satisfies it. */
|
|
16
|
+
export interface SceneMarkerLike {
|
|
17
|
+
id: string;
|
|
18
|
+
kind: string;
|
|
19
|
+
position: ScenePoint3;
|
|
20
|
+
meta?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
/** The minimal volume shape vegetation/clearance/triggers read; any `EditorVolume` satisfies it. */
|
|
23
|
+
export interface SceneVolumeLike {
|
|
24
|
+
id: string;
|
|
25
|
+
kind: string;
|
|
26
|
+
shape: "sphere" | "cylinder" | "box";
|
|
27
|
+
center: ScenePoint3;
|
|
28
|
+
radius?: number;
|
|
29
|
+
/** Cylinder height (m); containment and editors use this when shape is `cylinder`. */
|
|
30
|
+
height?: number;
|
|
31
|
+
halfExtents?: ScenePoint3;
|
|
32
|
+
meta?: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The minimal document shape scatter/vegetation resolve against — markers, volumes, and paths only.
|
|
36
|
+
* Any `EditorDocument` satisfies it structurally; this module never imports the editor domain, so
|
|
37
|
+
* world stays the one-directional dependency editor already builds on.
|
|
38
|
+
*/
|
|
39
|
+
export interface SceneDocumentLike {
|
|
40
|
+
markers: readonly SceneMarkerLike[];
|
|
41
|
+
volumes: readonly SceneVolumeLike[];
|
|
42
|
+
paths: readonly ScenePathLike[];
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Vec2 } from "./geometry.js";
|
|
2
|
+
/** Closest point on segment `a`→`b` to `p`, and the clamped parameter `t` in [0,1] where it lies. */
|
|
3
|
+
export interface ClosestOnSegment {
|
|
4
|
+
point: Vec2;
|
|
5
|
+
t: number;
|
|
6
|
+
}
|
|
7
|
+
/** Closest point on the segment `a`→`b` to point `p`, with the clamped parameter `t` (0 at `a`, 1 at `b`). */
|
|
8
|
+
export declare function closestPointOnSegment(p: Vec2, a: Vec2, b: Vec2): ClosestOnSegment;
|
|
9
|
+
/** A resolved circle-vs-segment contact. */
|
|
10
|
+
export interface CircleSegmentHit {
|
|
11
|
+
/** Unit surface normal pointing from the segment toward the circle center — the axis to bounce off. */
|
|
12
|
+
normal: Vec2;
|
|
13
|
+
/** Penetration depth; push the center out by `normal · depth` to separate. */
|
|
14
|
+
depth: number;
|
|
15
|
+
/** Closest point on the segment (the contact point). */
|
|
16
|
+
contact: Vec2;
|
|
17
|
+
/** Circle center pushed just outside the segment — a tunnel-proof resolved position. */
|
|
18
|
+
resolved: Vec2;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Circle (center + `radius`) against a capsule segment `a`→`b` of half-thickness `thickness` (0 for a thin
|
|
22
|
+
* wall). Returns the contact — surface normal, penetration depth, contact point, and the separated center —
|
|
23
|
+
* or `null` when they do not overlap. Endpoints are rounded (the segment is a capsule), so a ball never
|
|
24
|
+
* catches on a corner. Reflect the velocity across `normal` for the bounce; the pure geometry every 2D
|
|
25
|
+
* ball game (pinball, breakout, air hockey) reimplemented per wall, bumper, and paddle.
|
|
26
|
+
*/
|
|
27
|
+
export declare function circleVsSegment(center: Vec2, radius: number, a: Vec2, b: Vec2, thickness?: number): CircleSegmentHit | null;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Closest point on the segment `a`→`b` to point `p`, with the clamped parameter `t` (0 at `a`, 1 at `b`). */
|
|
2
|
+
export function closestPointOnSegment(p, a, b) {
|
|
3
|
+
const abx = b[0] - a[0];
|
|
4
|
+
const aby = b[1] - a[1];
|
|
5
|
+
const l2 = abx * abx + aby * aby;
|
|
6
|
+
const raw = l2 > 0 ? ((p[0] - a[0]) * abx + (p[1] - a[1]) * aby) / l2 : 0;
|
|
7
|
+
const t = raw < 0 ? 0 : raw > 1 ? 1 : raw;
|
|
8
|
+
return { point: [a[0] + t * abx, a[1] + t * aby], t };
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Circle (center + `radius`) against a capsule segment `a`→`b` of half-thickness `thickness` (0 for a thin
|
|
12
|
+
* wall). Returns the contact — surface normal, penetration depth, contact point, and the separated center —
|
|
13
|
+
* or `null` when they do not overlap. Endpoints are rounded (the segment is a capsule), so a ball never
|
|
14
|
+
* catches on a corner. Reflect the velocity across `normal` for the bounce; the pure geometry every 2D
|
|
15
|
+
* ball game (pinball, breakout, air hockey) reimplemented per wall, bumper, and paddle.
|
|
16
|
+
*/
|
|
17
|
+
export function circleVsSegment(center, radius, a, b, thickness = 0) {
|
|
18
|
+
const { point } = closestPointOnSegment(center, a, b);
|
|
19
|
+
const dx = center[0] - point[0];
|
|
20
|
+
const dy = center[1] - point[1];
|
|
21
|
+
const dist = Math.hypot(dx, dy);
|
|
22
|
+
const reach = radius + thickness;
|
|
23
|
+
if (dist >= reach)
|
|
24
|
+
return null;
|
|
25
|
+
let nx;
|
|
26
|
+
let ny;
|
|
27
|
+
if (dist > 1e-6) {
|
|
28
|
+
nx = dx / dist;
|
|
29
|
+
ny = dy / dist;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
const segLen = Math.hypot(b[0] - a[0], b[1] - a[1]);
|
|
33
|
+
nx = segLen > 1e-6 ? -(b[1] - a[1]) / segLen : 0;
|
|
34
|
+
ny = segLen > 1e-6 ? (b[0] - a[0]) / segLen : 1;
|
|
35
|
+
}
|
|
36
|
+
const depth = reach - dist;
|
|
37
|
+
return {
|
|
38
|
+
normal: [nx, ny],
|
|
39
|
+
depth,
|
|
40
|
+
contact: point,
|
|
41
|
+
resolved: [point[0] + nx * reach, point[1] + ny * reach],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `soil` studio: a crack/moss ground-variation patch authored as a box volume — its XZ footprint is
|
|
3
|
+
* the patch area, its top the ground height (same footprint convention as `grass_field`). Pure config
|
|
4
|
+
* here (crack scale/intensity/color, moss coverage/color, seed + a footprint resolver); the Worley
|
|
5
|
+
* (cellular) crack mask and FBM moss mask, injected via `onBeforeCompile` into a soil-patch material
|
|
6
|
+
* built from the same noise machinery as the terrain detail material, live in the `shell` renderer. An
|
|
7
|
+
* engine environment kind (like water/grass_field) so cracked, mossy ground is slider-authorable in
|
|
8
|
+
* the editor and persisted in `editor.scene.json` — the #815 soil studio.
|
|
9
|
+
*/
|
|
10
|
+
import { type ParamSchema, type SceneKindObject } from "../scene/sceneKinds.js";
|
|
11
|
+
/**
|
|
12
|
+
* The editor volume kind marking a box as a soil crack/moss patch.
|
|
13
|
+
* @capability soil-patch editor-authorable terrain crack/moss material variation
|
|
14
|
+
*/
|
|
15
|
+
export declare const SOIL_KIND = "soil";
|
|
16
|
+
/** Fully-defaulted soil params parsed from a volume's `meta`. */
|
|
17
|
+
export interface SoilRules {
|
|
18
|
+
/** Underlying dirt/soil base tint, hex. */
|
|
19
|
+
baseColor: string;
|
|
20
|
+
/** Worley cell size in meters — larger cells read as bigger cracked-earth plates. */
|
|
21
|
+
crackScale: number;
|
|
22
|
+
/** Crack-line color blended into the cell boundaries, hex. */
|
|
23
|
+
crackColor: string;
|
|
24
|
+
/** Crack blend strength, 0..1. */
|
|
25
|
+
crackIntensity: number;
|
|
26
|
+
/** Moss patch color, hex. */
|
|
27
|
+
mossColor: string;
|
|
28
|
+
/** Fraction of the patch covered by moss, 0..1. */
|
|
29
|
+
mossCoverage: number;
|
|
30
|
+
/** Seed string; same seed reproduces the same crack/moss layout. */
|
|
31
|
+
seed: string;
|
|
32
|
+
}
|
|
33
|
+
/** Soil defaults: sun-cracked dirt with light moss creep. */
|
|
34
|
+
export declare const SOIL_DEFAULTS: SoilRules;
|
|
35
|
+
/** The soil parameter schema — drives the inspector and `meta` parse via the studio seam. */
|
|
36
|
+
export declare const SOIL_SCHEMA: ParamSchema;
|
|
37
|
+
/** Parse a volume's `meta` into fully-defaulted soil rules. @internal */
|
|
38
|
+
export declare function readSoilRules(meta: Record<string, unknown> | undefined): SoilRules;
|
|
39
|
+
/** A resolved soil patch: world-space area center at ground height, footprint size (XZ), and rules. */
|
|
40
|
+
export interface ResolvedSoil {
|
|
41
|
+
center: readonly [number, number, number];
|
|
42
|
+
size: readonly [number, number];
|
|
43
|
+
rules: SoilRules;
|
|
44
|
+
}
|
|
45
|
+
/** Resolve a soil volume into an area footprint (from box half-extents / radius) at the box base. @internal */
|
|
46
|
+
export declare function resolveSoilObject(object: SceneKindObject): ResolvedSoil | null;
|
|
47
|
+
/** Registers the `soil` scene kind (schema + resolver). Called by {@link registerBuiltinSceneKinds}. @internal */
|
|
48
|
+
export declare function registerSoilKind(): void;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `soil` studio: a crack/moss ground-variation patch authored as a box volume — its XZ footprint is
|
|
3
|
+
* the patch area, its top the ground height (same footprint convention as `grass_field`). Pure config
|
|
4
|
+
* here (crack scale/intensity/color, moss coverage/color, seed + a footprint resolver); the Worley
|
|
5
|
+
* (cellular) crack mask and FBM moss mask, injected via `onBeforeCompile` into a soil-patch material
|
|
6
|
+
* built from the same noise machinery as the terrain detail material, live in the `shell` renderer. An
|
|
7
|
+
* engine environment kind (like water/grass_field) so cracked, mossy ground is slider-authorable in
|
|
8
|
+
* the editor and persisted in `editor.scene.json` — the #815 soil studio.
|
|
9
|
+
*/
|
|
10
|
+
import { registerSceneKind } from "../scene/sceneKinds.js";
|
|
11
|
+
/**
|
|
12
|
+
* The editor volume kind marking a box as a soil crack/moss patch.
|
|
13
|
+
* @capability soil-patch editor-authorable terrain crack/moss material variation
|
|
14
|
+
*/
|
|
15
|
+
export const SOIL_KIND = "soil";
|
|
16
|
+
/** Soil defaults: sun-cracked dirt with light moss creep. */
|
|
17
|
+
export const SOIL_DEFAULTS = {
|
|
18
|
+
baseColor: "#8a7256",
|
|
19
|
+
crackScale: 3,
|
|
20
|
+
crackColor: "#42301f",
|
|
21
|
+
crackIntensity: 0.65,
|
|
22
|
+
mossColor: "#5c7a3a",
|
|
23
|
+
mossCoverage: 0.22,
|
|
24
|
+
seed: "",
|
|
25
|
+
};
|
|
26
|
+
/** The soil parameter schema — drives the inspector and `meta` parse via the studio seam. */
|
|
27
|
+
export const SOIL_SCHEMA = {
|
|
28
|
+
fields: [
|
|
29
|
+
{ type: "color", key: "baseColor", label: "base color", default: SOIL_DEFAULTS.baseColor },
|
|
30
|
+
{ type: "range", key: "crackScale", label: "crack scale", min: 0.5, max: 12, step: 0.25, default: SOIL_DEFAULTS.crackScale, unit: "m" },
|
|
31
|
+
{ type: "color", key: "crackColor", label: "crack color", default: SOIL_DEFAULTS.crackColor },
|
|
32
|
+
{ type: "range", key: "crackIntensity", label: "crack intensity", min: 0, max: 1, step: 0.02, default: SOIL_DEFAULTS.crackIntensity },
|
|
33
|
+
{ type: "color", key: "mossColor", label: "moss color", default: SOIL_DEFAULTS.mossColor },
|
|
34
|
+
{ type: "range", key: "mossCoverage", label: "moss coverage", min: 0, max: 1, step: 0.02, default: SOIL_DEFAULTS.mossCoverage },
|
|
35
|
+
{ type: "seed", key: "seed", label: "seed", default: "" },
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
function metaNumber(meta, key, fallback) {
|
|
39
|
+
const value = meta?.[key];
|
|
40
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
41
|
+
}
|
|
42
|
+
function metaString(meta, key, fallback) {
|
|
43
|
+
const value = meta?.[key];
|
|
44
|
+
return typeof value === "string" ? value : fallback;
|
|
45
|
+
}
|
|
46
|
+
/** Parse a volume's `meta` into fully-defaulted soil rules. @internal */
|
|
47
|
+
export function readSoilRules(meta) {
|
|
48
|
+
return {
|
|
49
|
+
baseColor: metaString(meta, "baseColor", SOIL_DEFAULTS.baseColor),
|
|
50
|
+
crackScale: Math.max(0.1, metaNumber(meta, "crackScale", SOIL_DEFAULTS.crackScale)),
|
|
51
|
+
crackColor: metaString(meta, "crackColor", SOIL_DEFAULTS.crackColor),
|
|
52
|
+
crackIntensity: Math.min(1, Math.max(0, metaNumber(meta, "crackIntensity", SOIL_DEFAULTS.crackIntensity))),
|
|
53
|
+
mossColor: metaString(meta, "mossColor", SOIL_DEFAULTS.mossColor),
|
|
54
|
+
mossCoverage: Math.min(1, Math.max(0, metaNumber(meta, "mossCoverage", SOIL_DEFAULTS.mossCoverage))),
|
|
55
|
+
seed: metaString(meta, "seed", SOIL_DEFAULTS.seed),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function axis(value) {
|
|
59
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
60
|
+
}
|
|
61
|
+
/** Resolve a soil volume into an area footprint (from box half-extents / radius) at the box base. @internal */
|
|
62
|
+
export function resolveSoilObject(object) {
|
|
63
|
+
const center = object.center ?? object.position;
|
|
64
|
+
if (center === undefined)
|
|
65
|
+
return null;
|
|
66
|
+
const he = object.halfExtents;
|
|
67
|
+
const radius = object.radius;
|
|
68
|
+
const halfX = axis(he?.x) ?? radius ?? 6;
|
|
69
|
+
const halfZ = axis(he?.z) ?? radius ?? 6;
|
|
70
|
+
const halfY = axis(he?.y) ?? 0;
|
|
71
|
+
return {
|
|
72
|
+
center: [center.x, center.y - halfY, center.z],
|
|
73
|
+
size: [Math.max(0.5, halfX * 2), Math.max(0.5, halfZ * 2)],
|
|
74
|
+
rules: readSoilRules(object.meta),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/** Registers the `soil` scene kind (schema + resolver). Called by {@link registerBuiltinSceneKinds}. @internal */
|
|
78
|
+
export function registerSoilKind() {
|
|
79
|
+
registerSceneKind({
|
|
80
|
+
kind: SOIL_KIND,
|
|
81
|
+
target: "volume",
|
|
82
|
+
label: "Soil patch",
|
|
83
|
+
addCategory: "Studios",
|
|
84
|
+
accent: "#8a6d4a",
|
|
85
|
+
schema: SOIL_SCHEMA,
|
|
86
|
+
resolve: (object) => resolveSoilObject(object),
|
|
87
|
+
note: (object) => {
|
|
88
|
+
const resolved = resolveSoilObject(object);
|
|
89
|
+
return resolved === null ? "Give the volume a box footprint." : `${Math.round(resolved.size[0])}×${Math.round(resolved.size[1])} m patch`;
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
}
|