@jgengine/core 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -2
- package/LICENSE +203 -661
- package/README.md +1 -1
- package/dist/ai/crowd.d.ts +55 -0
- package/dist/ai/crowd.js +114 -0
- package/dist/ai/flock.d.ts +5 -2
- package/dist/ai/flock.js +5 -2
- package/dist/ai/groupAssist.d.ts +1 -0
- package/dist/ai/groupAssist.js +1 -0
- package/dist/ai/heatSystem.d.ts +76 -0
- package/dist/ai/heatSystem.js +97 -0
- package/dist/ai/interestScheduler.d.ts +107 -0
- package/dist/ai/interestScheduler.js +118 -0
- package/dist/ai/jobBoard.d.ts +1 -0
- package/dist/ai/jobBoard.js +1 -0
- package/dist/ai/laneSelect.d.ts +5 -2
- package/dist/ai/laneSelect.js +5 -2
- package/dist/ai/mobBrain.d.ts +1 -0
- package/dist/ai/mobBrain.js +1 -0
- package/dist/ai/populationDirector.d.ts +103 -0
- package/dist/ai/populationDirector.js +190 -0
- package/dist/ai/pursuit.d.ts +56 -0
- package/dist/ai/pursuit.js +53 -0
- package/dist/ai/spawnDirector.d.ts +7 -2
- package/dist/ai/spawnDirector.js +18 -15
- package/dist/ai/spawnPoint.d.ts +3 -1
- package/dist/ai/spawnPoint.js +3 -1
- package/dist/ai/targetAcquisition.d.ts +84 -0
- package/dist/ai/targetAcquisition.js +92 -0
- package/dist/anim/easing.d.ts +19 -0
- package/dist/anim/easing.js +19 -0
- package/dist/anim/oscillator.d.ts +12 -0
- package/dist/anim/oscillator.js +25 -0
- package/dist/area/areaEffectField.d.ts +127 -0
- package/dist/area/areaEffectField.js +175 -0
- package/dist/area/stackPolicy.d.ts +47 -0
- package/dist/area/stackPolicy.js +99 -0
- package/dist/authoring.d.ts +2 -1
- package/dist/authoring.js +2 -1
- package/dist/board/laneBoard.d.ts +13 -0
- package/dist/board/laneBoard.js +13 -0
- package/dist/board/timelineBoard.d.ts +10 -0
- package/dist/board/timelineBoard.js +10 -0
- package/dist/combat/abilityKit.d.ts +18 -0
- package/dist/combat/abilityKit.js +24 -9
- package/dist/combat/animationState.d.ts +5 -0
- package/dist/combat/animationState.js +5 -0
- package/dist/combat/breakMeters.d.ts +10 -0
- package/dist/combat/breakMeters.js +10 -0
- package/dist/combat/castRunner.d.ts +5 -0
- package/dist/combat/castRunner.js +5 -0
- package/dist/combat/comboPoints.d.ts +5 -0
- package/dist/combat/comboPoints.js +5 -0
- package/dist/combat/comboString.d.ts +5 -0
- package/dist/combat/comboString.js +5 -0
- package/dist/combat/damageInterceptors.d.ts +192 -0
- package/dist/combat/damageInterceptors.js +251 -0
- package/dist/combat/damageMatchup.d.ts +68 -0
- package/dist/combat/damageMatchup.js +42 -0
- package/dist/combat/damageResolution.d.ts +63 -0
- package/dist/combat/damageResolution.js +50 -0
- package/dist/combat/death.d.ts +7 -1
- package/dist/combat/death.js +5 -0
- package/dist/combat/deathReason.d.ts +14 -0
- package/dist/combat/deathReason.js +1 -0
- package/dist/combat/defensiveWindow.d.ts +5 -0
- package/dist/combat/defensiveWindow.js +5 -0
- package/dist/combat/dotField.d.ts +5 -1
- package/dist/combat/dotField.js +5 -1
- package/dist/combat/downed.d.ts +5 -0
- package/dist/combat/downed.js +5 -0
- package/dist/combat/effects.d.ts +5 -0
- package/dist/combat/effects.js +5 -0
- package/dist/combat/encounterSequence.d.ts +258 -0
- package/dist/combat/encounterSequence.js +426 -0
- package/dist/combat/hitReaction.d.ts +57 -1
- package/dist/combat/hitReaction.js +45 -5
- package/dist/combat/magazine.d.ts +54 -0
- package/dist/combat/magazine.js +96 -0
- package/dist/combat/projectiles.d.ts +5 -0
- package/dist/combat/projectiles.js +32 -14
- package/dist/combat/receivedDamage.d.ts +117 -0
- package/dist/combat/receivedDamage.js +92 -0
- package/dist/combat/regenShield.d.ts +5 -1
- package/dist/combat/regenShield.js +5 -1
- package/dist/combat/renderCues.d.ts +51 -0
- package/dist/combat/renderCues.js +44 -0
- package/dist/combat/resourcePool.d.ts +5 -0
- package/dist/combat/resourcePool.js +5 -0
- package/dist/combat/rewardAllocation.d.ts +121 -0
- package/dist/combat/rewardAllocation.js +251 -0
- package/dist/combat/shotOrigin.d.ts +18 -1
- package/dist/combat/shotOrigin.js +39 -0
- package/dist/combat/statusApplication.d.ts +96 -0
- package/dist/combat/statusApplication.js +81 -0
- package/dist/combat/telegraph.d.ts +11 -2
- package/dist/combat/telegraph.js +11 -2
- package/dist/combat/thresholdCrossings.d.ts +85 -0
- package/dist/combat/thresholdCrossings.js +116 -0
- package/dist/combat.d.ts +35 -0
- package/dist/combat.js +35 -0
- package/dist/commands/commandRegistry.d.ts +11 -0
- package/dist/commands/commandRegistry.js +9 -2
- package/dist/crafting/crop.d.ts +18 -0
- package/dist/crafting/crop.js +18 -0
- package/dist/crafting/production.d.ts +5 -0
- package/dist/crafting/production.js +5 -0
- package/dist/devtools/controls.d.ts +38 -0
- package/dist/devtools/controls.js +181 -0
- package/dist/devtools/devtools.d.ts +5 -197
- package/dist/devtools/devtools.js +38 -774
- package/dist/devtools/discover.d.ts +32 -0
- package/dist/devtools/discover.js +240 -0
- package/dist/devtools/fallbackSeams.d.ts +50 -0
- package/dist/devtools/fallbackSeams.js +111 -0
- package/dist/devtools/frame.d.ts +15 -0
- package/dist/devtools/frame.js +247 -0
- package/dist/devtools/logs.d.ts +15 -0
- package/dist/devtools/logs.js +86 -0
- package/dist/devtools/overrides.d.ts +16 -0
- package/dist/devtools/overrides.js +66 -0
- package/dist/devtools/rewriteTunables.d.ts +17 -0
- package/dist/devtools/rewriteTunables.js +276 -0
- package/dist/devtools/saveEndpoint.d.ts +46 -0
- package/dist/devtools/saveEndpoint.js +48 -0
- package/dist/devtools/transformTunables.d.ts +3 -0
- package/dist/devtools/transformTunables.js +3 -0
- package/dist/devtools/tunableSchema.d.ts +31 -0
- package/dist/devtools/tunableSchema.js +31 -0
- package/dist/devtools/types.d.ts +212 -0
- package/dist/devtools/types.js +1 -0
- package/dist/economy/auctionBook.d.ts +122 -0
- package/dist/economy/auctionBook.js +213 -0
- package/dist/economy/currency.d.ts +12 -1
- package/dist/economy/currency.js +12 -1
- package/dist/economy/listingBook.d.ts +91 -0
- package/dist/economy/listingBook.js +144 -0
- package/dist/economy/priceHistory.d.ts +44 -0
- package/dist/economy/priceHistory.js +89 -0
- package/dist/economy/resourceLedger.d.ts +222 -0
- package/dist/economy/resourceLedger.js +353 -0
- package/dist/economy/sharedWallet.d.ts +14 -0
- package/dist/economy/sharedWallet.js +14 -0
- package/dist/economy/techTree.d.ts +12 -0
- package/dist/economy/techTree.js +12 -0
- package/dist/economy/unlockPoints.d.ts +92 -0
- package/dist/economy/unlockPoints.js +103 -0
- package/dist/economy/wallet.d.ts +26 -2
- package/dist/economy/wallet.js +35 -6
- package/dist/editor/commandApply.d.ts +5 -0
- package/dist/editor/commandApply.js +755 -0
- package/dist/editor/commands.d.ts +235 -7
- package/dist/editor/commands.js +82 -124
- package/dist/editor/directives.d.ts +94 -0
- package/dist/editor/directives.js +206 -0
- package/dist/editor/document.d.ts +194 -13
- package/dist/editor/document.js +1166 -19
- package/dist/editor/entityCatalog.d.ts +42 -0
- package/dist/editor/entityCatalog.js +101 -0
- package/dist/editor/environment.d.ts +74 -0
- package/dist/editor/environment.js +169 -0
- package/dist/editor/grid.d.ts +229 -0
- package/dist/editor/grid.js +366 -0
- package/dist/editor/gridAdapters.d.ts +80 -0
- package/dist/editor/gridAdapters.js +134 -0
- package/dist/editor/index.d.ts +98 -6
- package/dist/editor/index.js +13 -2
- package/dist/editor/liveSync.d.ts +127 -0
- package/dist/editor/liveSync.js +249 -0
- package/dist/editor/minimap.d.ts +31 -0
- package/dist/editor/minimap.js +73 -0
- package/dist/editor/runtimeInspector.d.ts +88 -0
- package/dist/editor/runtimeInspector.js +239 -0
- package/dist/editor/streamer.d.ts +66 -0
- package/dist/editor/streamer.js +68 -0
- package/dist/editor/types.d.ts +250 -1
- package/dist/editor/world.d.ts +136 -0
- package/dist/editor/world.js +303 -0
- package/dist/faction/reputation.d.ts +5 -0
- package/dist/faction/reputation.js +5 -0
- package/dist/format/distance.d.ts +17 -0
- package/dist/format/distance.js +18 -0
- package/dist/format/duration.d.ts +27 -0
- package/dist/format/duration.js +45 -0
- package/dist/format/speed.d.ts +17 -0
- package/dist/format/speed.js +25 -0
- package/dist/game/breeding.d.ts +161 -0
- package/dist/game/breeding.js +181 -0
- package/dist/game/cameraConfig.d.ts +451 -0
- package/dist/game/cameraConfig.js +1 -0
- package/dist/game/connectedPlayers.d.ts +6 -4
- package/dist/game/connectedPlayers.js +2 -2
- package/dist/game/cosmetics.d.ts +9 -0
- package/dist/game/cosmetics.js +16 -0
- package/dist/game/defineGame.d.ts +110 -5
- package/dist/game/defineGame.js +49 -4
- package/dist/game/defineSystem.d.ts +90 -0
- package/dist/game/defineSystem.js +47 -0
- package/dist/game/dialogue.d.ts +33 -0
- package/dist/game/dialogue.js +21 -0
- package/dist/game/events.d.ts +63 -13
- package/dist/game/events.js +5 -0
- package/dist/game/feed.d.ts +33 -0
- package/dist/game/feed.js +35 -2
- package/dist/game/gamePhase.d.ts +11 -0
- package/dist/game/gamePhase.js +16 -0
- package/dist/game/leaderboard.d.ts +5 -0
- package/dist/game/leaderboard.js +5 -0
- package/dist/game/loadout.d.ts +5 -0
- package/dist/game/loadout.js +5 -0
- package/dist/game/lootFilter.d.ts +2 -0
- package/dist/game/lootFilter.js +2 -0
- package/dist/game/lootPipeline.d.ts +164 -0
- package/dist/game/lootPipeline.js +216 -0
- package/dist/game/lootTable.d.ts +19 -2
- package/dist/game/lootTable.js +15 -5
- package/dist/game/objectives.d.ts +43 -0
- package/dist/game/objectives.js +36 -0
- package/dist/game/paramLayers.d.ts +148 -0
- package/dist/game/paramLayers.js +185 -0
- package/dist/game/ping.d.ts +5 -0
- package/dist/game/ping.js +5 -0
- package/dist/game/playableGame.d.ts +116 -432
- package/dist/game/playableGame.js +1 -1
- package/dist/game/progression.d.ts +35 -0
- package/dist/game/progression.js +19 -0
- package/dist/game/quest.d.ts +8 -0
- package/dist/game/quest.js +30 -3
- package/dist/game/race.d.ts +141 -0
- package/dist/game/race.js +169 -0
- package/dist/game/recordBook.d.ts +3 -1
- package/dist/game/recordBook.js +4 -1
- package/dist/game/ruleSelection.d.ts +75 -0
- package/dist/game/ruleSelection.js +127 -0
- package/dist/game/runDraft.d.ts +5 -0
- package/dist/game/runDraft.js +5 -0
- package/dist/game/saveStore.d.ts +111 -0
- package/dist/game/saveStore.js +312 -0
- package/dist/game/snapshotHistory.d.ts +67 -0
- package/dist/game/snapshotHistory.js +95 -0
- package/dist/game/social.d.ts +38 -1
- package/dist/game/social.js +71 -0
- package/dist/game/spawnPoints.d.ts +5 -0
- package/dist/game/spawnPoints.js +5 -0
- package/dist/game/systemRuntime.d.ts +50 -0
- package/dist/game/systemRuntime.js +157 -0
- package/dist/game/systemSchedule.d.ts +72 -0
- package/dist/game/systemSchedule.js +242 -0
- package/dist/game/toasts.d.ts +43 -0
- package/dist/game/toasts.js +44 -0
- package/dist/game/trade.d.ts +10 -0
- package/dist/game/trade.js +10 -0
- package/dist/game/tribe.d.ts +184 -0
- package/dist/game/tribe.js +432 -0
- package/dist/game/unlocks.d.ts +8 -0
- package/dist/game/unlocks.js +16 -0
- package/dist/game/vfxInstance.d.ts +138 -0
- package/dist/game/vfxInstance.js +96 -0
- package/dist/game/worldItem.d.ts +17 -4
- package/dist/game/worldItem.js +17 -4
- package/dist/gameplay.d.ts +93 -0
- package/dist/gameplay.js +93 -0
- package/dist/index.d.ts +5 -14
- package/dist/index.js +5 -14
- package/dist/input/actionBindings.d.ts +23 -6
- package/dist/input/actionBindings.js +23 -6
- package/dist/input/axisInput.d.ts +7 -2
- package/dist/input/axisInput.js +7 -2
- package/dist/input/controlGroups.d.ts +67 -0
- package/dist/input/controlGroups.js +41 -0
- package/dist/input/touchScheme.d.ts +82 -6
- package/dist/input/touchScheme.js +123 -10
- package/dist/interaction/lockpick.d.ts +61 -0
- package/dist/interaction/lockpick.js +168 -0
- package/dist/interaction/proximityPrompt.d.ts +2 -0
- package/dist/interaction/proximityPrompt.js +2 -0
- package/dist/interaction/qte.d.ts +5 -0
- package/dist/interaction/qte.js +5 -0
- package/dist/interaction/skillCheck.d.ts +5 -0
- package/dist/interaction/skillCheck.js +5 -0
- package/dist/inventory/encumbrance.d.ts +69 -0
- package/dist/inventory/encumbrance.js +68 -0
- package/dist/inventory/inventoryModel.d.ts +12 -0
- package/dist/inventory/inventoryModel.js +12 -0
- package/dist/inventory/shapedGrid.d.ts +16 -0
- package/dist/inventory/shapedGrid.js +16 -0
- package/dist/inventory/slotModel.d.ts +10 -0
- package/dist/inventory/slotModel.js +10 -0
- package/dist/item/durability.d.ts +5 -0
- package/dist/item/durability.js +6 -3
- package/dist/item/itemIdentity.d.ts +171 -0
- package/dist/item/itemIdentity.js +138 -0
- package/dist/item/itemInstanceRegistry.d.ts +32 -0
- package/dist/item/itemInstanceRegistry.js +34 -0
- package/dist/item/itemgen.d.ts +163 -0
- package/dist/item/itemgen.js +151 -0
- package/dist/item/modularItem.d.ts +5 -0
- package/dist/item/modularItem.js +5 -0
- package/dist/item/use.d.ts +5 -0
- package/dist/item/use.js +5 -0
- package/dist/item/useBehavior.d.ts +142 -0
- package/dist/item/useBehavior.js +105 -0
- package/dist/item/weapon.d.ts +5 -0
- package/dist/item/weapon.js +5 -0
- package/dist/math/scalar.d.ts +25 -0
- package/dist/math/scalar.js +47 -0
- package/dist/meta/changelog.d.ts +4 -1
- package/dist/meta/changelog.js +39 -2
- package/dist/movement/avoidance.d.ts +45 -0
- package/dist/movement/avoidance.js +123 -0
- package/dist/movement/cameraRig.d.ts +8 -0
- package/dist/movement/cameraRig.js +8 -0
- package/dist/movement/dash.d.ts +11 -0
- package/dist/movement/dash.js +11 -0
- package/dist/movement/formation.d.ts +135 -0
- package/dist/movement/formation.js +194 -0
- package/dist/movement/glideModel.d.ts +5 -0
- package/dist/movement/glideModel.js +5 -0
- package/dist/movement/grappleSwing.d.ts +5 -0
- package/dist/movement/grappleSwing.js +5 -0
- package/dist/movement/leaderTrail.d.ts +5 -0
- package/dist/movement/leaderTrail.js +5 -0
- package/dist/movement/movementModel.d.ts +50 -9
- package/dist/movement/movementModel.js +75 -29
- package/dist/movement/playerMovement.js +167 -12
- package/dist/movement/poseState.d.ts +13 -0
- package/dist/movement/poseState.js +16 -0
- package/dist/movement/steering.d.ts +8 -0
- package/dist/movement/steering.js +18 -0
- package/dist/multiplayer/combatSnapshot.d.ts +3 -0
- package/dist/multiplayer/combatSnapshot.js +16 -8
- package/dist/multiplayer/lagCompensation.d.ts +5 -0
- package/dist/multiplayer/lagCompensation.js +5 -0
- package/dist/multiplayer/presenceModel.d.ts +11 -1
- package/dist/multiplayer/presenceModel.js +17 -1
- package/dist/multiplayer/simultaneousCommit.d.ts +3 -0
- package/dist/multiplayer/simultaneousCommit.js +3 -0
- package/dist/multiplayer.d.ts +11 -0
- package/dist/multiplayer.js +11 -0
- package/dist/nav/pathFollow.d.ts +57 -0
- package/dist/nav/pathFollow.js +109 -0
- package/dist/orders/orderKinds.d.ts +156 -0
- package/dist/orders/orderKinds.js +197 -0
- package/dist/orders/orderQueue.d.ts +208 -0
- package/dist/orders/orderQueue.js +211 -0
- package/dist/physics/drivableVehicle.d.ts +31 -0
- package/dist/physics/drivableVehicle.js +16 -0
- package/dist/physics/flightDynamics.d.ts +82 -0
- package/dist/physics/flightDynamics.js +165 -0
- package/dist/physics/gravityField.d.ts +25 -0
- package/dist/physics/gravityField.js +41 -0
- package/dist/physics/kinematicVehicle.d.ts +108 -1
- package/dist/physics/kinematicVehicle.js +212 -15
- package/dist/physics/physicsWorld.d.ts +50 -12
- package/dist/physics/physicsWorld.js +120 -122
- package/dist/physics/radialImpulse.d.ts +25 -0
- package/dist/physics/radialImpulse.js +27 -0
- package/dist/physics/vehicleObstacles.d.ts +44 -0
- package/dist/physics/vehicleObstacles.js +115 -0
- package/dist/procedural.d.ts +3 -0
- package/dist/procedural.js +3 -0
- package/dist/progression/statGraph.d.ts +116 -0
- package/dist/progression/statGraph.js +318 -0
- package/dist/puzzle/cellGrid.d.ts +9 -0
- package/dist/puzzle/cellGrid.js +9 -0
- package/dist/puzzle/fallingPiece.d.ts +9 -0
- package/dist/puzzle/fallingPiece.js +9 -0
- package/dist/puzzle/nonogram.d.ts +39 -0
- package/dist/puzzle/nonogram.js +136 -0
- package/dist/random/nameGen.d.ts +5 -0
- package/dist/random/nameGen.js +5 -0
- package/dist/random/pick.d.ts +8 -0
- package/dist/random/pick.js +31 -0
- package/dist/random/rng.d.ts +26 -1
- package/dist/random/rng.js +32 -9
- package/dist/random/seedLink.d.ts +4 -0
- package/dist/random/seedLink.js +4 -0
- package/dist/relation/keyedValues.d.ts +54 -0
- package/dist/relation/keyedValues.js +115 -0
- package/dist/relation/thresholds.d.ts +62 -0
- package/dist/relation/thresholds.js +62 -0
- package/dist/render/color.d.ts +12 -0
- package/dist/render/color.js +26 -0
- package/dist/render/lookPreset.d.ts +41 -0
- package/dist/render/lookPreset.js +29 -0
- package/dist/render/postProcessing.d.ts +20 -0
- package/dist/render/postProcessing.js +21 -1
- package/dist/rules/predicate.d.ts +53 -0
- package/dist/rules/predicate.js +64 -0
- package/dist/rules/ruleEffects.d.ts +38 -0
- package/dist/rules/ruleEffects.js +37 -0
- package/dist/rules/triggeredRules.d.ts +160 -0
- package/dist/rules/triggeredRules.js +241 -0
- package/dist/runtime/adapter.d.ts +55 -3
- package/dist/runtime/adapter.js +57 -1
- package/dist/runtime/cameraDirector.d.ts +1 -0
- package/dist/runtime/cameraDirector.js +1 -0
- package/dist/runtime/commandRunner.d.ts +1 -0
- package/dist/runtime/commandRunner.js +1 -0
- package/dist/runtime/context/combat.d.ts +51 -0
- package/dist/runtime/context/combat.js +130 -0
- package/dist/runtime/context/combatFx.d.ts +23 -0
- package/dist/runtime/context/combatFx.js +161 -0
- package/dist/runtime/context/deathLoot.d.ts +30 -0
- package/dist/runtime/context/deathLoot.js +39 -0
- package/dist/runtime/context/player.d.ts +48 -0
- package/dist/runtime/context/player.js +125 -0
- package/dist/runtime/context/registries.d.ts +13 -0
- package/dist/runtime/context/registries.js +84 -0
- package/dist/runtime/context/scene.d.ts +59 -0
- package/dist/runtime/context/scene.js +291 -0
- package/dist/runtime/context/snapshotCodecs.d.ts +12 -0
- package/dist/runtime/context/snapshotCodecs.js +26 -0
- package/dist/runtime/context/worldItems.d.ts +21 -0
- package/dist/runtime/context/worldItems.js +37 -0
- package/dist/runtime/descriptors/baseline.d.ts +48 -0
- package/dist/runtime/descriptors/baseline.js +157 -0
- package/dist/runtime/descriptors/features.d.ts +79 -0
- package/dist/runtime/descriptors/features.js +299 -0
- package/dist/runtime/gameContext.d.ts +2 -327
- package/dist/runtime/gameContext.js +273 -778
- package/dist/runtime/gameContextTypes.d.ts +467 -0
- package/dist/runtime/gameContextTypes.js +1 -0
- package/dist/runtime/gameRuntime.d.ts +1 -0
- package/dist/runtime/gameRuntime.js +1 -0
- package/dist/runtime/headlessRunner.d.ts +62 -0
- package/dist/runtime/headlessRunner.js +45 -0
- package/dist/runtime/hostPersistence.d.ts +12 -0
- package/dist/runtime/hostPersistence.js +12 -0
- package/dist/runtime/hostPolicy.d.ts +51 -0
- package/dist/runtime/hostPolicy.js +68 -0
- package/dist/runtime/hostedGameRunner.d.ts +11 -4
- package/dist/runtime/hostedGameRunner.js +27 -3
- package/dist/runtime/hostedWorldSession.d.ts +15 -5
- package/dist/runtime/hostedWorldSession.js +12 -4
- package/dist/runtime/inputSnapshot.d.ts +8 -3
- package/dist/runtime/inputSnapshot.js +6 -2
- package/dist/runtime/motionIntents.d.ts +11 -2
- package/dist/runtime/motionIntents.js +16 -2
- package/dist/runtime/perContext.d.ts +15 -0
- package/dist/runtime/perContext.js +21 -0
- package/dist/runtime/runtimeSave.d.ts +79 -0
- package/dist/runtime/runtimeSave.js +105 -0
- package/dist/runtime/save.d.ts +4 -0
- package/dist/runtime/save.js +4 -0
- package/dist/runtime/snapshot.d.ts +7 -0
- package/dist/runtime/snapshot.js +7 -0
- package/dist/runtime/visibility.d.ts +2 -1
- package/dist/runtime/visibility.js +2 -1
- package/dist/runtime/worldChannel.d.ts +7 -3
- package/dist/runtime/worldChannel.js +51 -6
- package/dist/runtime/worldMirror.d.ts +14 -2
- package/dist/runtime/worldMirror.js +15 -2
- package/dist/runtime/worldProjection.d.ts +47 -0
- package/dist/runtime/worldProjection.js +52 -0
- package/dist/runtime/worldReplication.d.ts +30 -7
- package/dist/runtime/worldReplication.js +41 -9
- package/dist/runtime/worldSnapshot.d.ts +42 -3
- package/dist/runtime/worldSnapshot.js +34 -7
- package/dist/scene/assetCatalog.d.ts +26 -0
- package/dist/scene/assetCatalog.js +4 -0
- package/dist/scene/assetDiagnostics.d.ts +69 -0
- package/dist/scene/assetDiagnostics.js +110 -0
- package/dist/scene/assetGenerator.d.ts +65 -0
- package/dist/scene/assetGenerator.js +64 -0
- package/dist/scene/assetSpace.d.ts +137 -0
- package/dist/scene/assetSpace.js +257 -0
- package/dist/scene/authoredTriggers.d.ts +219 -0
- package/dist/scene/authoredTriggers.js +454 -0
- package/dist/scene/behaviorRuntime.d.ts +75 -0
- package/dist/scene/behaviorRuntime.js +217 -0
- package/dist/scene/behaviors.d.ts +14 -2
- package/dist/scene/behaviors.js +7 -2
- package/dist/scene/bodyBind.d.ts +60 -0
- package/dist/scene/bodyBind.js +51 -0
- package/dist/scene/builtinSceneKinds.d.ts +5 -0
- package/dist/scene/builtinSceneKinds.js +67 -0
- package/dist/scene/colliders.d.ts +71 -1
- package/dist/scene/colliders.js +145 -1
- package/dist/scene/collisionMesh.d.ts +94 -0
- package/dist/scene/collisionMesh.js +699 -0
- package/dist/scene/companion.d.ts +147 -0
- package/dist/scene/companion.js +193 -0
- package/dist/scene/entityStats.d.ts +18 -11
- package/dist/scene/entityStats.js +21 -16
- package/dist/scene/entityStore.d.ts +12 -1
- package/dist/scene/entityStore.js +15 -0
- package/dist/scene/facing.d.ts +18 -0
- package/dist/scene/facing.js +26 -0
- package/dist/scene/modelSockets.d.ts +33 -0
- package/dist/scene/modelSockets.js +29 -0
- package/dist/scene/objectQuery.d.ts +2 -0
- package/dist/scene/objectQuery.js +2 -0
- package/dist/scene/objectStore.d.ts +4 -0
- package/dist/scene/objectStore.js +6 -0
- package/dist/scene/possession.d.ts +8 -0
- package/dist/scene/possession.js +14 -0
- package/dist/scene/roster.d.ts +3 -0
- package/dist/scene/roster.js +12 -0
- package/dist/scene/sceneKinds.d.ts +256 -0
- package/dist/scene/sceneKinds.js +218 -0
- package/dist/scene/sceneOwnership.d.ts +169 -0
- package/dist/scene/sceneOwnership.js +179 -0
- package/dist/scene/sceneRaycast.js +15 -0
- package/dist/scene/selectionBookmarks.d.ts +93 -0
- package/dist/scene/selectionBookmarks.js +122 -0
- package/dist/scene/vehicleSeat.d.ts +68 -0
- package/dist/scene/vehicleSeat.js +59 -0
- package/dist/sensor/concealment.d.ts +3 -0
- package/dist/sensor/concealment.js +3 -0
- package/dist/sensor/freezeMonitor.d.ts +1 -0
- package/dist/sensor/freezeMonitor.js +1 -0
- package/dist/sensor/frustumSensor.d.ts +7 -3
- package/dist/sensor/frustumSensor.js +7 -3
- package/dist/sensor/hiddenStateProbe.d.ts +5 -2
- package/dist/sensor/hiddenStateProbe.js +5 -2
- package/dist/sensor/recordingBuffer.d.ts +2 -1
- package/dist/sensor/recordingBuffer.js +2 -1
- package/dist/sensor/replayLoop.d.ts +6 -2
- package/dist/sensor/replayLoop.js +6 -2
- package/dist/sensor/revealQuery.d.ts +2 -1
- package/dist/sensor/revealQuery.js +2 -1
- package/dist/sensor/visionCone.d.ts +10 -3
- package/dist/sensor/visionCone.js +10 -3
- package/dist/session/contestedChannel.d.ts +1 -0
- package/dist/session/contestedChannel.js +1 -0
- package/dist/session/extraction.d.ts +1 -0
- package/dist/session/extraction.js +1 -0
- package/dist/session/roles.d.ts +6 -0
- package/dist/session/roles.js +6 -0
- package/dist/session/roundState.d.ts +8 -0
- package/dist/session/roundState.js +2 -0
- package/dist/settings/settingsModel.d.ts +1 -0
- package/dist/settings/settingsModel.js +1 -0
- package/dist/stats/accumulatorMeter.d.ts +6 -0
- package/dist/stats/accumulatorMeter.js +6 -0
- package/dist/stats/eventMeter.d.ts +5 -0
- package/dist/stats/eventMeter.js +5 -0
- package/dist/stats/rollCheck.d.ts +5 -0
- package/dist/stats/rollCheck.js +5 -0
- package/dist/stats/spawnLevelStats.d.ts +110 -0
- package/dist/stats/spawnLevelStats.js +140 -0
- package/dist/stats/statModifiers.d.ts +5 -0
- package/dist/stats/statModifiers.js +5 -0
- package/dist/stats/statPool.d.ts +70 -0
- package/dist/stats/statPool.js +59 -0
- package/dist/store/changeSignal.d.ts +3 -0
- package/dist/store/changeSignal.js +2 -0
- package/dist/store/defineKeyedStore.d.ts +38 -0
- package/dist/store/defineKeyedStore.js +42 -0
- package/dist/store/defineStore.d.ts +33 -0
- package/dist/store/defineStore.js +35 -0
- package/dist/store/observableKeyedStore.d.ts +7 -0
- package/dist/store/observableKeyedStore.js +35 -2
- package/dist/survival/decayMeter.d.ts +55 -0
- package/dist/survival/decayMeter.js +130 -57
- package/dist/survival/moodle.d.ts +2 -0
- package/dist/survival/regionHealth.d.ts +6 -0
- package/dist/survival/regionHealth.js +6 -3
- package/dist/tactics/fallingGrid.d.ts +4 -2
- package/dist/tactics/fallingGrid.js +4 -2
- package/dist/tactics/predictiveQuery.d.ts +3 -0
- package/dist/tactics/predictiveQuery.js +3 -0
- package/dist/tactics/snapshot.d.ts +2 -0
- package/dist/tactics/snapshot.js +2 -0
- package/dist/tactics/surface.d.ts +1 -0
- package/dist/tactics/surface.js +1 -0
- package/dist/tactics/tacticalGrid.d.ts +1 -0
- package/dist/tactics/tacticalGrid.js +1 -0
- package/dist/time/beatClock.d.ts +14 -4
- package/dist/time/beatClock.js +14 -4
- package/dist/time/calendarClock.d.ts +3 -0
- package/dist/time/calendarClock.js +3 -0
- package/dist/time/idleProgress.d.ts +4 -0
- package/dist/time/idleProgress.js +4 -0
- package/dist/time/simClock.d.ts +2 -0
- package/dist/time/simClock.js +20 -5
- package/dist/ui/actionModel.d.ts +110 -0
- package/dist/ui/actionModel.js +159 -0
- package/dist/ui/gameLayout.d.ts +30 -11
- package/dist/ui/gameLayout.js +30 -11
- package/dist/ui/hudDocument.d.ts +114 -0
- package/dist/ui/hudDocument.js +220 -0
- package/dist/ui/hudLayout.d.ts +38 -1
- package/dist/ui/hudLayout.js +220 -17
- package/dist/ui/selectionModel.d.ts +92 -0
- package/dist/ui/selectionModel.js +88 -0
- package/dist/ui/tooltipModel.d.ts +63 -0
- package/dist/ui/tooltipModel.js +82 -0
- package/dist/ui.d.ts +15 -0
- package/dist/ui.js +15 -0
- package/dist/visibility/bounds.d.ts +8 -1
- package/dist/visibility/bounds.js +8 -1
- package/dist/visibility/frustum.d.ts +12 -3
- package/dist/visibility/frustum.js +12 -3
- package/dist/work/jobQueue.d.ts +201 -0
- package/dist/work/jobQueue.js +253 -0
- package/dist/work/unitTraining.d.ts +58 -0
- package/dist/work/unitTraining.js +52 -0
- package/dist/world/authoredEntities.d.ts +29 -0
- package/dist/world/authoredEntities.js +29 -0
- package/dist/world/authoredObjects.d.ts +72 -0
- package/dist/world/authoredObjects.js +81 -0
- package/dist/world/authoredSpawn.d.ts +39 -0
- package/dist/world/authoredSpawn.js +37 -0
- package/dist/world/buildSockets.d.ts +152 -0
- package/dist/world/buildSockets.js +188 -0
- package/dist/world/buildingGenerator.d.ts +18 -0
- package/dist/world/buildingGenerator.js +62 -0
- package/dist/world/buildings.d.ts +5 -0
- package/dist/world/buildings.js +5 -0
- package/dist/world/catenary.d.ts +22 -0
- package/dist/world/catenary.js +69 -0
- package/dist/world/cityBlocks.d.ts +152 -0
- package/dist/world/cityBlocks.js +670 -0
- package/dist/world/cityContent.d.ts +77 -0
- package/dist/world/cityContent.js +261 -0
- package/dist/world/cityGeometry.d.ts +114 -0
- package/dist/world/cityGeometry.js +573 -0
- package/dist/world/cityKind.d.ts +314 -0
- package/dist/world/cityKind.js +1578 -0
- package/dist/world/features.d.ts +133 -10
- package/dist/world/features.js +66 -6
- package/dist/world/footprintGrid.d.ts +59 -0
- package/dist/world/footprintGrid.js +141 -0
- package/dist/world/geometry.d.ts +64 -0
- package/dist/world/geometry.js +94 -0
- package/dist/world/grassKind.d.ts +51 -0
- package/dist/world/grassKind.js +101 -0
- package/dist/world/markers.d.ts +50 -4
- package/dist/world/markers.js +49 -0
- package/dist/world/massing.js +1 -1
- package/dist/world/minimapBake.d.ts +78 -0
- package/dist/world/minimapBake.js +208 -0
- package/dist/world/minimapTrack.d.ts +17 -0
- package/dist/world/minimapTrack.js +22 -0
- package/dist/world/pathInstances.d.ts +27 -0
- package/dist/world/pathInstances.js +27 -0
- package/dist/world/pathNetwork.d.ts +159 -0
- package/dist/world/pathNetwork.js +684 -0
- package/dist/world/pathTerrain.d.ts +80 -0
- package/dist/world/pathTerrain.js +142 -0
- package/dist/world/place.d.ts +166 -0
- package/dist/world/place.js +96 -0
- package/dist/world/placeAsset.d.ts +81 -0
- package/dist/world/placeAsset.js +79 -0
- package/dist/world/placement.d.ts +4 -0
- package/dist/world/placement.js +4 -0
- package/dist/world/placementController.d.ts +6 -0
- package/dist/world/placementController.js +6 -0
- package/dist/world/poleLineKind.d.ts +40 -0
- package/dist/world/poleLineKind.js +97 -0
- package/dist/world/polyline.d.ts +34 -0
- package/dist/world/polyline.js +88 -0
- package/dist/world/resourceNode.d.ts +109 -0
- package/dist/world/resourceNode.js +140 -0
- package/dist/world/roads.d.ts +8 -0
- package/dist/world/roads.js +38 -0
- package/dist/world/scatter.js +2 -20
- package/dist/world/scatterRegion.d.ts +164 -0
- package/dist/world/scatterRegion.js +409 -0
- package/dist/world/sceneShapes.d.ts +43 -0
- package/dist/world/sceneShapes.js +1 -0
- package/dist/world/segment.d.ts +27 -0
- package/dist/world/segment.js +43 -0
- package/dist/world/soilKind.d.ts +48 -0
- package/dist/world/soilKind.js +92 -0
- package/dist/world/spatialSample.d.ts +241 -0
- package/dist/world/spatialSample.js +465 -0
- package/dist/world/streets.d.ts +29 -0
- package/dist/world/streets.js +55 -0
- package/dist/world/terraform.d.ts +213 -1
- package/dist/world/terraform.js +627 -22
- package/dist/world/terrain.d.ts +164 -20
- package/dist/world/terrain.js +325 -41
- package/dist/world/terrainGuides.d.ts +182 -0
- package/dist/world/terrainGuides.js +343 -0
- package/dist/world/vec2.d.ts +81 -0
- package/dist/world/vec2.js +128 -0
- package/dist/world/vec3.d.ts +49 -0
- package/dist/world/vec3.js +76 -0
- package/dist/world/vegetation.d.ts +71 -0
- package/dist/world/vegetation.js +151 -0
- package/dist/world/visibilityField.d.ts +154 -0
- package/dist/world/visibilityField.js +271 -0
- package/dist/world/volumetricClouds.d.ts +52 -0
- package/dist/world/volumetricClouds.js +43 -0
- package/dist/world/walls.d.ts +9 -0
- package/dist/world/walls.js +9 -0
- package/dist/world/waterKind.d.ts +55 -0
- package/dist/world/waterKind.js +99 -0
- package/dist/world.d.ts +141 -0
- package/dist/world.js +141 -0
- package/package.json +32 -8
- package/skills/game-design/SKILL.md +28 -0
- package/skills/game-design/agents/openai.yaml +4 -0
- package/skills/game-design/references/application-playbook.md +170 -0
- package/skills/game-design/references/field-guide.md +288 -0
- package/skills/jgengine/SKILL.md +54 -0
- package/skills/jgengine/api.md +527 -0
- package/skills/jgengine/capabilities.md +131 -0
- package/skills/jgengine/recipes/minimal-game.md +88 -0
- package/skills/jgengine-combat/SKILL.md +38 -0
- package/skills/jgengine-combat/api.md +585 -0
- package/skills/jgengine-combat/capabilities.md +213 -0
- package/skills/jgengine-combat/recipes/portable-stat-pools.md +122 -0
- package/skills/jgengine-combat/reference.md +108 -0
- package/skills/jgengine-gameplay/SKILL.md +39 -0
- package/skills/jgengine-gameplay/api.md +1920 -0
- package/skills/jgengine-gameplay/capabilities.md +454 -0
- package/skills/jgengine-gameplay/reference-systems.md +56 -0
- package/skills/jgengine-world/SKILL.md +51 -0
- package/skills/jgengine-world/api.md +3056 -0
- package/skills/jgengine-world/capabilities.md +476 -0
- package/skills/jgengine-world/recipes/commanding-entities.md +128 -0
- package/skills/jgengine-world/reference.md +261 -0
- package/dist/cartridge/runtime.d.ts +0 -15
- package/dist/cartridge/runtime.js +0 -556
- package/dist/cartridge/spec.d.ts +0 -188
- package/dist/cartridge/spec.js +0 -19
- package/dist/cartridge/validate.d.ts +0 -2
- package/dist/cartridge/validate.js +0 -78
- package/dist/store/engineStore.d.ts +0 -22
- package/dist/store/engineStore.js +0 -50
- package/dist/store/features.d.ts +0 -10
- package/dist/store/features.js +0 -23
- package/llms.txt +0 -5389
package/dist/ui/hudLayout.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** @internal */
|
|
1
2
|
export function isPanelDraggable(state, id) {
|
|
2
3
|
return state.panels[id] !== undefined && (state.editing || !state.locked);
|
|
3
4
|
}
|
|
@@ -13,9 +14,11 @@ export const HUD_ANCHOR_FRACTIONS = {
|
|
|
13
14
|
"bottom-right": { fx: 1, fy: 1 },
|
|
14
15
|
};
|
|
15
16
|
const HUD_ANCHORS = Object.keys(HUD_ANCHOR_FRACTIONS);
|
|
17
|
+
/** @internal */
|
|
16
18
|
export function isHudAnchor(value) {
|
|
17
19
|
return typeof value === "string" && value in HUD_ANCHOR_FRACTIONS;
|
|
18
20
|
}
|
|
21
|
+
/** @internal */
|
|
19
22
|
export function anchoredPlacement(anchor, inset) {
|
|
20
23
|
const { fx, fy } = HUD_ANCHOR_FRACTIONS[anchor];
|
|
21
24
|
return {
|
|
@@ -24,6 +27,7 @@ export function anchoredPlacement(anchor, inset) {
|
|
|
24
27
|
dy: fy === 1 ? -inset.y : inset.y,
|
|
25
28
|
};
|
|
26
29
|
}
|
|
30
|
+
/** @internal */
|
|
27
31
|
export function nearestAnchor(rect, viewport) {
|
|
28
32
|
const cx = viewport.width > 0 ? (rect.x + rect.width / 2) / viewport.width : 0.5;
|
|
29
33
|
const cy = viewport.height > 0 ? (rect.y + rect.height / 2) / viewport.height : 0.5;
|
|
@@ -39,6 +43,7 @@ export function nearestAnchor(rect, viewport) {
|
|
|
39
43
|
}
|
|
40
44
|
return best;
|
|
41
45
|
}
|
|
46
|
+
/** @internal */
|
|
42
47
|
export function clampRect(rect, viewport) {
|
|
43
48
|
return {
|
|
44
49
|
x: Math.max(0, Math.min(rect.x, viewport.width - rect.width)),
|
|
@@ -47,6 +52,7 @@ export function clampRect(rect, viewport) {
|
|
|
47
52
|
height: rect.height,
|
|
48
53
|
};
|
|
49
54
|
}
|
|
55
|
+
/** @internal */
|
|
50
56
|
export function placementFromRect(rect, viewport, snap = 1) {
|
|
51
57
|
const clamped = clampRect(rect, viewport);
|
|
52
58
|
const anchor = nearestAnchor(clamped, viewport);
|
|
@@ -60,6 +66,7 @@ export function placementFromRect(rect, viewport, snap = 1) {
|
|
|
60
66
|
dy: Math.round(dy / step) * step,
|
|
61
67
|
};
|
|
62
68
|
}
|
|
69
|
+
/** @internal */
|
|
63
70
|
export function rectFromPlacement(placement, size, viewport) {
|
|
64
71
|
const { fx, fy } = HUD_ANCHOR_FRACTIONS[placement.anchor];
|
|
65
72
|
return {
|
|
@@ -86,19 +93,93 @@ function parseSerialized(raw) {
|
|
|
86
93
|
for (const [id, value] of Object.entries(panels)) {
|
|
87
94
|
if (typeof value !== "object" || value === null)
|
|
88
95
|
continue;
|
|
89
|
-
const
|
|
96
|
+
const record = value;
|
|
97
|
+
const placementSource = record.placement ?? record;
|
|
98
|
+
const { anchor, dx, dy } = placementSource;
|
|
90
99
|
if (!isHudAnchor(anchor))
|
|
91
100
|
continue;
|
|
92
101
|
if (typeof dx !== "number" || !Number.isFinite(dx))
|
|
93
102
|
continue;
|
|
94
103
|
if (typeof dy !== "number" || !Number.isFinite(dy))
|
|
95
104
|
continue;
|
|
96
|
-
|
|
105
|
+
const override = { placement: { anchor, dx, dy } };
|
|
106
|
+
if (typeof record.width === "number" && Number.isFinite(record.width))
|
|
107
|
+
override.width = record.width;
|
|
108
|
+
if (typeof record.height === "number" && Number.isFinite(record.height))
|
|
109
|
+
override.height = record.height;
|
|
110
|
+
if (typeof record.visible === "boolean")
|
|
111
|
+
override.visible = record.visible;
|
|
112
|
+
if (typeof record.type === "string")
|
|
113
|
+
override.type = record.type;
|
|
114
|
+
out.set(id, override);
|
|
97
115
|
}
|
|
98
116
|
return out;
|
|
99
117
|
}
|
|
118
|
+
const activeHudLayouts = new Set();
|
|
119
|
+
const activeHudLayoutListeners = new Set();
|
|
120
|
+
/** @internal Agent bridge seam — HudCanvas registers its store while mounted so headless drivers can reach canvas mode. */
|
|
121
|
+
export function registerActiveHudLayout(store) {
|
|
122
|
+
activeHudLayouts.add(store);
|
|
123
|
+
for (const listener of [...activeHudLayoutListeners])
|
|
124
|
+
listener();
|
|
125
|
+
return () => {
|
|
126
|
+
if (!activeHudLayouts.delete(store))
|
|
127
|
+
return;
|
|
128
|
+
for (const listener of [...activeHudLayoutListeners])
|
|
129
|
+
listener();
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/** @internal */
|
|
133
|
+
export function listActiveHudLayouts() {
|
|
134
|
+
return [...activeHudLayouts];
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* @internal Notified whenever a `HudCanvas` mounts or unmounts (the active-layout set changes).
|
|
138
|
+
* Lets the editor's HUD-layout surface enable editing on layouts that mount after it opens.
|
|
139
|
+
*/
|
|
140
|
+
export function subscribeActiveHudLayouts(listener) {
|
|
141
|
+
activeHudLayoutListeners.add(listener);
|
|
142
|
+
return () => {
|
|
143
|
+
activeHudLayoutListeners.delete(listener);
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function panelToUi(panel) {
|
|
147
|
+
const layout = {
|
|
148
|
+
anchor: panel.placement.anchor,
|
|
149
|
+
dx: panel.placement.dx,
|
|
150
|
+
dy: panel.placement.dy,
|
|
151
|
+
};
|
|
152
|
+
if (panel.width !== undefined)
|
|
153
|
+
layout.width = panel.width;
|
|
154
|
+
if (panel.height !== undefined)
|
|
155
|
+
layout.height = panel.height;
|
|
156
|
+
if (panel.visible === false)
|
|
157
|
+
layout.visible = false;
|
|
158
|
+
if (panel.type !== undefined)
|
|
159
|
+
layout.type = panel.type;
|
|
160
|
+
return layout;
|
|
161
|
+
}
|
|
162
|
+
function overrideToUi(id, override) {
|
|
163
|
+
const layout = {
|
|
164
|
+
anchor: override.placement.anchor,
|
|
165
|
+
dx: override.placement.dx,
|
|
166
|
+
dy: override.placement.dy,
|
|
167
|
+
};
|
|
168
|
+
if (override.width !== undefined)
|
|
169
|
+
layout.width = override.width;
|
|
170
|
+
if (override.height !== undefined)
|
|
171
|
+
layout.height = override.height;
|
|
172
|
+
if (override.visible !== undefined)
|
|
173
|
+
layout.visible = override.visible;
|
|
174
|
+
if (override.type !== undefined)
|
|
175
|
+
layout.type = override.type;
|
|
176
|
+
void id;
|
|
177
|
+
return layout;
|
|
178
|
+
}
|
|
179
|
+
/** @internal */
|
|
100
180
|
export function createHudLayout(options) {
|
|
101
181
|
const snap = options?.snap ?? 1;
|
|
182
|
+
const onDocumentPatch = options?.onDocumentPatch;
|
|
102
183
|
const defaults = new Map();
|
|
103
184
|
const pending = new Map();
|
|
104
185
|
let topZ = 0;
|
|
@@ -111,22 +192,37 @@ export function createHudLayout(options) {
|
|
|
111
192
|
const setPanel = (panel) => {
|
|
112
193
|
state = { ...state, panels: { ...state.panels, [panel.id]: panel } };
|
|
113
194
|
};
|
|
195
|
+
const notifyDocument = (panel) => {
|
|
196
|
+
onDocumentPatch?.(panel.id, panelToUi(panel));
|
|
197
|
+
};
|
|
114
198
|
return {
|
|
115
199
|
getState: () => state,
|
|
116
200
|
subscribe(listener) {
|
|
117
201
|
listeners.add(listener);
|
|
118
202
|
return () => listeners.delete(listener);
|
|
119
203
|
},
|
|
120
|
-
register(id, placement) {
|
|
121
|
-
|
|
204
|
+
register(id, placement, registerOptions) {
|
|
205
|
+
const fallback = {
|
|
206
|
+
placement,
|
|
207
|
+
width: registerOptions?.width,
|
|
208
|
+
height: registerOptions?.height,
|
|
209
|
+
visible: registerOptions?.visible,
|
|
210
|
+
type: registerOptions?.type,
|
|
211
|
+
};
|
|
212
|
+
defaults.set(id, fallback);
|
|
122
213
|
if (state.panels[id] !== undefined)
|
|
123
214
|
return;
|
|
124
215
|
const override = pending.get(id);
|
|
216
|
+
const source = override ?? fallback;
|
|
125
217
|
setPanel({
|
|
126
218
|
id,
|
|
127
|
-
placement:
|
|
219
|
+
placement: source.placement,
|
|
128
220
|
z: ++topZ,
|
|
129
221
|
moved: override !== undefined,
|
|
222
|
+
width: source.width ?? fallback.width,
|
|
223
|
+
height: source.height ?? fallback.height,
|
|
224
|
+
visible: source.visible ?? fallback.visible !== false,
|
|
225
|
+
type: source.type ?? fallback.type,
|
|
130
226
|
});
|
|
131
227
|
},
|
|
132
228
|
move(id, rect, viewport) {
|
|
@@ -134,8 +230,32 @@ export function createHudLayout(options) {
|
|
|
134
230
|
if (panel === undefined)
|
|
135
231
|
return;
|
|
136
232
|
const placement = placementFromRect(rect, viewport, snap);
|
|
137
|
-
|
|
233
|
+
const next = { ...panel, placement, moved: true };
|
|
234
|
+
setPanel(next);
|
|
235
|
+
emit();
|
|
236
|
+
notifyDocument(next);
|
|
237
|
+
},
|
|
238
|
+
resize(id, size) {
|
|
239
|
+
const panel = state.panels[id];
|
|
240
|
+
if (panel === undefined)
|
|
241
|
+
return;
|
|
242
|
+
const width = Math.max(1, Math.round(size.width));
|
|
243
|
+
const height = Math.max(1, Math.round(size.height));
|
|
244
|
+
if (panel.width === width && panel.height === height)
|
|
245
|
+
return;
|
|
246
|
+
const next = { ...panel, width, height, moved: true };
|
|
247
|
+
setPanel(next);
|
|
138
248
|
emit();
|
|
249
|
+
notifyDocument(next);
|
|
250
|
+
},
|
|
251
|
+
setVisible(id, visible) {
|
|
252
|
+
const panel = state.panels[id];
|
|
253
|
+
if (panel === undefined || panel.visible === visible)
|
|
254
|
+
return;
|
|
255
|
+
const next = { ...panel, visible, moved: true };
|
|
256
|
+
setPanel(next);
|
|
257
|
+
emit();
|
|
258
|
+
notifyDocument(next);
|
|
139
259
|
},
|
|
140
260
|
bringToFront(id) {
|
|
141
261
|
const panel = state.panels[id];
|
|
@@ -163,9 +283,23 @@ export function createHudLayout(options) {
|
|
|
163
283
|
pending.delete(panelId);
|
|
164
284
|
const panel = state.panels[panelId];
|
|
165
285
|
const fallback = defaults.get(panelId);
|
|
166
|
-
if (panel === undefined || fallback === undefined
|
|
286
|
+
if (panel === undefined || fallback === undefined)
|
|
167
287
|
continue;
|
|
168
|
-
|
|
288
|
+
const hasAuthored = panel.moved ||
|
|
289
|
+
panel.width !== fallback.width ||
|
|
290
|
+
panel.height !== fallback.height ||
|
|
291
|
+
panel.visible !== (fallback.visible !== false);
|
|
292
|
+
if (!hasAuthored)
|
|
293
|
+
continue;
|
|
294
|
+
setPanel({
|
|
295
|
+
...panel,
|
|
296
|
+
placement: fallback.placement,
|
|
297
|
+
width: fallback.width,
|
|
298
|
+
height: fallback.height,
|
|
299
|
+
visible: fallback.visible !== false,
|
|
300
|
+
type: fallback.type,
|
|
301
|
+
moved: false,
|
|
302
|
+
});
|
|
169
303
|
changed = true;
|
|
170
304
|
}
|
|
171
305
|
if (changed)
|
|
@@ -174,14 +308,32 @@ export function createHudLayout(options) {
|
|
|
174
308
|
serialize() {
|
|
175
309
|
const panels = {};
|
|
176
310
|
for (const panel of Object.values(state.panels)) {
|
|
177
|
-
if (panel.moved)
|
|
178
|
-
|
|
311
|
+
if (!panel.moved)
|
|
312
|
+
continue;
|
|
313
|
+
panels[panel.id] = {
|
|
314
|
+
anchor: panel.placement.anchor,
|
|
315
|
+
dx: panel.placement.dx,
|
|
316
|
+
dy: panel.placement.dy,
|
|
317
|
+
...(panel.width === undefined ? {} : { width: panel.width }),
|
|
318
|
+
...(panel.height === undefined ? {} : { height: panel.height }),
|
|
319
|
+
...(panel.visible === false ? { visible: false } : {}),
|
|
320
|
+
...(panel.type === undefined ? {} : { type: panel.type }),
|
|
321
|
+
};
|
|
179
322
|
}
|
|
180
|
-
for (const [id,
|
|
181
|
-
if (state.panels[id]
|
|
182
|
-
|
|
323
|
+
for (const [id, override] of pending) {
|
|
324
|
+
if (state.panels[id] !== undefined)
|
|
325
|
+
continue;
|
|
326
|
+
panels[id] = {
|
|
327
|
+
anchor: override.placement.anchor,
|
|
328
|
+
dx: override.placement.dx,
|
|
329
|
+
dy: override.placement.dy,
|
|
330
|
+
...(override.width === undefined ? {} : { width: override.width }),
|
|
331
|
+
...(override.height === undefined ? {} : { height: override.height }),
|
|
332
|
+
...(override.visible === undefined ? {} : { visible: override.visible }),
|
|
333
|
+
...(override.type === undefined ? {} : { type: override.type }),
|
|
334
|
+
};
|
|
183
335
|
}
|
|
184
|
-
const data = { v:
|
|
336
|
+
const data = { v: 2, panels };
|
|
185
337
|
return JSON.stringify(data);
|
|
186
338
|
},
|
|
187
339
|
hydrate(raw) {
|
|
@@ -191,17 +343,68 @@ export function createHudLayout(options) {
|
|
|
191
343
|
if (parsed.size === 0)
|
|
192
344
|
return;
|
|
193
345
|
let changed = false;
|
|
194
|
-
for (const [id,
|
|
346
|
+
for (const [id, override] of parsed) {
|
|
195
347
|
const panel = state.panels[id];
|
|
196
348
|
if (panel === undefined) {
|
|
197
|
-
pending.set(id,
|
|
349
|
+
pending.set(id, override);
|
|
198
350
|
continue;
|
|
199
351
|
}
|
|
200
|
-
setPanel({
|
|
352
|
+
setPanel({
|
|
353
|
+
...panel,
|
|
354
|
+
placement: override.placement,
|
|
355
|
+
width: override.width ?? panel.width,
|
|
356
|
+
height: override.height ?? panel.height,
|
|
357
|
+
visible: override.visible ?? panel.visible,
|
|
358
|
+
type: override.type ?? panel.type,
|
|
359
|
+
moved: true,
|
|
360
|
+
});
|
|
201
361
|
changed = true;
|
|
202
362
|
}
|
|
203
363
|
if (changed)
|
|
204
364
|
emit();
|
|
205
365
|
},
|
|
366
|
+
applyDocumentUi(ui) {
|
|
367
|
+
if (ui === undefined)
|
|
368
|
+
return;
|
|
369
|
+
let changed = false;
|
|
370
|
+
for (const [id, layout] of Object.entries(ui.panels)) {
|
|
371
|
+
const override = {
|
|
372
|
+
placement: { anchor: layout.anchor, dx: layout.dx, dy: layout.dy },
|
|
373
|
+
width: layout.width,
|
|
374
|
+
height: layout.height,
|
|
375
|
+
visible: layout.visible,
|
|
376
|
+
type: layout.type,
|
|
377
|
+
};
|
|
378
|
+
const panel = state.panels[id];
|
|
379
|
+
if (panel === undefined) {
|
|
380
|
+
pending.set(id, override);
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
setPanel({
|
|
384
|
+
...panel,
|
|
385
|
+
placement: override.placement,
|
|
386
|
+
width: override.width ?? panel.width,
|
|
387
|
+
height: override.height ?? panel.height,
|
|
388
|
+
visible: override.visible ?? panel.visible,
|
|
389
|
+
type: override.type ?? panel.type,
|
|
390
|
+
moved: true,
|
|
391
|
+
});
|
|
392
|
+
changed = true;
|
|
393
|
+
}
|
|
394
|
+
if (changed)
|
|
395
|
+
emit();
|
|
396
|
+
},
|
|
397
|
+
toDocumentUi() {
|
|
398
|
+
const panels = {};
|
|
399
|
+
for (const panel of Object.values(state.panels)) {
|
|
400
|
+
if (panel.moved)
|
|
401
|
+
panels[panel.id] = panelToUi(panel);
|
|
402
|
+
}
|
|
403
|
+
for (const [id, override] of pending) {
|
|
404
|
+
if (state.panels[id] === undefined)
|
|
405
|
+
panels[id] = overrideToUi(id, override);
|
|
406
|
+
}
|
|
407
|
+
return { panels };
|
|
408
|
+
},
|
|
206
409
|
};
|
|
207
410
|
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless view model for a multi-entity selection — the data layer behind an RTS selection panel, a
|
|
3
|
+
* party frame, or a squad summary. It turns a caller-owned list of selected entity summaries into a
|
|
4
|
+
* primary detail focus, an ordered member strip, and same-kind buckets for large selections, with
|
|
5
|
+
* focus/hover and virtualization helpers. No rendering opinion: the React renderers in
|
|
6
|
+
* `@jgengine/react/selectionHud` consume this and a game supplies the entity data from wherever it
|
|
7
|
+
* lives (a `createSelectionSet`, a roster, a marquee query).
|
|
8
|
+
*/
|
|
9
|
+
/** One vital bar on an entity summary (health/shield/energy): current/max plus optional label and tone. */
|
|
10
|
+
export interface EntityVital {
|
|
11
|
+
id: string;
|
|
12
|
+
label?: string;
|
|
13
|
+
current: number;
|
|
14
|
+
max: number;
|
|
15
|
+
/** Colour-ramp hint the renderer maps to a palette (e.g. `"health" | "shield"`); caller-owned. */
|
|
16
|
+
tone?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A caller-provided summary of one selectable entity — the DATA input. `kind` is the grouping key
|
|
20
|
+
* used to bucket a large selection (unit type, catalog id); `icon` is a portrait id the renderer
|
|
21
|
+
* resolves. Only `id` is required.
|
|
22
|
+
*/
|
|
23
|
+
export interface EntitySummaryDef {
|
|
24
|
+
id: string;
|
|
25
|
+
name?: string;
|
|
26
|
+
/** Type/catalog id a large selection is bucketed by; falls back to `"unit"` when omitted. */
|
|
27
|
+
kind?: string;
|
|
28
|
+
/** Portrait id / glyph the caller maps to art. */
|
|
29
|
+
icon?: string;
|
|
30
|
+
vitals?: readonly EntityVital[];
|
|
31
|
+
tags?: readonly string[];
|
|
32
|
+
/** Ownership / faction tint id, caller-owned. */
|
|
33
|
+
owner?: string;
|
|
34
|
+
}
|
|
35
|
+
/** A bucket of same-`kind` entities in a large selection — the RTS "12 Marines" control chip. */
|
|
36
|
+
export interface SelectionGroup {
|
|
37
|
+
kind: string;
|
|
38
|
+
label: string;
|
|
39
|
+
icon?: string;
|
|
40
|
+
count: number;
|
|
41
|
+
ids: readonly string[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A resolved multi-selection view model. `primary` is the focused entity's full detail; `members` is
|
|
45
|
+
* the stable-order strip; `groups` buckets members by kind (count-desc, then label) for the
|
|
46
|
+
* portrait-overflow case; `grouped` is the hint that the selection is large enough to prefer groups
|
|
47
|
+
* over per-member portraits.
|
|
48
|
+
*/
|
|
49
|
+
export interface SelectionView {
|
|
50
|
+
count: number;
|
|
51
|
+
primary: EntitySummaryDef | null;
|
|
52
|
+
members: readonly EntitySummaryDef[];
|
|
53
|
+
groups: readonly SelectionGroup[];
|
|
54
|
+
focusIndex: number;
|
|
55
|
+
grouped: boolean;
|
|
56
|
+
}
|
|
57
|
+
/** Options for {@link summarizeSelection}. */
|
|
58
|
+
export interface SummarizeSelectionOptions {
|
|
59
|
+
/** Id of the entity to treat as primary/focused. Wins over `focusIndex`; ignored when not present. */
|
|
60
|
+
primaryId?: string;
|
|
61
|
+
/** Index of the primary/focused member when `primaryId` is absent (default 0). */
|
|
62
|
+
focusIndex?: number;
|
|
63
|
+
/** Member count above which `grouped` flips true (default 12). */
|
|
64
|
+
groupThreshold?: number;
|
|
65
|
+
/** Map a `kind` to a display label for its group (default: the kind itself). */
|
|
66
|
+
labelOf?: (kind: string) => string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Summarize a selected-entity list into a {@link SelectionView}: resolve the primary focus, keep the
|
|
70
|
+
* ordered members, and bucket them by `kind`. Pure and stable — deterministic group ordering and a
|
|
71
|
+
* clamped focus index, so it is safe to call every render and to assert on in a test.
|
|
72
|
+
*/
|
|
73
|
+
export declare function summarizeSelection(members: readonly EntitySummaryDef[], options?: SummarizeSelectionOptions): SelectionView;
|
|
74
|
+
/** A 1-D focus move within the selection member strip. */
|
|
75
|
+
export type SelectionFocusDirection = "next" | "prev" | "first" | "last";
|
|
76
|
+
/**
|
|
77
|
+
* Pure next-focus math for the selection strip: step the focus index by one member (wrapping), or
|
|
78
|
+
* jump to an end. `-1` (no focus) steps to the first/last member. Returns `-1` for an empty selection.
|
|
79
|
+
*/
|
|
80
|
+
export declare function moveSelectionFocus(count: number, current: number, dir: SelectionFocusDirection): number;
|
|
81
|
+
/** A contiguous slice of members for virtualizing a large selection strip. */
|
|
82
|
+
export interface SelectionWindow {
|
|
83
|
+
start: number;
|
|
84
|
+
end: number;
|
|
85
|
+
items: readonly EntitySummaryDef[];
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Compute a virtualization window over a large selection: a `size`-length slice starting near
|
|
89
|
+
* `scroll`, clamped so the window never runs past the end. Render only `items`, offsetting by
|
|
90
|
+
* `start * itemSize`, to keep a thousand-unit selection from mounting a thousand portraits.
|
|
91
|
+
*/
|
|
92
|
+
export declare function selectionWindow(members: readonly EntitySummaryDef[], scroll: number, size: number): SelectionWindow;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless view model for a multi-entity selection — the data layer behind an RTS selection panel, a
|
|
3
|
+
* party frame, or a squad summary. It turns a caller-owned list of selected entity summaries into a
|
|
4
|
+
* primary detail focus, an ordered member strip, and same-kind buckets for large selections, with
|
|
5
|
+
* focus/hover and virtualization helpers. No rendering opinion: the React renderers in
|
|
6
|
+
* `@jgengine/react/selectionHud` consume this and a game supplies the entity data from wherever it
|
|
7
|
+
* lives (a `createSelectionSet`, a roster, a marquee query).
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Summarize a selected-entity list into a {@link SelectionView}: resolve the primary focus, keep the
|
|
11
|
+
* ordered members, and bucket them by `kind`. Pure and stable — deterministic group ordering and a
|
|
12
|
+
* clamped focus index, so it is safe to call every render and to assert on in a test.
|
|
13
|
+
*/
|
|
14
|
+
export function summarizeSelection(members, options) {
|
|
15
|
+
const count = members.length;
|
|
16
|
+
const threshold = options?.groupThreshold ?? 12;
|
|
17
|
+
const labelOf = options?.labelOf ?? ((kind) => kind);
|
|
18
|
+
let focusIndex = -1;
|
|
19
|
+
if (options?.primaryId !== undefined) {
|
|
20
|
+
focusIndex = members.findIndex((m) => m.id === options.primaryId);
|
|
21
|
+
}
|
|
22
|
+
if (focusIndex < 0)
|
|
23
|
+
focusIndex = options?.focusIndex ?? 0;
|
|
24
|
+
focusIndex = count === 0 ? -1 : Math.max(0, Math.min(count - 1, focusIndex));
|
|
25
|
+
const primary = focusIndex < 0 ? null : (members[focusIndex] ?? null);
|
|
26
|
+
return {
|
|
27
|
+
count,
|
|
28
|
+
primary,
|
|
29
|
+
members,
|
|
30
|
+
groups: groupSelection(members, labelOf),
|
|
31
|
+
focusIndex,
|
|
32
|
+
grouped: count > threshold,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** @internal Bucket members by `kind` into count-desc, label-asc ordered {@link SelectionGroup}s. */
|
|
36
|
+
function groupSelection(members, labelOf) {
|
|
37
|
+
const order = [];
|
|
38
|
+
const byKind = new Map();
|
|
39
|
+
for (const member of members) {
|
|
40
|
+
const kind = member.kind ?? "unit";
|
|
41
|
+
let bucket = byKind.get(kind);
|
|
42
|
+
if (bucket === undefined) {
|
|
43
|
+
bucket = { icon: member.icon, ids: [] };
|
|
44
|
+
byKind.set(kind, bucket);
|
|
45
|
+
order.push(kind);
|
|
46
|
+
}
|
|
47
|
+
bucket.ids.push(member.id);
|
|
48
|
+
}
|
|
49
|
+
const groups = order.map((kind) => {
|
|
50
|
+
const bucket = byKind.get(kind);
|
|
51
|
+
const group = { kind, label: labelOf(kind), count: bucket.ids.length, ids: bucket.ids };
|
|
52
|
+
if (bucket.icon !== undefined)
|
|
53
|
+
group.icon = bucket.icon;
|
|
54
|
+
return group;
|
|
55
|
+
});
|
|
56
|
+
groups.sort((a, b) => (b.count - a.count) || a.label.localeCompare(b.label));
|
|
57
|
+
return groups;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Pure next-focus math for the selection strip: step the focus index by one member (wrapping), or
|
|
61
|
+
* jump to an end. `-1` (no focus) steps to the first/last member. Returns `-1` for an empty selection.
|
|
62
|
+
*/
|
|
63
|
+
export function moveSelectionFocus(count, current, dir) {
|
|
64
|
+
if (count <= 0)
|
|
65
|
+
return -1;
|
|
66
|
+
if (dir === "first")
|
|
67
|
+
return 0;
|
|
68
|
+
if (dir === "last")
|
|
69
|
+
return count - 1;
|
|
70
|
+
if (current < 0)
|
|
71
|
+
return dir === "prev" ? count - 1 : 0;
|
|
72
|
+
if (dir === "next")
|
|
73
|
+
return (current + 1) % count;
|
|
74
|
+
return (current - 1 + count) % count;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Compute a virtualization window over a large selection: a `size`-length slice starting near
|
|
78
|
+
* `scroll`, clamped so the window never runs past the end. Render only `items`, offsetting by
|
|
79
|
+
* `start * itemSize`, to keep a thousand-unit selection from mounting a thousand portraits.
|
|
80
|
+
*/
|
|
81
|
+
export function selectionWindow(members, scroll, size) {
|
|
82
|
+
const count = members.length;
|
|
83
|
+
const windowSize = Math.max(1, Math.floor(size));
|
|
84
|
+
const maxStart = Math.max(0, count - windowSize);
|
|
85
|
+
const start = Math.max(0, Math.min(maxStart, Math.floor(scroll)));
|
|
86
|
+
const end = Math.min(count, start + windowSize);
|
|
87
|
+
return { start, end, items: members.slice(start, end) };
|
|
88
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless model for HUD tooltips and popovers — the shared description seam games have been
|
|
3
|
+
* hand-rolling with a bare DOM `title=` attribute. {@link actionTooltip} turns a
|
|
4
|
+
* {@link ResolvedAction} into structured {@link TooltipContent} (title, cost, cooldown, blocking
|
|
5
|
+
* notes), and {@link placePopover} is the pure, SSR-safe positioning math the React tooltip uses to
|
|
6
|
+
* flip and clamp against the viewport. Both are rendering-agnostic.
|
|
7
|
+
*/
|
|
8
|
+
import type { ActionCooldown, ActionCost, ResolvedAction } from "./actionModel.js";
|
|
9
|
+
/** Structured tooltip content — a renderer lays these fields out; the model owns what is shown. */
|
|
10
|
+
export interface TooltipContent {
|
|
11
|
+
title: string;
|
|
12
|
+
subtitle?: string;
|
|
13
|
+
body?: string;
|
|
14
|
+
/** Hotkey label to echo (e.g. `"Q"`). */
|
|
15
|
+
hotkey?: string;
|
|
16
|
+
costs?: readonly ActionCost[];
|
|
17
|
+
cooldown?: ActionCooldown | null;
|
|
18
|
+
/** Blocking reasons rendered as a warning footer (why the action is unavailable). */
|
|
19
|
+
notes?: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Build {@link TooltipContent} from a {@link ResolvedAction}: title from the label, the group as a
|
|
23
|
+
* subtitle, the description as the body, and the blocking reasons as warning notes. The single place
|
|
24
|
+
* an action's tooltip copy is assembled, so every renderer shows the same thing.
|
|
25
|
+
*/
|
|
26
|
+
export declare function actionTooltip(action: ResolvedAction): TooltipContent;
|
|
27
|
+
/** Which side of its anchor a popover opens toward. */
|
|
28
|
+
export type PopoverSide = "top" | "bottom" | "left" | "right";
|
|
29
|
+
/** A positioned rectangle (viewport/anchor space, top-left origin, pixels). */
|
|
30
|
+
export interface UiRect {
|
|
31
|
+
x: number;
|
|
32
|
+
y: number;
|
|
33
|
+
width: number;
|
|
34
|
+
height: number;
|
|
35
|
+
}
|
|
36
|
+
/** A width/height pair (pixels). */
|
|
37
|
+
export interface UiSize {
|
|
38
|
+
width: number;
|
|
39
|
+
height: number;
|
|
40
|
+
}
|
|
41
|
+
/** The resolved popover position — the side it actually opened on and its clamped top-left. */
|
|
42
|
+
export interface PopoverPlacement {
|
|
43
|
+
side: PopoverSide;
|
|
44
|
+
left: number;
|
|
45
|
+
top: number;
|
|
46
|
+
}
|
|
47
|
+
/** Options for {@link placePopover}. */
|
|
48
|
+
export interface PopoverOptions {
|
|
49
|
+
/** Side to try first (default `"top"`). Flips to the opposite when it would overflow. */
|
|
50
|
+
preferred?: PopoverSide;
|
|
51
|
+
/** Gap between the anchor and the popover, in pixels (default 8). */
|
|
52
|
+
gap?: number;
|
|
53
|
+
/** Minimum inset from the viewport edges, in pixels (default 8). */
|
|
54
|
+
margin?: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Position a popover/tooltip of `content` size against its `anchor` rectangle inside `viewport`:
|
|
58
|
+
* open on the `preferred` side, flip to the opposite when that side lacks room and the opposite has
|
|
59
|
+
* more, then clamp the top-left so the box stays fully on screen (respecting `margin`). Pure and
|
|
60
|
+
* deterministic — no DOM reads — so it is SSR-safe and unit-testable, and the React tooltip is a thin
|
|
61
|
+
* shell over it.
|
|
62
|
+
*/
|
|
63
|
+
export declare function placePopover(anchor: UiRect, content: UiSize, viewport: UiSize, options?: PopoverOptions): PopoverPlacement;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless model for HUD tooltips and popovers — the shared description seam games have been
|
|
3
|
+
* hand-rolling with a bare DOM `title=` attribute. {@link actionTooltip} turns a
|
|
4
|
+
* {@link ResolvedAction} into structured {@link TooltipContent} (title, cost, cooldown, blocking
|
|
5
|
+
* notes), and {@link placePopover} is the pure, SSR-safe positioning math the React tooltip uses to
|
|
6
|
+
* flip and clamp against the viewport. Both are rendering-agnostic.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Build {@link TooltipContent} from a {@link ResolvedAction}: title from the label, the group as a
|
|
10
|
+
* subtitle, the description as the body, and the blocking reasons as warning notes. The single place
|
|
11
|
+
* an action's tooltip copy is assembled, so every renderer shows the same thing.
|
|
12
|
+
*/
|
|
13
|
+
export function actionTooltip(action) {
|
|
14
|
+
const content = { title: action.label };
|
|
15
|
+
if (action.group !== undefined)
|
|
16
|
+
content.subtitle = action.group;
|
|
17
|
+
if (action.description !== undefined)
|
|
18
|
+
content.body = action.description;
|
|
19
|
+
if (action.hotkey !== undefined)
|
|
20
|
+
content.hotkey = action.hotkey;
|
|
21
|
+
if (action.costs.length > 0)
|
|
22
|
+
content.costs = action.costs;
|
|
23
|
+
if (action.cooldown !== null && !action.cooldown.ready)
|
|
24
|
+
content.cooldown = action.cooldown;
|
|
25
|
+
if (action.reasons.length > 0)
|
|
26
|
+
content.notes = action.reasons.map((reason) => reason.message);
|
|
27
|
+
return content;
|
|
28
|
+
}
|
|
29
|
+
const OPPOSITE = {
|
|
30
|
+
top: "bottom",
|
|
31
|
+
bottom: "top",
|
|
32
|
+
left: "right",
|
|
33
|
+
right: "left",
|
|
34
|
+
};
|
|
35
|
+
const clamp = (value, min, max) => Math.max(min, Math.min(max, max < min ? min : value));
|
|
36
|
+
/** @internal Room in pixels between the anchor and the viewport edge on a given side. */
|
|
37
|
+
function roomOn(side, anchor, viewport) {
|
|
38
|
+
if (side === "top")
|
|
39
|
+
return anchor.y;
|
|
40
|
+
if (side === "bottom")
|
|
41
|
+
return viewport.height - (anchor.y + anchor.height);
|
|
42
|
+
if (side === "left")
|
|
43
|
+
return anchor.x;
|
|
44
|
+
return viewport.width - (anchor.x + anchor.width);
|
|
45
|
+
}
|
|
46
|
+
/** @internal The top-left of `content` placed on `side` of `anchor`, before viewport clamping. */
|
|
47
|
+
function topLeftFor(side, anchor, content, gap) {
|
|
48
|
+
if (side === "top") {
|
|
49
|
+
return { left: anchor.x + anchor.width / 2 - content.width / 2, top: anchor.y - gap - content.height };
|
|
50
|
+
}
|
|
51
|
+
if (side === "bottom") {
|
|
52
|
+
return { left: anchor.x + anchor.width / 2 - content.width / 2, top: anchor.y + anchor.height + gap };
|
|
53
|
+
}
|
|
54
|
+
if (side === "left") {
|
|
55
|
+
return { left: anchor.x - gap - content.width, top: anchor.y + anchor.height / 2 - content.height / 2 };
|
|
56
|
+
}
|
|
57
|
+
return { left: anchor.x + anchor.width + gap, top: anchor.y + anchor.height / 2 - content.height / 2 };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Position a popover/tooltip of `content` size against its `anchor` rectangle inside `viewport`:
|
|
61
|
+
* open on the `preferred` side, flip to the opposite when that side lacks room and the opposite has
|
|
62
|
+
* more, then clamp the top-left so the box stays fully on screen (respecting `margin`). Pure and
|
|
63
|
+
* deterministic — no DOM reads — so it is SSR-safe and unit-testable, and the React tooltip is a thin
|
|
64
|
+
* shell over it.
|
|
65
|
+
*/
|
|
66
|
+
export function placePopover(anchor, content, viewport, options) {
|
|
67
|
+
const gap = options?.gap ?? 8;
|
|
68
|
+
const margin = options?.margin ?? 8;
|
|
69
|
+
const preferred = options?.preferred ?? "top";
|
|
70
|
+
const needed = preferred === "top" || preferred === "bottom" ? content.height : content.width;
|
|
71
|
+
let side = preferred;
|
|
72
|
+
const opposite = OPPOSITE[preferred];
|
|
73
|
+
if (roomOn(preferred, anchor, viewport) < needed + gap && roomOn(opposite, anchor, viewport) > roomOn(preferred, anchor, viewport)) {
|
|
74
|
+
side = opposite;
|
|
75
|
+
}
|
|
76
|
+
const { left, top } = topLeftFor(side, anchor, content, gap);
|
|
77
|
+
return {
|
|
78
|
+
side,
|
|
79
|
+
left: clamp(left, margin, viewport.width - content.width - margin),
|
|
80
|
+
top: clamp(top, margin, viewport.height - content.height - margin),
|
|
81
|
+
};
|
|
82
|
+
}
|