@jgengine/core 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -3
- package/LICENSE +203 -661
- package/README.md +1 -1
- package/dist/ai/crowd.d.ts +55 -0
- package/dist/ai/crowd.js +114 -0
- package/dist/ai/flock.d.ts +5 -2
- package/dist/ai/flock.js +5 -2
- package/dist/ai/groupAssist.d.ts +1 -0
- package/dist/ai/groupAssist.js +1 -0
- package/dist/ai/heatSystem.d.ts +76 -0
- package/dist/ai/heatSystem.js +97 -0
- package/dist/ai/interestScheduler.d.ts +107 -0
- package/dist/ai/interestScheduler.js +118 -0
- package/dist/ai/jobBoard.d.ts +1 -0
- package/dist/ai/jobBoard.js +1 -0
- package/dist/ai/laneSelect.d.ts +5 -2
- package/dist/ai/laneSelect.js +5 -2
- package/dist/ai/mobBrain.d.ts +1 -0
- package/dist/ai/mobBrain.js +1 -0
- package/dist/ai/spawnDirector.d.ts +19 -1
- package/dist/ai/spawnDirector.js +53 -26
- package/dist/ai/spawnPoint.d.ts +7 -0
- package/dist/ai/spawnPoint.js +7 -0
- package/dist/ai/targetAcquisition.d.ts +84 -0
- package/dist/ai/targetAcquisition.js +92 -0
- package/dist/anim/easing.d.ts +19 -0
- package/dist/anim/easing.js +19 -0
- package/dist/anim/oscillator.d.ts +12 -0
- package/dist/anim/oscillator.js +25 -0
- package/dist/area/areaEffectField.d.ts +127 -0
- package/dist/area/areaEffectField.js +175 -0
- package/dist/area/stackPolicy.d.ts +47 -0
- package/dist/area/stackPolicy.js +99 -0
- package/dist/audio/audioFalloff.d.ts +5 -2
- package/dist/audio/music.d.ts +53 -0
- package/dist/audio/music.js +34 -0
- package/dist/audio/synth.d.ts +60 -0
- package/dist/audio/synth.js +8 -0
- package/dist/authoring.d.ts +15 -0
- package/dist/authoring.js +15 -0
- package/dist/board/laneBoard.d.ts +13 -0
- package/dist/board/laneBoard.js +13 -0
- package/dist/board/timelineBoard.d.ts +10 -0
- package/dist/board/timelineBoard.js +10 -0
- package/dist/combat/abilityKit.d.ts +18 -0
- package/dist/combat/abilityKit.js +24 -9
- package/dist/combat/animationState.d.ts +5 -0
- package/dist/combat/animationState.js +5 -0
- package/dist/combat/breakMeters.d.ts +10 -0
- package/dist/combat/breakMeters.js +10 -0
- package/dist/combat/castRunner.d.ts +5 -0
- package/dist/combat/castRunner.js +5 -0
- package/dist/combat/comboPoints.d.ts +5 -0
- package/dist/combat/comboPoints.js +5 -0
- package/dist/combat/comboString.d.ts +5 -0
- package/dist/combat/comboString.js +5 -0
- package/dist/combat/damageInterceptors.d.ts +192 -0
- package/dist/combat/damageInterceptors.js +251 -0
- package/dist/combat/damageMatchup.d.ts +68 -0
- package/dist/combat/damageMatchup.js +42 -0
- package/dist/combat/damageResolution.d.ts +63 -0
- package/dist/combat/damageResolution.js +50 -0
- package/dist/combat/death.d.ts +7 -1
- package/dist/combat/death.js +5 -0
- package/dist/combat/deathReason.d.ts +14 -0
- package/dist/combat/deathReason.js +1 -0
- package/dist/combat/defensiveWindow.d.ts +5 -0
- package/dist/combat/defensiveWindow.js +5 -0
- package/dist/combat/dotField.d.ts +47 -0
- package/dist/combat/dotField.js +52 -0
- package/dist/combat/downed.d.ts +5 -0
- package/dist/combat/downed.js +5 -0
- package/dist/combat/effects.d.ts +5 -0
- package/dist/combat/effects.js +5 -0
- package/dist/combat/encounterSequence.d.ts +258 -0
- package/dist/combat/encounterSequence.js +426 -0
- package/dist/combat/hitReaction.d.ts +57 -1
- package/dist/combat/hitReaction.js +45 -5
- package/dist/combat/magazine.d.ts +54 -0
- package/dist/combat/magazine.js +96 -0
- package/dist/combat/projectiles.d.ts +7 -2
- package/dist/combat/projectiles.js +48 -17
- package/dist/combat/receivedDamage.d.ts +117 -0
- package/dist/combat/receivedDamage.js +92 -0
- package/dist/combat/regenShield.d.ts +39 -0
- package/dist/combat/regenShield.js +49 -0
- package/dist/combat/renderCues.d.ts +51 -0
- package/dist/combat/renderCues.js +44 -0
- package/dist/combat/resourcePool.d.ts +5 -0
- package/dist/combat/resourcePool.js +5 -0
- package/dist/combat/rewardAllocation.d.ts +121 -0
- package/dist/combat/rewardAllocation.js +251 -0
- package/dist/combat/shotOrigin.d.ts +30 -1
- package/dist/combat/shotOrigin.js +70 -2
- package/dist/combat/statusApplication.d.ts +96 -0
- package/dist/combat/statusApplication.js +81 -0
- package/dist/combat/telegraph.d.ts +11 -2
- package/dist/combat/telegraph.js +11 -2
- package/dist/combat/thresholdCrossings.d.ts +85 -0
- package/dist/combat/thresholdCrossings.js +116 -0
- package/dist/combat.d.ts +34 -0
- package/dist/combat.js +34 -0
- package/dist/commands/commandRegistry.d.ts +11 -0
- package/dist/commands/commandRegistry.js +9 -2
- package/dist/crafting/crop.d.ts +18 -0
- package/dist/crafting/crop.js +18 -0
- package/dist/crafting/production.d.ts +5 -0
- package/dist/crafting/production.js +5 -0
- package/dist/devtools/rewriteTunables.d.ts +17 -0
- package/dist/devtools/rewriteTunables.js +276 -0
- package/dist/devtools/saveEndpoint.d.ts +46 -0
- package/dist/devtools/saveEndpoint.js +48 -0
- package/dist/devtools/transformTunables.d.ts +3 -0
- package/dist/devtools/transformTunables.js +3 -0
- package/dist/devtools/tunableSchema.d.ts +31 -0
- package/dist/devtools/tunableSchema.js +31 -0
- package/dist/economy/currency.d.ts +12 -1
- package/dist/economy/currency.js +12 -1
- package/dist/economy/listingBook.d.ts +91 -0
- package/dist/economy/listingBook.js +144 -0
- package/dist/economy/resourceLedger.d.ts +222 -0
- package/dist/economy/resourceLedger.js +353 -0
- package/dist/economy/sharedWallet.d.ts +14 -0
- package/dist/economy/sharedWallet.js +14 -0
- package/dist/economy/techTree.d.ts +12 -0
- package/dist/economy/techTree.js +12 -0
- package/dist/economy/wallet.d.ts +26 -2
- package/dist/economy/wallet.js +35 -6
- package/dist/editor/commands.d.ts +248 -0
- package/dist/editor/commands.js +865 -0
- package/dist/editor/document.d.ts +186 -0
- package/dist/editor/document.js +884 -0
- package/dist/editor/grid.d.ts +229 -0
- package/dist/editor/grid.js +366 -0
- package/dist/editor/gridAdapters.d.ts +80 -0
- package/dist/editor/gridAdapters.js +134 -0
- package/dist/editor/index.d.ts +97 -0
- package/dist/editor/index.js +10 -0
- package/dist/editor/liveSync.d.ts +127 -0
- package/dist/editor/liveSync.js +249 -0
- package/dist/editor/runtimeInspector.d.ts +88 -0
- package/dist/editor/runtimeInspector.js +239 -0
- package/dist/editor/types.d.ts +175 -0
- package/dist/editor/types.js +29 -0
- package/dist/faction/reputation.d.ts +5 -0
- package/dist/faction/reputation.js +5 -0
- package/dist/format/distance.d.ts +17 -0
- package/dist/format/distance.js +18 -0
- package/dist/format/duration.d.ts +27 -0
- package/dist/format/duration.js +45 -0
- package/dist/format/speed.d.ts +17 -0
- package/dist/format/speed.js +25 -0
- package/dist/game/connectedPlayers.d.ts +30 -0
- package/dist/game/connectedPlayers.js +21 -0
- package/dist/game/cosmetics.d.ts +9 -0
- package/dist/game/cosmetics.js +16 -0
- package/dist/game/defineGame.d.ts +141 -2
- package/dist/game/defineGame.js +39 -1
- package/dist/game/defineSystem.d.ts +88 -0
- package/dist/game/defineSystem.js +45 -0
- package/dist/game/dialogue.d.ts +33 -0
- package/dist/game/dialogue.js +21 -0
- package/dist/game/events.d.ts +50 -13
- package/dist/game/events.js +5 -0
- package/dist/game/feed.d.ts +33 -0
- package/dist/game/feed.js +35 -2
- package/dist/game/gamePhase.d.ts +11 -0
- package/dist/game/gamePhase.js +16 -0
- package/dist/game/keyValueStore.d.ts +25 -0
- package/dist/game/keyValueStore.js +61 -0
- package/dist/game/leaderboard.d.ts +5 -0
- package/dist/game/leaderboard.js +5 -0
- package/dist/game/levelSequence.d.ts +24 -2
- package/dist/game/levelSequence.js +51 -2
- package/dist/game/loadout.d.ts +5 -0
- package/dist/game/loadout.js +5 -0
- package/dist/game/lootFilter.d.ts +2 -0
- package/dist/game/lootFilter.js +2 -0
- package/dist/game/lootPipeline.d.ts +164 -0
- package/dist/game/lootPipeline.js +216 -0
- package/dist/game/lootTable.d.ts +21 -0
- package/dist/game/lootTable.js +15 -4
- package/dist/game/modelAnimation.d.ts +8 -0
- package/dist/game/modelAnimation.js +18 -0
- package/dist/game/objectives.d.ts +43 -0
- package/dist/game/objectives.js +36 -0
- package/dist/game/paramLayers.d.ts +148 -0
- package/dist/game/paramLayers.js +185 -0
- package/dist/game/ping.d.ts +5 -0
- package/dist/game/ping.js +5 -0
- package/dist/game/playableGame.d.ts +160 -12
- package/dist/game/quest.d.ts +8 -0
- package/dist/game/quest.js +29 -0
- package/dist/game/race.d.ts +141 -0
- package/dist/game/race.js +169 -0
- package/dist/game/recordBook.d.ts +5 -5
- package/dist/game/recordBook.js +4 -1
- package/dist/game/ruleSelection.d.ts +75 -0
- package/dist/game/ruleSelection.js +127 -0
- package/dist/game/runDraft.d.ts +5 -0
- package/dist/game/runDraft.js +5 -0
- package/dist/game/saveStore.d.ts +111 -0
- package/dist/game/saveStore.js +312 -0
- package/dist/game/snapshotHistory.d.ts +67 -0
- package/dist/game/snapshotHistory.js +95 -0
- package/dist/game/social.d.ts +38 -1
- package/dist/game/social.js +71 -0
- package/dist/game/spawnPoints.d.ts +5 -0
- package/dist/game/spawnPoints.js +5 -0
- package/dist/game/systemRuntime.d.ts +50 -0
- package/dist/game/systemRuntime.js +157 -0
- package/dist/game/systemSchedule.d.ts +72 -0
- package/dist/game/systemSchedule.js +242 -0
- package/dist/game/toasts.d.ts +43 -0
- package/dist/game/toasts.js +44 -0
- package/dist/game/trade.d.ts +10 -0
- package/dist/game/trade.js +10 -0
- package/dist/game/unlocks.d.ts +8 -0
- package/dist/game/unlocks.js +16 -0
- package/dist/game/vfxInstance.d.ts +138 -0
- package/dist/game/vfxInstance.js +96 -0
- package/dist/game/worldItem.d.ts +17 -4
- package/dist/game/worldItem.js +17 -4
- package/dist/gameplay.d.ts +91 -0
- package/dist/gameplay.js +91 -0
- package/dist/index.d.ts +5 -7
- package/dist/index.js +5 -7
- package/dist/input/actionBindings.d.ts +24 -5
- package/dist/input/actionBindings.js +23 -5
- package/dist/input/axisInput.d.ts +13 -1
- package/dist/input/axisInput.js +22 -1
- package/dist/input/controlGroups.d.ts +67 -0
- package/dist/input/controlGroups.js +41 -0
- package/dist/input/touchScheme.d.ts +82 -6
- package/dist/input/touchScheme.js +123 -10
- package/dist/interaction/contextMenu.d.ts +1 -0
- package/dist/interaction/contextMenu.js +1 -0
- package/dist/interaction/lockpick.d.ts +61 -0
- package/dist/interaction/lockpick.js +168 -0
- package/dist/interaction/proximityPrompt.d.ts +5 -1
- package/dist/interaction/proximityPrompt.js +5 -3
- package/dist/interaction/qte.d.ts +5 -0
- package/dist/interaction/qte.js +5 -0
- package/dist/interaction/skillCheck.d.ts +5 -0
- package/dist/interaction/skillCheck.js +5 -0
- package/dist/inventory/inventoryModel.d.ts +12 -0
- package/dist/inventory/inventoryModel.js +12 -0
- package/dist/inventory/shapedGrid.d.ts +16 -0
- package/dist/inventory/shapedGrid.js +16 -0
- package/dist/inventory/slotModel.d.ts +10 -0
- package/dist/inventory/slotModel.js +10 -0
- package/dist/item/durability.d.ts +5 -0
- package/dist/item/durability.js +5 -0
- package/dist/item/itemIdentity.d.ts +171 -0
- package/dist/item/itemIdentity.js +138 -0
- package/dist/item/itemInstanceRegistry.d.ts +32 -0
- package/dist/item/itemInstanceRegistry.js +34 -0
- package/dist/item/itemgen.d.ts +163 -0
- package/dist/item/itemgen.js +151 -0
- package/dist/item/modularItem.d.ts +5 -0
- package/dist/item/modularItem.js +5 -0
- package/dist/item/use.d.ts +5 -0
- package/dist/item/use.js +5 -0
- package/dist/item/useBehavior.d.ts +142 -0
- package/dist/item/useBehavior.js +105 -0
- package/dist/item/weapon.d.ts +5 -0
- package/dist/item/weapon.js +5 -0
- package/dist/math/scalar.d.ts +25 -0
- package/dist/math/scalar.js +47 -0
- package/dist/meta/changelog.d.ts +4 -1
- package/dist/meta/changelog.js +36 -1
- package/dist/movement/avoidance.d.ts +45 -0
- package/dist/movement/avoidance.js +123 -0
- package/dist/movement/cameraRig.d.ts +8 -0
- package/dist/movement/cameraRig.js +8 -0
- package/dist/movement/dash.d.ts +11 -0
- package/dist/movement/dash.js +11 -0
- package/dist/movement/formation.d.ts +135 -0
- package/dist/movement/formation.js +194 -0
- package/dist/movement/glideModel.d.ts +5 -0
- package/dist/movement/glideModel.js +5 -0
- package/dist/movement/grappleSwing.d.ts +5 -0
- package/dist/movement/grappleSwing.js +5 -0
- package/dist/movement/leaderTrail.d.ts +5 -0
- package/dist/movement/leaderTrail.js +5 -0
- package/dist/movement/movementModel.d.ts +38 -7
- package/dist/movement/movementModel.js +54 -13
- package/dist/movement/playerMovement.d.ts +41 -0
- package/dist/movement/playerMovement.js +236 -0
- package/dist/movement/poseState.d.ts +13 -0
- package/dist/movement/poseState.js +16 -0
- package/dist/movement/steering.d.ts +8 -0
- package/dist/movement/steering.js +18 -0
- package/dist/multiplayer/combatSnapshot.d.ts +3 -0
- package/dist/multiplayer/combatSnapshot.js +3 -0
- package/dist/multiplayer/lagCompensation.d.ts +5 -0
- package/dist/multiplayer/lagCompensation.js +5 -0
- package/dist/multiplayer/presenceModel.d.ts +9 -1
- package/dist/multiplayer/presenceModel.js +9 -1
- package/dist/multiplayer/simultaneousCommit.d.ts +3 -0
- package/dist/multiplayer/simultaneousCommit.js +3 -0
- package/dist/multiplayer.d.ts +11 -0
- package/dist/multiplayer.js +11 -0
- package/dist/nav/pathFollow.d.ts +57 -0
- package/dist/nav/pathFollow.js +109 -0
- package/dist/orders/orderKinds.d.ts +156 -0
- package/dist/orders/orderKinds.js +197 -0
- package/dist/orders/orderQueue.d.ts +208 -0
- package/dist/orders/orderQueue.js +211 -0
- package/dist/physics/drivableVehicle.d.ts +29 -0
- package/dist/physics/drivableVehicle.js +16 -0
- package/dist/physics/kinematicVehicle.d.ts +21 -1
- package/dist/physics/kinematicVehicle.js +23 -8
- package/dist/physics/physicsWorld.d.ts +24 -12
- package/dist/physics/physicsWorld.js +11 -5
- package/dist/physics/radialImpulse.d.ts +25 -0
- package/dist/physics/radialImpulse.js +27 -0
- package/dist/procedural.d.ts +3 -0
- package/dist/procedural.js +3 -0
- package/dist/progression/statGraph.d.ts +116 -0
- package/dist/progression/statGraph.js +318 -0
- package/dist/puzzle/cellGrid.d.ts +9 -0
- package/dist/puzzle/cellGrid.js +9 -0
- package/dist/puzzle/fallingPiece.d.ts +9 -0
- package/dist/puzzle/fallingPiece.js +9 -0
- package/dist/puzzle/nonogram.d.ts +39 -0
- package/dist/puzzle/nonogram.js +136 -0
- package/dist/random/nameGen.d.ts +5 -0
- package/dist/random/nameGen.js +5 -0
- package/dist/random/pick.d.ts +8 -0
- package/dist/random/pick.js +31 -0
- package/dist/random/rng.d.ts +24 -0
- package/dist/random/rng.js +30 -8
- package/dist/random/seedLink.d.ts +4 -0
- package/dist/random/seedLink.js +4 -0
- package/dist/relation/keyedValues.d.ts +54 -0
- package/dist/relation/keyedValues.js +115 -0
- package/dist/relation/thresholds.d.ts +62 -0
- package/dist/relation/thresholds.js +62 -0
- package/dist/render/color.d.ts +12 -0
- package/dist/render/color.js +26 -0
- package/dist/render/lookPreset.d.ts +41 -0
- package/dist/render/lookPreset.js +29 -0
- package/dist/render/postProcessing.d.ts +76 -0
- package/dist/render/postProcessing.js +21 -0
- package/dist/rules/predicate.d.ts +53 -0
- package/dist/rules/predicate.js +64 -0
- package/dist/rules/ruleEffects.d.ts +38 -0
- package/dist/rules/ruleEffects.js +37 -0
- package/dist/rules/triggeredRules.d.ts +160 -0
- package/dist/rules/triggeredRules.js +241 -0
- package/dist/runtime/adapter.d.ts +58 -0
- package/dist/runtime/adapter.js +54 -5
- package/dist/runtime/cameraDirector.d.ts +1 -0
- package/dist/runtime/cameraDirector.js +1 -0
- package/dist/runtime/commandRunner.d.ts +1 -0
- package/dist/runtime/commandRunner.js +1 -0
- package/dist/runtime/context/combatFx.d.ts +23 -0
- package/dist/runtime/context/combatFx.js +161 -0
- package/dist/runtime/context/registries.d.ts +13 -0
- package/dist/runtime/context/registries.js +84 -0
- package/dist/runtime/context/worldItems.d.ts +21 -0
- package/dist/runtime/context/worldItems.js +37 -0
- package/dist/runtime/descriptors/baseline.d.ts +48 -0
- package/dist/runtime/descriptors/baseline.js +134 -0
- package/dist/runtime/descriptors/features.d.ts +74 -0
- package/dist/runtime/descriptors/features.js +281 -0
- package/dist/runtime/gameContext.d.ts +163 -19
- package/dist/runtime/gameContext.js +385 -339
- package/dist/runtime/gameRuntime.d.ts +1 -0
- package/dist/runtime/gameRuntime.js +1 -0
- package/dist/runtime/headlessRunner.d.ts +60 -0
- package/dist/runtime/headlessRunner.js +44 -0
- package/dist/runtime/hostPersistence.d.ts +12 -0
- package/dist/runtime/hostPersistence.js +12 -0
- package/dist/runtime/hostedGameRunner.d.ts +54 -0
- package/dist/runtime/hostedGameRunner.js +85 -0
- package/dist/runtime/hostedWorldSession.d.ts +73 -0
- package/dist/runtime/hostedWorldSession.js +64 -0
- package/dist/runtime/inputSnapshot.d.ts +21 -3
- package/dist/runtime/inputSnapshot.js +8 -2
- package/dist/runtime/motionIntents.d.ts +12 -1
- package/dist/runtime/motionIntents.js +26 -1
- package/dist/runtime/perContext.d.ts +15 -0
- package/dist/runtime/perContext.js +21 -0
- package/dist/runtime/runtimeSave.d.ts +79 -0
- package/dist/runtime/runtimeSave.js +105 -0
- package/dist/runtime/save.d.ts +4 -0
- package/dist/runtime/save.js +4 -0
- package/dist/runtime/snapshot.d.ts +7 -0
- package/dist/runtime/snapshot.js +7 -0
- package/dist/runtime/visibility.d.ts +2 -1
- package/dist/runtime/visibility.js +2 -1
- package/dist/runtime/worldChannel.d.ts +62 -0
- package/dist/runtime/worldChannel.js +114 -0
- package/dist/runtime/worldMirror.d.ts +36 -0
- package/dist/runtime/worldMirror.js +44 -0
- package/dist/runtime/worldProjection.d.ts +47 -0
- package/dist/runtime/worldProjection.js +52 -0
- package/dist/runtime/worldReplication.d.ts +69 -0
- package/dist/runtime/worldReplication.js +217 -0
- package/dist/runtime/worldSnapshot.d.ts +44 -0
- package/dist/runtime/worldSnapshot.js +30 -0
- package/dist/scene/assetCatalog.d.ts +22 -0
- package/dist/scene/assetCatalog.js +4 -0
- package/dist/scene/assetDiagnostics.d.ts +69 -0
- package/dist/scene/assetDiagnostics.js +110 -0
- package/dist/scene/assetGenerator.d.ts +65 -0
- package/dist/scene/assetGenerator.js +64 -0
- package/dist/scene/assetSpace.d.ts +137 -0
- package/dist/scene/assetSpace.js +259 -0
- package/dist/scene/authoredTriggers.d.ts +169 -0
- package/dist/scene/authoredTriggers.js +340 -0
- package/dist/scene/behaviorRuntime.d.ts +75 -0
- package/dist/scene/behaviorRuntime.js +217 -0
- package/dist/scene/behaviors.d.ts +14 -2
- package/dist/scene/behaviors.js +7 -2
- package/dist/scene/bodyBind.d.ts +60 -0
- package/dist/scene/bodyBind.js +51 -0
- package/dist/scene/builtinSceneKinds.d.ts +5 -0
- package/dist/scene/builtinSceneKinds.js +62 -0
- package/dist/scene/colliders.d.ts +18 -0
- package/dist/scene/colliders.js +61 -1
- package/dist/scene/entityStats.d.ts +13 -0
- package/dist/scene/entityStats.js +30 -0
- package/dist/scene/entityStore.d.ts +41 -1
- package/dist/scene/entityStore.js +75 -0
- package/dist/scene/facing.d.ts +18 -0
- package/dist/scene/facing.js +26 -0
- package/dist/scene/modelSockets.d.ts +33 -0
- package/dist/scene/modelSockets.js +29 -0
- package/dist/scene/objectQuery.d.ts +2 -0
- package/dist/scene/objectQuery.js +2 -0
- package/dist/scene/objectStore.d.ts +4 -0
- package/dist/scene/objectStore.js +6 -0
- package/dist/scene/possession.d.ts +8 -0
- package/dist/scene/possession.js +14 -0
- package/dist/scene/roster.d.ts +3 -0
- package/dist/scene/roster.js +12 -0
- package/dist/scene/sceneKinds.d.ts +240 -0
- package/dist/scene/sceneKinds.js +214 -0
- package/dist/scene/sceneOwnership.d.ts +169 -0
- package/dist/scene/sceneOwnership.js +179 -0
- package/dist/scene/selectionBookmarks.d.ts +93 -0
- package/dist/scene/selectionBookmarks.js +122 -0
- package/dist/scene/vehicleSeat.d.ts +68 -0
- package/dist/scene/vehicleSeat.js +59 -0
- package/dist/sensor/concealment.d.ts +3 -0
- package/dist/sensor/concealment.js +3 -0
- package/dist/sensor/freezeMonitor.d.ts +1 -0
- package/dist/sensor/freezeMonitor.js +1 -0
- package/dist/sensor/frustumSensor.d.ts +7 -3
- package/dist/sensor/frustumSensor.js +7 -3
- package/dist/sensor/hiddenStateProbe.d.ts +5 -2
- package/dist/sensor/hiddenStateProbe.js +5 -2
- package/dist/sensor/recordingBuffer.d.ts +2 -1
- package/dist/sensor/recordingBuffer.js +2 -1
- package/dist/sensor/replayLoop.d.ts +6 -2
- package/dist/sensor/replayLoop.js +6 -2
- package/dist/sensor/revealQuery.d.ts +2 -1
- package/dist/sensor/revealQuery.js +2 -1
- package/dist/sensor/visionCone.d.ts +10 -3
- package/dist/sensor/visionCone.js +10 -3
- package/dist/session/contestedChannel.d.ts +1 -0
- package/dist/session/contestedChannel.js +1 -0
- package/dist/session/extraction.d.ts +1 -0
- package/dist/session/extraction.js +1 -0
- package/dist/session/roles.d.ts +6 -0
- package/dist/session/roles.js +6 -0
- package/dist/session/roundState.d.ts +8 -0
- package/dist/session/roundState.js +2 -0
- package/dist/settings/settingsModel.d.ts +1 -0
- package/dist/settings/settingsModel.js +1 -0
- package/dist/stats/accumulatorMeter.d.ts +6 -0
- package/dist/stats/accumulatorMeter.js +6 -0
- package/dist/stats/eventMeter.d.ts +5 -0
- package/dist/stats/eventMeter.js +5 -0
- package/dist/stats/rollCheck.d.ts +5 -0
- package/dist/stats/rollCheck.js +5 -0
- package/dist/stats/statModifiers.d.ts +5 -0
- package/dist/stats/statModifiers.js +5 -0
- package/dist/store/defineKeyedStore.d.ts +38 -0
- package/dist/store/defineKeyedStore.js +42 -0
- package/dist/store/defineStore.d.ts +33 -0
- package/dist/store/defineStore.js +35 -0
- package/dist/store/observableKeyedStore.d.ts +9 -0
- package/dist/store/observableKeyedStore.js +43 -2
- package/dist/survival/decayMeter.d.ts +55 -0
- package/dist/survival/decayMeter.js +130 -55
- package/dist/survival/moodle.d.ts +2 -0
- package/dist/survival/regionHealth.d.ts +6 -0
- package/dist/survival/regionHealth.js +5 -0
- package/dist/tactics/fallingGrid.d.ts +6 -4
- package/dist/tactics/fallingGrid.js +6 -4
- package/dist/tactics/predictiveQuery.d.ts +3 -0
- package/dist/tactics/predictiveQuery.js +3 -0
- package/dist/tactics/snapshot.d.ts +2 -0
- package/dist/tactics/snapshot.js +2 -0
- package/dist/tactics/surface.d.ts +1 -0
- package/dist/tactics/surface.js +1 -0
- package/dist/tactics/tacticalGrid.d.ts +1 -0
- package/dist/tactics/tacticalGrid.js +1 -0
- package/dist/time/beatClock.d.ts +14 -4
- package/dist/time/beatClock.js +14 -4
- package/dist/time/calendarClock.d.ts +3 -0
- package/dist/time/calendarClock.js +3 -0
- package/dist/time/idleProgress.d.ts +4 -0
- package/dist/time/idleProgress.js +4 -0
- package/dist/time/simClock.d.ts +2 -0
- package/dist/time/simClock.js +20 -5
- package/dist/ui/actionModel.d.ts +110 -0
- package/dist/ui/actionModel.js +159 -0
- package/dist/ui/gameLayout.d.ts +144 -0
- package/dist/ui/gameLayout.js +148 -0
- package/dist/ui/hudDocument.d.ts +114 -0
- package/dist/ui/hudDocument.js +220 -0
- package/dist/ui/hudLayout.d.ts +33 -1
- package/dist/ui/hudLayout.js +204 -17
- package/dist/ui/orientation.d.ts +39 -0
- package/dist/ui/orientation.js +49 -0
- package/dist/ui/selectionModel.d.ts +92 -0
- package/dist/ui/selectionModel.js +88 -0
- package/dist/ui/swingTimer.d.ts +42 -0
- package/dist/ui/swingTimer.js +38 -0
- package/dist/ui/tooltipModel.d.ts +63 -0
- package/dist/ui/tooltipModel.js +82 -0
- package/dist/ui.d.ts +15 -0
- package/dist/ui.js +15 -0
- package/dist/visibility/bounds.d.ts +8 -1
- package/dist/visibility/bounds.js +8 -1
- package/dist/visibility/frustum.d.ts +12 -3
- package/dist/visibility/frustum.js +12 -3
- package/dist/visibility/spatialIndex.js +34 -20
- package/dist/work/jobQueue.d.ts +201 -0
- package/dist/work/jobQueue.js +253 -0
- package/dist/work/unitTraining.d.ts +58 -0
- package/dist/work/unitTraining.js +52 -0
- package/dist/world/authoredObjects.d.ts +58 -0
- package/dist/world/authoredObjects.js +69 -0
- package/dist/world/buildingGenerator.d.ts +18 -0
- package/dist/world/buildingGenerator.js +62 -0
- package/dist/world/buildings.d.ts +5 -0
- package/dist/world/buildings.js +5 -0
- package/dist/world/catenary.d.ts +22 -0
- package/dist/world/catenary.js +69 -0
- package/dist/world/environmentSummary.d.ts +11 -2
- package/dist/world/environmentSummary.js +11 -1
- package/dist/world/features.d.ts +244 -7
- package/dist/world/features.js +92 -4
- package/dist/world/footprintGrid.d.ts +59 -0
- package/dist/world/footprintGrid.js +141 -0
- package/dist/world/geometry.d.ts +64 -0
- package/dist/world/geometry.js +94 -0
- package/dist/world/grassKind.d.ts +51 -0
- package/dist/world/grassKind.js +101 -0
- package/dist/world/pathInstances.d.ts +27 -0
- package/dist/world/pathInstances.js +27 -0
- package/dist/world/pathTerrain.d.ts +80 -0
- package/dist/world/pathTerrain.js +142 -0
- package/dist/world/placeAsset.d.ts +81 -0
- package/dist/world/placeAsset.js +77 -0
- package/dist/world/placement.d.ts +4 -0
- package/dist/world/placement.js +4 -0
- package/dist/world/placementController.d.ts +6 -0
- package/dist/world/placementController.js +6 -0
- package/dist/world/polyline.d.ts +34 -0
- package/dist/world/polyline.js +81 -0
- package/dist/world/roads.d.ts +55 -0
- package/dist/world/roads.js +184 -0
- package/dist/world/scatterRegion.d.ts +164 -0
- package/dist/world/scatterRegion.js +409 -0
- package/dist/world/sceneShapes.d.ts +43 -0
- package/dist/world/sceneShapes.js +1 -0
- package/dist/world/segment.d.ts +27 -0
- package/dist/world/segment.js +43 -0
- package/dist/world/soilKind.d.ts +48 -0
- package/dist/world/soilKind.js +92 -0
- package/dist/world/spatialSample.d.ts +241 -0
- package/dist/world/spatialSample.js +465 -0
- package/dist/world/streets.d.ts +80 -0
- package/dist/world/streets.js +144 -0
- package/dist/world/terraform.d.ts +213 -1
- package/dist/world/terraform.js +655 -20
- package/dist/world/terrain.d.ts +138 -12
- package/dist/world/terrain.js +346 -28
- package/dist/world/terrainGuides.d.ts +182 -0
- package/dist/world/terrainGuides.js +343 -0
- package/dist/world/vec2.d.ts +81 -0
- package/dist/world/vec2.js +128 -0
- package/dist/world/vec3.d.ts +49 -0
- package/dist/world/vec3.js +76 -0
- package/dist/world/vegetation.d.ts +71 -0
- package/dist/world/vegetation.js +151 -0
- package/dist/world/visibilityField.d.ts +154 -0
- package/dist/world/visibilityField.js +271 -0
- package/dist/world/volumetricClouds.d.ts +52 -0
- package/dist/world/volumetricClouds.js +43 -0
- package/dist/world/walls.d.ts +9 -0
- package/dist/world/walls.js +9 -0
- package/dist/world/waterKind.d.ts +55 -0
- package/dist/world/waterKind.js +99 -0
- package/dist/world.d.ts +132 -0
- package/dist/world.js +132 -0
- package/package.json +32 -8
- package/skills/jgengine/SKILL.md +51 -0
- package/skills/jgengine/api.md +434 -0
- package/skills/jgengine/capabilities.md +29 -0
- package/skills/jgengine/reference-cartridge.md +72 -0
- package/skills/jgengine-combat/SKILL.md +34 -0
- package/skills/jgengine-combat/api.md +549 -0
- package/skills/jgengine-combat/capabilities.md +169 -0
- package/skills/jgengine-combat/reference.md +98 -0
- package/skills/jgengine-gameplay/SKILL.md +39 -0
- package/skills/jgengine-gameplay/api.md +1794 -0
- package/skills/jgengine-gameplay/capabilities.md +378 -0
- package/skills/jgengine-gameplay/reference-systems.md +60 -0
- package/skills/jgengine-world/SKILL.md +38 -0
- package/skills/jgengine-world/api.md +2652 -0
- package/skills/jgengine-world/capabilities.md +370 -0
- package/skills/jgengine-world/reference.md +222 -0
- package/dist/cartridge/runtime.d.ts +0 -15
- package/dist/cartridge/runtime.js +0 -556
- package/dist/cartridge/spec.d.ts +0 -188
- package/dist/cartridge/spec.js +0 -19
- package/dist/cartridge/validate.d.ts +0 -2
- package/dist/cartridge/validate.js +0 -78
- package/dist/store/engineStore.d.ts +0 -22
- package/dist/store/engineStore.js +0 -50
- package/dist/store/features.d.ts +0 -10
- package/dist/store/features.js +0 -23
- package/llms.txt +0 -4983
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
import { pointInAabb } from "./geometry.js";
|
|
2
|
+
import { pointInPolygon, polygonBounds } from "./scatterRegion.js";
|
|
3
|
+
const DEFAULT_MAX_ATTEMPTS = 30;
|
|
4
|
+
const TWO_PI = Math.PI * 2;
|
|
5
|
+
function distanceSq(a, b) {
|
|
6
|
+
const n = a.length < b.length ? a.length : b.length;
|
|
7
|
+
let sum = 0;
|
|
8
|
+
for (let i = 0; i < n; i += 1) {
|
|
9
|
+
const d = a[i] - b[i];
|
|
10
|
+
sum += d * d;
|
|
11
|
+
}
|
|
12
|
+
return sum;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A uniform spatial hash over cells of `cellSize`, used to enforce minimum separation in O(1)
|
|
16
|
+
* neighborhood checks instead of an O(n²) scan — the allocation-aware index behind batch spacing.
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
class SeparationIndex {
|
|
20
|
+
cellSize;
|
|
21
|
+
dimensions;
|
|
22
|
+
buckets = new Map();
|
|
23
|
+
minSq;
|
|
24
|
+
constructor(cellSize, dimensions) {
|
|
25
|
+
this.cellSize = cellSize;
|
|
26
|
+
this.dimensions = dimensions;
|
|
27
|
+
this.minSq = cellSize * cellSize;
|
|
28
|
+
}
|
|
29
|
+
key(cell) {
|
|
30
|
+
return cell.join(":");
|
|
31
|
+
}
|
|
32
|
+
cellOf(point) {
|
|
33
|
+
const cell = [];
|
|
34
|
+
for (let i = 0; i < this.dimensions; i += 1)
|
|
35
|
+
cell.push(Math.floor(point[i] / this.cellSize));
|
|
36
|
+
return cell;
|
|
37
|
+
}
|
|
38
|
+
/** True when `point` sits within `cellSize` of any inserted point. */
|
|
39
|
+
tooClose(point) {
|
|
40
|
+
const base = this.cellOf(point);
|
|
41
|
+
const dims = this.dimensions;
|
|
42
|
+
const stack = [];
|
|
43
|
+
const walk = (axis) => {
|
|
44
|
+
if (axis === dims) {
|
|
45
|
+
const bucket = this.buckets.get(this.key(stack));
|
|
46
|
+
if (bucket !== undefined)
|
|
47
|
+
for (const other of bucket)
|
|
48
|
+
if (distanceSq(point, other) < this.minSq)
|
|
49
|
+
return true;
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
for (let d = -1; d <= 1; d += 1) {
|
|
53
|
+
stack[axis] = base[axis] + d;
|
|
54
|
+
if (walk(axis + 1))
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
};
|
|
59
|
+
return walk(0);
|
|
60
|
+
}
|
|
61
|
+
insert(point) {
|
|
62
|
+
const key = this.key(this.cellOf(point));
|
|
63
|
+
const bucket = this.buckets.get(key);
|
|
64
|
+
if (bucket !== undefined)
|
|
65
|
+
bucket.push(point);
|
|
66
|
+
else
|
|
67
|
+
this.buckets.set(key, [point]);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function passesConstraints(point, constraints, index) {
|
|
71
|
+
if (constraints === undefined)
|
|
72
|
+
return index === null || !index.tooClose(point);
|
|
73
|
+
if (constraints.include !== undefined)
|
|
74
|
+
for (const region of constraints.include)
|
|
75
|
+
if (!region.contains(point))
|
|
76
|
+
return false;
|
|
77
|
+
if (constraints.exclude !== undefined)
|
|
78
|
+
for (const region of constraints.exclude)
|
|
79
|
+
if (region.contains(point))
|
|
80
|
+
return false;
|
|
81
|
+
if (index !== null && index.tooClose(point))
|
|
82
|
+
return false;
|
|
83
|
+
if (constraints.accept !== undefined && !constraints.accept(point))
|
|
84
|
+
return false;
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
function resolveFallback(fallback, last, attempts, reason) {
|
|
88
|
+
if (fallback !== undefined && fallback !== "none") {
|
|
89
|
+
if (fallback === "last-candidate") {
|
|
90
|
+
if (last !== null)
|
|
91
|
+
return { ok: false, point: last, attempts, reason, usedFallback: true };
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
return { ok: false, point: fallback.point, attempts, reason, usedFallback: true };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return { ok: false, point: null, attempts, reason, usedFallback: false };
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Draw one point from `region` that clears every constraint, retrying up to `maxAttempts` times with
|
|
101
|
+
* the injected `rng`, then apply the `fallback` policy. Returns a structured {@link SampleResult} —
|
|
102
|
+
* a clean hit is `ok: true`; exhaustion or a degenerate region is reported with the attempt count and
|
|
103
|
+
* an explicit fallback flag, never a silently-accepted bad point.
|
|
104
|
+
*
|
|
105
|
+
* @capability spatial-sample draw one deterministic position from a region under include/exclude/distance constraints
|
|
106
|
+
*/
|
|
107
|
+
export function samplePoint(options) {
|
|
108
|
+
const { region, rng, constraints } = options;
|
|
109
|
+
const maxAttempts = options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
110
|
+
if (region.isEmpty)
|
|
111
|
+
return resolveFallback(options.fallback, null, 0, "empty-region");
|
|
112
|
+
let index = null;
|
|
113
|
+
const minSeparation = constraints?.minSeparation ?? 0;
|
|
114
|
+
const placed = options.placed ?? [];
|
|
115
|
+
if (minSeparation > 0 && placed.length > 0) {
|
|
116
|
+
index = new SeparationIndex(minSeparation, region.dimensions);
|
|
117
|
+
for (const point of placed)
|
|
118
|
+
index.insert(point);
|
|
119
|
+
}
|
|
120
|
+
let last = null;
|
|
121
|
+
let attempts = 0;
|
|
122
|
+
while (attempts < maxAttempts) {
|
|
123
|
+
attempts += 1;
|
|
124
|
+
let candidate = region.sample(rng);
|
|
125
|
+
if (constraints?.project !== undefined)
|
|
126
|
+
candidate = constraints.project(candidate);
|
|
127
|
+
if (candidate === null)
|
|
128
|
+
continue;
|
|
129
|
+
last = candidate;
|
|
130
|
+
if (passesConstraints(candidate, constraints, index)) {
|
|
131
|
+
return { ok: true, point: candidate, attempts, reason: "accepted", usedFallback: false };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return resolveFallback(options.fallback, last, attempts, "exhausted");
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Place up to `count` points from `region`, each clearing the shared constraints and honoring
|
|
138
|
+
* `minSeparation` against every point already placed in this batch (via a spatial hash, not an
|
|
139
|
+
* O(n²) scan). Order is deterministic for a given `rng` stream. Batches never silently under-fill:
|
|
140
|
+
* the result reports `placed`, `complete`, and the total attempt count.
|
|
141
|
+
*
|
|
142
|
+
* @capability spatial-sample-batch place many deterministic spaced points from a region with minimum separation
|
|
143
|
+
*/
|
|
144
|
+
export function sampleBatch(options) {
|
|
145
|
+
const { region, rng, constraints } = options;
|
|
146
|
+
const maxAttempts = options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
147
|
+
const requested = Math.max(0, Math.floor(options.count));
|
|
148
|
+
const points = [];
|
|
149
|
+
if (requested === 0 || region.isEmpty) {
|
|
150
|
+
return { points, requested, placed: 0, complete: requested === 0, attempts: 0 };
|
|
151
|
+
}
|
|
152
|
+
const minSeparation = constraints?.minSeparation ?? 0;
|
|
153
|
+
const index = minSeparation > 0 ? new SeparationIndex(minSeparation, region.dimensions) : null;
|
|
154
|
+
let attempts = 0;
|
|
155
|
+
for (let i = 0; i < requested; i += 1) {
|
|
156
|
+
let placed = null;
|
|
157
|
+
let tries = 0;
|
|
158
|
+
while (tries < maxAttempts) {
|
|
159
|
+
tries += 1;
|
|
160
|
+
attempts += 1;
|
|
161
|
+
let candidate = region.sample(rng);
|
|
162
|
+
if (constraints?.project !== undefined)
|
|
163
|
+
candidate = constraints.project(candidate);
|
|
164
|
+
if (candidate === null)
|
|
165
|
+
continue;
|
|
166
|
+
if (passesConstraints(candidate, constraints, index)) {
|
|
167
|
+
placed = candidate;
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (placed === null)
|
|
172
|
+
break;
|
|
173
|
+
points.push(placed);
|
|
174
|
+
if (index !== null)
|
|
175
|
+
index.insert(placed);
|
|
176
|
+
}
|
|
177
|
+
return { points, requested, placed: points.length, complete: points.length === requested, attempts };
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* A fixed set of candidate points sampled by (optionally weighted) selection — the "spawn point
|
|
181
|
+
* table" region. `contains` is exact membership. Empty when the set is empty.
|
|
182
|
+
*
|
|
183
|
+
* @capability spatial-region-point-set sample from a fixed, optionally weighted candidate point table
|
|
184
|
+
*/
|
|
185
|
+
export function pointSetRegion(points, options = {}) {
|
|
186
|
+
const pool = points.map((p) => [p[0], p[1]]);
|
|
187
|
+
const weights = options.weights;
|
|
188
|
+
const total = weights === undefined ? pool.length : weights.reduce((sum, w) => sum + Math.max(0, w), 0);
|
|
189
|
+
return {
|
|
190
|
+
dimensions: 2,
|
|
191
|
+
isEmpty: pool.length === 0 || total <= 0,
|
|
192
|
+
sample(rng) {
|
|
193
|
+
if (weights === undefined)
|
|
194
|
+
return pool[Math.min(pool.length - 1, Math.floor(rng() * pool.length))];
|
|
195
|
+
let roll = rng() * total;
|
|
196
|
+
for (let i = 0; i < pool.length; i += 1) {
|
|
197
|
+
roll -= Math.max(0, weights[i] ?? 0);
|
|
198
|
+
if (roll <= 0)
|
|
199
|
+
return pool[i];
|
|
200
|
+
}
|
|
201
|
+
return pool[pool.length - 1];
|
|
202
|
+
},
|
|
203
|
+
contains(point) {
|
|
204
|
+
return pool.some((p) => p[0] === point[0] && p[1] === point[1]);
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* An axis-aligned rectangle on the XZ plane. `"uniform"` spreads evenly (draws x then z);
|
|
210
|
+
* `"edge"` biases onto a boundary band of `edgeThickness` meters (perimeter-weighted side, then
|
|
211
|
+
* inward depth) — the two are never conflated, the caller opts in.
|
|
212
|
+
*/
|
|
213
|
+
export function rectRegion(area, options = {}) {
|
|
214
|
+
const width = area.maxX - area.minX;
|
|
215
|
+
const depth = area.maxZ - area.minZ;
|
|
216
|
+
const distribution = options.distribution ?? "uniform";
|
|
217
|
+
const edgeThickness = Math.max(0, options.edgeThickness ?? Math.min(width, depth) * 0.25);
|
|
218
|
+
return {
|
|
219
|
+
dimensions: 2,
|
|
220
|
+
isEmpty: !(width > 0) || !(depth > 0),
|
|
221
|
+
sample(rng) {
|
|
222
|
+
if (distribution === "edge") {
|
|
223
|
+
const perim = 2 * (width + depth);
|
|
224
|
+
const sidePick = rng() * perim;
|
|
225
|
+
const t = rng();
|
|
226
|
+
const inset = Math.min(edgeThickness, Math.min(width, depth) / 2) * rng();
|
|
227
|
+
if (sidePick < width)
|
|
228
|
+
return [area.minX + t * width, area.minZ + inset];
|
|
229
|
+
if (sidePick < width + depth)
|
|
230
|
+
return [area.maxX - inset, area.minZ + t * depth];
|
|
231
|
+
if (sidePick < 2 * width + depth)
|
|
232
|
+
return [area.minX + t * width, area.maxZ - inset];
|
|
233
|
+
return [area.minX + inset, area.minZ + t * depth];
|
|
234
|
+
}
|
|
235
|
+
const x = area.minX + rng() * width;
|
|
236
|
+
const z = area.minZ + rng() * depth;
|
|
237
|
+
return [x, z];
|
|
238
|
+
},
|
|
239
|
+
contains(point) {
|
|
240
|
+
return pointInAabb(point, area);
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function radialRadius(rng, inner, outer, distribution) {
|
|
245
|
+
const u = rng();
|
|
246
|
+
if (distribution === "area")
|
|
247
|
+
return Math.sqrt(inner * inner + u * (outer * outer - inner * inner));
|
|
248
|
+
return inner + u * (outer - inner);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* A filled circle. `"area"` is area-uniform (√-corrected radius, even density); `"radial"` is
|
|
252
|
+
* radius-uniform (clumps toward the center). Angle is drawn first, then radius — matching the common
|
|
253
|
+
* `angle = rng()·2π; r = rng()·R` idiom so migrations keep their stream.
|
|
254
|
+
*/
|
|
255
|
+
export function discRegion(center, radius, options = {}) {
|
|
256
|
+
const distribution = options.distribution ?? "area";
|
|
257
|
+
const r = Math.max(0, radius);
|
|
258
|
+
return {
|
|
259
|
+
dimensions: 2,
|
|
260
|
+
isEmpty: !(r > 0),
|
|
261
|
+
sample(rng) {
|
|
262
|
+
const angle = rng() * TWO_PI;
|
|
263
|
+
const dist = radialRadius(rng, 0, r, distribution);
|
|
264
|
+
return [center[0] + Math.cos(angle) * dist, center[1] + Math.sin(angle) * dist];
|
|
265
|
+
},
|
|
266
|
+
contains(point) {
|
|
267
|
+
return distanceSq(point, center) <= r * r;
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* A ring (annulus) between `innerRadius` and `outerRadius`. `"area"` fills the band with even
|
|
273
|
+
* density; `"radial"` spreads uniformly in radius. Angle first, then radius.
|
|
274
|
+
*/
|
|
275
|
+
export function annulusRegion(center, innerRadius, outerRadius, options = {}) {
|
|
276
|
+
const distribution = options.distribution ?? "area";
|
|
277
|
+
const inner = Math.max(0, Math.min(innerRadius, outerRadius));
|
|
278
|
+
const outer = Math.max(innerRadius, outerRadius, 0);
|
|
279
|
+
return {
|
|
280
|
+
dimensions: 2,
|
|
281
|
+
isEmpty: !(outer > 0) || outer <= inner,
|
|
282
|
+
sample(rng) {
|
|
283
|
+
const angle = rng() * TWO_PI;
|
|
284
|
+
const dist = radialRadius(rng, inner, outer, distribution);
|
|
285
|
+
return [center[0] + Math.cos(angle) * dist, center[1] + Math.sin(angle) * dist];
|
|
286
|
+
},
|
|
287
|
+
contains(point) {
|
|
288
|
+
const d2 = distanceSq(point, center);
|
|
289
|
+
return d2 >= inner * inner && d2 <= outer * outer;
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* An arbitrary closed polygon on the XZ plane. Candidates are drawn uniformly from the polygon's
|
|
295
|
+
* bounding box and gated by point-in-polygon `contains`, so a run's draw count stays fixed (the
|
|
296
|
+
* sampler's attempt budget bounds the rejection, not a hidden inner loop).
|
|
297
|
+
*
|
|
298
|
+
* @capability spatial-region-polygon sample within an arbitrary closed polygon on the XZ plane
|
|
299
|
+
*/
|
|
300
|
+
export function polygonRegion(polygon) {
|
|
301
|
+
const bounds = polygonBounds(polygon);
|
|
302
|
+
const width = bounds === null ? 0 : bounds.maxX - bounds.minX;
|
|
303
|
+
const depth = bounds === null ? 0 : bounds.maxZ - bounds.minZ;
|
|
304
|
+
return {
|
|
305
|
+
dimensions: 2,
|
|
306
|
+
isEmpty: bounds === null || polygon.length < 3 || !(width > 0) || !(depth > 0),
|
|
307
|
+
sample(rng) {
|
|
308
|
+
const b = bounds ?? { minX: 0, minZ: 0, maxX: 0, maxZ: 0 };
|
|
309
|
+
return [b.minX + rng() * width, b.minZ + rng() * depth];
|
|
310
|
+
},
|
|
311
|
+
contains(point) {
|
|
312
|
+
return pointInPolygon(point, polygon);
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* A composite that first picks one member by weight, then delegates to its sampler — the
|
|
318
|
+
* "weighted subregions" distribution policy. `contains` is true when any member contains the point.
|
|
319
|
+
*
|
|
320
|
+
* @capability spatial-region-weighted compose sub-regions behind a weighted selection policy
|
|
321
|
+
*/
|
|
322
|
+
export function weightedRegion(entries) {
|
|
323
|
+
const usable = entries.filter((e) => e.weight > 0 && !e.region.isEmpty);
|
|
324
|
+
const total = usable.reduce((sum, e) => sum + e.weight, 0);
|
|
325
|
+
const dimensions = entries[0]?.region.dimensions ?? 2;
|
|
326
|
+
return {
|
|
327
|
+
dimensions,
|
|
328
|
+
isEmpty: usable.length === 0,
|
|
329
|
+
sample(rng) {
|
|
330
|
+
let roll = rng() * total;
|
|
331
|
+
for (const entry of usable) {
|
|
332
|
+
roll -= entry.weight;
|
|
333
|
+
if (roll <= 0)
|
|
334
|
+
return entry.region.sample(rng);
|
|
335
|
+
}
|
|
336
|
+
return usable[usable.length - 1].region.sample(rng);
|
|
337
|
+
},
|
|
338
|
+
contains(point) {
|
|
339
|
+
return entries.some((e) => e.region.contains(point));
|
|
340
|
+
},
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Wrap a caller-defined sampler and bounds test as a {@link SampleRegion} — the escape hatch for
|
|
345
|
+
* regions the built-ins do not cover (a navmesh cell, a heightfield mask, a spline tube).
|
|
346
|
+
*
|
|
347
|
+
* @capability spatial-region-custom wrap a caller-defined sampler and bounds test as a region
|
|
348
|
+
*/
|
|
349
|
+
export function customRegion(spec) {
|
|
350
|
+
return {
|
|
351
|
+
dimensions: spec.dimensions,
|
|
352
|
+
isEmpty: spec.isEmpty ?? false,
|
|
353
|
+
sample: spec.sample,
|
|
354
|
+
contains: spec.contains,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
// ---------------------------------------------------------------------------
|
|
358
|
+
// 3D region builders
|
|
359
|
+
// ---------------------------------------------------------------------------
|
|
360
|
+
/**
|
|
361
|
+
* An axis-aligned box `[min..max]` in 3D. Uniform density; draws x, y, z in order.
|
|
362
|
+
*
|
|
363
|
+
* @capability spatial-region-box sample uniformly within an axis-aligned 3D box
|
|
364
|
+
*/
|
|
365
|
+
export function boxRegion(min, max) {
|
|
366
|
+
const size = [max[0] - min[0], max[1] - min[1], max[2] - min[2]];
|
|
367
|
+
return {
|
|
368
|
+
dimensions: 3,
|
|
369
|
+
isEmpty: !(size[0] > 0) || !(size[1] > 0) || !(size[2] > 0),
|
|
370
|
+
sample(rng) {
|
|
371
|
+
return [min[0] + rng() * size[0], min[1] + rng() * size[1], min[2] + rng() * size[2]];
|
|
372
|
+
},
|
|
373
|
+
contains(point) {
|
|
374
|
+
return (point[0] >= min[0] && point[0] <= max[0] &&
|
|
375
|
+
point[1] >= min[1] && point[1] <= max[1] &&
|
|
376
|
+
point[2] >= min[2] && point[2] <= max[2]);
|
|
377
|
+
},
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function sphereDirection(rng) {
|
|
381
|
+
const cosTheta = 1 - 2 * rng();
|
|
382
|
+
const sinTheta = Math.sqrt(Math.max(0, 1 - cosTheta * cosTheta));
|
|
383
|
+
const phi = rng() * TWO_PI;
|
|
384
|
+
return [sinTheta * Math.cos(phi), sinTheta * Math.sin(phi), cosTheta];
|
|
385
|
+
}
|
|
386
|
+
function volumeRadius(rng, inner, outer, distribution) {
|
|
387
|
+
const u = rng();
|
|
388
|
+
if (distribution === "volume")
|
|
389
|
+
return Math.cbrt(inner ** 3 + u * (outer ** 3 - inner ** 3));
|
|
390
|
+
return inner + u * (outer - inner);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* A filled ball. `"volume"` is volume-uniform (∛-corrected radius); `"radial"` is radius-uniform.
|
|
394
|
+
* Direction is drawn first (two draws), then radius.
|
|
395
|
+
*
|
|
396
|
+
* @capability spatial-region-sphere sample within a filled 3D ball, volume- or radius-uniform
|
|
397
|
+
*/
|
|
398
|
+
export function sphereRegion(center, radius, options = {}) {
|
|
399
|
+
const distribution = options.distribution ?? "volume";
|
|
400
|
+
const r = Math.max(0, radius);
|
|
401
|
+
return {
|
|
402
|
+
dimensions: 3,
|
|
403
|
+
isEmpty: !(r > 0),
|
|
404
|
+
sample(rng) {
|
|
405
|
+
const dir = sphereDirection(rng);
|
|
406
|
+
const dist = volumeRadius(rng, 0, r, distribution);
|
|
407
|
+
return [center[0] + dir[0] * dist, center[1] + dir[1] * dist, center[2] + dir[2] * dist];
|
|
408
|
+
},
|
|
409
|
+
contains(point) {
|
|
410
|
+
return distanceSq(point, center) <= r * r;
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* A spherical shell between `innerRadius` and `outerRadius`. `"volume"` fills the shell with even
|
|
416
|
+
* density; `"radial"` spreads uniformly in radius. Direction first, then radius.
|
|
417
|
+
*
|
|
418
|
+
* @capability spatial-region-shell sample within a 3D spherical shell, volume- or radius-uniform
|
|
419
|
+
*/
|
|
420
|
+
export function shellRegion(center, innerRadius, outerRadius, options = {}) {
|
|
421
|
+
const distribution = options.distribution ?? "volume";
|
|
422
|
+
const inner = Math.max(0, Math.min(innerRadius, outerRadius));
|
|
423
|
+
const outer = Math.max(innerRadius, outerRadius, 0);
|
|
424
|
+
return {
|
|
425
|
+
dimensions: 3,
|
|
426
|
+
isEmpty: !(outer > 0) || outer <= inner,
|
|
427
|
+
sample(rng) {
|
|
428
|
+
const dir = sphereDirection(rng);
|
|
429
|
+
const dist = volumeRadius(rng, inner, outer, distribution);
|
|
430
|
+
return [center[0] + dir[0] * dist, center[1] + dir[1] * dist, center[2] + dir[2] * dist];
|
|
431
|
+
},
|
|
432
|
+
contains(point) {
|
|
433
|
+
const d2 = distanceSq(point, center);
|
|
434
|
+
return d2 >= inner * inner && d2 <= outer * outer;
|
|
435
|
+
},
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Stratified 2D sampling: one jittered point per grid cell over `area`, in deterministic row-major
|
|
440
|
+
* order. Unlike pure rejection sampling this guarantees even coverage (no clumps, no gaps) — the
|
|
441
|
+
* distinct "stratified" distribution policy, kept separate from area-uniform draws.
|
|
442
|
+
*
|
|
443
|
+
* @capability spatial-sample-stratified evenly cover an area with one jittered point per grid cell, deterministic
|
|
444
|
+
*/
|
|
445
|
+
export function sampleStratified(options) {
|
|
446
|
+
const { area, rng } = options;
|
|
447
|
+
const cols = Math.max(0, Math.floor(options.cols));
|
|
448
|
+
const rows = Math.max(0, Math.floor(options.rows));
|
|
449
|
+
const width = area.maxX - area.minX;
|
|
450
|
+
const depth = area.maxZ - area.minZ;
|
|
451
|
+
if (cols === 0 || rows === 0 || !(width > 0) || !(depth > 0))
|
|
452
|
+
return [];
|
|
453
|
+
const jitter = Math.min(1, Math.max(0, options.jitter ?? 1));
|
|
454
|
+
const cellW = width / cols;
|
|
455
|
+
const cellD = depth / rows;
|
|
456
|
+
const points = [];
|
|
457
|
+
for (let row = 0; row < rows; row += 1) {
|
|
458
|
+
for (let col = 0; col < cols; col += 1) {
|
|
459
|
+
const jx = 0.5 + jitter * (rng() - 0.5);
|
|
460
|
+
const jz = 0.5 + jitter * (rng() - 0.5);
|
|
461
|
+
points.push([area.minX + (col + jx) * cellW, area.minZ + (row + jz) * cellD]);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return points;
|
|
465
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { RoadEnvironmentDescriptor } from "./features.js";
|
|
2
|
+
import { type RoadPoint } from "./roads.js";
|
|
3
|
+
/**
|
|
4
|
+
* Offset a centerline sideways by a signed distance along its local perpendicular — the
|
|
5
|
+
* building block for lanes, curb lines, and sidewalk paths. Positive offsets fall on the
|
|
6
|
+
* left of the direction of travel, negative on the right.
|
|
7
|
+
*/
|
|
8
|
+
export declare function offsetPath(path: readonly RoadPoint[], offset: number): readonly RoadPoint[];
|
|
9
|
+
/** A directed lane derived from a road: an offset centerline plus its direction of travel. */
|
|
10
|
+
export interface StreetLane {
|
|
11
|
+
/** Lane centerline, ordered in the direction of travel. */
|
|
12
|
+
path: readonly RoadPoint[];
|
|
13
|
+
/** "forward" runs with the road's declared path order; "reverse" runs against it. */
|
|
14
|
+
direction: "forward" | "reverse";
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Two right-hand-traffic lane centerlines for a road — each offset a quarter of the drivable
|
|
18
|
+
* width from the center and ordered in its direction of travel. Feed a lane's `path` straight
|
|
19
|
+
* into `nav/pathFollow` for traffic AI, or use its endpoints as directed car spawn points.
|
|
20
|
+
*/
|
|
21
|
+
export declare function laneCenters(road: RoadEnvironmentDescriptor): readonly [StreetLane, StreetLane];
|
|
22
|
+
/** Resolved sidewalk band widths for a road; zero when the road declares no sidewalk. */
|
|
23
|
+
export declare function sidewalkWidthOf(road: RoadEnvironmentDescriptor): number;
|
|
24
|
+
/**
|
|
25
|
+
* The two sidewalk walking paths of a road — offset polylines running down the middle of each
|
|
26
|
+
* sidewalk band. Pedestrians spawn and route along these instead of the asphalt.
|
|
27
|
+
*/
|
|
28
|
+
export declare function sidewalkPaths(road: RoadEnvironmentDescriptor): readonly (readonly RoadPoint[])[];
|
|
29
|
+
/** A placement anchor on the curb line: position, outward-facing heading, and which side it sits on. */
|
|
30
|
+
export interface FurnitureSpot {
|
|
31
|
+
position: RoadPoint;
|
|
32
|
+
/** Yaw (radians, engine `rotationY` convention) facing away from the road. */
|
|
33
|
+
heading: number;
|
|
34
|
+
side: "left" | "right";
|
|
35
|
+
/** Arc-length distance along the road at this spot. */
|
|
36
|
+
along: number;
|
|
37
|
+
}
|
|
38
|
+
/** Options for {@link furnitureSpots}. */
|
|
39
|
+
export interface FurnitureSpotOptions {
|
|
40
|
+
/** Distance between consecutive spots along the road. Default 24. */
|
|
41
|
+
spacing?: number;
|
|
42
|
+
/** Which curb lines to populate. Default "both". */
|
|
43
|
+
sides?: "both" | "left" | "right";
|
|
44
|
+
/** Alternate sides spot-by-spot instead of mirroring every spot. Default true. */
|
|
45
|
+
stagger?: boolean;
|
|
46
|
+
/** Extra outward shift past the curb line (e.g. to center furniture in the sidewalk). Default 0. */
|
|
47
|
+
outset?: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Evenly spaced street-furniture anchors along a road's curb lines — streetlights, palms, signs,
|
|
51
|
+
* hydrants, benches. Each spot sits just outside the asphalt (plus `outset`), faces away from
|
|
52
|
+
* the street, and alternates sides by default so lights stagger like a real avenue. This is the
|
|
53
|
+
* answer to "where do I put it": furniture is an asset of the street, never a hand-typed coordinate.
|
|
54
|
+
*/
|
|
55
|
+
export declare function furnitureSpots(road: RoadEnvironmentDescriptor, options?: FurnitureSpotOptions): readonly FurnitureSpot[];
|
|
56
|
+
/** Options for {@link parkingSpots}. */
|
|
57
|
+
export interface ParkingSpotOptions {
|
|
58
|
+
/** Distance between consecutive parked-car anchors. Default 18. */
|
|
59
|
+
spacing?: number;
|
|
60
|
+
/** Which side(s) of the street to line with parking. Default "both". */
|
|
61
|
+
sides?: "both" | "left" | "right";
|
|
62
|
+
}
|
|
63
|
+
/** A curbside parking anchor: position at the road edge and a heading parallel to the street. */
|
|
64
|
+
export interface ParkingSpot {
|
|
65
|
+
position: RoadPoint;
|
|
66
|
+
/** Yaw (radians) aligned with the direction of travel on that side (right-hand traffic). */
|
|
67
|
+
heading: number;
|
|
68
|
+
side: "left" | "right";
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Curbside parking anchors along a road: hugging the edge of the asphalt, headed parallel to the
|
|
72
|
+
* street in that side's direction of travel. Spawn parked vehicles here instead of eyeballing
|
|
73
|
+
* coordinates in the middle of the carriageway.
|
|
74
|
+
*/
|
|
75
|
+
export declare function parkingSpots(road: RoadEnvironmentDescriptor, options?: ParkingSpotOptions): readonly ParkingSpot[];
|
|
76
|
+
/**
|
|
77
|
+
* A deterministic point on one of a road's sidewalks at a normalized position — `side` picks the
|
|
78
|
+
* band, `fraction` (0..1) picks how far along. The canonical pedestrian spawn helper.
|
|
79
|
+
*/
|
|
80
|
+
export declare function sidewalkPoint(road: RoadEnvironmentDescriptor, side: "left" | "right", fraction: number): RoadPoint | null;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { pathLength } from "./roads.js";
|
|
2
|
+
function normalAt(path, index) {
|
|
3
|
+
const prev = path[Math.max(0, index - 1)];
|
|
4
|
+
const next = path[Math.min(path.length - 1, index + 1)];
|
|
5
|
+
const dx = next[0] - prev[0];
|
|
6
|
+
const dz = next[1] - prev[1];
|
|
7
|
+
const length = Math.hypot(dx, dz) || 1;
|
|
8
|
+
return [-dz / length, dx / length];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Offset a centerline sideways by a signed distance along its local perpendicular — the
|
|
12
|
+
* building block for lanes, curb lines, and sidewalk paths. Positive offsets fall on the
|
|
13
|
+
* left of the direction of travel, negative on the right.
|
|
14
|
+
*/
|
|
15
|
+
export function offsetPath(path, offset) {
|
|
16
|
+
return path.map((point, index) => {
|
|
17
|
+
const [nx, nz] = normalAt(path, index);
|
|
18
|
+
return [point[0] + nx * offset, point[1] + nz * offset];
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Two right-hand-traffic lane centerlines for a road — each offset a quarter of the drivable
|
|
23
|
+
* width from the center and ordered in its direction of travel. Feed a lane's `path` straight
|
|
24
|
+
* into `nav/pathFollow` for traffic AI, or use its endpoints as directed car spawn points.
|
|
25
|
+
*/
|
|
26
|
+
export function laneCenters(road) {
|
|
27
|
+
const laneOffset = road.width / 4;
|
|
28
|
+
const forward = offsetPath(road.path, -laneOffset);
|
|
29
|
+
const reverse = offsetPath(road.path, laneOffset);
|
|
30
|
+
return [
|
|
31
|
+
{ path: forward, direction: "forward" },
|
|
32
|
+
{ path: [...reverse].reverse(), direction: "reverse" },
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
/** Resolved sidewalk band widths for a road; zero when the road declares no sidewalk. */
|
|
36
|
+
export function sidewalkWidthOf(road) {
|
|
37
|
+
return road.sidewalk === false ? 0 : road.sidewalk.width;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The two sidewalk walking paths of a road — offset polylines running down the middle of each
|
|
41
|
+
* sidewalk band. Pedestrians spawn and route along these instead of the asphalt.
|
|
42
|
+
*/
|
|
43
|
+
export function sidewalkPaths(road) {
|
|
44
|
+
const width = sidewalkWidthOf(road);
|
|
45
|
+
if (width <= 0)
|
|
46
|
+
return [];
|
|
47
|
+
const offset = road.width / 2 + width / 2;
|
|
48
|
+
return [offsetPath(road.path, offset), offsetPath(road.path, -offset)];
|
|
49
|
+
}
|
|
50
|
+
function pointAlong(path, along) {
|
|
51
|
+
let remaining = along;
|
|
52
|
+
for (let i = 0; i < path.length - 1; i += 1) {
|
|
53
|
+
const a = path[i];
|
|
54
|
+
const b = path[i + 1];
|
|
55
|
+
const segLength = Math.hypot(b[0] - a[0], b[1] - a[1]);
|
|
56
|
+
if (remaining <= segLength || i === path.length - 2) {
|
|
57
|
+
const t = segLength <= 0 ? 0 : Math.min(1, remaining / segLength);
|
|
58
|
+
const length = segLength || 1;
|
|
59
|
+
return {
|
|
60
|
+
point: [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t],
|
|
61
|
+
tangent: [(b[0] - a[0]) / length, (b[1] - a[1]) / length],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
remaining -= segLength;
|
|
65
|
+
}
|
|
66
|
+
const first = path[0] ?? [0, 0];
|
|
67
|
+
return { point: first, tangent: [0, 1] };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Evenly spaced street-furniture anchors along a road's curb lines — streetlights, palms, signs,
|
|
71
|
+
* hydrants, benches. Each spot sits just outside the asphalt (plus `outset`), faces away from
|
|
72
|
+
* the street, and alternates sides by default so lights stagger like a real avenue. This is the
|
|
73
|
+
* answer to "where do I put it": furniture is an asset of the street, never a hand-typed coordinate.
|
|
74
|
+
*/
|
|
75
|
+
export function furnitureSpots(road, options = {}) {
|
|
76
|
+
const spacing = options.spacing ?? 24;
|
|
77
|
+
const sides = options.sides ?? "both";
|
|
78
|
+
const stagger = options.stagger ?? true;
|
|
79
|
+
const outset = options.outset ?? 0;
|
|
80
|
+
if (spacing <= 0)
|
|
81
|
+
return [];
|
|
82
|
+
const total = pathLength(road.path);
|
|
83
|
+
const curbOffset = road.width / 2 + 0.6 + outset;
|
|
84
|
+
const spots = [];
|
|
85
|
+
let index = 0;
|
|
86
|
+
for (let along = spacing / 2; along < total; along += spacing) {
|
|
87
|
+
const { point, tangent } = pointAlong(road.path, along);
|
|
88
|
+
const normal = [-tangent[1], tangent[0]];
|
|
89
|
+
const wanted = sides === "both" ? (stagger ? [index % 2 === 0 ? "left" : "right"] : ["left", "right"]) : [sides];
|
|
90
|
+
for (const side of wanted) {
|
|
91
|
+
const sign = side === "left" ? 1 : -1;
|
|
92
|
+
const position = [point[0] + normal[0] * curbOffset * sign, point[1] + normal[1] * curbOffset * sign];
|
|
93
|
+
spots.push({
|
|
94
|
+
position,
|
|
95
|
+
heading: Math.atan2(normal[0] * sign, normal[1] * sign),
|
|
96
|
+
side,
|
|
97
|
+
along,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
index += 1;
|
|
101
|
+
}
|
|
102
|
+
return spots;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Curbside parking anchors along a road: hugging the edge of the asphalt, headed parallel to the
|
|
106
|
+
* street in that side's direction of travel. Spawn parked vehicles here instead of eyeballing
|
|
107
|
+
* coordinates in the middle of the carriageway.
|
|
108
|
+
*/
|
|
109
|
+
export function parkingSpots(road, options = {}) {
|
|
110
|
+
const spacing = options.spacing ?? 18;
|
|
111
|
+
const sides = options.sides ?? "both";
|
|
112
|
+
if (spacing <= 0)
|
|
113
|
+
return [];
|
|
114
|
+
const total = pathLength(road.path);
|
|
115
|
+
const edgeOffset = road.width / 2 - 1.2;
|
|
116
|
+
const spots = [];
|
|
117
|
+
for (let along = spacing; along < total - spacing / 2; along += spacing) {
|
|
118
|
+
const { point, tangent } = pointAlong(road.path, along);
|
|
119
|
+
const normal = [-tangent[1], tangent[0]];
|
|
120
|
+
const wanted = sides === "both" ? ["left", "right"] : [sides];
|
|
121
|
+
for (const side of wanted) {
|
|
122
|
+
const sign = side === "left" ? 1 : -1;
|
|
123
|
+
const dir = side === "left" ? 1 : -1;
|
|
124
|
+
spots.push({
|
|
125
|
+
position: [point[0] + normal[0] * edgeOffset * sign, point[1] + normal[1] * edgeOffset * sign],
|
|
126
|
+
heading: Math.atan2(tangent[0] * dir, tangent[1] * dir),
|
|
127
|
+
side,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return spots;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* A deterministic point on one of a road's sidewalks at a normalized position — `side` picks the
|
|
135
|
+
* band, `fraction` (0..1) picks how far along. The canonical pedestrian spawn helper.
|
|
136
|
+
*/
|
|
137
|
+
export function sidewalkPoint(road, side, fraction) {
|
|
138
|
+
const paths = sidewalkPaths(road);
|
|
139
|
+
const path = side === "left" ? paths[0] : paths[1];
|
|
140
|
+
if (path === undefined)
|
|
141
|
+
return null;
|
|
142
|
+
const along = Math.max(0, Math.min(1, fraction)) * pathLength(path);
|
|
143
|
+
return pointAlong(path, along).point;
|
|
144
|
+
}
|