@jgengine/core 0.10.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 +13 -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/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 +1 -0
- package/dist/authoring.js +1 -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 +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 +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 +190 -7
- package/dist/editor/commands.js +727 -55
- package/dist/editor/document.d.ts +159 -13
- package/dist/editor/document.js +750 -19
- 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 +73 -6
- package/dist/editor/index.js +6 -2
- 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 +107 -1
- 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 +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 +100 -2
- package/dist/game/defineGame.js +39 -2
- 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 +35 -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 +92 -7
- 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 +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/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 -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/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 +16 -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 +35 -8
- package/dist/movement/movementModel.js +42 -14
- package/dist/movement/playerMovement.js +65 -10
- 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 +22 -0
- package/dist/random/rng.js +28 -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 +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 +42 -3
- package/dist/runtime/adapter.js +44 -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/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 +114 -13
- package/dist/runtime/gameContext.js +303 -395
- 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 +8 -3
- package/dist/runtime/hostedGameRunner.js +26 -3
- package/dist/runtime/hostedWorldSession.d.ts +12 -4
- package/dist/runtime/hostedWorldSession.js +11 -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 +30 -3
- package/dist/runtime/worldSnapshot.js +20 -5
- package/dist/scene/assetCatalog.d.ts +21 -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 +13 -1
- package/dist/scene/colliders.js +37 -1
- package/dist/scene/entityStats.d.ts +11 -2
- package/dist/scene/entityStats.js +11 -2
- 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 +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 +5 -0
- package/dist/store/observableKeyedStore.js +34 -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 +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 +33 -1
- package/dist/ui/hudLayout.js +204 -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/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/features.d.ts +115 -7
- package/dist/world/features.js +56 -5
- 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 +8 -0
- package/dist/world/roads.js +38 -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/terraform.d.ts +213 -1
- package/dist/world/terraform.js +655 -20
- package/dist/world/terrain.d.ts +119 -20
- package/dist/world/terrain.js +247 -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 +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 -5389
package/dist/game/quest.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
function clamp(value, min, max) {
|
|
2
2
|
return Math.min(max, Math.max(min, value));
|
|
3
3
|
}
|
|
4
|
+
/**
|
|
5
|
+
* Track accepted quests and their per-objective progress, granting rewards on completion.
|
|
6
|
+
*
|
|
7
|
+
* @capability quest-log track accepted quests and their per-objective progress
|
|
8
|
+
*/
|
|
4
9
|
export function createQuestJournal(deps) {
|
|
5
10
|
const catalog = new Map();
|
|
6
11
|
const users = new Map();
|
|
@@ -230,6 +235,30 @@ export function createQuestJournal(deps) {
|
|
|
230
235
|
}
|
|
231
236
|
users.set(userId, quests);
|
|
232
237
|
},
|
|
238
|
+
snapshotAll() {
|
|
239
|
+
const out = {};
|
|
240
|
+
for (const [userId, quests] of users) {
|
|
241
|
+
out[userId] = Array.from(quests, ([questId, state]) => ({
|
|
242
|
+
questId,
|
|
243
|
+
status: state.status,
|
|
244
|
+
progress: Object.fromEntries(state.progress),
|
|
245
|
+
}));
|
|
246
|
+
}
|
|
247
|
+
return out;
|
|
248
|
+
},
|
|
249
|
+
hydrateAll(data) {
|
|
250
|
+
users.clear();
|
|
251
|
+
for (const [userId, entries] of Object.entries(data)) {
|
|
252
|
+
const quests = new Map();
|
|
253
|
+
for (const entry of entries) {
|
|
254
|
+
quests.set(entry.questId, {
|
|
255
|
+
status: entry.status,
|
|
256
|
+
progress: new Map(Object.entries(entry.progress)),
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
users.set(userId, quests);
|
|
260
|
+
}
|
|
261
|
+
},
|
|
233
262
|
};
|
|
234
263
|
}
|
|
235
264
|
export function applyQuestRewards(rewards, appliers) {
|
package/dist/game/race.d.ts
CHANGED
|
@@ -135,4 +135,145 @@ export declare class RaceState {
|
|
|
135
135
|
private compare;
|
|
136
136
|
update(now: number, positions: Record<string, readonly [number, number, number]> | Map<string, readonly [number, number, number]>): readonly RaceEvent[];
|
|
137
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* A checkpoint race state machine — laps, forks, live standings, splits, and pluggable win conditions.
|
|
140
|
+
*
|
|
141
|
+
* @capability race-track a checkpoint race with laps, standings, splits, and win conditions
|
|
142
|
+
*/
|
|
138
143
|
export declare function createRaceState(config: RaceStateConfig): RaceState;
|
|
144
|
+
/** Wall-clock lap timing: the current/last/best/total split-book every racing HUD reads. */
|
|
145
|
+
export interface LapTimerSnapshot {
|
|
146
|
+
/** Seconds elapsed in the lap now underway. */
|
|
147
|
+
currentLap: number;
|
|
148
|
+
/** Duration of the lap just completed, or `null` before the first lap closes. */
|
|
149
|
+
lastLap: number | null;
|
|
150
|
+
/** Fastest completed lap so far, or `null` before the first lap closes. */
|
|
151
|
+
bestLap: number | null;
|
|
152
|
+
/** Total time across every lap, including the current one. */
|
|
153
|
+
total: number;
|
|
154
|
+
/** Number of laps completed. */
|
|
155
|
+
lapCount: number;
|
|
156
|
+
/** Completed lap durations in order — the split book for a results screen. */
|
|
157
|
+
splits: readonly number[];
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Wall-clock lap timer complementing {@link RaceState}: feed it `tick(dt)` each frame and call
|
|
161
|
+
* `completeLap()` when the race emits a player `lap.completed` event. It accumulates the current lap,
|
|
162
|
+
* carries best/last, banks splits, and `penalize()` folds a time penalty into the running lap — the
|
|
163
|
+
* timing bookkeeping every racer reimplemented on top of the position-only race state.
|
|
164
|
+
*/
|
|
165
|
+
export interface LapTimer {
|
|
166
|
+
/** Advance the current lap and total by `dt` seconds. */
|
|
167
|
+
tick(dt: number): void;
|
|
168
|
+
/** Close the current lap: bank its split, update last/best, reset the current lap to 0, and return its time. */
|
|
169
|
+
completeLap(): number;
|
|
170
|
+
/** Add a time penalty (seconds) to the lap currently underway. */
|
|
171
|
+
penalize(seconds: number): void;
|
|
172
|
+
/** Clear all timing back to the start line. */
|
|
173
|
+
reset(): void;
|
|
174
|
+
/** Immutable view of the current timing state. */
|
|
175
|
+
snapshot(): LapTimerSnapshot;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Create a {@link LapTimer} starting at lap 0 with no splits, best, or last time recorded.
|
|
179
|
+
*
|
|
180
|
+
* @capability lap-timer wall-clock current/last/best lap timing with splits
|
|
181
|
+
*/
|
|
182
|
+
export declare function createLapTimer(): LapTimer;
|
|
183
|
+
/**
|
|
184
|
+
* The lifecycle every race runs through before, during, and after the clock: `idle` on the grid,
|
|
185
|
+
* `countdown` on the lights, `racing` once they go green, `finished` at the flag. A pure value driven
|
|
186
|
+
* by the transition functions below — it carries no track, racer, or lap data, so it composes with
|
|
187
|
+
* {@link RaceState} (position/laps) and {@link LapTimer} (wall-clock) rather than duplicating them.
|
|
188
|
+
*/
|
|
189
|
+
export type RacePhase = "idle" | "countdown" | "racing" | "finished";
|
|
190
|
+
/** Immutable lifecycle snapshot: the current {@link RacePhase} plus the two clocks it owns. */
|
|
191
|
+
export interface RaceSessionState {
|
|
192
|
+
/** Where the session sits in the start→countdown→race→finish lifecycle. */
|
|
193
|
+
readonly phase: RacePhase;
|
|
194
|
+
/** Seconds left on the countdown, or 0 outside the `countdown` phase. */
|
|
195
|
+
readonly countdown: number;
|
|
196
|
+
/** Seconds of green-flag racing accumulated so far. */
|
|
197
|
+
readonly elapsed: number;
|
|
198
|
+
}
|
|
199
|
+
/** Options for {@link startRaceCountdown}. */
|
|
200
|
+
export interface RaceCountdownOptions {
|
|
201
|
+
/** Countdown length in seconds before the race goes green (default 3). A value ≤ 0 starts racing immediately. */
|
|
202
|
+
readonly seconds?: number;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* The pre-race session on the grid: `idle`, both clocks at zero. Call {@link startRaceCountdown} to
|
|
206
|
+
* light the lights, or hold here until the field is ready.
|
|
207
|
+
*
|
|
208
|
+
* @capability race-session pure idle→countdown→racing→finished clock every racer wraps around its state
|
|
209
|
+
*/
|
|
210
|
+
export declare function idleRaceSession(): RaceSessionState;
|
|
211
|
+
/**
|
|
212
|
+
* Drop the lights: return a fresh `countdown` session of `seconds` (default 3). A non-positive length
|
|
213
|
+
* skips straight to `racing` for a standing start with no countdown.
|
|
214
|
+
*/
|
|
215
|
+
export declare function startRaceCountdown(options?: RaceCountdownOptions): RaceSessionState;
|
|
216
|
+
/**
|
|
217
|
+
* Advance the session by `dt` seconds: bleed the countdown down and flip to `racing` when it reaches
|
|
218
|
+
* zero, or accumulate `elapsed` while `racing`. `idle` and `finished` are inert. Overshoot past the
|
|
219
|
+
* countdown is dropped rather than banked into `elapsed`, so the race clock always starts from zero.
|
|
220
|
+
*/
|
|
221
|
+
export declare function tickRaceSession(session: RaceSessionState, dt: number): RaceSessionState;
|
|
222
|
+
/** Cross the flag: move a `racing` session to `finished`, freezing its `elapsed`. A no-op in any other phase. */
|
|
223
|
+
export declare function finishRaceSession(session: RaceSessionState): RaceSessionState;
|
|
224
|
+
/** Whether a racer won or lost, derived from where they placed against the winning-places cutoff. */
|
|
225
|
+
export type RaceOutcome = "win" | "lose";
|
|
226
|
+
/** A racer's finishing position (`place`, 1-based) in a finish order, and the {@link RaceOutcome} it earns. */
|
|
227
|
+
export interface RacePlacement {
|
|
228
|
+
readonly racerId: string;
|
|
229
|
+
/** 1-based finishing position: `1` is the winner. */
|
|
230
|
+
readonly place: number;
|
|
231
|
+
readonly outcome: RaceOutcome;
|
|
232
|
+
}
|
|
233
|
+
/** Options for {@link racePlacements} / {@link placementOf} / {@link raceOutcomeOf}. */
|
|
234
|
+
export interface PlacementOptions {
|
|
235
|
+
/** How many top places count as a win (default 1). A podium finish would pass `3`. */
|
|
236
|
+
readonly winningPlaces?: number;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Turn a finish-order ranking (index 0 = winner, e.g. the `ranking` of a `race.finished` event) into
|
|
240
|
+
* per-racer {@link RacePlacement}s — the `1st/2nd/3rd` + win/lose every results screen shows.
|
|
241
|
+
*
|
|
242
|
+
* @capability race-placements finish order into per-racer place + win/lose for a results screen
|
|
243
|
+
*/
|
|
244
|
+
export declare function racePlacements(finishOrder: readonly string[], options?: PlacementOptions): readonly RacePlacement[];
|
|
245
|
+
/**
|
|
246
|
+
* One racer's {@link RacePlacement} within a finish order, or `null` if they never crossed the line.
|
|
247
|
+
*
|
|
248
|
+
* @capability race-placements look up one racer's place + win/lose within a finish order
|
|
249
|
+
*/
|
|
250
|
+
export declare function placementOf(finishOrder: readonly string[], racerId: string, options?: PlacementOptions): RacePlacement | null;
|
|
251
|
+
/**
|
|
252
|
+
* The win/lose verdict for one racer in a finish order — `ranking[0] === player ? "win" : "lose"`, the
|
|
253
|
+
* check every racing game hand-rolls, generalized to a `winningPlaces` cutoff. A racer absent from the
|
|
254
|
+
* order counts as a `lose`.
|
|
255
|
+
*/
|
|
256
|
+
export declare function raceOutcomeOf(finishOrder: readonly string[], racerId: string, options?: PlacementOptions): RaceOutcome;
|
|
257
|
+
/**
|
|
258
|
+
* Per-segment durations from a cumulative split book (`splits[i]` = elapsed time at checkpoint `i`):
|
|
259
|
+
* `segments[i] = splits[i] − splits[i−1]`, the first measured from `start` (default 0). Turns the
|
|
260
|
+
* cumulative splits {@link RacerProgress} records into the individual leg times a results screen shows.
|
|
261
|
+
*
|
|
262
|
+
* @capability lap-splits turn cumulative split times into per-segment leg times
|
|
263
|
+
*/
|
|
264
|
+
export declare function splitSegments(splits: readonly number[], start?: number): number[];
|
|
265
|
+
/**
|
|
266
|
+
* Per-lap durations from a cumulative split book with `gatesPerLap` checkpoints per lap — each lap's time
|
|
267
|
+
* is its finish-gate split minus the previous lap's finish. Only complete laps are returned.
|
|
268
|
+
*
|
|
269
|
+
* @capability lap-splits per-lap durations from a cumulative split book
|
|
270
|
+
*/
|
|
271
|
+
export declare function lapDurations(splits: readonly number[], gatesPerLap: number): number[];
|
|
272
|
+
/**
|
|
273
|
+
* Elementwise delta of a cumulative split book against a `reference` book (a personal best or par lap):
|
|
274
|
+
* positive means behind the reference at that checkpoint. Compared up to the shorter length — the
|
|
275
|
+
* `+0.3s` / `−1.2s` gap every racing HUD shows against its ghost.
|
|
276
|
+
*
|
|
277
|
+
* @capability lap-splits delta of splits against a personal-best or par reference
|
|
278
|
+
*/
|
|
279
|
+
export declare function parDelta(splits: readonly number[], reference: readonly number[]): number[];
|
package/dist/game/race.js
CHANGED
|
@@ -316,6 +316,175 @@ export class RaceState {
|
|
|
316
316
|
return events;
|
|
317
317
|
}
|
|
318
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* A checkpoint race state machine — laps, forks, live standings, splits, and pluggable win conditions.
|
|
321
|
+
*
|
|
322
|
+
* @capability race-track a checkpoint race with laps, standings, splits, and win conditions
|
|
323
|
+
*/
|
|
319
324
|
export function createRaceState(config) {
|
|
320
325
|
return new RaceState(config);
|
|
321
326
|
}
|
|
327
|
+
/**
|
|
328
|
+
* Create a {@link LapTimer} starting at lap 0 with no splits, best, or last time recorded.
|
|
329
|
+
*
|
|
330
|
+
* @capability lap-timer wall-clock current/last/best lap timing with splits
|
|
331
|
+
*/
|
|
332
|
+
export function createLapTimer() {
|
|
333
|
+
let currentLap = 0;
|
|
334
|
+
let lastLap = null;
|
|
335
|
+
let bestLap = null;
|
|
336
|
+
let total = 0;
|
|
337
|
+
const splits = [];
|
|
338
|
+
return {
|
|
339
|
+
tick(dt) {
|
|
340
|
+
if (dt <= 0)
|
|
341
|
+
return;
|
|
342
|
+
currentLap += dt;
|
|
343
|
+
total += dt;
|
|
344
|
+
},
|
|
345
|
+
completeLap() {
|
|
346
|
+
const lapTime = currentLap;
|
|
347
|
+
lastLap = lapTime;
|
|
348
|
+
bestLap = bestLap === null || lapTime < bestLap ? lapTime : bestLap;
|
|
349
|
+
splits.push(lapTime);
|
|
350
|
+
currentLap = 0;
|
|
351
|
+
return lapTime;
|
|
352
|
+
},
|
|
353
|
+
penalize(seconds) {
|
|
354
|
+
if (seconds <= 0)
|
|
355
|
+
return;
|
|
356
|
+
currentLap += seconds;
|
|
357
|
+
total += seconds;
|
|
358
|
+
},
|
|
359
|
+
reset() {
|
|
360
|
+
currentLap = 0;
|
|
361
|
+
lastLap = null;
|
|
362
|
+
bestLap = null;
|
|
363
|
+
total = 0;
|
|
364
|
+
splits.length = 0;
|
|
365
|
+
},
|
|
366
|
+
snapshot() {
|
|
367
|
+
return { currentLap, lastLap, bestLap, total, lapCount: splits.length, splits: [...splits] };
|
|
368
|
+
},
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
const DEFAULT_COUNTDOWN_SECONDS = 3;
|
|
372
|
+
/**
|
|
373
|
+
* The pre-race session on the grid: `idle`, both clocks at zero. Call {@link startRaceCountdown} to
|
|
374
|
+
* light the lights, or hold here until the field is ready.
|
|
375
|
+
*
|
|
376
|
+
* @capability race-session pure idle→countdown→racing→finished clock every racer wraps around its state
|
|
377
|
+
*/
|
|
378
|
+
export function idleRaceSession() {
|
|
379
|
+
return { phase: "idle", countdown: 0, elapsed: 0 };
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Drop the lights: return a fresh `countdown` session of `seconds` (default 3). A non-positive length
|
|
383
|
+
* skips straight to `racing` for a standing start with no countdown.
|
|
384
|
+
*/
|
|
385
|
+
export function startRaceCountdown(options) {
|
|
386
|
+
const seconds = Math.max(0, options?.seconds ?? DEFAULT_COUNTDOWN_SECONDS);
|
|
387
|
+
return seconds === 0 ? { phase: "racing", countdown: 0, elapsed: 0 } : { phase: "countdown", countdown: seconds, elapsed: 0 };
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Advance the session by `dt` seconds: bleed the countdown down and flip to `racing` when it reaches
|
|
391
|
+
* zero, or accumulate `elapsed` while `racing`. `idle` and `finished` are inert. Overshoot past the
|
|
392
|
+
* countdown is dropped rather than banked into `elapsed`, so the race clock always starts from zero.
|
|
393
|
+
*/
|
|
394
|
+
export function tickRaceSession(session, dt) {
|
|
395
|
+
if (dt <= 0)
|
|
396
|
+
return session;
|
|
397
|
+
if (session.phase === "countdown") {
|
|
398
|
+
const countdown = session.countdown - dt;
|
|
399
|
+
return countdown <= 0 ? { phase: "racing", countdown: 0, elapsed: 0 } : { phase: "countdown", countdown, elapsed: session.elapsed };
|
|
400
|
+
}
|
|
401
|
+
if (session.phase === "racing")
|
|
402
|
+
return { phase: "racing", countdown: 0, elapsed: session.elapsed + dt };
|
|
403
|
+
return session;
|
|
404
|
+
}
|
|
405
|
+
/** Cross the flag: move a `racing` session to `finished`, freezing its `elapsed`. A no-op in any other phase. */
|
|
406
|
+
export function finishRaceSession(session) {
|
|
407
|
+
return session.phase === "racing" ? { phase: "finished", countdown: 0, elapsed: session.elapsed } : session;
|
|
408
|
+
}
|
|
409
|
+
function outcomeForPlace(place, winningPlaces) {
|
|
410
|
+
return place <= winningPlaces ? "win" : "lose";
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Turn a finish-order ranking (index 0 = winner, e.g. the `ranking` of a `race.finished` event) into
|
|
414
|
+
* per-racer {@link RacePlacement}s — the `1st/2nd/3rd` + win/lose every results screen shows.
|
|
415
|
+
*
|
|
416
|
+
* @capability race-placements finish order into per-racer place + win/lose for a results screen
|
|
417
|
+
*/
|
|
418
|
+
export function racePlacements(finishOrder, options) {
|
|
419
|
+
const winningPlaces = Math.max(0, Math.floor(options?.winningPlaces ?? 1));
|
|
420
|
+
return finishOrder.map((racerId, index) => ({ racerId, place: index + 1, outcome: outcomeForPlace(index + 1, winningPlaces) }));
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* One racer's {@link RacePlacement} within a finish order, or `null` if they never crossed the line.
|
|
424
|
+
*
|
|
425
|
+
* @capability race-placements look up one racer's place + win/lose within a finish order
|
|
426
|
+
*/
|
|
427
|
+
export function placementOf(finishOrder, racerId, options) {
|
|
428
|
+
const index = finishOrder.indexOf(racerId);
|
|
429
|
+
if (index < 0)
|
|
430
|
+
return null;
|
|
431
|
+
const winningPlaces = Math.max(0, Math.floor(options?.winningPlaces ?? 1));
|
|
432
|
+
return { racerId, place: index + 1, outcome: outcomeForPlace(index + 1, winningPlaces) };
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* The win/lose verdict for one racer in a finish order — `ranking[0] === player ? "win" : "lose"`, the
|
|
436
|
+
* check every racing game hand-rolls, generalized to a `winningPlaces` cutoff. A racer absent from the
|
|
437
|
+
* order counts as a `lose`.
|
|
438
|
+
*/
|
|
439
|
+
export function raceOutcomeOf(finishOrder, racerId, options) {
|
|
440
|
+
return placementOf(finishOrder, racerId, options)?.outcome ?? "lose";
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Per-segment durations from a cumulative split book (`splits[i]` = elapsed time at checkpoint `i`):
|
|
444
|
+
* `segments[i] = splits[i] − splits[i−1]`, the first measured from `start` (default 0). Turns the
|
|
445
|
+
* cumulative splits {@link RacerProgress} records into the individual leg times a results screen shows.
|
|
446
|
+
*
|
|
447
|
+
* @capability lap-splits turn cumulative split times into per-segment leg times
|
|
448
|
+
*/
|
|
449
|
+
export function splitSegments(splits, start = 0) {
|
|
450
|
+
const segments = [];
|
|
451
|
+
let previous = start;
|
|
452
|
+
for (const split of splits) {
|
|
453
|
+
segments.push(split - previous);
|
|
454
|
+
previous = split;
|
|
455
|
+
}
|
|
456
|
+
return segments;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Per-lap durations from a cumulative split book with `gatesPerLap` checkpoints per lap — each lap's time
|
|
460
|
+
* is its finish-gate split minus the previous lap's finish. Only complete laps are returned.
|
|
461
|
+
*
|
|
462
|
+
* @capability lap-splits per-lap durations from a cumulative split book
|
|
463
|
+
*/
|
|
464
|
+
export function lapDurations(splits, gatesPerLap) {
|
|
465
|
+
if (gatesPerLap <= 0)
|
|
466
|
+
return [];
|
|
467
|
+
const laps = [];
|
|
468
|
+
for (let lap = 0;; lap += 1) {
|
|
469
|
+
const finish = lap * gatesPerLap + (gatesPerLap - 1);
|
|
470
|
+
if (finish >= splits.length)
|
|
471
|
+
break;
|
|
472
|
+
const previousFinish = lap === 0 ? 0 : (splits[lap * gatesPerLap - 1] ?? 0);
|
|
473
|
+
laps.push(splits[finish] - previousFinish);
|
|
474
|
+
}
|
|
475
|
+
return laps;
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Elementwise delta of a cumulative split book against a `reference` book (a personal best or par lap):
|
|
479
|
+
* positive means behind the reference at that checkpoint. Compared up to the shorter length — the
|
|
480
|
+
* `+0.3s` / `−1.2s` gap every racing HUD shows against its ghost.
|
|
481
|
+
*
|
|
482
|
+
* @capability lap-splits delta of splits against a personal-best or par reference
|
|
483
|
+
*/
|
|
484
|
+
export function parDelta(splits, reference) {
|
|
485
|
+
const count = Math.min(splits.length, reference.length);
|
|
486
|
+
const deltas = [];
|
|
487
|
+
for (let i = 0; i < count; i += 1)
|
|
488
|
+
deltas.push(splits[i] - reference[i]);
|
|
489
|
+
return deltas;
|
|
490
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type KeyValueStorage } from "./keyValueStore.js";
|
|
2
2
|
/** The structural storage backend a record book persists through — an alias of the shared {@link KeyValueStorage} seam (browser `localStorage`, a test stub, or `null`). */
|
|
3
3
|
export type RecordStorage = KeyValueStorage;
|
|
4
4
|
export type RecordDirection = "lower" | "higher";
|
|
@@ -23,5 +23,7 @@ export interface RecordBook<K extends string> {
|
|
|
23
23
|
* key-value storage (pass `localStorage` in a browser, a stub in tests, or
|
|
24
24
|
* `null` for in-memory only). Corrupt or unavailable storage degrades to an
|
|
25
25
|
* empty book — a record write never throws into a game tick.
|
|
26
|
+
*
|
|
27
|
+
* @capability best-record persist personal-best times/scores with safe storage fallback
|
|
26
28
|
*/
|
|
27
29
|
export declare function createRecordBook<K extends string>(config: RecordBookConfig<K>): RecordBook<K>;
|
package/dist/game/recordBook.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { defaultKeyValueStorage } from "./keyValueStore.js";
|
|
1
2
|
function loadStored(config, storage) {
|
|
2
3
|
if (storage === null)
|
|
3
4
|
return {};
|
|
@@ -33,9 +34,11 @@ function loadStored(config, storage) {
|
|
|
33
34
|
* key-value storage (pass `localStorage` in a browser, a stub in tests, or
|
|
34
35
|
* `null` for in-memory only). Corrupt or unavailable storage degrades to an
|
|
35
36
|
* empty book — a record write never throws into a game tick.
|
|
37
|
+
*
|
|
38
|
+
* @capability best-record persist personal-best times/scores with safe storage fallback
|
|
36
39
|
*/
|
|
37
40
|
export function createRecordBook(config) {
|
|
38
|
-
const storage = config.storage
|
|
41
|
+
const storage = config.storage === undefined ? defaultKeyValueStorage() : config.storage;
|
|
39
42
|
let best = loadStored(config, storage);
|
|
40
43
|
function persist() {
|
|
41
44
|
if (storage === null)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type ParamLayer, type LayerSelection } from "./paramLayers.js";
|
|
2
|
+
/**
|
|
3
|
+
* A selectable rule over a tagged pool. `tags` classify it for include/exclude filtering and for
|
|
4
|
+
* `requires`/`conflicts` matching (which match either another rule's id or one of its tags). `layers`
|
|
5
|
+
* are the parameter layers this rule contributes when active; `payload` is opaque caller data.
|
|
6
|
+
*/
|
|
7
|
+
export interface RuleDef<TPayload = unknown> {
|
|
8
|
+
readonly id: string;
|
|
9
|
+
readonly tags?: readonly string[];
|
|
10
|
+
/** Relative pick weight; non-positive or omitted is treated as `1`. */
|
|
11
|
+
readonly weight?: number;
|
|
12
|
+
/** Ids/tags that must already be selected for this rule to become eligible. */
|
|
13
|
+
readonly requires?: readonly string[];
|
|
14
|
+
/** Ids/tags that, if already selected (either direction), make this rule ineligible. */
|
|
15
|
+
readonly conflicts?: readonly string[];
|
|
16
|
+
/** Parameter layers contributed when this rule is active, read back via {@link RuleRegistry.layersFor}. */
|
|
17
|
+
readonly layers?: readonly ParamLayer[];
|
|
18
|
+
readonly payload?: TPayload;
|
|
19
|
+
}
|
|
20
|
+
/** Deterministic selection inputs — plain serializable data a host can persist and replay. */
|
|
21
|
+
export interface RuleSelectionConfig {
|
|
22
|
+
readonly count: number;
|
|
23
|
+
readonly seed: string | number;
|
|
24
|
+
/** If set, only rules carrying at least one of these tags are eligible. */
|
|
25
|
+
readonly includeTags?: readonly string[];
|
|
26
|
+
/** Rules carrying any of these tags are excluded outright. */
|
|
27
|
+
readonly excludeTags?: readonly string[];
|
|
28
|
+
/** Rule ids force-selected first, in order; counts toward `count`. Unknown ids are ignored. */
|
|
29
|
+
readonly locked?: readonly string[];
|
|
30
|
+
}
|
|
31
|
+
/** The result of a selection — the chosen rules and their ids (a serializable {@link LayerSelection}-style list). */
|
|
32
|
+
export interface RuleSelection<TPayload = unknown> {
|
|
33
|
+
readonly rules: readonly RuleDef<TPayload>[];
|
|
34
|
+
readonly ids: readonly string[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Select up to `count` rules from `pool` deterministically from `config.seed`. Locked ids are placed
|
|
38
|
+
* first (in order), then remaining slots are filled by weighted draw from tag-filtered, still-eligible
|
|
39
|
+
* candidates, honoring `requires`/`conflicts` after each pick. Each slot draws from an independent
|
|
40
|
+
* seed stream keyed by slot index, so a later slot's outcome never shifts an earlier one. Selection
|
|
41
|
+
* stops early when no compatible candidate remains. Pure given `pool` + `config`.
|
|
42
|
+
*
|
|
43
|
+
* @capability rule-selection deterministically select rules from a tagged pool under compatibility/exclusion constraints
|
|
44
|
+
*/
|
|
45
|
+
export declare function selectRules<TPayload = unknown>(pool: readonly RuleDef<TPayload>[], config: RuleSelectionConfig): RuleSelection<TPayload>;
|
|
46
|
+
/**
|
|
47
|
+
* Reroll a prior selection: keep `keepIds` (the locked slots) and re-draw the rest under a fresh,
|
|
48
|
+
* still-deterministic stream derived from `config.seed` and `salt`. The same seed + salt + keep set
|
|
49
|
+
* reproduce the same reroll, so reroll history stays replayable.
|
|
50
|
+
*
|
|
51
|
+
* @capability rule-selection reroll unlocked slots of a selection deterministically from seed and salt
|
|
52
|
+
*/
|
|
53
|
+
export declare function rerollRules<TPayload = unknown>(pool: readonly RuleDef<TPayload>[], config: RuleSelectionConfig, keepIds: readonly string[], salt?: string | number): RuleSelection<TPayload>;
|
|
54
|
+
/** Registered pool of {@link RuleDef}s — the install seam games register mutators/modes/events into. */
|
|
55
|
+
export interface RuleRegistry<TPayload = unknown> {
|
|
56
|
+
register(rule: RuleDef<TPayload>): void;
|
|
57
|
+
has(id: string): boolean;
|
|
58
|
+
get(id: string): RuleDef<TPayload> | undefined;
|
|
59
|
+
all(): readonly RuleDef<TPayload>[];
|
|
60
|
+
/** Run {@link selectRules} over the registered pool. */
|
|
61
|
+
select(config: RuleSelectionConfig): RuleSelection<TPayload>;
|
|
62
|
+
/**
|
|
63
|
+
* Collect the parameter layers contributed by the given selected rule ids, in id order. Unknown ids
|
|
64
|
+
* are skipped so stale/migrated selections resolve without throwing. This is the bridge from a
|
|
65
|
+
* rule selection to a {@link ParamLayer} stack — core reads `.layers`, never a rule's name.
|
|
66
|
+
*/
|
|
67
|
+
layersFor(ids: LayerSelection): readonly ParamLayer[];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Create a {@link RuleRegistry}. Rules install through `register` (duplicate ids throw); selection and
|
|
71
|
+
* `layersFor` are data-driven, so the core never branches on which mutators exist.
|
|
72
|
+
*
|
|
73
|
+
* @capability rule-selection register rules and bridge a selection to its contributed parameter layers
|
|
74
|
+
*/
|
|
75
|
+
export declare function createRuleRegistry<TPayload = unknown>(initial?: readonly RuleDef<TPayload>[]): RuleRegistry<TPayload>;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seeded rule selection over a tagged pool.
|
|
3
|
+
*
|
|
4
|
+
* A {@link RuleDef} is a caller-owned selectable — a mutator, game mode, event, or challenge modifier
|
|
5
|
+
* — carrying tags, an optional weight, compatibility (`requires`) and exclusion (`conflicts`)
|
|
6
|
+
* constraints, and any {@link ParamLayer}s it contributes when active. {@link selectRules} draws a
|
|
7
|
+
* requested count from the eligible pool using injected, deterministic randomness: the same seed and
|
|
8
|
+
* pool always yield the same rules, so a host can reproduce a session from `{ seed, count }` alone and
|
|
9
|
+
* every peer agrees without replicating the roll.
|
|
10
|
+
*
|
|
11
|
+
* Core never branches on known rule names — selection is driven entirely by tags and the caller's
|
|
12
|
+
* constraint data, and a selected rule's behavior is read back through its `layers`/`payload`. That
|
|
13
|
+
* keeps the seam genre-agnostic and lets games install rules through {@link createRuleRegistry}
|
|
14
|
+
* without the primitive knowing what any mutator "means".
|
|
15
|
+
*/
|
|
16
|
+
import { pickWeighted } from "../random/pick.js";
|
|
17
|
+
import { seededStreams } from "../random/rng.js";
|
|
18
|
+
import {} from "./paramLayers.js";
|
|
19
|
+
function tokensOf(rule) {
|
|
20
|
+
return [rule.id, ...(rule.tags ?? [])];
|
|
21
|
+
}
|
|
22
|
+
function passesTagFilter(rule, config) {
|
|
23
|
+
const tags = rule.tags ?? [];
|
|
24
|
+
if (config.excludeTags?.some((tag) => tags.includes(tag)))
|
|
25
|
+
return false;
|
|
26
|
+
if (config.includeTags && config.includeTags.length > 0) {
|
|
27
|
+
return config.includeTags.some((tag) => tags.includes(tag));
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
function isCompatible(rule, selected) {
|
|
32
|
+
const selectedTokens = new Set(selected.flatMap(tokensOf));
|
|
33
|
+
if (rule.requires && !rule.requires.every((token) => selectedTokens.has(token)))
|
|
34
|
+
return false;
|
|
35
|
+
if (rule.conflicts?.some((token) => selectedTokens.has(token)))
|
|
36
|
+
return false;
|
|
37
|
+
const ruleTokens = new Set(tokensOf(rule));
|
|
38
|
+
for (const other of selected) {
|
|
39
|
+
if (other.conflicts?.some((token) => ruleTokens.has(token)))
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Select up to `count` rules from `pool` deterministically from `config.seed`. Locked ids are placed
|
|
46
|
+
* first (in order), then remaining slots are filled by weighted draw from tag-filtered, still-eligible
|
|
47
|
+
* candidates, honoring `requires`/`conflicts` after each pick. Each slot draws from an independent
|
|
48
|
+
* seed stream keyed by slot index, so a later slot's outcome never shifts an earlier one. Selection
|
|
49
|
+
* stops early when no compatible candidate remains. Pure given `pool` + `config`.
|
|
50
|
+
*
|
|
51
|
+
* @capability rule-selection deterministically select rules from a tagged pool under compatibility/exclusion constraints
|
|
52
|
+
*/
|
|
53
|
+
export function selectRules(pool, config) {
|
|
54
|
+
const byId = new Map(pool.map((rule) => [rule.id, rule]));
|
|
55
|
+
const chosen = [];
|
|
56
|
+
const chosenIds = new Set();
|
|
57
|
+
for (const id of config.locked ?? []) {
|
|
58
|
+
const rule = byId.get(id);
|
|
59
|
+
if (rule && !chosenIds.has(id)) {
|
|
60
|
+
chosen.push(rule);
|
|
61
|
+
chosenIds.add(id);
|
|
62
|
+
}
|
|
63
|
+
if (chosen.length >= config.count)
|
|
64
|
+
return { rules: chosen, ids: chosen.map((r) => r.id) };
|
|
65
|
+
}
|
|
66
|
+
const streams = seededStreams(config.seed);
|
|
67
|
+
let slot = chosen.length;
|
|
68
|
+
while (chosen.length < config.count) {
|
|
69
|
+
const candidates = pool.filter((rule) => !chosenIds.has(rule.id) && passesTagFilter(rule, config) && isCompatible(rule, chosen));
|
|
70
|
+
if (candidates.length === 0)
|
|
71
|
+
break;
|
|
72
|
+
const picked = pickWeighted(streams(`slot:${slot}`), candidates, (rule) => rule.weight !== undefined && rule.weight > 0 ? rule.weight : 1);
|
|
73
|
+
if (!picked)
|
|
74
|
+
break;
|
|
75
|
+
chosen.push(picked);
|
|
76
|
+
chosenIds.add(picked.id);
|
|
77
|
+
slot += 1;
|
|
78
|
+
}
|
|
79
|
+
return { rules: chosen, ids: chosen.map((r) => r.id) };
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Reroll a prior selection: keep `keepIds` (the locked slots) and re-draw the rest under a fresh,
|
|
83
|
+
* still-deterministic stream derived from `config.seed` and `salt`. The same seed + salt + keep set
|
|
84
|
+
* reproduce the same reroll, so reroll history stays replayable.
|
|
85
|
+
*
|
|
86
|
+
* @capability rule-selection reroll unlocked slots of a selection deterministically from seed and salt
|
|
87
|
+
*/
|
|
88
|
+
export function rerollRules(pool, config, keepIds, salt = 0) {
|
|
89
|
+
return selectRules(pool, {
|
|
90
|
+
...config,
|
|
91
|
+
locked: keepIds,
|
|
92
|
+
seed: `${config.seed}:reroll:${salt}`,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Create a {@link RuleRegistry}. Rules install through `register` (duplicate ids throw); selection and
|
|
97
|
+
* `layersFor` are data-driven, so the core never branches on which mutators exist.
|
|
98
|
+
*
|
|
99
|
+
* @capability rule-selection register rules and bridge a selection to its contributed parameter layers
|
|
100
|
+
*/
|
|
101
|
+
export function createRuleRegistry(initial) {
|
|
102
|
+
const rules = new Map();
|
|
103
|
+
const register = (rule) => {
|
|
104
|
+
if (rules.has(rule.id))
|
|
105
|
+
throw new Error(`rule "${rule.id}" is already registered`);
|
|
106
|
+
rules.set(rule.id, rule);
|
|
107
|
+
};
|
|
108
|
+
if (initial)
|
|
109
|
+
for (const rule of initial)
|
|
110
|
+
register(rule);
|
|
111
|
+
return {
|
|
112
|
+
register,
|
|
113
|
+
has: (id) => rules.has(id),
|
|
114
|
+
get: (id) => rules.get(id),
|
|
115
|
+
all: () => [...rules.values()],
|
|
116
|
+
select: (config) => selectRules([...rules.values()], config),
|
|
117
|
+
layersFor(ids) {
|
|
118
|
+
const layers = [];
|
|
119
|
+
for (const id of ids) {
|
|
120
|
+
const rule = rules.get(id);
|
|
121
|
+
if (rule?.layers)
|
|
122
|
+
layers.push(...rule.layers);
|
|
123
|
+
}
|
|
124
|
+
return layers;
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
package/dist/game/runDraft.d.ts
CHANGED
|
@@ -31,4 +31,9 @@ export interface RunDraft<TStat extends string = string, TData = unknown> {
|
|
|
31
31
|
choose(offerId: string): boolean;
|
|
32
32
|
stack(): RunModifierStack<TStat, TData>;
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* A roguelike run built from stacking drafted modifier picks that reshape the run.
|
|
36
|
+
*
|
|
37
|
+
* @capability run-modifiers a roguelike run built from stacking drafted modifier picks
|
|
38
|
+
*/
|
|
34
39
|
export declare function createRunDraft<TStat extends string = string, TData = unknown>(config: RunDraftConfig<TStat, TData>): RunDraft<TStat, TData>;
|
package/dist/game/runDraft.js
CHANGED
|
@@ -68,6 +68,11 @@ export function createRunModifierStack(offers) {
|
|
|
68
68
|
},
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* A roguelike run built from stacking drafted modifier picks that reshape the run.
|
|
73
|
+
*
|
|
74
|
+
* @capability run-modifiers a roguelike run built from stacking drafted modifier picks
|
|
75
|
+
*/
|
|
71
76
|
export function createRunDraft(config) {
|
|
72
77
|
const stack = createRunModifierStack(config.offers);
|
|
73
78
|
const defaultRng = config.rng ?? Math.random;
|