@jgengine/core 0.10.0 → 0.12.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 +39 -2
- package/LICENSE +203 -661
- package/README.md +1 -1
- package/dist/ai/crowd.d.ts +55 -0
- package/dist/ai/crowd.js +114 -0
- package/dist/ai/flock.d.ts +5 -2
- package/dist/ai/flock.js +5 -2
- package/dist/ai/groupAssist.d.ts +1 -0
- package/dist/ai/groupAssist.js +1 -0
- package/dist/ai/heatSystem.d.ts +76 -0
- package/dist/ai/heatSystem.js +97 -0
- package/dist/ai/interestScheduler.d.ts +107 -0
- package/dist/ai/interestScheduler.js +118 -0
- package/dist/ai/jobBoard.d.ts +1 -0
- package/dist/ai/jobBoard.js +1 -0
- package/dist/ai/laneSelect.d.ts +5 -2
- package/dist/ai/laneSelect.js +5 -2
- package/dist/ai/mobBrain.d.ts +1 -0
- package/dist/ai/mobBrain.js +1 -0
- package/dist/ai/populationDirector.d.ts +103 -0
- package/dist/ai/populationDirector.js +190 -0
- package/dist/ai/pursuit.d.ts +56 -0
- package/dist/ai/pursuit.js +53 -0
- package/dist/ai/spawnDirector.d.ts +7 -2
- package/dist/ai/spawnDirector.js +18 -15
- package/dist/ai/spawnPoint.d.ts +3 -1
- package/dist/ai/spawnPoint.js +3 -1
- package/dist/ai/targetAcquisition.d.ts +84 -0
- package/dist/ai/targetAcquisition.js +92 -0
- package/dist/anim/easing.d.ts +19 -0
- package/dist/anim/easing.js +19 -0
- package/dist/anim/oscillator.d.ts +12 -0
- package/dist/anim/oscillator.js +25 -0
- package/dist/area/areaEffectField.d.ts +127 -0
- package/dist/area/areaEffectField.js +175 -0
- package/dist/area/stackPolicy.d.ts +47 -0
- package/dist/area/stackPolicy.js +99 -0
- package/dist/authoring.d.ts +2 -1
- package/dist/authoring.js +2 -1
- package/dist/board/laneBoard.d.ts +13 -0
- package/dist/board/laneBoard.js +13 -0
- package/dist/board/timelineBoard.d.ts +10 -0
- package/dist/board/timelineBoard.js +10 -0
- package/dist/combat/abilityKit.d.ts +18 -0
- package/dist/combat/abilityKit.js +24 -9
- package/dist/combat/animationState.d.ts +5 -0
- package/dist/combat/animationState.js +5 -0
- package/dist/combat/breakMeters.d.ts +10 -0
- package/dist/combat/breakMeters.js +10 -0
- package/dist/combat/castRunner.d.ts +5 -0
- package/dist/combat/castRunner.js +5 -0
- package/dist/combat/comboPoints.d.ts +5 -0
- package/dist/combat/comboPoints.js +5 -0
- package/dist/combat/comboString.d.ts +5 -0
- package/dist/combat/comboString.js +5 -0
- package/dist/combat/damageInterceptors.d.ts +192 -0
- package/dist/combat/damageInterceptors.js +251 -0
- package/dist/combat/damageMatchup.d.ts +68 -0
- package/dist/combat/damageMatchup.js +42 -0
- package/dist/combat/damageResolution.d.ts +63 -0
- package/dist/combat/damageResolution.js +50 -0
- package/dist/combat/death.d.ts +7 -1
- package/dist/combat/death.js +5 -0
- package/dist/combat/deathReason.d.ts +14 -0
- package/dist/combat/deathReason.js +1 -0
- package/dist/combat/defensiveWindow.d.ts +5 -0
- package/dist/combat/defensiveWindow.js +5 -0
- package/dist/combat/dotField.d.ts +5 -1
- package/dist/combat/dotField.js +5 -1
- package/dist/combat/downed.d.ts +5 -0
- package/dist/combat/downed.js +5 -0
- package/dist/combat/effects.d.ts +5 -0
- package/dist/combat/effects.js +5 -0
- package/dist/combat/encounterSequence.d.ts +258 -0
- package/dist/combat/encounterSequence.js +426 -0
- package/dist/combat/hitReaction.d.ts +57 -1
- package/dist/combat/hitReaction.js +45 -5
- package/dist/combat/magazine.d.ts +54 -0
- package/dist/combat/magazine.js +96 -0
- package/dist/combat/projectiles.d.ts +5 -0
- package/dist/combat/projectiles.js +32 -14
- package/dist/combat/receivedDamage.d.ts +117 -0
- package/dist/combat/receivedDamage.js +92 -0
- package/dist/combat/regenShield.d.ts +5 -1
- package/dist/combat/regenShield.js +5 -1
- package/dist/combat/renderCues.d.ts +51 -0
- package/dist/combat/renderCues.js +44 -0
- package/dist/combat/resourcePool.d.ts +5 -0
- package/dist/combat/resourcePool.js +5 -0
- package/dist/combat/rewardAllocation.d.ts +121 -0
- package/dist/combat/rewardAllocation.js +251 -0
- package/dist/combat/shotOrigin.d.ts +18 -1
- package/dist/combat/shotOrigin.js +39 -0
- package/dist/combat/statusApplication.d.ts +96 -0
- package/dist/combat/statusApplication.js +81 -0
- package/dist/combat/telegraph.d.ts +11 -2
- package/dist/combat/telegraph.js +11 -2
- package/dist/combat/thresholdCrossings.d.ts +85 -0
- package/dist/combat/thresholdCrossings.js +116 -0
- package/dist/combat.d.ts +35 -0
- package/dist/combat.js +35 -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/controls.d.ts +38 -0
- package/dist/devtools/controls.js +181 -0
- package/dist/devtools/devtools.d.ts +5 -197
- package/dist/devtools/devtools.js +38 -774
- package/dist/devtools/discover.d.ts +32 -0
- package/dist/devtools/discover.js +240 -0
- package/dist/devtools/fallbackSeams.d.ts +50 -0
- package/dist/devtools/fallbackSeams.js +111 -0
- package/dist/devtools/frame.d.ts +15 -0
- package/dist/devtools/frame.js +247 -0
- package/dist/devtools/logs.d.ts +15 -0
- package/dist/devtools/logs.js +86 -0
- package/dist/devtools/overrides.d.ts +16 -0
- package/dist/devtools/overrides.js +66 -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/devtools/types.d.ts +212 -0
- package/dist/devtools/types.js +1 -0
- package/dist/economy/auctionBook.d.ts +122 -0
- package/dist/economy/auctionBook.js +213 -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/priceHistory.d.ts +44 -0
- package/dist/economy/priceHistory.js +89 -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/unlockPoints.d.ts +92 -0
- package/dist/economy/unlockPoints.js +103 -0
- package/dist/economy/wallet.d.ts +26 -2
- package/dist/economy/wallet.js +35 -6
- package/dist/editor/commandApply.d.ts +5 -0
- package/dist/editor/commandApply.js +755 -0
- package/dist/editor/commands.d.ts +235 -7
- package/dist/editor/commands.js +82 -124
- package/dist/editor/directives.d.ts +94 -0
- package/dist/editor/directives.js +206 -0
- package/dist/editor/document.d.ts +194 -13
- package/dist/editor/document.js +1166 -19
- package/dist/editor/entityCatalog.d.ts +42 -0
- package/dist/editor/entityCatalog.js +101 -0
- package/dist/editor/environment.d.ts +74 -0
- package/dist/editor/environment.js +169 -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 +98 -6
- package/dist/editor/index.js +13 -2
- package/dist/editor/liveSync.d.ts +127 -0
- package/dist/editor/liveSync.js +249 -0
- package/dist/editor/minimap.d.ts +31 -0
- package/dist/editor/minimap.js +73 -0
- package/dist/editor/runtimeInspector.d.ts +88 -0
- package/dist/editor/runtimeInspector.js +239 -0
- package/dist/editor/streamer.d.ts +66 -0
- package/dist/editor/streamer.js +68 -0
- package/dist/editor/types.d.ts +250 -1
- package/dist/editor/world.d.ts +136 -0
- package/dist/editor/world.js +303 -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/breeding.d.ts +161 -0
- package/dist/game/breeding.js +181 -0
- package/dist/game/cameraConfig.d.ts +451 -0
- package/dist/game/cameraConfig.js +1 -0
- package/dist/game/connectedPlayers.d.ts +6 -4
- package/dist/game/connectedPlayers.js +2 -2
- package/dist/game/cosmetics.d.ts +9 -0
- package/dist/game/cosmetics.js +16 -0
- package/dist/game/defineGame.d.ts +110 -5
- package/dist/game/defineGame.js +49 -4
- package/dist/game/defineSystem.d.ts +90 -0
- package/dist/game/defineSystem.js +47 -0
- package/dist/game/dialogue.d.ts +33 -0
- package/dist/game/dialogue.js +21 -0
- package/dist/game/events.d.ts +63 -13
- package/dist/game/events.js +5 -0
- package/dist/game/feed.d.ts +33 -0
- package/dist/game/feed.js +35 -2
- package/dist/game/gamePhase.d.ts +11 -0
- package/dist/game/gamePhase.js +16 -0
- package/dist/game/leaderboard.d.ts +5 -0
- package/dist/game/leaderboard.js +5 -0
- package/dist/game/loadout.d.ts +5 -0
- package/dist/game/loadout.js +5 -0
- package/dist/game/lootFilter.d.ts +2 -0
- package/dist/game/lootFilter.js +2 -0
- package/dist/game/lootPipeline.d.ts +164 -0
- package/dist/game/lootPipeline.js +216 -0
- package/dist/game/lootTable.d.ts +19 -2
- package/dist/game/lootTable.js +15 -5
- package/dist/game/objectives.d.ts +43 -0
- package/dist/game/objectives.js +36 -0
- package/dist/game/paramLayers.d.ts +148 -0
- package/dist/game/paramLayers.js +185 -0
- package/dist/game/ping.d.ts +5 -0
- package/dist/game/ping.js +5 -0
- package/dist/game/playableGame.d.ts +116 -432
- package/dist/game/playableGame.js +1 -1
- package/dist/game/progression.d.ts +35 -0
- package/dist/game/progression.js +19 -0
- package/dist/game/quest.d.ts +8 -0
- package/dist/game/quest.js +30 -3
- package/dist/game/race.d.ts +141 -0
- package/dist/game/race.js +169 -0
- package/dist/game/recordBook.d.ts +3 -1
- package/dist/game/recordBook.js +4 -1
- package/dist/game/ruleSelection.d.ts +75 -0
- package/dist/game/ruleSelection.js +127 -0
- package/dist/game/runDraft.d.ts +5 -0
- package/dist/game/runDraft.js +5 -0
- package/dist/game/saveStore.d.ts +111 -0
- package/dist/game/saveStore.js +312 -0
- package/dist/game/snapshotHistory.d.ts +67 -0
- package/dist/game/snapshotHistory.js +95 -0
- package/dist/game/social.d.ts +38 -1
- package/dist/game/social.js +71 -0
- package/dist/game/spawnPoints.d.ts +5 -0
- package/dist/game/spawnPoints.js +5 -0
- package/dist/game/systemRuntime.d.ts +50 -0
- package/dist/game/systemRuntime.js +157 -0
- package/dist/game/systemSchedule.d.ts +72 -0
- package/dist/game/systemSchedule.js +242 -0
- package/dist/game/toasts.d.ts +43 -0
- package/dist/game/toasts.js +44 -0
- package/dist/game/trade.d.ts +10 -0
- package/dist/game/trade.js +10 -0
- package/dist/game/tribe.d.ts +184 -0
- package/dist/game/tribe.js +432 -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 +93 -0
- package/dist/gameplay.js +93 -0
- package/dist/index.d.ts +5 -14
- package/dist/index.js +5 -14
- package/dist/input/actionBindings.d.ts +23 -6
- package/dist/input/actionBindings.js +23 -6
- package/dist/input/axisInput.d.ts +7 -2
- package/dist/input/axisInput.js +7 -2
- package/dist/input/controlGroups.d.ts +67 -0
- package/dist/input/controlGroups.js +41 -0
- package/dist/input/touchScheme.d.ts +82 -6
- package/dist/input/touchScheme.js +123 -10
- package/dist/interaction/lockpick.d.ts +61 -0
- package/dist/interaction/lockpick.js +168 -0
- package/dist/interaction/proximityPrompt.d.ts +2 -0
- package/dist/interaction/proximityPrompt.js +2 -0
- package/dist/interaction/qte.d.ts +5 -0
- package/dist/interaction/qte.js +5 -0
- package/dist/interaction/skillCheck.d.ts +5 -0
- package/dist/interaction/skillCheck.js +5 -0
- package/dist/inventory/encumbrance.d.ts +69 -0
- package/dist/inventory/encumbrance.js +68 -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 +6 -3
- 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 +39 -2
- package/dist/movement/avoidance.d.ts +45 -0
- package/dist/movement/avoidance.js +123 -0
- package/dist/movement/cameraRig.d.ts +8 -0
- package/dist/movement/cameraRig.js +8 -0
- package/dist/movement/dash.d.ts +11 -0
- package/dist/movement/dash.js +11 -0
- package/dist/movement/formation.d.ts +135 -0
- package/dist/movement/formation.js +194 -0
- package/dist/movement/glideModel.d.ts +5 -0
- package/dist/movement/glideModel.js +5 -0
- package/dist/movement/grappleSwing.d.ts +5 -0
- package/dist/movement/grappleSwing.js +5 -0
- package/dist/movement/leaderTrail.d.ts +5 -0
- package/dist/movement/leaderTrail.js +5 -0
- package/dist/movement/movementModel.d.ts +50 -9
- package/dist/movement/movementModel.js +75 -29
- package/dist/movement/playerMovement.js +167 -12
- 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 +16 -8
- package/dist/multiplayer/lagCompensation.d.ts +5 -0
- package/dist/multiplayer/lagCompensation.js +5 -0
- package/dist/multiplayer/presenceModel.d.ts +11 -1
- package/dist/multiplayer/presenceModel.js +17 -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 +31 -0
- package/dist/physics/drivableVehicle.js +16 -0
- package/dist/physics/flightDynamics.d.ts +82 -0
- package/dist/physics/flightDynamics.js +165 -0
- package/dist/physics/gravityField.d.ts +25 -0
- package/dist/physics/gravityField.js +41 -0
- package/dist/physics/kinematicVehicle.d.ts +108 -1
- package/dist/physics/kinematicVehicle.js +212 -15
- package/dist/physics/physicsWorld.d.ts +50 -12
- package/dist/physics/physicsWorld.js +120 -122
- package/dist/physics/radialImpulse.d.ts +25 -0
- package/dist/physics/radialImpulse.js +27 -0
- package/dist/physics/vehicleObstacles.d.ts +44 -0
- package/dist/physics/vehicleObstacles.js +115 -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 +26 -1
- package/dist/random/rng.js +32 -9
- package/dist/random/seedLink.d.ts +4 -0
- package/dist/random/seedLink.js +4 -0
- package/dist/relation/keyedValues.d.ts +54 -0
- package/dist/relation/keyedValues.js +115 -0
- package/dist/relation/thresholds.d.ts +62 -0
- package/dist/relation/thresholds.js +62 -0
- package/dist/render/color.d.ts +12 -0
- package/dist/render/color.js +26 -0
- package/dist/render/lookPreset.d.ts +41 -0
- package/dist/render/lookPreset.js +29 -0
- package/dist/render/postProcessing.d.ts +20 -0
- package/dist/render/postProcessing.js +21 -1
- package/dist/rules/predicate.d.ts +53 -0
- package/dist/rules/predicate.js +64 -0
- package/dist/rules/ruleEffects.d.ts +38 -0
- package/dist/rules/ruleEffects.js +37 -0
- package/dist/rules/triggeredRules.d.ts +160 -0
- package/dist/rules/triggeredRules.js +241 -0
- package/dist/runtime/adapter.d.ts +55 -3
- package/dist/runtime/adapter.js +57 -1
- package/dist/runtime/cameraDirector.d.ts +1 -0
- package/dist/runtime/cameraDirector.js +1 -0
- package/dist/runtime/commandRunner.d.ts +1 -0
- package/dist/runtime/commandRunner.js +1 -0
- package/dist/runtime/context/combat.d.ts +51 -0
- package/dist/runtime/context/combat.js +130 -0
- package/dist/runtime/context/combatFx.d.ts +23 -0
- package/dist/runtime/context/combatFx.js +161 -0
- package/dist/runtime/context/deathLoot.d.ts +30 -0
- package/dist/runtime/context/deathLoot.js +39 -0
- package/dist/runtime/context/player.d.ts +48 -0
- package/dist/runtime/context/player.js +125 -0
- package/dist/runtime/context/registries.d.ts +13 -0
- package/dist/runtime/context/registries.js +84 -0
- package/dist/runtime/context/scene.d.ts +59 -0
- package/dist/runtime/context/scene.js +291 -0
- package/dist/runtime/context/snapshotCodecs.d.ts +12 -0
- package/dist/runtime/context/snapshotCodecs.js +26 -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 +157 -0
- package/dist/runtime/descriptors/features.d.ts +79 -0
- package/dist/runtime/descriptors/features.js +299 -0
- package/dist/runtime/gameContext.d.ts +2 -327
- package/dist/runtime/gameContext.js +273 -778
- package/dist/runtime/gameContextTypes.d.ts +467 -0
- package/dist/runtime/gameContextTypes.js +1 -0
- package/dist/runtime/gameRuntime.d.ts +1 -0
- package/dist/runtime/gameRuntime.js +1 -0
- package/dist/runtime/headlessRunner.d.ts +62 -0
- package/dist/runtime/headlessRunner.js +45 -0
- package/dist/runtime/hostPersistence.d.ts +12 -0
- package/dist/runtime/hostPersistence.js +12 -0
- package/dist/runtime/hostPolicy.d.ts +51 -0
- package/dist/runtime/hostPolicy.js +68 -0
- package/dist/runtime/hostedGameRunner.d.ts +11 -4
- package/dist/runtime/hostedGameRunner.js +27 -3
- package/dist/runtime/hostedWorldSession.d.ts +15 -5
- package/dist/runtime/hostedWorldSession.js +12 -4
- package/dist/runtime/inputSnapshot.d.ts +8 -3
- package/dist/runtime/inputSnapshot.js +6 -2
- package/dist/runtime/motionIntents.d.ts +11 -2
- package/dist/runtime/motionIntents.js +16 -2
- package/dist/runtime/perContext.d.ts +15 -0
- package/dist/runtime/perContext.js +21 -0
- package/dist/runtime/runtimeSave.d.ts +79 -0
- package/dist/runtime/runtimeSave.js +105 -0
- package/dist/runtime/save.d.ts +4 -0
- package/dist/runtime/save.js +4 -0
- package/dist/runtime/snapshot.d.ts +7 -0
- package/dist/runtime/snapshot.js +7 -0
- package/dist/runtime/visibility.d.ts +2 -1
- package/dist/runtime/visibility.js +2 -1
- package/dist/runtime/worldChannel.d.ts +7 -3
- package/dist/runtime/worldChannel.js +51 -6
- package/dist/runtime/worldMirror.d.ts +14 -2
- package/dist/runtime/worldMirror.js +15 -2
- package/dist/runtime/worldProjection.d.ts +47 -0
- package/dist/runtime/worldProjection.js +52 -0
- package/dist/runtime/worldReplication.d.ts +30 -7
- package/dist/runtime/worldReplication.js +41 -9
- package/dist/runtime/worldSnapshot.d.ts +42 -3
- package/dist/runtime/worldSnapshot.js +34 -7
- package/dist/scene/assetCatalog.d.ts +26 -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 +257 -0
- package/dist/scene/authoredTriggers.d.ts +219 -0
- package/dist/scene/authoredTriggers.js +454 -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 +67 -0
- package/dist/scene/colliders.d.ts +71 -1
- package/dist/scene/colliders.js +145 -1
- package/dist/scene/collisionMesh.d.ts +94 -0
- package/dist/scene/collisionMesh.js +699 -0
- package/dist/scene/companion.d.ts +147 -0
- package/dist/scene/companion.js +193 -0
- package/dist/scene/entityStats.d.ts +18 -11
- package/dist/scene/entityStats.js +21 -16
- package/dist/scene/entityStore.d.ts +12 -1
- package/dist/scene/entityStore.js +15 -0
- package/dist/scene/facing.d.ts +18 -0
- package/dist/scene/facing.js +26 -0
- package/dist/scene/modelSockets.d.ts +33 -0
- package/dist/scene/modelSockets.js +29 -0
- package/dist/scene/objectQuery.d.ts +2 -0
- package/dist/scene/objectQuery.js +2 -0
- package/dist/scene/objectStore.d.ts +4 -0
- package/dist/scene/objectStore.js +6 -0
- package/dist/scene/possession.d.ts +8 -0
- package/dist/scene/possession.js +14 -0
- package/dist/scene/roster.d.ts +3 -0
- package/dist/scene/roster.js +12 -0
- package/dist/scene/sceneKinds.d.ts +256 -0
- package/dist/scene/sceneKinds.js +218 -0
- package/dist/scene/sceneOwnership.d.ts +169 -0
- package/dist/scene/sceneOwnership.js +179 -0
- package/dist/scene/sceneRaycast.js +15 -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/spawnLevelStats.d.ts +110 -0
- package/dist/stats/spawnLevelStats.js +140 -0
- package/dist/stats/statModifiers.d.ts +5 -0
- package/dist/stats/statModifiers.js +5 -0
- package/dist/stats/statPool.d.ts +70 -0
- package/dist/stats/statPool.js +59 -0
- package/dist/store/changeSignal.d.ts +3 -0
- package/dist/store/changeSignal.js +2 -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 +7 -0
- package/dist/store/observableKeyedStore.js +35 -2
- package/dist/survival/decayMeter.d.ts +55 -0
- package/dist/survival/decayMeter.js +130 -57
- package/dist/survival/moodle.d.ts +2 -0
- package/dist/survival/regionHealth.d.ts +6 -0
- package/dist/survival/regionHealth.js +6 -3
- package/dist/tactics/fallingGrid.d.ts +4 -2
- package/dist/tactics/fallingGrid.js +4 -2
- package/dist/tactics/predictiveQuery.d.ts +3 -0
- package/dist/tactics/predictiveQuery.js +3 -0
- package/dist/tactics/snapshot.d.ts +2 -0
- package/dist/tactics/snapshot.js +2 -0
- package/dist/tactics/surface.d.ts +1 -0
- package/dist/tactics/surface.js +1 -0
- package/dist/tactics/tacticalGrid.d.ts +1 -0
- package/dist/tactics/tacticalGrid.js +1 -0
- package/dist/time/beatClock.d.ts +14 -4
- package/dist/time/beatClock.js +14 -4
- package/dist/time/calendarClock.d.ts +3 -0
- package/dist/time/calendarClock.js +3 -0
- package/dist/time/idleProgress.d.ts +4 -0
- package/dist/time/idleProgress.js +4 -0
- package/dist/time/simClock.d.ts +2 -0
- package/dist/time/simClock.js +20 -5
- package/dist/ui/actionModel.d.ts +110 -0
- package/dist/ui/actionModel.js +159 -0
- package/dist/ui/gameLayout.d.ts +30 -11
- package/dist/ui/gameLayout.js +30 -11
- package/dist/ui/hudDocument.d.ts +114 -0
- package/dist/ui/hudDocument.js +220 -0
- package/dist/ui/hudLayout.d.ts +38 -1
- package/dist/ui/hudLayout.js +220 -17
- package/dist/ui/selectionModel.d.ts +92 -0
- package/dist/ui/selectionModel.js +88 -0
- package/dist/ui/tooltipModel.d.ts +63 -0
- package/dist/ui/tooltipModel.js +82 -0
- package/dist/ui.d.ts +15 -0
- package/dist/ui.js +15 -0
- package/dist/visibility/bounds.d.ts +8 -1
- package/dist/visibility/bounds.js +8 -1
- package/dist/visibility/frustum.d.ts +12 -3
- package/dist/visibility/frustum.js +12 -3
- package/dist/work/jobQueue.d.ts +201 -0
- package/dist/work/jobQueue.js +253 -0
- package/dist/work/unitTraining.d.ts +58 -0
- package/dist/work/unitTraining.js +52 -0
- package/dist/world/authoredEntities.d.ts +29 -0
- package/dist/world/authoredEntities.js +29 -0
- package/dist/world/authoredObjects.d.ts +72 -0
- package/dist/world/authoredObjects.js +81 -0
- package/dist/world/authoredSpawn.d.ts +39 -0
- package/dist/world/authoredSpawn.js +37 -0
- package/dist/world/buildSockets.d.ts +152 -0
- package/dist/world/buildSockets.js +188 -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/cityBlocks.d.ts +152 -0
- package/dist/world/cityBlocks.js +670 -0
- package/dist/world/cityContent.d.ts +77 -0
- package/dist/world/cityContent.js +261 -0
- package/dist/world/cityGeometry.d.ts +114 -0
- package/dist/world/cityGeometry.js +573 -0
- package/dist/world/cityKind.d.ts +314 -0
- package/dist/world/cityKind.js +1578 -0
- package/dist/world/features.d.ts +133 -10
- package/dist/world/features.js +66 -6
- 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/markers.d.ts +50 -4
- package/dist/world/markers.js +49 -0
- package/dist/world/massing.js +1 -1
- package/dist/world/minimapBake.d.ts +78 -0
- package/dist/world/minimapBake.js +208 -0
- package/dist/world/minimapTrack.d.ts +17 -0
- package/dist/world/minimapTrack.js +22 -0
- package/dist/world/pathInstances.d.ts +27 -0
- package/dist/world/pathInstances.js +27 -0
- package/dist/world/pathNetwork.d.ts +159 -0
- package/dist/world/pathNetwork.js +684 -0
- package/dist/world/pathTerrain.d.ts +80 -0
- package/dist/world/pathTerrain.js +142 -0
- package/dist/world/place.d.ts +166 -0
- package/dist/world/place.js +96 -0
- package/dist/world/placeAsset.d.ts +81 -0
- package/dist/world/placeAsset.js +79 -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/poleLineKind.d.ts +40 -0
- package/dist/world/poleLineKind.js +97 -0
- package/dist/world/polyline.d.ts +34 -0
- package/dist/world/polyline.js +88 -0
- package/dist/world/resourceNode.d.ts +109 -0
- package/dist/world/resourceNode.js +140 -0
- package/dist/world/roads.d.ts +8 -0
- package/dist/world/roads.js +38 -0
- package/dist/world/scatter.js +2 -20
- 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 +29 -0
- package/dist/world/streets.js +55 -0
- package/dist/world/terraform.d.ts +213 -1
- package/dist/world/terraform.js +627 -22
- package/dist/world/terrain.d.ts +164 -20
- package/dist/world/terrain.js +325 -41
- package/dist/world/terrainGuides.d.ts +182 -0
- package/dist/world/terrainGuides.js +343 -0
- package/dist/world/vec2.d.ts +81 -0
- package/dist/world/vec2.js +128 -0
- package/dist/world/vec3.d.ts +49 -0
- package/dist/world/vec3.js +76 -0
- package/dist/world/vegetation.d.ts +71 -0
- package/dist/world/vegetation.js +151 -0
- package/dist/world/visibilityField.d.ts +154 -0
- package/dist/world/visibilityField.js +271 -0
- package/dist/world/volumetricClouds.d.ts +52 -0
- package/dist/world/volumetricClouds.js +43 -0
- package/dist/world/walls.d.ts +9 -0
- package/dist/world/walls.js +9 -0
- package/dist/world/waterKind.d.ts +55 -0
- package/dist/world/waterKind.js +99 -0
- package/dist/world.d.ts +141 -0
- package/dist/world.js +141 -0
- package/package.json +32 -8
- package/skills/game-design/SKILL.md +28 -0
- package/skills/game-design/agents/openai.yaml +4 -0
- package/skills/game-design/references/application-playbook.md +170 -0
- package/skills/game-design/references/field-guide.md +288 -0
- package/skills/jgengine/SKILL.md +54 -0
- package/skills/jgengine/api.md +527 -0
- package/skills/jgengine/capabilities.md +131 -0
- package/skills/jgengine/recipes/minimal-game.md +88 -0
- package/skills/jgengine-combat/SKILL.md +38 -0
- package/skills/jgengine-combat/api.md +585 -0
- package/skills/jgengine-combat/capabilities.md +213 -0
- package/skills/jgengine-combat/recipes/portable-stat-pools.md +122 -0
- package/skills/jgengine-combat/reference.md +108 -0
- package/skills/jgengine-gameplay/SKILL.md +39 -0
- package/skills/jgengine-gameplay/api.md +1920 -0
- package/skills/jgengine-gameplay/capabilities.md +454 -0
- package/skills/jgengine-gameplay/reference-systems.md +56 -0
- package/skills/jgengine-world/SKILL.md +51 -0
- package/skills/jgengine-world/api.md +3056 -0
- package/skills/jgengine-world/capabilities.md +476 -0
- package/skills/jgengine-world/recipes/commanding-entities.md +128 -0
- package/skills/jgengine-world/reference.md +261 -0
- package/dist/cartridge/runtime.d.ts +0 -15
- package/dist/cartridge/runtime.js +0 -556
- package/dist/cartridge/spec.d.ts +0 -188
- package/dist/cartridge/spec.js +0 -19
- package/dist/cartridge/validate.d.ts +0 -2
- package/dist/cartridge/validate.js +0 -78
- package/dist/store/engineStore.d.ts +0 -22
- package/dist/store/engineStore.js +0 -50
- package/dist/store/features.d.ts +0 -10
- package/dist/store/features.js +0 -23
- package/llms.txt +0 -5389
package/dist/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,32 +131,105 @@ 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;
|
|
87
162
|
}
|
|
88
163
|
}
|
|
89
164
|
return changed;
|
|
90
165
|
}
|
|
91
|
-
function
|
|
92
|
-
const
|
|
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;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return changed;
|
|
206
|
+
}
|
|
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
|
+
/**
|
|
222
|
+
* Visits every cell in a brush footprint on the CELL grid, yielding each cell's flat `index`, its
|
|
223
|
+
* {@link brushWeight} `weight`, world center (`wx`,`wz`), and raw `dist` from the brush center.
|
|
224
|
+
* Shared by the cell-grid brushes ({@link paintSurface}, {@link blendPaint}); each applies its own
|
|
225
|
+
* inclusion test on the yielded values (paint uses `dist <= radius`, blend uses `weight > 0`).
|
|
226
|
+
*/
|
|
227
|
+
function forEachBrushCell(edit, visit) {
|
|
228
|
+
const falloff = edit.falloff ?? "smooth";
|
|
229
|
+
const shape = edit.shape ?? "circle";
|
|
93
230
|
const rCells = Math.ceil(edit.radius / cellSize) + 1;
|
|
94
231
|
const cx = gridX(edit.center[0]);
|
|
95
232
|
const cz = gridZ(edit.center[1]);
|
|
96
|
-
let painted = 0;
|
|
97
233
|
for (let iz = -rCells; iz <= rCells; iz += 1) {
|
|
98
234
|
const gz = Math.floor(cz) + iz;
|
|
99
235
|
if (gz < 0 || gz >= rows)
|
|
@@ -102,36 +238,276 @@ export function createEditableTerrain(config) {
|
|
|
102
238
|
const gx = Math.floor(cx) + ix;
|
|
103
239
|
if (gx < 0 || gx >= cols)
|
|
104
240
|
continue;
|
|
105
|
-
const wx =
|
|
106
|
-
const wz =
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
241
|
+
const wx = cellWorldX(gx);
|
|
242
|
+
const wz = cellWorldZ(gz);
|
|
243
|
+
const dx = wx - edit.center[0];
|
|
244
|
+
const dz = wz - edit.center[1];
|
|
245
|
+
const dist = shape === "square" ? Math.max(Math.abs(dx), Math.abs(dz)) : Math.hypot(dx, dz);
|
|
246
|
+
visit(gz * cols + gx, brushWeight(dist, edit.radius, falloff), wx, wz, dist);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
function paintSurface(edit, record) {
|
|
251
|
+
const surface = edit.surface ?? null;
|
|
252
|
+
let painted = 0;
|
|
253
|
+
// Binary inclusion by raw distance (includes dist == radius), independent of falloff shaping.
|
|
254
|
+
forEachBrushCell(edit, (index, _weight, _wx, _wz, dist) => {
|
|
255
|
+
if (dist > edit.radius)
|
|
256
|
+
return;
|
|
257
|
+
if (writeSurface(index, surface, record))
|
|
110
258
|
painted += 1;
|
|
259
|
+
});
|
|
260
|
+
return painted;
|
|
261
|
+
}
|
|
262
|
+
function localNormal(wx, wz) {
|
|
263
|
+
const eps = Math.max(cellSize, 0.5);
|
|
264
|
+
const hx = sampleHeight(wx + eps, wz) - sampleHeight(wx - eps, wz);
|
|
265
|
+
const hz = sampleHeight(wx, wz + eps) - sampleHeight(wx, wz - eps);
|
|
266
|
+
const nx = -hx;
|
|
267
|
+
const nz = -hz;
|
|
268
|
+
const ny = 2 * eps;
|
|
269
|
+
const length = Math.hypot(nx, ny, nz) || 1;
|
|
270
|
+
return [nx / length, ny / length, nz / length];
|
|
271
|
+
}
|
|
272
|
+
function autoPaint(rule, record) {
|
|
273
|
+
let painted = 0;
|
|
274
|
+
for (let gz = 0; gz < rows; gz += 1) {
|
|
275
|
+
for (let gx = 0; gx < cols; gx += 1) {
|
|
276
|
+
const wx = cellWorldX(gx);
|
|
277
|
+
const wz = cellWorldZ(gz);
|
|
278
|
+
const height = sampleHeight(wx, wz);
|
|
279
|
+
if (rule.minHeight !== undefined && height < rule.minHeight)
|
|
280
|
+
continue;
|
|
281
|
+
if (rule.maxHeight !== undefined && height > rule.maxHeight)
|
|
282
|
+
continue;
|
|
283
|
+
if (rule.minSlope !== undefined || rule.maxSlope !== undefined) {
|
|
284
|
+
const [nx, ny, nz] = localNormal(wx, wz);
|
|
285
|
+
const slope = Math.hypot(nx, nz) / Math.max(ny, 1e-9);
|
|
286
|
+
if (rule.minSlope !== undefined && slope < rule.minSlope)
|
|
287
|
+
continue;
|
|
288
|
+
if (rule.maxSlope !== undefined && slope > rule.maxSlope)
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
if (writeSurface(gz * cols + gx, rule.surface, record))
|
|
292
|
+
painted += 1;
|
|
111
293
|
}
|
|
112
294
|
}
|
|
113
295
|
return painted;
|
|
114
296
|
}
|
|
115
|
-
function
|
|
297
|
+
function fillSurface(surface, record) {
|
|
298
|
+
let painted = 0;
|
|
299
|
+
for (let index = 0; index < surfaces.length; index += 1) {
|
|
300
|
+
if (writeSurface(index, surface, record))
|
|
301
|
+
painted += 1;
|
|
302
|
+
}
|
|
303
|
+
return painted;
|
|
304
|
+
}
|
|
305
|
+
function cellIndexAt(x, z) {
|
|
116
306
|
const gx = Math.floor(gridX(x));
|
|
117
307
|
const gz = Math.floor(gridZ(z));
|
|
118
308
|
if (gx < 0 || gx >= cols || gz < 0 || gz >= rows)
|
|
309
|
+
return -1;
|
|
310
|
+
return gz * cols + gx;
|
|
311
|
+
}
|
|
312
|
+
function dominantLayerAt(cell) {
|
|
313
|
+
if (weights === null || layers.length === 0)
|
|
314
|
+
return -1;
|
|
315
|
+
const base = cell * layers.length;
|
|
316
|
+
let best = -1;
|
|
317
|
+
let bestWeight = 0;
|
|
318
|
+
for (let l = 0; l < layers.length; l += 1) {
|
|
319
|
+
const w = weights[base + l];
|
|
320
|
+
if (w > bestWeight) {
|
|
321
|
+
bestWeight = w;
|
|
322
|
+
best = l;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return best;
|
|
326
|
+
}
|
|
327
|
+
function surfaceAt(x, z) {
|
|
328
|
+
const cell = cellIndexAt(x, z);
|
|
329
|
+
if (cell < 0)
|
|
119
330
|
return null;
|
|
120
|
-
|
|
331
|
+
const dominant = dominantLayerAt(cell);
|
|
332
|
+
if (dominant >= 0)
|
|
333
|
+
return layers[dominant].surface;
|
|
334
|
+
return surfaces[cell] ?? null;
|
|
335
|
+
}
|
|
336
|
+
function weightsAt(x, z) {
|
|
337
|
+
const cell = cellIndexAt(x, z);
|
|
338
|
+
if (cell < 0 || weights === null || layers.length === 0)
|
|
339
|
+
return [];
|
|
340
|
+
const base = cell * layers.length;
|
|
341
|
+
return Array.from({ length: layers.length }, (_, l) => weights[base + l]);
|
|
342
|
+
}
|
|
343
|
+
/** Materializes the weight buffer, seeding each cell to weight 1 on its current dominant surface. */
|
|
344
|
+
function ensureWeights() {
|
|
345
|
+
if (weights !== null)
|
|
346
|
+
return weights;
|
|
347
|
+
const buffer = new Float32Array(cellCount * Math.max(1, layers.length));
|
|
348
|
+
if (layers.length > 0) {
|
|
349
|
+
for (let cell = 0; cell < cellCount; cell += 1) {
|
|
350
|
+
const surface = surfaces[cell];
|
|
351
|
+
const layer = surface === null ? -1 : layers.findIndex((entry) => entry.surface === surface);
|
|
352
|
+
if (layer >= 0)
|
|
353
|
+
buffer[cell * layers.length + layer] = 1;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
weights = buffer;
|
|
357
|
+
return buffer;
|
|
358
|
+
}
|
|
359
|
+
function blendCell(cell, target, amount, record) {
|
|
360
|
+
const buffer = ensureWeights();
|
|
361
|
+
const L = layers.length;
|
|
362
|
+
const base = cell * L;
|
|
363
|
+
const prevTarget = buffer[base + target];
|
|
364
|
+
const nextTarget = prevTarget + (1 - prevTarget) * amount;
|
|
365
|
+
if (nextTarget === prevTarget)
|
|
366
|
+
return false;
|
|
367
|
+
let othersSum = 0;
|
|
368
|
+
for (let l = 0; l < L; l += 1)
|
|
369
|
+
if (l !== target)
|
|
370
|
+
othersSum += buffer[base + l];
|
|
371
|
+
const remainder = 1 - nextTarget;
|
|
372
|
+
for (let l = 0; l < L; l += 1) {
|
|
373
|
+
const prev = buffer[base + l];
|
|
374
|
+
const next = l === target ? nextTarget : othersSum > 0 ? (prev / othersSum) * remainder : 0;
|
|
375
|
+
if (next !== prev) {
|
|
376
|
+
buffer[base + l] = next;
|
|
377
|
+
record?.(base + l, prev, next);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return true;
|
|
381
|
+
}
|
|
382
|
+
function blendPaint(edit, record) {
|
|
383
|
+
const surface = edit.surface;
|
|
384
|
+
if (surface === undefined || layers.length === 0)
|
|
385
|
+
return 0;
|
|
386
|
+
const target = layers.findIndex((entry) => entry.surface === surface);
|
|
387
|
+
if (target < 0)
|
|
388
|
+
return 0;
|
|
389
|
+
const strength = edit.strength ?? 1;
|
|
390
|
+
let painted = 0;
|
|
391
|
+
forEachBrushCell(edit, (index, weight) => {
|
|
392
|
+
if (weight <= 0)
|
|
393
|
+
return;
|
|
394
|
+
if (blendCell(index, target, weight * strength, record))
|
|
395
|
+
painted += 1;
|
|
396
|
+
});
|
|
397
|
+
return painted;
|
|
398
|
+
}
|
|
399
|
+
function runHeightEdit(edit, record) {
|
|
400
|
+
if (edit.mode === "ramp")
|
|
401
|
+
return editRamp(edit, record);
|
|
402
|
+
return editHeight(edit, record);
|
|
403
|
+
}
|
|
404
|
+
function surfaceDeltaFrom(run) {
|
|
405
|
+
const indices = [];
|
|
406
|
+
const before = [];
|
|
407
|
+
const after = [];
|
|
408
|
+
run((index, prev, next) => {
|
|
409
|
+
indices.push(index);
|
|
410
|
+
before.push(prev);
|
|
411
|
+
after.push(next);
|
|
412
|
+
});
|
|
413
|
+
return { indices, before, after };
|
|
121
414
|
}
|
|
122
415
|
const field = {
|
|
416
|
+
cols,
|
|
417
|
+
rows,
|
|
418
|
+
cellSize,
|
|
123
419
|
sampleHeight,
|
|
124
420
|
sampleNormal,
|
|
125
421
|
...(base.bounds === undefined ? {} : { bounds: base.bounds }),
|
|
126
422
|
...(base.waterLevel === undefined ? {} : { waterLevel: base.waterLevel }),
|
|
127
423
|
apply(edit) {
|
|
128
|
-
return edit.mode === "paint" ? paintSurface(edit) :
|
|
424
|
+
return edit.mode === "paint" ? paintSurface(edit, null) : runHeightEdit(edit, null);
|
|
425
|
+
},
|
|
426
|
+
applyRecording(edit, record) {
|
|
427
|
+
return runHeightEdit(edit, record);
|
|
428
|
+
},
|
|
429
|
+
editDelta(edit) {
|
|
430
|
+
const indices = [];
|
|
431
|
+
const before = [];
|
|
432
|
+
const after = [];
|
|
433
|
+
runHeightEdit(edit, (index, prev, next) => {
|
|
434
|
+
indices.push(index);
|
|
435
|
+
before.push(prev);
|
|
436
|
+
after.push(next);
|
|
437
|
+
});
|
|
438
|
+
return { indices, before, after };
|
|
439
|
+
},
|
|
440
|
+
applyDelta(delta) {
|
|
441
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
442
|
+
offsets[delta.indices[i]] = delta.after[i];
|
|
443
|
+
},
|
|
444
|
+
revertDelta(delta) {
|
|
445
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
446
|
+
offsets[delta.indices[i]] = delta.before[i];
|
|
447
|
+
},
|
|
448
|
+
paintRecording(edit, record) {
|
|
449
|
+
return paintSurface(edit, record);
|
|
450
|
+
},
|
|
451
|
+
paintDelta(edit) {
|
|
452
|
+
return surfaceDeltaFrom((record) => paintSurface(edit, record));
|
|
453
|
+
},
|
|
454
|
+
fillSurfaceDelta(surface) {
|
|
455
|
+
return surfaceDeltaFrom((record) => fillSurface(surface, record));
|
|
456
|
+
},
|
|
457
|
+
autoPaintDelta(rule) {
|
|
458
|
+
return surfaceDeltaFrom((record) => autoPaint(rule, record));
|
|
459
|
+
},
|
|
460
|
+
applySurfaceDelta(delta) {
|
|
461
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
462
|
+
surfaces[delta.indices[i]] = delta.after[i] ?? null;
|
|
463
|
+
},
|
|
464
|
+
revertSurfaceDelta(delta) {
|
|
465
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
466
|
+
surfaces[delta.indices[i]] = delta.before[i] ?? null;
|
|
129
467
|
},
|
|
468
|
+
get layers() {
|
|
469
|
+
return layers;
|
|
470
|
+
},
|
|
471
|
+
setLayers(next) {
|
|
472
|
+
layers = next.map((layer) => ({ ...layer }));
|
|
473
|
+
// Layer count changed shape: drop the weight buffer, blends re-seed from surfaces on demand.
|
|
474
|
+
weights = null;
|
|
475
|
+
},
|
|
476
|
+
blendRecording(edit, record) {
|
|
477
|
+
return blendPaint(edit, record);
|
|
478
|
+
},
|
|
479
|
+
blendPaintDelta(edit) {
|
|
480
|
+
const indices = [];
|
|
481
|
+
const before = [];
|
|
482
|
+
const after = [];
|
|
483
|
+
blendPaint(edit, (index, prev, next) => {
|
|
484
|
+
indices.push(index);
|
|
485
|
+
before.push(prev);
|
|
486
|
+
after.push(next);
|
|
487
|
+
});
|
|
488
|
+
return { layerCount: layers.length, indices, before, after };
|
|
489
|
+
},
|
|
490
|
+
applyWeightDelta(delta) {
|
|
491
|
+
if (delta.layerCount !== layers.length)
|
|
492
|
+
return;
|
|
493
|
+
const buffer = ensureWeights();
|
|
494
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
495
|
+
buffer[delta.indices[i]] = delta.after[i];
|
|
496
|
+
},
|
|
497
|
+
revertWeightDelta(delta) {
|
|
498
|
+
if (delta.layerCount !== layers.length)
|
|
499
|
+
return;
|
|
500
|
+
const buffer = ensureWeights();
|
|
501
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
502
|
+
buffer[delta.indices[i]] = delta.before[i];
|
|
503
|
+
},
|
|
504
|
+
weightsAt,
|
|
130
505
|
heightOffsetAt,
|
|
131
506
|
surfaceAt,
|
|
132
507
|
reset() {
|
|
133
508
|
offsets.fill(0);
|
|
134
509
|
surfaces.fill(null);
|
|
510
|
+
weights = null;
|
|
135
511
|
},
|
|
136
512
|
snapshot() {
|
|
137
513
|
return {
|
|
@@ -141,6 +517,8 @@ export function createEditableTerrain(config) {
|
|
|
141
517
|
rows,
|
|
142
518
|
offsets: Array.from(offsets),
|
|
143
519
|
surfaces: surfaces.slice(),
|
|
520
|
+
...(layers.length === 0 ? {} : { layers: layers.map((layer) => ({ ...layer })) }),
|
|
521
|
+
...(weights === null ? {} : { weights: Array.from(weights) }),
|
|
144
522
|
};
|
|
145
523
|
},
|
|
146
524
|
restore(snapshot) {
|
|
@@ -150,10 +528,237 @@ export function createEditableTerrain(config) {
|
|
|
150
528
|
const surfaceCount = Math.min(surfaces.length, snapshot.surfaces.length);
|
|
151
529
|
for (let index = 0; index < surfaceCount; index += 1)
|
|
152
530
|
surfaces[index] = snapshot.surfaces[index] ?? null;
|
|
531
|
+
layers = (snapshot.layers ?? []).map((layer) => ({ ...layer }));
|
|
532
|
+
if (snapshot.weights === undefined || layers.length === 0) {
|
|
533
|
+
weights = null;
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
const buffer = new Float32Array(cellCount * layers.length);
|
|
537
|
+
const copy = Math.min(buffer.length, snapshot.weights.length);
|
|
538
|
+
for (let index = 0; index < copy; index += 1)
|
|
539
|
+
buffer[index] = snapshot.weights[index];
|
|
540
|
+
weights = buffer;
|
|
541
|
+
}
|
|
153
542
|
},
|
|
154
543
|
};
|
|
155
544
|
return field;
|
|
156
545
|
}
|
|
546
|
+
/**
|
|
547
|
+
* The shared first/latest Map accumulator behind every `begin*Stroke`. Records each key's first
|
|
548
|
+
* `before` and latest `after` across many stamps, then assembles the net changed entries (skipping
|
|
549
|
+
* keys whose latest equals their first). Each stroke supplies its own recording call via `recordStamp`
|
|
550
|
+
* and wraps {@link StrokeChanges} into its concrete delta shape.
|
|
551
|
+
*/
|
|
552
|
+
function createStroke(recordStamp) {
|
|
553
|
+
const first = new Map();
|
|
554
|
+
const latest = new Map();
|
|
555
|
+
return {
|
|
556
|
+
stamp(edit) {
|
|
557
|
+
return recordStamp(edit, (index, before, after) => {
|
|
558
|
+
if (!first.has(index))
|
|
559
|
+
first.set(index, before);
|
|
560
|
+
latest.set(index, after);
|
|
561
|
+
});
|
|
562
|
+
},
|
|
563
|
+
changes() {
|
|
564
|
+
const indices = [];
|
|
565
|
+
const before = [];
|
|
566
|
+
const after = [];
|
|
567
|
+
for (const [index, next] of latest) {
|
|
568
|
+
// Every latest key was set in `first` on the same callback, so this is always present.
|
|
569
|
+
const prev = first.get(index);
|
|
570
|
+
if (next === prev)
|
|
571
|
+
continue;
|
|
572
|
+
indices.push(index);
|
|
573
|
+
before.push(prev);
|
|
574
|
+
after.push(next);
|
|
575
|
+
}
|
|
576
|
+
return { indices, before, after };
|
|
577
|
+
},
|
|
578
|
+
isEmpty() {
|
|
579
|
+
return latest.size === 0;
|
|
580
|
+
},
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
/** Opens a stroke recorder over `terrain`; stamp edits into it, then read one net delta. */
|
|
584
|
+
export function beginTerraformStroke(terrain) {
|
|
585
|
+
const stroke = createStroke((edit, record) => terrain.applyRecording(edit, record));
|
|
586
|
+
return {
|
|
587
|
+
stamp: stroke.stamp,
|
|
588
|
+
delta: () => stroke.changes(),
|
|
589
|
+
isEmpty: stroke.isEmpty,
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
/** Opens a paint-stroke recorder over `terrain`; stamp paint edits into it, then read one net delta. */
|
|
593
|
+
export function beginSurfaceStroke(terrain) {
|
|
594
|
+
const stroke = createStroke((edit, record) => terrain.paintRecording(edit, record));
|
|
595
|
+
return {
|
|
596
|
+
stamp: stroke.stamp,
|
|
597
|
+
delta: () => stroke.changes(),
|
|
598
|
+
isEmpty: stroke.isEmpty,
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
/** Returns a new snapshot with a surface delta's `after` ids applied (copy-on-write). */
|
|
602
|
+
export function applySurfaceDeltaToSnapshot(snapshot, delta) {
|
|
603
|
+
const surfaces = snapshot.surfaces.slice();
|
|
604
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
605
|
+
surfaces[delta.indices[i]] = delta.after[i] ?? null;
|
|
606
|
+
return { ...snapshot, surfaces };
|
|
607
|
+
}
|
|
608
|
+
/** Returns a new snapshot with a surface delta's `before` ids restored (copy-on-write undo). */
|
|
609
|
+
export function revertSurfaceDeltaFromSnapshot(snapshot, delta) {
|
|
610
|
+
const surfaces = snapshot.surfaces.slice();
|
|
611
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
612
|
+
surfaces[delta.indices[i]] = delta.before[i] ?? null;
|
|
613
|
+
return { ...snapshot, surfaces };
|
|
614
|
+
}
|
|
615
|
+
/** The distinct non-null surface ids painted in a snapshot, in first-seen order. */
|
|
616
|
+
function distinctSurfaces(snapshot) {
|
|
617
|
+
const seen = new Set();
|
|
618
|
+
const out = [];
|
|
619
|
+
for (const surface of snapshot.surfaces) {
|
|
620
|
+
if (surface !== null && !seen.has(surface)) {
|
|
621
|
+
seen.add(surface);
|
|
622
|
+
out.push(surface);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return out;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Upgrades a pre-2.0 snapshot in place-safe (copy-on-write) form: derives a {@link TerrainMaterialLayer}
|
|
629
|
+
* stack from the distinct painted surfaces (first-seen order, default params) when none exists.
|
|
630
|
+
* Leaves the lazy `weights` buffer absent — a single-layer terrain stays compact until blended.
|
|
631
|
+
* Idempotent: a snapshot that already carries `layers` is returned unchanged.
|
|
632
|
+
*/
|
|
633
|
+
export function migrateTerrainSnapshot(snapshot) {
|
|
634
|
+
if (snapshot.layers !== undefined)
|
|
635
|
+
return snapshot;
|
|
636
|
+
const layers = distinctSurfaces(snapshot).map((surface) => ({
|
|
637
|
+
id: surface,
|
|
638
|
+
surface,
|
|
639
|
+
roughness: 0.9,
|
|
640
|
+
tiling: 8,
|
|
641
|
+
}));
|
|
642
|
+
return { ...snapshot, layers };
|
|
643
|
+
}
|
|
644
|
+
/** Materializes a snapshot's blend weights, seeding weight 1 on each cell's dominant surface. */
|
|
645
|
+
function materializeSnapshotWeights(snapshot) {
|
|
646
|
+
const layers = snapshot.layers ?? [];
|
|
647
|
+
const cells = snapshot.cols * snapshot.rows;
|
|
648
|
+
const buffer = new Array(cells * Math.max(1, layers.length)).fill(0);
|
|
649
|
+
if (snapshot.weights !== undefined) {
|
|
650
|
+
const copy = Math.min(buffer.length, snapshot.weights.length);
|
|
651
|
+
for (let i = 0; i < copy; i += 1)
|
|
652
|
+
buffer[i] = snapshot.weights[i];
|
|
653
|
+
return buffer;
|
|
654
|
+
}
|
|
655
|
+
if (layers.length > 0) {
|
|
656
|
+
for (let cell = 0; cell < cells; cell += 1) {
|
|
657
|
+
const surface = snapshot.surfaces[cell] ?? null;
|
|
658
|
+
const layer = surface === null ? -1 : layers.findIndex((entry) => entry.surface === surface);
|
|
659
|
+
if (layer >= 0)
|
|
660
|
+
buffer[cell * layers.length + layer] = 1;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return buffer;
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Returns a new snapshot with a weight delta's `after` values applied (materializing weights first).
|
|
667
|
+
* @internal — undo/redo plumbing behind the `blendTerrain` command.
|
|
668
|
+
*/
|
|
669
|
+
export function applyWeightDeltaToSnapshot(snapshot, delta) {
|
|
670
|
+
if (delta.layerCount !== (snapshot.layers?.length ?? 0))
|
|
671
|
+
return snapshot;
|
|
672
|
+
const weights = materializeSnapshotWeights(snapshot);
|
|
673
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
674
|
+
weights[delta.indices[i]] = delta.after[i];
|
|
675
|
+
return { ...snapshot, weights };
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Returns a new snapshot with a weight delta's `before` values restored (copy-on-write undo).
|
|
679
|
+
* @internal — undo/redo plumbing behind the `blendTerrain` command.
|
|
680
|
+
*/
|
|
681
|
+
export function revertWeightDeltaFromSnapshot(snapshot, delta) {
|
|
682
|
+
if (delta.layerCount !== (snapshot.layers?.length ?? 0))
|
|
683
|
+
return snapshot;
|
|
684
|
+
const weights = materializeSnapshotWeights(snapshot);
|
|
685
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
686
|
+
weights[delta.indices[i]] = delta.before[i];
|
|
687
|
+
return { ...snapshot, weights };
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Opens a blend-stroke recorder over `terrain`; stamp blend edits into it, then read one net delta.
|
|
691
|
+
* @internal — the stroke recorder behind the blend brush and the `blendTerrain` command.
|
|
692
|
+
*/
|
|
693
|
+
export function beginBlendStroke(terrain) {
|
|
694
|
+
const stroke = createStroke((edit, record) => terrain.blendRecording(edit, record));
|
|
695
|
+
return {
|
|
696
|
+
stamp: stroke.stamp,
|
|
697
|
+
// layerCount is read at delta() time, guarding replay after the layer stack changed shape.
|
|
698
|
+
delta: () => ({ layerCount: terrain.layers.length, ...stroke.changes() }),
|
|
699
|
+
isEmpty: stroke.isEmpty,
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* The world-space {@link Aabb} spanning the vertices a stroke's `indices` touched (indices into the
|
|
704
|
+
* `(cols+1)×(rows+1)` offset grid), expanded by `margin` meters. Drives partial preview-mesh
|
|
705
|
+
* rebuilds — only the dirty submesh re-samples instead of the whole heightfield. Null if empty.
|
|
706
|
+
* @internal — the editor's preview-mesh rebuild uses this; not a game-facing primitive.
|
|
707
|
+
*/
|
|
708
|
+
export function dirtyBoundsFromIndices(snapshot, indices, margin = 0) {
|
|
709
|
+
if (indices.length === 0)
|
|
710
|
+
return null;
|
|
711
|
+
const vertsX = snapshot.cols + 1;
|
|
712
|
+
const spanX = Math.max(snapshot.bounds.maxX - snapshot.bounds.minX, snapshot.cellSize);
|
|
713
|
+
const spanZ = Math.max(snapshot.bounds.maxZ - snapshot.bounds.minZ, snapshot.cellSize);
|
|
714
|
+
let minX = Infinity;
|
|
715
|
+
let minZ = Infinity;
|
|
716
|
+
let maxX = -Infinity;
|
|
717
|
+
let maxZ = -Infinity;
|
|
718
|
+
for (const index of indices) {
|
|
719
|
+
const gx = index % vertsX;
|
|
720
|
+
const gz = Math.floor(index / vertsX);
|
|
721
|
+
const wx = snapshot.bounds.minX + (gx / snapshot.cols) * spanX;
|
|
722
|
+
const wz = snapshot.bounds.minZ + (gz / snapshot.rows) * spanZ;
|
|
723
|
+
if (wx < minX)
|
|
724
|
+
minX = wx;
|
|
725
|
+
if (wx > maxX)
|
|
726
|
+
maxX = wx;
|
|
727
|
+
if (wz < minZ)
|
|
728
|
+
minZ = wz;
|
|
729
|
+
if (wz > maxZ)
|
|
730
|
+
maxZ = wz;
|
|
731
|
+
}
|
|
732
|
+
return { minX: minX - margin, minZ: minZ - margin, maxX: maxX + margin, maxZ: maxZ + margin };
|
|
733
|
+
}
|
|
734
|
+
/** A fresh, unedited terrain snapshot sized to `bounds`/`cellSize` — the seed for a new sculpt document. */
|
|
735
|
+
export function createTerrainSnapshot(config) {
|
|
736
|
+
return createEditableTerrain(config).snapshot();
|
|
737
|
+
}
|
|
738
|
+
/** Rebuilds a live {@link EditableTerrain} from a snapshot, layered over `base` ground. */
|
|
739
|
+
export function editableTerrainFromSnapshot(snapshot, base) {
|
|
740
|
+
const terrain = createEditableTerrain({
|
|
741
|
+
bounds: snapshot.bounds,
|
|
742
|
+
cellSize: snapshot.cellSize,
|
|
743
|
+
...(base === undefined ? {} : { base }),
|
|
744
|
+
});
|
|
745
|
+
terrain.restore(snapshot);
|
|
746
|
+
return terrain;
|
|
747
|
+
}
|
|
748
|
+
/** Returns a new snapshot with a delta's `after` offsets applied (copy-on-write — inputs untouched). */
|
|
749
|
+
export function applyDeltaToSnapshot(snapshot, delta) {
|
|
750
|
+
const offsets = snapshot.offsets.slice();
|
|
751
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
752
|
+
offsets[delta.indices[i]] = delta.after[i];
|
|
753
|
+
return { ...snapshot, offsets };
|
|
754
|
+
}
|
|
755
|
+
/** Returns a new snapshot with a delta's `before` offsets restored (copy-on-write undo). */
|
|
756
|
+
export function revertDeltaFromSnapshot(snapshot, delta) {
|
|
757
|
+
const offsets = snapshot.offsets.slice();
|
|
758
|
+
for (let i = 0; i < delta.indices.length; i += 1)
|
|
759
|
+
offsets[delta.indices[i]] = delta.before[i];
|
|
760
|
+
return { ...snapshot, offsets };
|
|
761
|
+
}
|
|
157
762
|
export function createTerraformBrush(terrain, config = {}) {
|
|
158
763
|
let radius = config.radius ?? 3;
|
|
159
764
|
let strength = config.strength ?? 0.5;
|