@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** @internal */
|
|
1
2
|
export function idleWindow(lastSeenMs, nowMs, config = {}) {
|
|
2
3
|
const elapsedSeconds = Math.max(0, (nowMs - lastSeenMs) / 1000);
|
|
3
4
|
const capped = config.maxSeconds !== undefined && elapsedSeconds > config.maxSeconds;
|
|
@@ -9,17 +10,20 @@ export function idleWindow(lastSeenMs, nowMs, config = {}) {
|
|
|
9
10
|
capped,
|
|
10
11
|
};
|
|
11
12
|
}
|
|
13
|
+
/** @internal */
|
|
12
14
|
export function linearCatchUp(seconds, input) {
|
|
13
15
|
const clampedSeconds = Math.max(0, seconds);
|
|
14
16
|
const value = input.current + input.ratePerSecond * clampedSeconds;
|
|
15
17
|
return Math.min(input.max ?? Number.POSITIVE_INFINITY, Math.max(input.min ?? Number.NEGATIVE_INFINITY, value));
|
|
16
18
|
}
|
|
19
|
+
/** @internal */
|
|
17
20
|
export function exponentialCatchUp(seconds, input) {
|
|
18
21
|
const clampedSeconds = Math.max(0, seconds);
|
|
19
22
|
const factor = Math.max(0, input.factorPerSecond);
|
|
20
23
|
const value = input.current * factor ** clampedSeconds;
|
|
21
24
|
return Math.min(input.max ?? Number.POSITIVE_INFINITY, Math.max(input.min ?? Number.NEGATIVE_INFINITY, value));
|
|
22
25
|
}
|
|
26
|
+
/** @internal */
|
|
23
27
|
export function steppedCatchUp(seconds, stepSeconds, step, maxSteps) {
|
|
24
28
|
if (stepSeconds <= 0 || seconds <= 0) {
|
|
25
29
|
return { steps: 0, remainderSeconds: Math.max(0, seconds) };
|
package/dist/time/simClock.d.ts
CHANGED
|
@@ -51,6 +51,8 @@ export interface SimClock {
|
|
|
51
51
|
advance(realDt: number): number;
|
|
52
52
|
now(): number;
|
|
53
53
|
snapshot(): ClockSnapshot;
|
|
54
|
+
/** Restore clock position — game-time, pause state, and speed multipliers — from a {@link ClockSnapshot} for whole-world save/load. Live timers registered via `after`/`every`/`at` are unaffected. */
|
|
55
|
+
hydrate(snapshot: ClockSnapshot): void;
|
|
54
56
|
calendar(): CalendarTime;
|
|
55
57
|
isPaused(): boolean;
|
|
56
58
|
speed(): number;
|
package/dist/time/simClock.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { SECONDS_PER_GAME_DAY } from "./gameClock.js";
|
|
2
|
-
const
|
|
2
|
+
const MAX_TIMER_FIRES_PER_ADVANCE = 10_000;
|
|
3
|
+
function requireFiniteTimerValue(label, value) {
|
|
4
|
+
if (!Number.isFinite(value)) {
|
|
5
|
+
throw new Error(`SimClock ${label} must be finite, got ${value}`);
|
|
6
|
+
}
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
3
9
|
export function createSimClock(options = {}) {
|
|
4
10
|
const config = options.config ?? {};
|
|
5
11
|
const onChange = options.onChange ?? (() => { });
|
|
@@ -62,7 +68,7 @@ export function createSimClock(options = {}) {
|
|
|
62
68
|
}
|
|
63
69
|
timer.callback();
|
|
64
70
|
fires += 1;
|
|
65
|
-
if (fires >=
|
|
71
|
+
if (fires >= MAX_TIMER_FIRES_PER_ADVANCE)
|
|
66
72
|
return;
|
|
67
73
|
}
|
|
68
74
|
}
|
|
@@ -121,10 +127,19 @@ export function createSimClock(options = {}) {
|
|
|
121
127
|
timers.delete(id);
|
|
122
128
|
};
|
|
123
129
|
}
|
|
130
|
+
function hydrate(state) {
|
|
131
|
+
now = state.now;
|
|
132
|
+
paused = state.paused;
|
|
133
|
+
playSpeed = state.playSpeed;
|
|
134
|
+
timescale = state.timescale;
|
|
135
|
+
displayMinute = minuteIndex(now, dayLength);
|
|
136
|
+
onChange();
|
|
137
|
+
}
|
|
124
138
|
return {
|
|
125
139
|
advance,
|
|
126
140
|
now: () => now,
|
|
127
141
|
calendar,
|
|
142
|
+
hydrate,
|
|
128
143
|
snapshot: () => ({
|
|
129
144
|
now,
|
|
130
145
|
paused,
|
|
@@ -144,12 +159,12 @@ export function createSimClock(options = {}) {
|
|
|
144
159
|
cycleSpeed,
|
|
145
160
|
timescale: () => timescale,
|
|
146
161
|
setTimescale,
|
|
147
|
-
after: (seconds, callback) => register(now + Math.max(0, seconds), null, callback),
|
|
162
|
+
after: (seconds, callback) => register(now + Math.max(0, requireFiniteTimerValue("after(seconds)", seconds)), null, callback),
|
|
148
163
|
every: (seconds, callback) => {
|
|
149
|
-
const interval = Math.max(Number.EPSILON, seconds);
|
|
164
|
+
const interval = Math.max(Number.EPSILON, requireFiniteTimerValue("every(seconds)", seconds));
|
|
150
165
|
return register(now + interval, interval, callback);
|
|
151
166
|
},
|
|
152
|
-
at: (time, callback) => register(time, null, callback),
|
|
167
|
+
at: (time, callback) => register(requireFiniteTimerValue("at(time)", time), null, callback),
|
|
153
168
|
};
|
|
154
169
|
}
|
|
155
170
|
function minuteIndex(seconds, dayLength) {
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless view model for a contextual action collection — the data layer behind an RTS command
|
|
3
|
+
* card, an ability action bar, a build menu, or a radial. It carries *what the buttons mean*
|
|
4
|
+
* (label, hotkey, cost, cooldown, disabled reasons, toggle state) with zero rendering opinion, so a
|
|
5
|
+
* game keeps layout, dimensions, hotkeys, catalog-id mapping, renderer, and chrome caller-owned. The
|
|
6
|
+
* React renderers in `@jgengine/react/actionHud` consume this; a game can swap either without
|
|
7
|
+
* forking the logic here.
|
|
8
|
+
*/
|
|
9
|
+
/** A spendable-resource line on an action (e.g. `40` mana). `met` is false only when `available` is known and short. */
|
|
10
|
+
export interface ActionCost {
|
|
11
|
+
resourceId: string;
|
|
12
|
+
amount: number;
|
|
13
|
+
/** Current pool, when the caller knows it — drives {@link ActionCost.met}. Omit when unknown. */
|
|
14
|
+
available?: number;
|
|
15
|
+
/** True unless `available` is known and below `amount`. */
|
|
16
|
+
met: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** Cooldown state for an action, expressed so a radial sweep or countdown can render directly. */
|
|
19
|
+
export interface ActionCooldown {
|
|
20
|
+
remainingMs: number;
|
|
21
|
+
totalMs: number;
|
|
22
|
+
/** Remaining share of the cooldown, `0..1` — the fraction a radial wipe should still cover. */
|
|
23
|
+
fraction: number;
|
|
24
|
+
ready: boolean;
|
|
25
|
+
}
|
|
26
|
+
/** A single blocking reason: a stable machine `code` plus caller-facing `message`. */
|
|
27
|
+
export interface ActionReason {
|
|
28
|
+
/** Stable tag for styling/branching, e.g. `"cooldown" | "cost" | "range" | "disabled"`. */
|
|
29
|
+
code: string;
|
|
30
|
+
message: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A caller-authored contextual action — the DATA input to the model. Every field except `id` is
|
|
34
|
+
* optional so a build button, a stance toggle, and a cooldown ability all use the same shape.
|
|
35
|
+
* Catalog-id mapping (turning `icon`/`label` into art and copy) stays caller-owned.
|
|
36
|
+
*/
|
|
37
|
+
export interface ActionDef {
|
|
38
|
+
id: string;
|
|
39
|
+
label?: string;
|
|
40
|
+
description?: string;
|
|
41
|
+
/** Icon id / glyph the caller maps to a catalog; the model only carries it through. */
|
|
42
|
+
icon?: string;
|
|
43
|
+
/** Human hotkey label (e.g. `"Q"`, `"1"`). Matched case-insensitively by {@link actionByHotkey}. */
|
|
44
|
+
hotkey?: string;
|
|
45
|
+
/** Section id for grouping a large card into rows/tabs; caller-owned. */
|
|
46
|
+
group?: string;
|
|
47
|
+
cooldown?: ActionCooldown;
|
|
48
|
+
costs?: readonly ActionCost[];
|
|
49
|
+
/** Toggle/stance state — a mode that is currently on. */
|
|
50
|
+
active?: boolean;
|
|
51
|
+
/** Hard-disable regardless of cost/cooldown (not yet researched, no valid target). */
|
|
52
|
+
disabled?: boolean;
|
|
53
|
+
/** Extra caller reasons (out of range, needs tech). Merged after the derived cost/cooldown ones. */
|
|
54
|
+
reasons?: readonly ActionReason[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* A resolved action view model — availability computed once from cooldown, cost, `disabled`, and any
|
|
58
|
+
* caller reasons. `enabled` is the single truth a renderer gates interaction on; `reasons` is the
|
|
59
|
+
* ordered explanation (cooldown, then unmet costs, then caller reasons, then a generic disable).
|
|
60
|
+
*/
|
|
61
|
+
export interface ResolvedAction {
|
|
62
|
+
id: string;
|
|
63
|
+
label: string;
|
|
64
|
+
description?: string;
|
|
65
|
+
icon?: string;
|
|
66
|
+
hotkey?: string;
|
|
67
|
+
group?: string;
|
|
68
|
+
active: boolean;
|
|
69
|
+
enabled: boolean;
|
|
70
|
+
cooldown: ActionCooldown | null;
|
|
71
|
+
costs: readonly ActionCost[];
|
|
72
|
+
reasons: readonly ActionReason[];
|
|
73
|
+
}
|
|
74
|
+
/** Build an {@link ActionCooldown} from remaining/total milliseconds. `ready` when nothing remains. */
|
|
75
|
+
export declare function actionCooldown(remainingMs: number, totalMs: number): ActionCooldown;
|
|
76
|
+
/**
|
|
77
|
+
* Build an {@link ActionCooldown} from remaining milliseconds and a known remaining fraction — the
|
|
78
|
+
* shape adapters get from sources (like an ability snapshot) that report a fraction but not a total.
|
|
79
|
+
*/
|
|
80
|
+
export declare function actionCooldownFromFraction(remainingMs: number, fraction: number): ActionCooldown;
|
|
81
|
+
/** Build an {@link ActionCost}. `met` is true unless `available` is provided and below `amount`. */
|
|
82
|
+
export declare function actionCost(resourceId: string, amount: number, available?: number): ActionCost;
|
|
83
|
+
/**
|
|
84
|
+
* Resolve one {@link ActionDef} into a {@link ResolvedAction}: normalize label, compute `enabled`,
|
|
85
|
+
* and order the blocking `reasons`. Pure — the same input always yields the same view model.
|
|
86
|
+
*/
|
|
87
|
+
export declare function resolveAction(def: ActionDef): ResolvedAction;
|
|
88
|
+
/** Resolve an ordered list of actions, preserving input order. */
|
|
89
|
+
export declare function resolveActionCollection(defs: readonly ActionDef[]): ResolvedAction[];
|
|
90
|
+
/**
|
|
91
|
+
* Find the action whose `hotkey` matches `key`, case-insensitively — the keyboard-shortcut router for
|
|
92
|
+
* an action bar. Returns the first match's id, or null. Matching by hotkey does not consider
|
|
93
|
+
* `enabled`; the caller decides whether to fire a disabled action's feedback.
|
|
94
|
+
*/
|
|
95
|
+
export declare function actionByHotkey(actions: readonly ResolvedAction[], key: string): string | null;
|
|
96
|
+
/** A focus move for keyboard/controller grid navigation over an action collection. */
|
|
97
|
+
export type FocusDirection = "left" | "right" | "up" | "down" | "next" | "prev" | "first" | "last";
|
|
98
|
+
/** Options for {@link moveGridFocus}. */
|
|
99
|
+
export interface GridFocusOptions {
|
|
100
|
+
/** Wrap around edges instead of clamping (default false). */
|
|
101
|
+
wrap?: boolean;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Pure grid focus math for keyboard and controller navigation of an action card laid out in
|
|
105
|
+
* `columns`-wide rows. Given the current index (`-1` for none) it returns the next index, clamped to
|
|
106
|
+
* `[0, count)` — or wrapped when `wrap` is set. `left/right` move within/across columns, `up/down`
|
|
107
|
+
* across rows, `next/prev` walk the flat order, `first/last` jump to the ends. Renderer-agnostic:
|
|
108
|
+
* radial and list layouts pass `columns: 1` (or `count`) to get sensible 1-D stepping.
|
|
109
|
+
*/
|
|
110
|
+
export declare function moveGridFocus(current: number, count: number, columns: number, dir: FocusDirection, options?: GridFocusOptions): number;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless view model for a contextual action collection — the data layer behind an RTS command
|
|
3
|
+
* card, an ability action bar, a build menu, or a radial. It carries *what the buttons mean*
|
|
4
|
+
* (label, hotkey, cost, cooldown, disabled reasons, toggle state) with zero rendering opinion, so a
|
|
5
|
+
* game keeps layout, dimensions, hotkeys, catalog-id mapping, renderer, and chrome caller-owned. The
|
|
6
|
+
* React renderers in `@jgengine/react/actionHud` consume this; a game can swap either without
|
|
7
|
+
* forking the logic here.
|
|
8
|
+
*/
|
|
9
|
+
const clamp01 = (value) => (value < 0 ? 0 : value > 1 ? 1 : value);
|
|
10
|
+
/** Build an {@link ActionCooldown} from remaining/total milliseconds. `ready` when nothing remains. */
|
|
11
|
+
export function actionCooldown(remainingMs, totalMs) {
|
|
12
|
+
const remaining = Math.max(0, remainingMs);
|
|
13
|
+
const total = Math.max(0, totalMs);
|
|
14
|
+
return {
|
|
15
|
+
remainingMs: remaining,
|
|
16
|
+
totalMs: total,
|
|
17
|
+
fraction: total <= 0 ? 0 : clamp01(remaining / total),
|
|
18
|
+
ready: remaining <= 0,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Build an {@link ActionCooldown} from remaining milliseconds and a known remaining fraction — the
|
|
23
|
+
* shape adapters get from sources (like an ability snapshot) that report a fraction but not a total.
|
|
24
|
+
*/
|
|
25
|
+
export function actionCooldownFromFraction(remainingMs, fraction) {
|
|
26
|
+
const remaining = Math.max(0, remainingMs);
|
|
27
|
+
const frac = clamp01(fraction);
|
|
28
|
+
return {
|
|
29
|
+
remainingMs: remaining,
|
|
30
|
+
totalMs: frac > 0 ? Math.round(remaining / frac) : remaining,
|
|
31
|
+
fraction: frac,
|
|
32
|
+
ready: remaining <= 0,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Build an {@link ActionCost}. `met` is true unless `available` is provided and below `amount`. */
|
|
36
|
+
export function actionCost(resourceId, amount, available) {
|
|
37
|
+
const cost = {
|
|
38
|
+
resourceId,
|
|
39
|
+
amount,
|
|
40
|
+
met: available === undefined ? true : available >= amount,
|
|
41
|
+
};
|
|
42
|
+
if (available !== undefined)
|
|
43
|
+
cost.available = available;
|
|
44
|
+
return cost;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Resolve one {@link ActionDef} into a {@link ResolvedAction}: normalize label, compute `enabled`,
|
|
48
|
+
* and order the blocking `reasons`. Pure — the same input always yields the same view model.
|
|
49
|
+
*/
|
|
50
|
+
export function resolveAction(def) {
|
|
51
|
+
const costs = def.costs ?? [];
|
|
52
|
+
const reasons = [];
|
|
53
|
+
const cooldown = def.cooldown ?? null;
|
|
54
|
+
if (cooldown !== null && !cooldown.ready) {
|
|
55
|
+
reasons.push({ code: "cooldown", message: cooldownMessage(cooldown) });
|
|
56
|
+
}
|
|
57
|
+
for (const cost of costs) {
|
|
58
|
+
if (!cost.met)
|
|
59
|
+
reasons.push({ code: "cost", message: costMessage(cost) });
|
|
60
|
+
}
|
|
61
|
+
for (const reason of def.reasons ?? [])
|
|
62
|
+
reasons.push(reason);
|
|
63
|
+
if (def.disabled === true && reasons.length === 0) {
|
|
64
|
+
reasons.push({ code: "disabled", message: "Unavailable" });
|
|
65
|
+
}
|
|
66
|
+
const enabled = def.disabled !== true && reasons.length === 0;
|
|
67
|
+
const resolved = {
|
|
68
|
+
id: def.id,
|
|
69
|
+
label: def.label ?? def.id,
|
|
70
|
+
active: def.active === true,
|
|
71
|
+
enabled,
|
|
72
|
+
cooldown,
|
|
73
|
+
costs,
|
|
74
|
+
reasons,
|
|
75
|
+
};
|
|
76
|
+
if (def.description !== undefined)
|
|
77
|
+
resolved.description = def.description;
|
|
78
|
+
if (def.icon !== undefined)
|
|
79
|
+
resolved.icon = def.icon;
|
|
80
|
+
if (def.hotkey !== undefined)
|
|
81
|
+
resolved.hotkey = def.hotkey;
|
|
82
|
+
if (def.group !== undefined)
|
|
83
|
+
resolved.group = def.group;
|
|
84
|
+
return resolved;
|
|
85
|
+
}
|
|
86
|
+
/** Resolve an ordered list of actions, preserving input order. */
|
|
87
|
+
export function resolveActionCollection(defs) {
|
|
88
|
+
return defs.map(resolveAction);
|
|
89
|
+
}
|
|
90
|
+
/** @internal Default cooldown reason copy: `Ready in 2.4s`. */
|
|
91
|
+
function cooldownMessage(cooldown) {
|
|
92
|
+
return `Ready in ${(cooldown.remainingMs / 1000).toFixed(1)}s`;
|
|
93
|
+
}
|
|
94
|
+
/** @internal Default unmet-cost reason copy: `Needs 40 mana`. */
|
|
95
|
+
function costMessage(cost) {
|
|
96
|
+
return `Needs ${cost.amount} ${cost.resourceId}`;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Find the action whose `hotkey` matches `key`, case-insensitively — the keyboard-shortcut router for
|
|
100
|
+
* an action bar. Returns the first match's id, or null. Matching by hotkey does not consider
|
|
101
|
+
* `enabled`; the caller decides whether to fire a disabled action's feedback.
|
|
102
|
+
*/
|
|
103
|
+
export function actionByHotkey(actions, key) {
|
|
104
|
+
const needle = key.toLowerCase();
|
|
105
|
+
for (const action of actions) {
|
|
106
|
+
if (action.hotkey !== undefined && action.hotkey.toLowerCase() === needle)
|
|
107
|
+
return action.id;
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Pure grid focus math for keyboard and controller navigation of an action card laid out in
|
|
113
|
+
* `columns`-wide rows. Given the current index (`-1` for none) it returns the next index, clamped to
|
|
114
|
+
* `[0, count)` — or wrapped when `wrap` is set. `left/right` move within/across columns, `up/down`
|
|
115
|
+
* across rows, `next/prev` walk the flat order, `first/last` jump to the ends. Renderer-agnostic:
|
|
116
|
+
* radial and list layouts pass `columns: 1` (or `count`) to get sensible 1-D stepping.
|
|
117
|
+
*/
|
|
118
|
+
export function moveGridFocus(current, count, columns, dir, options) {
|
|
119
|
+
if (count <= 0)
|
|
120
|
+
return -1;
|
|
121
|
+
const cols = Math.max(1, Math.floor(columns));
|
|
122
|
+
const wrap = options?.wrap === true;
|
|
123
|
+
if (dir === "first")
|
|
124
|
+
return 0;
|
|
125
|
+
if (dir === "last")
|
|
126
|
+
return count - 1;
|
|
127
|
+
if (current < 0) {
|
|
128
|
+
return dir === "prev" || dir === "left" || dir === "up" ? count - 1 : 0;
|
|
129
|
+
}
|
|
130
|
+
const clampIndex = (index) => Math.max(0, Math.min(count - 1, index));
|
|
131
|
+
if (dir === "next")
|
|
132
|
+
return current + 1 >= count ? (wrap ? 0 : count - 1) : current + 1;
|
|
133
|
+
if (dir === "prev")
|
|
134
|
+
return current - 1 < 0 ? (wrap ? count - 1 : 0) : current - 1;
|
|
135
|
+
const row = Math.floor(current / cols);
|
|
136
|
+
const col = current % cols;
|
|
137
|
+
if (dir === "right" || dir === "left") {
|
|
138
|
+
const rowStart = row * cols;
|
|
139
|
+
const rowEnd = Math.min(count - 1, rowStart + cols - 1);
|
|
140
|
+
const width = rowEnd - rowStart + 1;
|
|
141
|
+
if (width <= 1)
|
|
142
|
+
return current;
|
|
143
|
+
const nextCol = dir === "right" ? col + 1 : col - 1;
|
|
144
|
+
if (nextCol < 0)
|
|
145
|
+
return wrap ? rowEnd : current;
|
|
146
|
+
if (nextCol > rowEnd - rowStart)
|
|
147
|
+
return wrap ? rowStart : current;
|
|
148
|
+
return rowStart + nextCol;
|
|
149
|
+
}
|
|
150
|
+
// up / down
|
|
151
|
+
const nextRow = dir === "down" ? row + 1 : row - 1;
|
|
152
|
+
const rowCount = Math.ceil(count / cols);
|
|
153
|
+
if (nextRow < 0)
|
|
154
|
+
return wrap ? clampIndex((rowCount - 1) * cols + col) : current;
|
|
155
|
+
if (nextRow > rowCount - 1)
|
|
156
|
+
return wrap ? col : current;
|
|
157
|
+
const target = nextRow * cols + col;
|
|
158
|
+
return target >= count ? (dir === "down" ? current : clampIndex(target)) : target;
|
|
159
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared mobile-composition geometry. Every UI subsystem (HUD, touch controls,
|
|
3
|
+
* system chrome, screens) registers the physical rectangle it occupies; the
|
|
4
|
+
* engine allocates the viewport once and detects when two subsystems collide,
|
|
5
|
+
* instead of each independently claiming an edge. Pure math — no DOM, no React.
|
|
6
|
+
*/
|
|
7
|
+
import type { LayoutOrientation } from "./orientation.js";
|
|
8
|
+
/** The explicit composition mode a game renders for — not a scaled desktop layout. */
|
|
9
|
+
export type GameLayoutMode = "desktop-wide" | "desktop-compact" | "mobile-landscape" | "mobile-portrait";
|
|
10
|
+
/** Edge insets in CSS pixels (safe areas, reservations). */
|
|
11
|
+
export interface Insets {
|
|
12
|
+
top: number;
|
|
13
|
+
right: number;
|
|
14
|
+
bottom: number;
|
|
15
|
+
left: number;
|
|
16
|
+
}
|
|
17
|
+
/** All-zero insets. */
|
|
18
|
+
export declare const ZERO_INSETS: Insets;
|
|
19
|
+
/** Axis-aligned rectangle in CSS pixels (origin top-left). Structurally compatible with a `DOMRect`'s edge fields. */
|
|
20
|
+
export interface LayoutRect {
|
|
21
|
+
left: number;
|
|
22
|
+
top: number;
|
|
23
|
+
right: number;
|
|
24
|
+
bottom: number;
|
|
25
|
+
}
|
|
26
|
+
/** Width of a rect (clamped to ≥0).
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
29
|
+
export declare function rectWidth(rect: LayoutRect): number;
|
|
30
|
+
/** Height of a rect (clamped to ≥0).
|
|
31
|
+
* @internal
|
|
32
|
+
*/
|
|
33
|
+
export declare function rectHeight(rect: LayoutRect): number;
|
|
34
|
+
/** Area of a rect in px².
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
export declare function rectArea(rect: LayoutRect): number;
|
|
38
|
+
/** Do the two rectangles share any interior area? Touching edges do not count.
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
41
|
+
export declare function intersects(a: LayoutRect, b: LayoutRect): boolean;
|
|
42
|
+
/** Overlapping area in px²; 0 when the rectangles don't intersect.
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
export declare function overlapArea(a: LayoutRect, b: LayoutRect): number;
|
|
46
|
+
/** Portrait when taller than wide, else landscape.
|
|
47
|
+
* @internal
|
|
48
|
+
*/
|
|
49
|
+
export declare function orientationOf(width: number, height: number): LayoutOrientation;
|
|
50
|
+
/** Inputs to `resolveLayoutMode`. */
|
|
51
|
+
export interface LayoutModeInput {
|
|
52
|
+
/** Live visible viewport dimensions (prefer `visualViewport`). */
|
|
53
|
+
width: number;
|
|
54
|
+
height: number;
|
|
55
|
+
/** Touchscreen is the primary input (`(pointer: coarse)`). */
|
|
56
|
+
coarsePointer: boolean;
|
|
57
|
+
/** Does the game support phones at all (`platforms` includes `"mobile"`)? Default true. */
|
|
58
|
+
mobileSupported?: boolean;
|
|
59
|
+
/** Desktop width below which the layout is "compact". Default 900. */
|
|
60
|
+
compactWidth?: number;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Resolve the explicit composition mode. A mobile layout is not a scaled
|
|
64
|
+
* desktop layout: a coarse-pointer device on a game that supports mobile is
|
|
65
|
+
* always a `mobile-*` mode, split by live orientation; everything else is a
|
|
66
|
+
* desktop mode split by width.
|
|
67
|
+
* @internal
|
|
68
|
+
*/
|
|
69
|
+
export declare function resolveLayoutMode(input: LayoutModeInput): GameLayoutMode;
|
|
70
|
+
/** Whether a mode is one of the phone modes.
|
|
71
|
+
* @internal
|
|
72
|
+
*/
|
|
73
|
+
export declare function isMobileMode(mode: GameLayoutMode): boolean;
|
|
74
|
+
/** What kind of UI a registered region belongs to. */
|
|
75
|
+
export type LayoutRegionKind = "hud" | "control" | "system" | "screen";
|
|
76
|
+
/** How a region participates in collision reporting. */
|
|
77
|
+
export type LayoutCollisionPolicy = "forbid" | "allow" | "warn";
|
|
78
|
+
/** Gameplay-importance tier of a HUD element. */
|
|
79
|
+
export type HudPriority = "critical" | "secondary" | "tertiary";
|
|
80
|
+
/** How a HUD element adapts on phones. */
|
|
81
|
+
export type MobileHudBehavior = "persistent" | "compact" | "icon" | "transient" | "hidden" | "sheet" | "modal";
|
|
82
|
+
/** A physical rectangle a UI subsystem occupies, published to the shared registry. */
|
|
83
|
+
export interface LayoutRegion {
|
|
84
|
+
id: string;
|
|
85
|
+
kind: LayoutRegionKind;
|
|
86
|
+
rect: LayoutRect;
|
|
87
|
+
/** `forbid` reports any overlap, `warn` reports softly, `allow` opts this region out of collision reporting entirely. */
|
|
88
|
+
collisionPolicy: LayoutCollisionPolicy;
|
|
89
|
+
priority?: HudPriority;
|
|
90
|
+
/** Region ids this one may overlap without a report. */
|
|
91
|
+
allowOverlapWith?: readonly string[];
|
|
92
|
+
/** Regions sharing a non-empty group never report against each other. */
|
|
93
|
+
collisionGroup?: string;
|
|
94
|
+
}
|
|
95
|
+
/** One detected forbidden/warned overlap between two regions. */
|
|
96
|
+
export interface LayoutCollision {
|
|
97
|
+
a: string;
|
|
98
|
+
b: string;
|
|
99
|
+
kindA: LayoutRegionKind;
|
|
100
|
+
kindB: LayoutRegionKind;
|
|
101
|
+
/** Overlapping area in px². */
|
|
102
|
+
area: number;
|
|
103
|
+
severity: "forbid" | "warn";
|
|
104
|
+
}
|
|
105
|
+
/** Options for `detectLayoutCollisions`. */
|
|
106
|
+
export interface DetectCollisionsOptions {
|
|
107
|
+
/** Ignore overlaps smaller than this many px² — avoids meaningless sub-pixel warnings. Default 64. */
|
|
108
|
+
minArea?: number;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Every forbidden or warned rectangle intersection across the registered
|
|
112
|
+
* regions. A pair is skipped when either side's policy is `allow`, when either
|
|
113
|
+
* opts into the other via `allowOverlapWith`, when they share a
|
|
114
|
+
* `collisionGroup`, or when the overlap is below `minArea`.
|
|
115
|
+
* @internal
|
|
116
|
+
*/
|
|
117
|
+
export declare function detectLayoutCollisions(regions: readonly LayoutRegion[], options?: DetectCollisionsOptions): LayoutCollision[];
|
|
118
|
+
/** Human-readable diagnostic block for a set of collisions (empty string when there are none).
|
|
119
|
+
* @internal
|
|
120
|
+
*/
|
|
121
|
+
export declare function formatLayoutCollisions(collisions: readonly LayoutCollision[]): string;
|
|
122
|
+
/**
|
|
123
|
+
* The usable gameplay rectangle after reserving physical zones for touch
|
|
124
|
+
* controls and system UI. Starts from the safe-area-inset viewport and carves
|
|
125
|
+
* each reserved zone from whichever edge it hugs.
|
|
126
|
+
* @internal
|
|
127
|
+
*/
|
|
128
|
+
export declare function computeGameplayRect(viewport: LayoutRect, safeArea: Insets, reserved: readonly LayoutRect[]): LayoutRect;
|
|
129
|
+
/** The shared live geometry the engine allocates once and every UI subsystem reads. */
|
|
130
|
+
export interface GameViewportLayout {
|
|
131
|
+
/** The layout (CSS-pixel) viewport. */
|
|
132
|
+
viewport: LayoutRect;
|
|
133
|
+
/** The live visible viewport (`window.visualViewport`), which excludes mobile browser chrome. */
|
|
134
|
+
visualViewport: LayoutRect;
|
|
135
|
+
safeArea: Insets;
|
|
136
|
+
mode: GameLayoutMode;
|
|
137
|
+
orientation: LayoutOrientation;
|
|
138
|
+
/** All registered regions with their live rects. */
|
|
139
|
+
regions: readonly LayoutRegion[];
|
|
140
|
+
/** Rects reserved by touch controls and system UI. */
|
|
141
|
+
controlZones: readonly LayoutRect[];
|
|
142
|
+
/** Usable world/gameplay area after reservations. */
|
|
143
|
+
gameplayRect: LayoutRect;
|
|
144
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared mobile-composition geometry. Every UI subsystem (HUD, touch controls,
|
|
3
|
+
* system chrome, screens) registers the physical rectangle it occupies; the
|
|
4
|
+
* engine allocates the viewport once and detects when two subsystems collide,
|
|
5
|
+
* instead of each independently claiming an edge. Pure math — no DOM, no React.
|
|
6
|
+
*/
|
|
7
|
+
/** All-zero insets. */
|
|
8
|
+
export const ZERO_INSETS = { top: 0, right: 0, bottom: 0, left: 0 };
|
|
9
|
+
/** Width of a rect (clamped to ≥0).
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export function rectWidth(rect) {
|
|
13
|
+
return Math.max(0, rect.right - rect.left);
|
|
14
|
+
}
|
|
15
|
+
/** Height of a rect (clamped to ≥0).
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
export function rectHeight(rect) {
|
|
19
|
+
return Math.max(0, rect.bottom - rect.top);
|
|
20
|
+
}
|
|
21
|
+
/** Area of a rect in px².
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
export function rectArea(rect) {
|
|
25
|
+
return rectWidth(rect) * rectHeight(rect);
|
|
26
|
+
}
|
|
27
|
+
/** Do the two rectangles share any interior area? Touching edges do not count.
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
export function intersects(a, b) {
|
|
31
|
+
return a.left < b.right && a.right > b.left && a.top < b.bottom && a.bottom > b.top;
|
|
32
|
+
}
|
|
33
|
+
/** Overlapping area in px²; 0 when the rectangles don't intersect.
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
export function overlapArea(a, b) {
|
|
37
|
+
const w = Math.min(a.right, b.right) - Math.max(a.left, b.left);
|
|
38
|
+
const h = Math.min(a.bottom, b.bottom) - Math.max(a.top, b.top);
|
|
39
|
+
return w > 0 && h > 0 ? w * h : 0;
|
|
40
|
+
}
|
|
41
|
+
/** Portrait when taller than wide, else landscape.
|
|
42
|
+
* @internal
|
|
43
|
+
*/
|
|
44
|
+
export function orientationOf(width, height) {
|
|
45
|
+
return height > width ? "portrait" : "landscape";
|
|
46
|
+
}
|
|
47
|
+
const DEFAULT_COMPACT_WIDTH = 900;
|
|
48
|
+
/**
|
|
49
|
+
* Resolve the explicit composition mode. A mobile layout is not a scaled
|
|
50
|
+
* desktop layout: a coarse-pointer device on a game that supports mobile is
|
|
51
|
+
* always a `mobile-*` mode, split by live orientation; everything else is a
|
|
52
|
+
* desktop mode split by width.
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
55
|
+
export function resolveLayoutMode(input) {
|
|
56
|
+
const orientation = orientationOf(input.width, input.height);
|
|
57
|
+
const mobile = input.coarsePointer && (input.mobileSupported ?? true);
|
|
58
|
+
if (mobile)
|
|
59
|
+
return orientation === "portrait" ? "mobile-portrait" : "mobile-landscape";
|
|
60
|
+
const compactWidth = input.compactWidth ?? DEFAULT_COMPACT_WIDTH;
|
|
61
|
+
return input.width < compactWidth ? "desktop-compact" : "desktop-wide";
|
|
62
|
+
}
|
|
63
|
+
/** Whether a mode is one of the phone modes.
|
|
64
|
+
* @internal
|
|
65
|
+
*/
|
|
66
|
+
export function isMobileMode(mode) {
|
|
67
|
+
return mode === "mobile-portrait" || mode === "mobile-landscape";
|
|
68
|
+
}
|
|
69
|
+
function allowsOverlap(a, b) {
|
|
70
|
+
if (a.allowOverlapWith?.includes(b.id) === true)
|
|
71
|
+
return true;
|
|
72
|
+
if (b.allowOverlapWith?.includes(a.id) === true)
|
|
73
|
+
return true;
|
|
74
|
+
if (a.collisionGroup !== undefined && a.collisionGroup === b.collisionGroup)
|
|
75
|
+
return true;
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
const DEFAULT_MIN_AREA = 64;
|
|
79
|
+
/**
|
|
80
|
+
* Every forbidden or warned rectangle intersection across the registered
|
|
81
|
+
* regions. A pair is skipped when either side's policy is `allow`, when either
|
|
82
|
+
* opts into the other via `allowOverlapWith`, when they share a
|
|
83
|
+
* `collisionGroup`, or when the overlap is below `minArea`.
|
|
84
|
+
* @internal
|
|
85
|
+
*/
|
|
86
|
+
export function detectLayoutCollisions(regions, options) {
|
|
87
|
+
const minArea = options?.minArea ?? DEFAULT_MIN_AREA;
|
|
88
|
+
const out = [];
|
|
89
|
+
for (let i = 0; i < regions.length; i += 1) {
|
|
90
|
+
for (let j = i + 1; j < regions.length; j += 1) {
|
|
91
|
+
const a = regions[i];
|
|
92
|
+
const b = regions[j];
|
|
93
|
+
if (a.collisionPolicy === "allow" || b.collisionPolicy === "allow")
|
|
94
|
+
continue;
|
|
95
|
+
if (allowsOverlap(a, b))
|
|
96
|
+
continue;
|
|
97
|
+
const area = overlapArea(a.rect, b.rect);
|
|
98
|
+
if (area < minArea)
|
|
99
|
+
continue;
|
|
100
|
+
const severity = a.collisionPolicy === "forbid" || b.collisionPolicy === "forbid" ? "forbid" : "warn";
|
|
101
|
+
out.push({ a: a.id, b: b.id, kindA: a.kind, kindB: b.kind, area: Math.round(area), severity });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
function withThousands(n) {
|
|
107
|
+
return Math.round(n)
|
|
108
|
+
.toString()
|
|
109
|
+
.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
110
|
+
}
|
|
111
|
+
/** Human-readable diagnostic block for a set of collisions (empty string when there are none).
|
|
112
|
+
* @internal
|
|
113
|
+
*/
|
|
114
|
+
export function formatLayoutCollisions(collisions) {
|
|
115
|
+
if (collisions.length === 0)
|
|
116
|
+
return "";
|
|
117
|
+
const lines = collisions.map((c) => `${c.a} intersects ${c.b} by ${withThousands(c.area)} px²`);
|
|
118
|
+
return ["JGENGINE MOBILE LAYOUT COLLISION", ...lines].join("\n");
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The usable gameplay rectangle after reserving physical zones for touch
|
|
122
|
+
* controls and system UI. Starts from the safe-area-inset viewport and carves
|
|
123
|
+
* each reserved zone from whichever edge it hugs.
|
|
124
|
+
* @internal
|
|
125
|
+
*/
|
|
126
|
+
export function computeGameplayRect(viewport, safeArea, reserved) {
|
|
127
|
+
let top = viewport.top + safeArea.top;
|
|
128
|
+
let bottom = viewport.bottom - safeArea.bottom;
|
|
129
|
+
let left = viewport.left + safeArea.left;
|
|
130
|
+
let right = viewport.right - safeArea.right;
|
|
131
|
+
const midX = (viewport.left + viewport.right) / 2;
|
|
132
|
+
const midY = (viewport.top + viewport.bottom) / 2;
|
|
133
|
+
for (const zone of reserved) {
|
|
134
|
+
const zoneMidX = (zone.left + zone.right) / 2;
|
|
135
|
+
const zoneMidY = (zone.top + zone.bottom) / 2;
|
|
136
|
+
const spanX = zone.right - zone.left;
|
|
137
|
+
const spanY = zone.bottom - zone.top;
|
|
138
|
+
if (zoneMidY > midY)
|
|
139
|
+
bottom = Math.min(bottom, zone.top);
|
|
140
|
+
else if (zoneMidY < midY && spanX >= spanY)
|
|
141
|
+
top = Math.max(top, zone.bottom);
|
|
142
|
+
else if (zoneMidX < midX)
|
|
143
|
+
left = Math.max(left, zone.right);
|
|
144
|
+
else
|
|
145
|
+
right = Math.min(right, zone.left);
|
|
146
|
+
}
|
|
147
|
+
return { left, top, right: Math.max(left, right), bottom: Math.max(top, bottom) };
|
|
148
|
+
}
|