@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
package/dist/world/terraform.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { FLAT_FIELD, withNormal } from "./terrain.js";
|
|
1
|
+
import { fractalNoise, FLAT_FIELD, withNormal } from "./terrain.js";
|
|
2
|
+
/** Smooth/linear/hard brush weight for a sample `distance` from the brush center. */
|
|
2
3
|
export function brushWeight(distance, radius, falloff) {
|
|
3
4
|
if (radius <= 0)
|
|
4
5
|
return distance <= 0 ? 1 : 0;
|
|
@@ -15,6 +16,13 @@ export function brushWeight(distance, radius, falloff) {
|
|
|
15
16
|
function clampIndex(value, max) {
|
|
16
17
|
return value < 0 ? 0 : value > max ? max : value;
|
|
17
18
|
}
|
|
19
|
+
function clampRange(value, min, max) {
|
|
20
|
+
if (min !== undefined && value < min)
|
|
21
|
+
return min;
|
|
22
|
+
if (max !== undefined && value > max)
|
|
23
|
+
return max;
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
18
26
|
export function createEditableTerrain(config) {
|
|
19
27
|
const base = config.base ?? FLAT_FIELD;
|
|
20
28
|
const bounds = config.bounds;
|
|
@@ -27,12 +35,22 @@ export function createEditableTerrain(config) {
|
|
|
27
35
|
const vertsZ = rows + 1;
|
|
28
36
|
const offsets = new Float32Array(vertsX * vertsZ);
|
|
29
37
|
const surfaces = new Array(cols * rows).fill(null);
|
|
38
|
+
const cellCount = cols * rows;
|
|
39
|
+
let layers = [];
|
|
40
|
+
// Lazily allocated: null until the first blend, so single-layer terrains carry no weight buffer.
|
|
41
|
+
let weights = null;
|
|
30
42
|
function gridX(x) {
|
|
31
43
|
return ((x - bounds.minX) / spanX) * cols;
|
|
32
44
|
}
|
|
33
45
|
function gridZ(z) {
|
|
34
46
|
return ((z - bounds.minZ) / spanZ) * rows;
|
|
35
47
|
}
|
|
48
|
+
function vertexWorldX(gx) {
|
|
49
|
+
return bounds.minX + (gx / cols) * spanX;
|
|
50
|
+
}
|
|
51
|
+
function vertexWorldZ(gz) {
|
|
52
|
+
return bounds.minZ + (gz / rows) * spanZ;
|
|
53
|
+
}
|
|
36
54
|
function heightOffsetAt(x, z) {
|
|
37
55
|
const fx = gridX(x);
|
|
38
56
|
const fz = gridZ(z);
|
|
@@ -52,10 +70,55 @@ export function createEditableTerrain(config) {
|
|
|
52
70
|
}
|
|
53
71
|
const sampleHeight = (x, z) => base.sampleHeight(x, z) + heightOffsetAt(x, z);
|
|
54
72
|
const sampleNormal = withNormal(sampleHeight);
|
|
55
|
-
function
|
|
73
|
+
function totalHeightAtVertex(gx, gz) {
|
|
74
|
+
const wx = vertexWorldX(gx);
|
|
75
|
+
const wz = vertexWorldZ(gz);
|
|
76
|
+
return base.sampleHeight(wx, wz) + offsets[gz * vertsX + gx];
|
|
77
|
+
}
|
|
78
|
+
function neighborAverageHeight(gx, gz) {
|
|
79
|
+
let sum = 0;
|
|
80
|
+
let count = 0;
|
|
81
|
+
for (const [dx, dz] of [
|
|
82
|
+
[-1, 0],
|
|
83
|
+
[1, 0],
|
|
84
|
+
[0, -1],
|
|
85
|
+
[0, 1],
|
|
86
|
+
]) {
|
|
87
|
+
const nx = gx + dx;
|
|
88
|
+
const nz = gz + dz;
|
|
89
|
+
if (nx < 0 || nx >= vertsX || nz < 0 || nz >= vertsZ)
|
|
90
|
+
continue;
|
|
91
|
+
sum += totalHeightAtVertex(nx, nz);
|
|
92
|
+
count += 1;
|
|
93
|
+
}
|
|
94
|
+
return count === 0 ? totalHeightAtVertex(gx, gz) : sum / count;
|
|
95
|
+
}
|
|
96
|
+
function writeOffset(index, next, limit, wx, wz, record) {
|
|
97
|
+
let value = next;
|
|
98
|
+
if (limit !== undefined) {
|
|
99
|
+
value = clampRange(base.sampleHeight(wx, wz) + value, limit.min, limit.max) - base.sampleHeight(wx, wz);
|
|
100
|
+
}
|
|
101
|
+
const before = offsets[index];
|
|
102
|
+
if (value === before)
|
|
103
|
+
return false;
|
|
104
|
+
offsets[index] = value;
|
|
105
|
+
record?.(index, before, value);
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
function editHeight(edit, record) {
|
|
56
109
|
const falloff = edit.falloff ?? "smooth";
|
|
110
|
+
const shape = edit.shape ?? "circle";
|
|
57
111
|
const strength = edit.strength ?? 1;
|
|
58
|
-
const
|
|
112
|
+
const limit = edit.heightLimit;
|
|
113
|
+
const flattenTarget = edit.mode === "flatten" ? edit.target ?? sampleHeight(edit.center[0], edit.center[1]) : 0;
|
|
114
|
+
const noiseConfig = {
|
|
115
|
+
seed: edit.seed ?? 1337,
|
|
116
|
+
frequency: edit.frequency ?? 0.15,
|
|
117
|
+
octaves: 3,
|
|
118
|
+
lacunarity: 2,
|
|
119
|
+
persistence: 0.5,
|
|
120
|
+
ridged: false,
|
|
121
|
+
};
|
|
59
122
|
const rCells = Math.ceil(edit.radius / cellSize) + 1;
|
|
60
123
|
const cx = gridX(edit.center[0]);
|
|
61
124
|
const cz = gridZ(edit.center[1]);
|
|
@@ -68,28 +131,96 @@ export function createEditableTerrain(config) {
|
|
|
68
131
|
const gx = Math.round(cx) + ix;
|
|
69
132
|
if (gx < 0 || gx >= vertsX)
|
|
70
133
|
continue;
|
|
71
|
-
const wx =
|
|
72
|
-
const wz =
|
|
73
|
-
const
|
|
134
|
+
const wx = vertexWorldX(gx);
|
|
135
|
+
const wz = vertexWorldZ(gz);
|
|
136
|
+
const dx = wx - edit.center[0];
|
|
137
|
+
const dz = wz - edit.center[1];
|
|
138
|
+
const dist = shape === "square" ? Math.max(Math.abs(dx), Math.abs(dz)) : Math.hypot(dx, dz);
|
|
74
139
|
const weight = brushWeight(dist, edit.radius, falloff);
|
|
75
140
|
if (weight <= 0)
|
|
76
141
|
continue;
|
|
77
142
|
const index = gz * vertsX + gx;
|
|
143
|
+
const current = offsets[index];
|
|
144
|
+
let next = current;
|
|
78
145
|
if (edit.mode === "raise")
|
|
79
|
-
|
|
146
|
+
next = current + strength * weight;
|
|
80
147
|
else if (edit.mode === "lower")
|
|
81
|
-
|
|
148
|
+
next = current - strength * weight;
|
|
149
|
+
else if (edit.mode === "noise")
|
|
150
|
+
next = current + fractalNoise(wx, wz, noiseConfig) * strength * weight;
|
|
151
|
+
else if (edit.mode === "smooth") {
|
|
152
|
+
const target = neighborAverageHeight(gx, gz);
|
|
153
|
+
const currentTotal = base.sampleHeight(wx, wz) + current;
|
|
154
|
+
next = current + (target - currentTotal) * weight * strength;
|
|
155
|
+
}
|
|
82
156
|
else {
|
|
83
|
-
const
|
|
84
|
-
|
|
157
|
+
const currentTotal = base.sampleHeight(wx, wz) + current;
|
|
158
|
+
next = current + (flattenTarget - currentTotal) * weight * strength;
|
|
85
159
|
}
|
|
86
|
-
|
|
160
|
+
if (writeOffset(index, next, limit, wx, wz, record))
|
|
161
|
+
changed += 1;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return changed;
|
|
165
|
+
}
|
|
166
|
+
function editRamp(edit, record) {
|
|
167
|
+
const to = edit.to;
|
|
168
|
+
if (to === undefined)
|
|
169
|
+
return 0;
|
|
170
|
+
const falloff = edit.falloff ?? "smooth";
|
|
171
|
+
const strength = edit.strength ?? 1;
|
|
172
|
+
const limit = edit.heightLimit;
|
|
173
|
+
const [ax, az] = edit.center;
|
|
174
|
+
const [bx, bz] = to;
|
|
175
|
+
const hFrom = edit.target ?? sampleHeight(ax, az);
|
|
176
|
+
const hTo = edit.targetTo ?? sampleHeight(bx, bz);
|
|
177
|
+
const segX = bx - ax;
|
|
178
|
+
const segZ = bz - az;
|
|
179
|
+
const segLenSq = segX * segX + segZ * segZ || 1;
|
|
180
|
+
const minGx = clampIndex(Math.floor(gridX(Math.min(ax, bx) - edit.radius)), vertsX - 1);
|
|
181
|
+
const maxGx = clampIndex(Math.ceil(gridX(Math.max(ax, bx) + edit.radius)), vertsX - 1);
|
|
182
|
+
const minGz = clampIndex(Math.floor(gridZ(Math.min(az, bz) - edit.radius)), vertsZ - 1);
|
|
183
|
+
const maxGz = clampIndex(Math.ceil(gridZ(Math.max(az, bz) + edit.radius)), vertsZ - 1);
|
|
184
|
+
let changed = 0;
|
|
185
|
+
for (let gz = minGz; gz <= maxGz; gz += 1) {
|
|
186
|
+
for (let gx = minGx; gx <= maxGx; gx += 1) {
|
|
187
|
+
const wx = vertexWorldX(gx);
|
|
188
|
+
const wz = vertexWorldZ(gz);
|
|
189
|
+
let t = ((wx - ax) * segX + (wz - az) * segZ) / segLenSq;
|
|
190
|
+
t = t < 0 ? 0 : t > 1 ? 1 : t;
|
|
191
|
+
const px = ax + segX * t;
|
|
192
|
+
const pz = az + segZ * t;
|
|
193
|
+
const weight = brushWeight(Math.hypot(wx - px, wz - pz), edit.radius, falloff);
|
|
194
|
+
if (weight <= 0)
|
|
195
|
+
continue;
|
|
196
|
+
const index = gz * vertsX + gx;
|
|
197
|
+
const current = offsets[index];
|
|
198
|
+
const target = hFrom + (hTo - hFrom) * t;
|
|
199
|
+
const currentTotal = base.sampleHeight(wx, wz) + current;
|
|
200
|
+
const next = current + (target - currentTotal) * weight * strength;
|
|
201
|
+
if (writeOffset(index, next, limit, wx, wz, record))
|
|
202
|
+
changed += 1;
|
|
87
203
|
}
|
|
88
204
|
}
|
|
89
205
|
return changed;
|
|
90
206
|
}
|
|
91
|
-
function
|
|
207
|
+
function cellWorldX(gx) {
|
|
208
|
+
return bounds.minX + ((gx + 0.5) / cols) * spanX;
|
|
209
|
+
}
|
|
210
|
+
function cellWorldZ(gz) {
|
|
211
|
+
return bounds.minZ + ((gz + 0.5) / rows) * spanZ;
|
|
212
|
+
}
|
|
213
|
+
function writeSurface(index, surface, record) {
|
|
214
|
+
const before = surfaces[index] ?? null;
|
|
215
|
+
if (before === surface)
|
|
216
|
+
return false;
|
|
217
|
+
surfaces[index] = surface;
|
|
218
|
+
record?.(index, before, surface);
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
function paintSurface(edit, record) {
|
|
92
222
|
const surface = edit.surface ?? null;
|
|
223
|
+
const shape = edit.shape ?? "circle";
|
|
93
224
|
const rCells = Math.ceil(edit.radius / cellSize) + 1;
|
|
94
225
|
const cx = gridX(edit.center[0]);
|
|
95
226
|
const cz = gridZ(edit.center[1]);
|
|
@@ -102,36 +233,283 @@ export function createEditableTerrain(config) {
|
|
|
102
233
|
const gx = Math.floor(cx) + ix;
|
|
103
234
|
if (gx < 0 || gx >= cols)
|
|
104
235
|
continue;
|
|
105
|
-
const
|
|
106
|
-
const
|
|
107
|
-
|
|
236
|
+
const dx = cellWorldX(gx) - edit.center[0];
|
|
237
|
+
const dz = cellWorldZ(gz) - edit.center[1];
|
|
238
|
+
const dist = shape === "square" ? Math.max(Math.abs(dx), Math.abs(dz)) : Math.hypot(dx, dz);
|
|
239
|
+
if (dist > edit.radius)
|
|
108
240
|
continue;
|
|
109
|
-
|
|
110
|
-
|
|
241
|
+
if (writeSurface(gz * cols + gx, surface, record))
|
|
242
|
+
painted += 1;
|
|
111
243
|
}
|
|
112
244
|
}
|
|
113
245
|
return painted;
|
|
114
246
|
}
|
|
115
|
-
function
|
|
247
|
+
function localNormal(wx, wz) {
|
|
248
|
+
const eps = Math.max(cellSize, 0.5);
|
|
249
|
+
const hx = sampleHeight(wx + eps, wz) - sampleHeight(wx - eps, wz);
|
|
250
|
+
const hz = sampleHeight(wx, wz + eps) - sampleHeight(wx, wz - eps);
|
|
251
|
+
const nx = -hx;
|
|
252
|
+
const nz = -hz;
|
|
253
|
+
const ny = 2 * eps;
|
|
254
|
+
const length = Math.hypot(nx, ny, nz) || 1;
|
|
255
|
+
return [nx / length, ny / length, nz / length];
|
|
256
|
+
}
|
|
257
|
+
function autoPaint(rule, record) {
|
|
258
|
+
let painted = 0;
|
|
259
|
+
for (let gz = 0; gz < rows; gz += 1) {
|
|
260
|
+
for (let gx = 0; gx < cols; gx += 1) {
|
|
261
|
+
const wx = cellWorldX(gx);
|
|
262
|
+
const wz = cellWorldZ(gz);
|
|
263
|
+
const height = sampleHeight(wx, wz);
|
|
264
|
+
if (rule.minHeight !== undefined && height < rule.minHeight)
|
|
265
|
+
continue;
|
|
266
|
+
if (rule.maxHeight !== undefined && height > rule.maxHeight)
|
|
267
|
+
continue;
|
|
268
|
+
if (rule.minSlope !== undefined || rule.maxSlope !== undefined) {
|
|
269
|
+
const [nx, ny, nz] = localNormal(wx, wz);
|
|
270
|
+
const slope = Math.hypot(nx, nz) / Math.max(ny, 1e-9);
|
|
271
|
+
if (rule.minSlope !== undefined && slope < rule.minSlope)
|
|
272
|
+
continue;
|
|
273
|
+
if (rule.maxSlope !== undefined && slope > rule.maxSlope)
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (writeSurface(gz * cols + gx, rule.surface, record))
|
|
277
|
+
painted += 1;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return painted;
|
|
281
|
+
}
|
|
282
|
+
function fillSurface(surface, record) {
|
|
283
|
+
let painted = 0;
|
|
284
|
+
for (let index = 0; index < surfaces.length; index += 1) {
|
|
285
|
+
if (writeSurface(index, surface, record))
|
|
286
|
+
painted += 1;
|
|
287
|
+
}
|
|
288
|
+
return painted;
|
|
289
|
+
}
|
|
290
|
+
function cellIndexAt(x, z) {
|
|
116
291
|
const gx = Math.floor(gridX(x));
|
|
117
292
|
const gz = Math.floor(gridZ(z));
|
|
118
293
|
if (gx < 0 || gx >= cols || gz < 0 || gz >= rows)
|
|
294
|
+
return -1;
|
|
295
|
+
return gz * cols + gx;
|
|
296
|
+
}
|
|
297
|
+
function dominantLayerAt(cell) {
|
|
298
|
+
if (weights === null || layers.length === 0)
|
|
299
|
+
return -1;
|
|
300
|
+
const base = cell * layers.length;
|
|
301
|
+
let best = -1;
|
|
302
|
+
let bestWeight = 0;
|
|
303
|
+
for (let l = 0; l < layers.length; l += 1) {
|
|
304
|
+
const w = weights[base + l];
|
|
305
|
+
if (w > bestWeight) {
|
|
306
|
+
bestWeight = w;
|
|
307
|
+
best = l;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return best;
|
|
311
|
+
}
|
|
312
|
+
function surfaceAt(x, z) {
|
|
313
|
+
const cell = cellIndexAt(x, z);
|
|
314
|
+
if (cell < 0)
|
|
119
315
|
return null;
|
|
120
|
-
|
|
316
|
+
const dominant = dominantLayerAt(cell);
|
|
317
|
+
if (dominant >= 0)
|
|
318
|
+
return layers[dominant].surface;
|
|
319
|
+
return surfaces[cell] ?? null;
|
|
320
|
+
}
|
|
321
|
+
function weightsAt(x, z) {
|
|
322
|
+
const cell = cellIndexAt(x, z);
|
|
323
|
+
if (cell < 0 || weights === null || layers.length === 0)
|
|
324
|
+
return [];
|
|
325
|
+
const base = cell * layers.length;
|
|
326
|
+
return Array.from({ length: layers.length }, (_, l) => weights[base + l]);
|
|
327
|
+
}
|
|
328
|
+
/** Materializes the weight buffer, seeding each cell to weight 1 on its current dominant surface. */
|
|
329
|
+
function ensureWeights() {
|
|
330
|
+
if (weights !== null)
|
|
331
|
+
return weights;
|
|
332
|
+
const buffer = new Float32Array(cellCount * Math.max(1, layers.length));
|
|
333
|
+
if (layers.length > 0) {
|
|
334
|
+
for (let cell = 0; cell < cellCount; cell += 1) {
|
|
335
|
+
const surface = surfaces[cell];
|
|
336
|
+
const layer = surface === null ? -1 : layers.findIndex((entry) => entry.surface === surface);
|
|
337
|
+
if (layer >= 0)
|
|
338
|
+
buffer[cell * layers.length + layer] = 1;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
weights = buffer;
|
|
342
|
+
return buffer;
|
|
343
|
+
}
|
|
344
|
+
function blendCell(cell, target, amount, record) {
|
|
345
|
+
const buffer = ensureWeights();
|
|
346
|
+
const L = layers.length;
|
|
347
|
+
const base = cell * L;
|
|
348
|
+
const prevTarget = buffer[base + target];
|
|
349
|
+
const nextTarget = prevTarget + (1 - prevTarget) * amount;
|
|
350
|
+
if (nextTarget === prevTarget)
|
|
351
|
+
return false;
|
|
352
|
+
let othersSum = 0;
|
|
353
|
+
for (let l = 0; l < L; l += 1)
|
|
354
|
+
if (l !== target)
|
|
355
|
+
othersSum += buffer[base + l];
|
|
356
|
+
const remainder = 1 - nextTarget;
|
|
357
|
+
for (let l = 0; l < L; l += 1) {
|
|
358
|
+
const prev = buffer[base + l];
|
|
359
|
+
const next = l === target ? nextTarget : othersSum > 0 ? (prev / othersSum) * remainder : 0;
|
|
360
|
+
if (next !== prev) {
|
|
361
|
+
buffer[base + l] = next;
|
|
362
|
+
record?.(base + l, prev, next);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
function blendPaint(edit, record) {
|
|
368
|
+
const surface = edit.surface;
|
|
369
|
+
if (surface === undefined || layers.length === 0)
|
|
370
|
+
return 0;
|
|
371
|
+
const target = layers.findIndex((entry) => entry.surface === surface);
|
|
372
|
+
if (target < 0)
|
|
373
|
+
return 0;
|
|
374
|
+
const falloff = edit.falloff ?? "smooth";
|
|
375
|
+
const shape = edit.shape ?? "circle";
|
|
376
|
+
const strength = edit.strength ?? 1;
|
|
377
|
+
const rCells = Math.ceil(edit.radius / cellSize) + 1;
|
|
378
|
+
const cx = gridX(edit.center[0]);
|
|
379
|
+
const cz = gridZ(edit.center[1]);
|
|
380
|
+
let painted = 0;
|
|
381
|
+
for (let iz = -rCells; iz <= rCells; iz += 1) {
|
|
382
|
+
const gz = Math.floor(cz) + iz;
|
|
383
|
+
if (gz < 0 || gz >= rows)
|
|
384
|
+
continue;
|
|
385
|
+
for (let ix = -rCells; ix <= rCells; ix += 1) {
|
|
386
|
+
const gx = Math.floor(cx) + ix;
|
|
387
|
+
if (gx < 0 || gx >= cols)
|
|
388
|
+
continue;
|
|
389
|
+
const dx = cellWorldX(gx) - edit.center[0];
|
|
390
|
+
const dz = cellWorldZ(gz) - edit.center[1];
|
|
391
|
+
const dist = shape === "square" ? Math.max(Math.abs(dx), Math.abs(dz)) : Math.hypot(dx, dz);
|
|
392
|
+
const weight = brushWeight(dist, edit.radius, falloff);
|
|
393
|
+
if (weight <= 0)
|
|
394
|
+
continue;
|
|
395
|
+
if (blendCell(gz * cols + gx, target, weight * strength, record))
|
|
396
|
+
painted += 1;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return painted;
|
|
400
|
+
}
|
|
401
|
+
function runHeightEdit(edit, record) {
|
|
402
|
+
if (edit.mode === "ramp")
|
|
403
|
+
return editRamp(edit, record);
|
|
404
|
+
return editHeight(edit, record);
|
|
405
|
+
}
|
|
406
|
+
function surfaceDeltaFrom(run) {
|
|
407
|
+
const indices = [];
|
|
408
|
+
const before = [];
|
|
409
|
+
const after = [];
|
|
410
|
+
run((index, prev, next) => {
|
|
411
|
+
indices.push(index);
|
|
412
|
+
before.push(prev);
|
|
413
|
+
after.push(next);
|
|
414
|
+
});
|
|
415
|
+
return { indices, before, after };
|
|
121
416
|
}
|
|
122
417
|
const field = {
|
|
418
|
+
cols,
|
|
419
|
+
rows,
|
|
420
|
+
cellSize,
|
|
123
421
|
sampleHeight,
|
|
124
422
|
sampleNormal,
|
|
125
423
|
...(base.bounds === undefined ? {} : { bounds: base.bounds }),
|
|
126
424
|
...(base.waterLevel === undefined ? {} : { waterLevel: base.waterLevel }),
|
|
127
425
|
apply(edit) {
|
|
128
|
-
return edit.mode === "paint" ? paintSurface(edit) :
|
|
426
|
+
return edit.mode === "paint" ? paintSurface(edit, null) : runHeightEdit(edit, null);
|
|
427
|
+
},
|
|
428
|
+
applyRecording(edit, record) {
|
|
429
|
+
return runHeightEdit(edit, record);
|
|
430
|
+
},
|
|
431
|
+
editDelta(edit) {
|
|
432
|
+
const indices = [];
|
|
433
|
+
const before = [];
|
|
434
|
+
const after = [];
|
|
435
|
+
runHeightEdit(edit, (index, prev, next) => {
|
|
436
|
+
indices.push(index);
|
|
437
|
+
before.push(prev);
|
|
438
|
+
after.push(next);
|
|
439
|
+
});
|
|
440
|
+
return { indices, before, after };
|
|
441
|
+
},
|
|
442
|
+
applyDelta(delta) {
|
|
443
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
444
|
+
offsets[delta.indices[i]] = delta.after[i];
|
|
445
|
+
},
|
|
446
|
+
revertDelta(delta) {
|
|
447
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
448
|
+
offsets[delta.indices[i]] = delta.before[i];
|
|
449
|
+
},
|
|
450
|
+
paintRecording(edit, record) {
|
|
451
|
+
return paintSurface(edit, record);
|
|
452
|
+
},
|
|
453
|
+
paintDelta(edit) {
|
|
454
|
+
return surfaceDeltaFrom((record) => paintSurface(edit, record));
|
|
455
|
+
},
|
|
456
|
+
fillSurfaceDelta(surface) {
|
|
457
|
+
return surfaceDeltaFrom((record) => fillSurface(surface, record));
|
|
458
|
+
},
|
|
459
|
+
autoPaintDelta(rule) {
|
|
460
|
+
return surfaceDeltaFrom((record) => autoPaint(rule, record));
|
|
461
|
+
},
|
|
462
|
+
applySurfaceDelta(delta) {
|
|
463
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
464
|
+
surfaces[delta.indices[i]] = delta.after[i] ?? null;
|
|
465
|
+
},
|
|
466
|
+
revertSurfaceDelta(delta) {
|
|
467
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
468
|
+
surfaces[delta.indices[i]] = delta.before[i] ?? null;
|
|
129
469
|
},
|
|
470
|
+
get layers() {
|
|
471
|
+
return layers;
|
|
472
|
+
},
|
|
473
|
+
setLayers(next) {
|
|
474
|
+
layers = next.map((layer) => ({ ...layer }));
|
|
475
|
+
// Layer count changed shape: drop the weight buffer, blends re-seed from surfaces on demand.
|
|
476
|
+
weights = null;
|
|
477
|
+
},
|
|
478
|
+
blendRecording(edit, record) {
|
|
479
|
+
return blendPaint(edit, record);
|
|
480
|
+
},
|
|
481
|
+
blendPaintDelta(edit) {
|
|
482
|
+
const indices = [];
|
|
483
|
+
const before = [];
|
|
484
|
+
const after = [];
|
|
485
|
+
blendPaint(edit, (index, prev, next) => {
|
|
486
|
+
indices.push(index);
|
|
487
|
+
before.push(prev);
|
|
488
|
+
after.push(next);
|
|
489
|
+
});
|
|
490
|
+
return { layerCount: layers.length, indices, before, after };
|
|
491
|
+
},
|
|
492
|
+
applyWeightDelta(delta) {
|
|
493
|
+
if (delta.layerCount !== layers.length)
|
|
494
|
+
return;
|
|
495
|
+
const buffer = ensureWeights();
|
|
496
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
497
|
+
buffer[delta.indices[i]] = delta.after[i];
|
|
498
|
+
},
|
|
499
|
+
revertWeightDelta(delta) {
|
|
500
|
+
if (delta.layerCount !== layers.length)
|
|
501
|
+
return;
|
|
502
|
+
const buffer = ensureWeights();
|
|
503
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
504
|
+
buffer[delta.indices[i]] = delta.before[i];
|
|
505
|
+
},
|
|
506
|
+
weightsAt,
|
|
130
507
|
heightOffsetAt,
|
|
131
508
|
surfaceAt,
|
|
132
509
|
reset() {
|
|
133
510
|
offsets.fill(0);
|
|
134
511
|
surfaces.fill(null);
|
|
512
|
+
weights = null;
|
|
135
513
|
},
|
|
136
514
|
snapshot() {
|
|
137
515
|
return {
|
|
@@ -141,6 +519,8 @@ export function createEditableTerrain(config) {
|
|
|
141
519
|
rows,
|
|
142
520
|
offsets: Array.from(offsets),
|
|
143
521
|
surfaces: surfaces.slice(),
|
|
522
|
+
...(layers.length === 0 ? {} : { layers: layers.map((layer) => ({ ...layer })) }),
|
|
523
|
+
...(weights === null ? {} : { weights: Array.from(weights) }),
|
|
144
524
|
};
|
|
145
525
|
},
|
|
146
526
|
restore(snapshot) {
|
|
@@ -150,10 +530,265 @@ export function createEditableTerrain(config) {
|
|
|
150
530
|
const surfaceCount = Math.min(surfaces.length, snapshot.surfaces.length);
|
|
151
531
|
for (let index = 0; index < surfaceCount; index += 1)
|
|
152
532
|
surfaces[index] = snapshot.surfaces[index] ?? null;
|
|
533
|
+
layers = (snapshot.layers ?? []).map((layer) => ({ ...layer }));
|
|
534
|
+
if (snapshot.weights === undefined || layers.length === 0) {
|
|
535
|
+
weights = null;
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
const buffer = new Float32Array(cellCount * layers.length);
|
|
539
|
+
const copy = Math.min(buffer.length, snapshot.weights.length);
|
|
540
|
+
for (let index = 0; index < copy; index += 1)
|
|
541
|
+
buffer[index] = snapshot.weights[index];
|
|
542
|
+
weights = buffer;
|
|
543
|
+
}
|
|
153
544
|
},
|
|
154
545
|
};
|
|
155
546
|
return field;
|
|
156
547
|
}
|
|
548
|
+
/** Opens a stroke recorder over `terrain`; stamp edits into it, then read one net delta. */
|
|
549
|
+
export function beginTerraformStroke(terrain) {
|
|
550
|
+
const first = new Map();
|
|
551
|
+
const latest = new Map();
|
|
552
|
+
return {
|
|
553
|
+
stamp(edit) {
|
|
554
|
+
return terrain.applyRecording(edit, (index, before, after) => {
|
|
555
|
+
if (!first.has(index))
|
|
556
|
+
first.set(index, before);
|
|
557
|
+
latest.set(index, after);
|
|
558
|
+
});
|
|
559
|
+
},
|
|
560
|
+
delta() {
|
|
561
|
+
const indices = [];
|
|
562
|
+
const before = [];
|
|
563
|
+
const after = [];
|
|
564
|
+
for (const [index, next] of latest) {
|
|
565
|
+
const prev = first.get(index);
|
|
566
|
+
if (next === prev)
|
|
567
|
+
continue;
|
|
568
|
+
indices.push(index);
|
|
569
|
+
before.push(prev);
|
|
570
|
+
after.push(next);
|
|
571
|
+
}
|
|
572
|
+
return { indices, before, after };
|
|
573
|
+
},
|
|
574
|
+
isEmpty() {
|
|
575
|
+
return latest.size === 0;
|
|
576
|
+
},
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
/** Opens a paint-stroke recorder over `terrain`; stamp paint edits into it, then read one net delta. */
|
|
580
|
+
export function beginSurfaceStroke(terrain) {
|
|
581
|
+
const first = new Map();
|
|
582
|
+
const latest = new Map();
|
|
583
|
+
return {
|
|
584
|
+
stamp(edit) {
|
|
585
|
+
return terrain.paintRecording(edit, (index, before, after) => {
|
|
586
|
+
if (!first.has(index))
|
|
587
|
+
first.set(index, before);
|
|
588
|
+
latest.set(index, after);
|
|
589
|
+
});
|
|
590
|
+
},
|
|
591
|
+
delta() {
|
|
592
|
+
const indices = [];
|
|
593
|
+
const before = [];
|
|
594
|
+
const after = [];
|
|
595
|
+
for (const [index, next] of latest) {
|
|
596
|
+
const prev = first.get(index) ?? null;
|
|
597
|
+
if (next === prev)
|
|
598
|
+
continue;
|
|
599
|
+
indices.push(index);
|
|
600
|
+
before.push(prev);
|
|
601
|
+
after.push(next);
|
|
602
|
+
}
|
|
603
|
+
return { indices, before, after };
|
|
604
|
+
},
|
|
605
|
+
isEmpty() {
|
|
606
|
+
return latest.size === 0;
|
|
607
|
+
},
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
/** Returns a new snapshot with a surface delta's `after` ids applied (copy-on-write). */
|
|
611
|
+
export function applySurfaceDeltaToSnapshot(snapshot, delta) {
|
|
612
|
+
const surfaces = snapshot.surfaces.slice();
|
|
613
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
614
|
+
surfaces[delta.indices[i]] = delta.after[i] ?? null;
|
|
615
|
+
return { ...snapshot, surfaces };
|
|
616
|
+
}
|
|
617
|
+
/** Returns a new snapshot with a surface delta's `before` ids restored (copy-on-write undo). */
|
|
618
|
+
export function revertSurfaceDeltaFromSnapshot(snapshot, delta) {
|
|
619
|
+
const surfaces = snapshot.surfaces.slice();
|
|
620
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
621
|
+
surfaces[delta.indices[i]] = delta.before[i] ?? null;
|
|
622
|
+
return { ...snapshot, surfaces };
|
|
623
|
+
}
|
|
624
|
+
/** The distinct non-null surface ids painted in a snapshot, in first-seen order. */
|
|
625
|
+
function distinctSurfaces(snapshot) {
|
|
626
|
+
const seen = new Set();
|
|
627
|
+
const out = [];
|
|
628
|
+
for (const surface of snapshot.surfaces) {
|
|
629
|
+
if (surface !== null && !seen.has(surface)) {
|
|
630
|
+
seen.add(surface);
|
|
631
|
+
out.push(surface);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return out;
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Upgrades a pre-2.0 snapshot in place-safe (copy-on-write) form: derives a {@link TerrainMaterialLayer}
|
|
638
|
+
* stack from the distinct painted surfaces (first-seen order, default params) when none exists.
|
|
639
|
+
* Leaves the lazy `weights` buffer absent — a single-layer terrain stays compact until blended.
|
|
640
|
+
* Idempotent: a snapshot that already carries `layers` is returned unchanged.
|
|
641
|
+
*/
|
|
642
|
+
export function migrateTerrainSnapshot(snapshot) {
|
|
643
|
+
if (snapshot.layers !== undefined)
|
|
644
|
+
return snapshot;
|
|
645
|
+
const layers = distinctSurfaces(snapshot).map((surface) => ({
|
|
646
|
+
id: surface,
|
|
647
|
+
surface,
|
|
648
|
+
roughness: 0.9,
|
|
649
|
+
tiling: 8,
|
|
650
|
+
}));
|
|
651
|
+
return { ...snapshot, layers };
|
|
652
|
+
}
|
|
653
|
+
/** Materializes a snapshot's blend weights, seeding weight 1 on each cell's dominant surface. */
|
|
654
|
+
function materializeSnapshotWeights(snapshot) {
|
|
655
|
+
const layers = snapshot.layers ?? [];
|
|
656
|
+
const cells = snapshot.cols * snapshot.rows;
|
|
657
|
+
const buffer = new Array(cells * Math.max(1, layers.length)).fill(0);
|
|
658
|
+
if (snapshot.weights !== undefined) {
|
|
659
|
+
const copy = Math.min(buffer.length, snapshot.weights.length);
|
|
660
|
+
for (let i = 0; i < copy; i += 1)
|
|
661
|
+
buffer[i] = snapshot.weights[i];
|
|
662
|
+
return buffer;
|
|
663
|
+
}
|
|
664
|
+
if (layers.length > 0) {
|
|
665
|
+
for (let cell = 0; cell < cells; cell += 1) {
|
|
666
|
+
const surface = snapshot.surfaces[cell] ?? null;
|
|
667
|
+
const layer = surface === null ? -1 : layers.findIndex((entry) => entry.surface === surface);
|
|
668
|
+
if (layer >= 0)
|
|
669
|
+
buffer[cell * layers.length + layer] = 1;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
return buffer;
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Returns a new snapshot with a weight delta's `after` values applied (materializing weights first).
|
|
676
|
+
* @internal — undo/redo plumbing behind the `blendTerrain` command.
|
|
677
|
+
*/
|
|
678
|
+
export function applyWeightDeltaToSnapshot(snapshot, delta) {
|
|
679
|
+
if (delta.layerCount !== (snapshot.layers?.length ?? 0))
|
|
680
|
+
return snapshot;
|
|
681
|
+
const weights = materializeSnapshotWeights(snapshot);
|
|
682
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
683
|
+
weights[delta.indices[i]] = delta.after[i];
|
|
684
|
+
return { ...snapshot, weights };
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Returns a new snapshot with a weight delta's `before` values restored (copy-on-write undo).
|
|
688
|
+
* @internal — undo/redo plumbing behind the `blendTerrain` command.
|
|
689
|
+
*/
|
|
690
|
+
export function revertWeightDeltaFromSnapshot(snapshot, delta) {
|
|
691
|
+
if (delta.layerCount !== (snapshot.layers?.length ?? 0))
|
|
692
|
+
return snapshot;
|
|
693
|
+
const weights = materializeSnapshotWeights(snapshot);
|
|
694
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
695
|
+
weights[delta.indices[i]] = delta.before[i];
|
|
696
|
+
return { ...snapshot, weights };
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Opens a blend-stroke recorder over `terrain`; stamp blend edits into it, then read one net delta.
|
|
700
|
+
* @internal — the stroke recorder behind the blend brush and the `blendTerrain` command.
|
|
701
|
+
*/
|
|
702
|
+
export function beginBlendStroke(terrain) {
|
|
703
|
+
const first = new Map();
|
|
704
|
+
const latest = new Map();
|
|
705
|
+
return {
|
|
706
|
+
stamp(edit) {
|
|
707
|
+
return terrain.blendRecording(edit, (index, before, after) => {
|
|
708
|
+
if (!first.has(index))
|
|
709
|
+
first.set(index, before);
|
|
710
|
+
latest.set(index, after);
|
|
711
|
+
});
|
|
712
|
+
},
|
|
713
|
+
delta() {
|
|
714
|
+
const indices = [];
|
|
715
|
+
const before = [];
|
|
716
|
+
const after = [];
|
|
717
|
+
for (const [index, next] of latest) {
|
|
718
|
+
const prev = first.get(index);
|
|
719
|
+
if (next === prev)
|
|
720
|
+
continue;
|
|
721
|
+
indices.push(index);
|
|
722
|
+
before.push(prev);
|
|
723
|
+
after.push(next);
|
|
724
|
+
}
|
|
725
|
+
return { layerCount: terrain.layers.length, indices, before, after };
|
|
726
|
+
},
|
|
727
|
+
isEmpty() {
|
|
728
|
+
return latest.size === 0;
|
|
729
|
+
},
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* The world-space {@link Aabb} spanning the vertices a stroke's `indices` touched (indices into the
|
|
734
|
+
* `(cols+1)×(rows+1)` offset grid), expanded by `margin` meters. Drives partial preview-mesh
|
|
735
|
+
* rebuilds — only the dirty submesh re-samples instead of the whole heightfield. Null if empty.
|
|
736
|
+
* @internal — the editor's preview-mesh rebuild uses this; not a game-facing primitive.
|
|
737
|
+
*/
|
|
738
|
+
export function dirtyBoundsFromIndices(snapshot, indices, margin = 0) {
|
|
739
|
+
if (indices.length === 0)
|
|
740
|
+
return null;
|
|
741
|
+
const vertsX = snapshot.cols + 1;
|
|
742
|
+
const spanX = Math.max(snapshot.bounds.maxX - snapshot.bounds.minX, snapshot.cellSize);
|
|
743
|
+
const spanZ = Math.max(snapshot.bounds.maxZ - snapshot.bounds.minZ, snapshot.cellSize);
|
|
744
|
+
let minX = Infinity;
|
|
745
|
+
let minZ = Infinity;
|
|
746
|
+
let maxX = -Infinity;
|
|
747
|
+
let maxZ = -Infinity;
|
|
748
|
+
for (const index of indices) {
|
|
749
|
+
const gx = index % vertsX;
|
|
750
|
+
const gz = Math.floor(index / vertsX);
|
|
751
|
+
const wx = snapshot.bounds.minX + (gx / snapshot.cols) * spanX;
|
|
752
|
+
const wz = snapshot.bounds.minZ + (gz / snapshot.rows) * spanZ;
|
|
753
|
+
if (wx < minX)
|
|
754
|
+
minX = wx;
|
|
755
|
+
if (wx > maxX)
|
|
756
|
+
maxX = wx;
|
|
757
|
+
if (wz < minZ)
|
|
758
|
+
minZ = wz;
|
|
759
|
+
if (wz > maxZ)
|
|
760
|
+
maxZ = wz;
|
|
761
|
+
}
|
|
762
|
+
return { minX: minX - margin, minZ: minZ - margin, maxX: maxX + margin, maxZ: maxZ + margin };
|
|
763
|
+
}
|
|
764
|
+
/** A fresh, unedited terrain snapshot sized to `bounds`/`cellSize` — the seed for a new sculpt document. */
|
|
765
|
+
export function createTerrainSnapshot(config) {
|
|
766
|
+
return createEditableTerrain(config).snapshot();
|
|
767
|
+
}
|
|
768
|
+
/** Rebuilds a live {@link EditableTerrain} from a snapshot, layered over `base` ground. */
|
|
769
|
+
export function editableTerrainFromSnapshot(snapshot, base) {
|
|
770
|
+
const terrain = createEditableTerrain({
|
|
771
|
+
bounds: snapshot.bounds,
|
|
772
|
+
cellSize: snapshot.cellSize,
|
|
773
|
+
...(base === undefined ? {} : { base }),
|
|
774
|
+
});
|
|
775
|
+
terrain.restore(snapshot);
|
|
776
|
+
return terrain;
|
|
777
|
+
}
|
|
778
|
+
/** Returns a new snapshot with a delta's `after` offsets applied (copy-on-write — inputs untouched). */
|
|
779
|
+
export function applyDeltaToSnapshot(snapshot, delta) {
|
|
780
|
+
const offsets = snapshot.offsets.slice();
|
|
781
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
782
|
+
offsets[delta.indices[i]] = delta.after[i];
|
|
783
|
+
return { ...snapshot, offsets };
|
|
784
|
+
}
|
|
785
|
+
/** Returns a new snapshot with a delta's `before` offsets restored (copy-on-write undo). */
|
|
786
|
+
export function revertDeltaFromSnapshot(snapshot, delta) {
|
|
787
|
+
const offsets = snapshot.offsets.slice();
|
|
788
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
789
|
+
offsets[delta.indices[i]] = delta.before[i];
|
|
790
|
+
return { ...snapshot, offsets };
|
|
791
|
+
}
|
|
157
792
|
export function createTerraformBrush(terrain, config = {}) {
|
|
158
793
|
let radius = config.radius ?? 3;
|
|
159
794
|
let strength = config.strength ?? 0.5;
|