1681-engine 0.5.8 → 0.5.9
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 +53 -0
- package/dist/1681-engine.js +463 -409
- package/dist/ambient/ColorGrade.js +0 -1
- package/dist/ambient/DayClock.js +0 -1
- package/dist/ambient/WeatherLayer.js +0 -1
- package/dist/ambient/grade.js +0 -1
- package/dist/ambient/weather.js +0 -1
- package/dist/anim/AnimStateMachine.js +0 -1
- package/dist/anim/Animator.js +0 -1
- package/dist/anim/animStateCompile.js +0 -1
- package/dist/anim/animStateSystem.js +0 -1
- package/dist/anim/animationSystem.js +0 -1
- package/dist/anim/asepriteSlices.js +0 -1
- package/dist/anim/asepriteTags.js +0 -1
- package/dist/anim/parseAseprite.js +0 -1
- package/dist/assets/AssetLoader.js +112 -14
- package/dist/assets/loadAseprite.js +0 -1
- package/dist/assets/takeCleanerWorker-DTcI_Jqr.js +0 -1
- package/dist/assets/textureAtlas.js +58 -0
- package/dist/audio/Footsteps.js +0 -1
- package/dist/audio/MusicPlayer.js +0 -1
- package/dist/audio/SoundEngine.js +13 -7
- package/dist/audio/cleanTake.js +0 -1
- package/dist/audio/music/ElementVoice.js +0 -1
- package/dist/audio/music/GraphVoice.js +0 -1
- package/dist/audio/music/MusicChannel.js +0 -1
- package/dist/audio/music/musicVoice.js +0 -1
- package/dist/audio/sampleBank.js +0 -1
- package/dist/audio/soundTable.js +0 -1
- package/dist/audio/synth.js +0 -1
- package/dist/core/BitmapLabel.js +0 -1
- package/dist/core/Camera.js +1 -2
- package/dist/core/Clock.js +31 -21
- package/dist/core/Graphics.js +0 -1
- package/dist/core/Group.js +0 -1
- package/dist/core/Image.js +0 -1
- package/dist/core/ObjectFactory.js +0 -1
- package/dist/core/Pointer.js +46 -24
- package/dist/core/Rectangle.js +0 -1
- package/dist/core/Stage.js +5 -6
- package/dist/core/Text.js +0 -1
- package/dist/core/TextureRegistry.js +54 -24
- package/dist/core/Timeline.js +0 -1
- package/dist/core/TimelineBuilder.js +0 -1
- package/dist/core/TimelineChild.js +0 -1
- package/dist/core/Tweener.js +0 -1
- package/dist/core/atlas.js +0 -1
- package/dist/core/atlasPlan.js +0 -1
- package/dist/core/blendLayer.js +11 -0
- package/dist/core/eases.js +0 -1
- package/dist/core/gameObject.js +22 -20
- package/dist/core/pixelZoom.js +43 -0
- package/dist/core/pixiPatches.js +0 -1
- package/dist/core/runDue.js +28 -0
- package/dist/core/silhouette.js +49 -0
- package/dist/core/timelinePlace.js +0 -1
- package/dist/core/tweenProps.js +0 -1
- package/dist/debug/DebugOverlay.js +0 -1
- package/dist/debug/DrawCallCounter.js +15 -12
- package/dist/debug/ErrorLog.js +0 -1
- package/dist/debug/FrameStats.js +0 -1
- package/dist/debug/inspectWorld.js +0 -1
- package/dist/diagnostics/ErrorDeduper.js +0 -1
- package/dist/diagnostics/FrameSampler.js +0 -1
- package/dist/diagnostics/bootWatch.js +0 -1
- package/dist/diagnostics/captureErrors.js +0 -1
- package/dist/diagnostics/contextLoss.js +0 -1
- package/dist/diagnostics/frameStats.js +0 -1
- package/dist/diagnostics/gpuInfo.js +0 -1
- package/dist/diagnostics/renderGuard.js +0 -1
- package/dist/ecs/SparseSet.js +0 -1
- package/dist/ecs/World.js +0 -1
- package/dist/ecs/fields.js +0 -1
- package/dist/engine/Engine.js +42 -18
- package/dist/engine/FixedLoop.js +0 -1
- package/dist/engine/HiddenPause.js +27 -0
- package/dist/engine/Scene.js +0 -1
- package/dist/engine/SceneManager.js +0 -1
- package/dist/events/EventBus.js +0 -1
- package/dist/fx/Afterimages.js +82 -0
- package/dist/fx/FloatingText.js +0 -1
- package/dist/fx/hitStop.js +0 -1
- package/dist/fx/viewCull.js +0 -1
- package/dist/i18n/Translator.js +0 -1
- package/dist/i18n/extractStrings.js +0 -1
- package/dist/i18n/formatMessage.js +0 -1
- package/dist/input/Actions.js +29 -21
- package/dist/input/AimGesture.js +0 -1
- package/dist/input/Keyboard.js +0 -1
- package/dist/input/Keymap.js +0 -1
- package/dist/input/TargetLock.js +0 -1
- package/dist/input/aimAxis.js +0 -1
- package/dist/input/keyChord.js +0 -1
- package/dist/input/longPress.js +0 -1
- package/dist/input/rumble.js +42 -0
- package/dist/input/stickAxis.js +0 -1
- package/dist/input/touchDetect.js +0 -1
- package/dist/light/LightLayer.js +0 -1
- package/dist/nav/MinHeap.js +0 -1
- package/dist/nav/findPath.js +0 -1
- package/dist/nav/navGrid.js +0 -1
- package/dist/nav/nearestOpen.js +0 -1
- package/dist/nav/straightenPath.js +0 -1
- package/dist/net/Connection.js +39 -16
- package/dist/net/InterpolationBuffer.js +0 -1
- package/dist/net/PredictedMover.js +0 -1
- package/dist/net/Room.js +0 -1
- package/dist/net/RoomHost.js +0 -1
- package/dist/net/cadence.js +0 -1
- package/dist/net/constants.js +0 -1
- package/dist/net/lobby/LobbyClient.js +0 -1
- package/dist/net/lobby/LobbyHost.js +0 -1
- package/dist/net/lobby/LobbyRooms.js +0 -1
- package/dist/net/lobby/Matchmaker.js +0 -1
- package/dist/net/movement.js +40 -39
- package/dist/net/protocol.js +0 -1
- package/dist/net/reconnect.js +10 -0
- package/dist/net/serverClock.js +0 -1
- package/dist/net/snapshotCodec.js +0 -1
- package/dist/parallax/Parallax.js +0 -1
- package/dist/parallax/ParallaxLayer.js +0 -1
- package/dist/parallax/parallaxMath.js +0 -1
- package/dist/particles/Emitter.js +0 -1
- package/dist/particles/ParticleBudget.js +0 -1
- package/dist/particles/ParticleSim.js +0 -1
- package/dist/particles/particleCurves.js +0 -1
- package/dist/particles/particleSampling.js +0 -1
- package/dist/physics/Body.js +0 -1
- package/dist/physics/BroadPhase.js +0 -1
- package/dist/physics/Contact.js +0 -1
- package/dist/physics/ContactSolver.js +0 -1
- package/dist/physics/PhysicsWorld.js +0 -1
- package/dist/physics/SpatialHash.js +0 -1
- package/dist/physics/collide.js +0 -1
- package/dist/physics/collidePolygon.js +0 -1
- package/dist/physics/contactReport.js +0 -1
- package/dist/physics/drawColliders.js +0 -1
- package/dist/physics/physicsSystem.js +0 -1
- package/dist/physics/polygon.js +0 -1
- package/dist/physics/raycast.js +0 -1
- package/dist/physics/syncToBody.js +0 -1
- package/dist/physics/tileBodies.js +0 -1
- package/dist/physics/types.js +0 -1
- package/dist/pixel/Darkness.js +50 -47
- package/dist/pixel/DitherVignette.js +0 -1
- package/dist/pixel/Evaporation.js +0 -1
- package/dist/pixel/NightShade.js +0 -1
- package/dist/pixel/PixelLayer.js +0 -1
- package/dist/pixel/PixelMarks.js +99 -0
- package/dist/pixel/SwayProp.js +0 -1
- package/dist/pixel/aimPixels.js +67 -0
- package/dist/pixel/lights.js +0 -1
- package/dist/pixel/nineSlice.js +0 -1
- package/dist/pixel/paintLayer.js +0 -1
- package/dist/pixel/palette.js +0 -1
- package/dist/pixel/pixel.js +0 -1
- package/dist/pixel/pixelPaint.js +27 -24
- package/dist/pixel/pixelParticles.js +35 -0
- package/dist/pixel/pixelTone.js +0 -1
- package/dist/pixel/propKit.js +0 -1
- package/dist/pixel/ramp.js +0 -1
- package/dist/pixel/selout.js +0 -1
- package/dist/pixel/spritePixels.js +0 -1
- package/dist/pixel/swayRig.js +0 -1
- package/dist/pixel/vignetteMask.js +0 -1
- package/dist/pixel/weatherArt.js +0 -1
- package/dist/pixel.js +71 -65
- package/dist/platform/appResume.js +18 -0
- package/dist/platform/fullscreen.js +52 -0
- package/dist/platform/orientation.js +26 -0
- package/dist/platform/pointerLock.js +37 -0
- package/dist/platform/reducedMotion.js +23 -0
- package/dist/platform/timeoutSignal.js +14 -0
- package/dist/platform/touch.js +0 -1
- package/dist/postfx/BloomFilter.js +0 -1
- package/dist/postfx/ChromaticAberrationFilter.js +0 -1
- package/dist/postfx/ColorAdjustFilter.js +0 -1
- package/dist/postfx/CrtFilter.js +0 -1
- package/dist/postfx/PixelateFilter.js +0 -1
- package/dist/postfx/PostFx.js +0 -1
- package/dist/postfx/ShockwaveFilter.js +0 -1
- package/dist/postfx/VignetteFilter.js +0 -1
- package/dist/postfx/fxAnimator.js +0 -1
- package/dist/postfx/fxShader.js +0 -1
- package/dist/postfx.js +0 -1
- package/dist/react/ChoiceDialog.js +0 -1
- package/dist/react/ConfirmDialog.js +0 -1
- package/dist/react/Glyph.js +0 -1
- package/dist/react/Modal.js +0 -1
- package/dist/react/Panel.js +0 -1
- package/dist/react/PixelImage.js +0 -1
- package/dist/react/Select.js +0 -1
- package/dist/react/Slider.js +0 -1
- package/dist/react/SplitPane.js +0 -1
- package/dist/react/TabStrip.js +0 -1
- package/dist/react/TitleTips.js +0 -1
- package/dist/react/Tooltip.js +0 -1
- package/dist/react/UiProvider.js +0 -1
- package/dist/react/baseCss.js +0 -1
- package/dist/react/choiceStore.js +0 -1
- package/dist/react/classNames.js +0 -1
- package/dist/react/confirmStore.js +0 -1
- package/dist/react/labels.js +0 -1
- package/dist/react/placement.js +0 -1
- package/dist/react/useFillSheet.js +0 -1
- package/dist/react/useFitToViewport.js +0 -1
- package/dist/react/usePicked.js +0 -1
- package/dist/react/useStore.js +0 -1
- package/dist/react/useViewport.js +0 -1
- package/dist/react.js +0 -1
- package/dist/rig/PosePlayer.js +0 -1
- package/dist/rig/RigView.js +0 -1
- package/dist/rig/Skeleton.js +0 -1
- package/dist/rig/humanoid.js +0 -1
- package/dist/rig/humanoid.json.js +0 -1
- package/dist/rig/humanoidPoses.js +0 -1
- package/dist/rig/poseMath.js +0 -1
- package/dist/rig/poses.json.js +0 -1
- package/dist/save/Autosaver.js +0 -1
- package/dist/save/MemoryBackend.js +0 -1
- package/dist/save/SaveStore.js +0 -1
- package/dist/save/WebStorageBackend.js +0 -1
- package/dist/save/checksum.js +0 -1
- package/dist/save/migrateSave.js +0 -1
- package/dist/settings.js +0 -1
- package/dist/tilemap/TilemapView.js +0 -1
- package/dist/tilemap/collision.js +0 -1
- package/dist/tilemap/tiled.js +0 -1
- package/dist/transitions/CrossfadeTransition.js +0 -1
- package/dist/transitions/DitherWipeTransition.js +0 -1
- package/dist/transitions/FadeTransition.js +0 -1
- package/dist/transitions/Letterbox.js +0 -1
- package/dist/transitions/ditherMask.js +0 -1
- package/dist/transitions/transitionTo.js +0 -1
- package/dist/transitions/tweenDone.js +0 -1
- package/dist/types/assets/AssetLoader.d.ts +63 -8
- package/dist/types/assets/AssetLoader.d.ts.map +1 -1
- package/dist/types/assets/textureAtlas.d.ts +35 -0
- package/dist/types/assets/textureAtlas.d.ts.map +1 -0
- package/dist/types/audio/SoundEngine.d.ts +5 -0
- package/dist/types/audio/SoundEngine.d.ts.map +1 -1
- package/dist/types/core/Camera.d.ts +1 -1
- package/dist/types/core/Camera.d.ts.map +1 -1
- package/dist/types/core/Clock.d.ts +14 -2
- package/dist/types/core/Clock.d.ts.map +1 -1
- package/dist/types/core/Pointer.d.ts +18 -0
- package/dist/types/core/Pointer.d.ts.map +1 -1
- package/dist/types/core/Stage.d.ts.map +1 -1
- package/dist/types/core/TextureRegistry.d.ts +39 -0
- package/dist/types/core/TextureRegistry.d.ts.map +1 -1
- package/dist/types/core/blendLayer.d.ts +23 -0
- package/dist/types/core/blendLayer.d.ts.map +1 -0
- package/dist/types/core/gameObject.d.ts.map +1 -1
- package/dist/types/core/pixelZoom.d.ts +45 -0
- package/dist/types/core/pixelZoom.d.ts.map +1 -0
- package/dist/types/core/runDue.d.ts +15 -0
- package/dist/types/core/runDue.d.ts.map +1 -0
- package/dist/types/core/silhouette.d.ts +16 -0
- package/dist/types/core/silhouette.d.ts.map +1 -0
- package/dist/types/debug/DrawCallCounter.d.ts.map +1 -1
- package/dist/types/engine/Engine.d.ts +16 -0
- package/dist/types/engine/Engine.d.ts.map +1 -1
- package/dist/types/engine/HiddenPause.d.ts +22 -0
- package/dist/types/engine/HiddenPause.d.ts.map +1 -0
- package/dist/types/fx/Afterimages.d.ts +62 -0
- package/dist/types/fx/Afterimages.d.ts.map +1 -0
- package/dist/types/index.d.ts +18 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/input/Actions.d.ts +7 -1
- package/dist/types/input/Actions.d.ts.map +1 -1
- package/dist/types/input/rumble.d.ts +41 -0
- package/dist/types/input/rumble.d.ts.map +1 -0
- package/dist/types/net/Connection.d.ts +22 -0
- package/dist/types/net/Connection.d.ts.map +1 -1
- package/dist/types/net/constants.d.ts +13 -4
- package/dist/types/net/constants.d.ts.map +1 -1
- package/dist/types/net/movement.d.ts.map +1 -1
- package/dist/types/net/reconnect.d.ts +14 -0
- package/dist/types/net/reconnect.d.ts.map +1 -0
- package/dist/types/pixel/Darkness.d.ts +17 -5
- package/dist/types/pixel/Darkness.d.ts.map +1 -1
- package/dist/types/pixel/PixelMarks.d.ts +68 -0
- package/dist/types/pixel/PixelMarks.d.ts.map +1 -0
- package/dist/types/pixel/aimPixels.d.ts +23 -0
- package/dist/types/pixel/aimPixels.d.ts.map +1 -0
- package/dist/types/pixel/index.d.ts +3 -1
- package/dist/types/pixel/index.d.ts.map +1 -1
- package/dist/types/pixel/pixelPaint.d.ts +1 -0
- package/dist/types/pixel/pixelPaint.d.ts.map +1 -1
- package/dist/types/pixel/pixelParticles.d.ts +21 -0
- package/dist/types/pixel/pixelParticles.d.ts.map +1 -0
- package/dist/types/platform/appResume.d.ts +25 -0
- package/dist/types/platform/appResume.d.ts.map +1 -0
- package/dist/types/platform/fullscreen.d.ts +37 -0
- package/dist/types/platform/fullscreen.d.ts.map +1 -0
- package/dist/types/platform/orientation.d.ts +19 -0
- package/dist/types/platform/orientation.d.ts.map +1 -0
- package/dist/types/platform/pointerLock.d.ts +26 -0
- package/dist/types/platform/pointerLock.d.ts.map +1 -0
- package/dist/types/platform/reducedMotion.d.ts +20 -0
- package/dist/types/platform/reducedMotion.d.ts.map +1 -0
- package/dist/types/platform/timeoutSignal.d.ts +6 -0
- package/dist/types/platform/timeoutSignal.d.ts.map +1 -0
- package/dist/types/ui/index.d.ts +4 -0
- package/dist/types/ui/index.d.ts.map +1 -1
- package/dist/types/ui/rich/RichText.d.ts +48 -0
- package/dist/types/ui/rich/RichText.d.ts.map +1 -0
- package/dist/types/ui/rich/richFonts.d.ts +70 -0
- package/dist/types/ui/rich/richFonts.d.ts.map +1 -0
- package/dist/types/ui/rich/richLayout.d.ts +76 -0
- package/dist/types/ui/rich/richLayout.d.ts.map +1 -0
- package/dist/types/ui/rich/richMarkup.d.ts +48 -0
- package/dist/types/ui/rich/richMarkup.d.ts.map +1 -0
- package/dist/ui/Bar.js +0 -1
- package/dist/ui/Button.js +0 -1
- package/dist/ui/Dialog.js +0 -1
- package/dist/ui/FocusManager.js +0 -1
- package/dist/ui/Label.js +0 -1
- package/dist/ui/List.js +0 -1
- package/dist/ui/Panel.js +0 -1
- package/dist/ui/ScrollView.js +0 -1
- package/dist/ui/Slider.js +0 -1
- package/dist/ui/Stack.js +0 -1
- package/dist/ui/Toggle.js +0 -1
- package/dist/ui/UiHost.js +0 -1
- package/dist/ui/UiPointerGate.js +0 -1
- package/dist/ui/UiRoot.js +0 -1
- package/dist/ui/Widget.js +0 -1
- package/dist/ui/dom/DomOverlay.js +0 -1
- package/dist/ui/dom/DomWorldAnchors.js +0 -1
- package/dist/ui/dom/InputShield.js +0 -1
- package/dist/ui/dom/createStore.js +0 -1
- package/dist/ui/dom/shieldDecision.js +0 -1
- package/dist/ui/dom/worldToScreen.js +0 -1
- package/dist/ui/layout/anchor.js +0 -1
- package/dist/ui/layout/barDrain.js +0 -1
- package/dist/ui/layout/fitToViewport.js +0 -1
- package/dist/ui/layout/focusNav.js +0 -1
- package/dist/ui/layout/geometry.js +0 -1
- package/dist/ui/layout/listWidth.js +0 -1
- package/dist/ui/layout/placeTooltip.js +0 -1
- package/dist/ui/layout/scroll.js +0 -1
- package/dist/ui/layout/slider.js +0 -1
- package/dist/ui/layout/stack.js +0 -1
- package/dist/ui/layout/wrapRow.js +0 -1
- package/dist/ui/rich/RichText.js +87 -0
- package/dist/ui/rich/richFonts.js +105 -0
- package/dist/ui/rich/richLayout.js +132 -0
- package/dist/ui/rich/richMarkup.js +67 -0
- package/dist/ui.js +75 -65
- package/package.json +1 -4
- package/dist/1681-engine.js.map +0 -1
- package/dist/ambient/ColorGrade.js.map +0 -1
- package/dist/ambient/DayClock.js.map +0 -1
- package/dist/ambient/WeatherLayer.js.map +0 -1
- package/dist/ambient/grade.js.map +0 -1
- package/dist/ambient/weather.js.map +0 -1
- package/dist/anim/AnimStateMachine.js.map +0 -1
- package/dist/anim/Animator.js.map +0 -1
- package/dist/anim/animStateCompile.js.map +0 -1
- package/dist/anim/animStateSystem.js.map +0 -1
- package/dist/anim/animationSystem.js.map +0 -1
- package/dist/anim/asepriteSlices.js.map +0 -1
- package/dist/anim/asepriteTags.js.map +0 -1
- package/dist/anim/parseAseprite.js.map +0 -1
- package/dist/assets/AssetLoader.js.map +0 -1
- package/dist/assets/loadAseprite.js.map +0 -1
- package/dist/assets/takeCleanerWorker-DTcI_Jqr.js.map +0 -1
- package/dist/audio/Footsteps.js.map +0 -1
- package/dist/audio/MusicPlayer.js.map +0 -1
- package/dist/audio/SoundEngine.js.map +0 -1
- package/dist/audio/cleanTake.js.map +0 -1
- package/dist/audio/music/ElementVoice.js.map +0 -1
- package/dist/audio/music/GraphVoice.js.map +0 -1
- package/dist/audio/music/MusicChannel.js.map +0 -1
- package/dist/audio/music/musicVoice.js.map +0 -1
- package/dist/audio/sampleBank.js.map +0 -1
- package/dist/audio/soundTable.js.map +0 -1
- package/dist/audio/synth.js.map +0 -1
- package/dist/core/BitmapLabel.js.map +0 -1
- package/dist/core/Camera.js.map +0 -1
- package/dist/core/Clock.js.map +0 -1
- package/dist/core/Graphics.js.map +0 -1
- package/dist/core/Group.js.map +0 -1
- package/dist/core/Image.js.map +0 -1
- package/dist/core/ObjectFactory.js.map +0 -1
- package/dist/core/Pointer.js.map +0 -1
- package/dist/core/Rectangle.js.map +0 -1
- package/dist/core/Stage.js.map +0 -1
- package/dist/core/Text.js.map +0 -1
- package/dist/core/TextureRegistry.js.map +0 -1
- package/dist/core/Timeline.js.map +0 -1
- package/dist/core/TimelineBuilder.js.map +0 -1
- package/dist/core/TimelineChild.js.map +0 -1
- package/dist/core/Tweener.js.map +0 -1
- package/dist/core/atlas.js.map +0 -1
- package/dist/core/atlasPlan.js.map +0 -1
- package/dist/core/eases.js.map +0 -1
- package/dist/core/gameObject.js.map +0 -1
- package/dist/core/pixiPatches.js.map +0 -1
- package/dist/core/timelinePlace.js.map +0 -1
- package/dist/core/tweenProps.js.map +0 -1
- package/dist/debug/DebugOverlay.js.map +0 -1
- package/dist/debug/DrawCallCounter.js.map +0 -1
- package/dist/debug/ErrorLog.js.map +0 -1
- package/dist/debug/FrameStats.js.map +0 -1
- package/dist/debug/inspectWorld.js.map +0 -1
- package/dist/diagnostics/ErrorDeduper.js.map +0 -1
- package/dist/diagnostics/FrameSampler.js.map +0 -1
- package/dist/diagnostics/bootWatch.js.map +0 -1
- package/dist/diagnostics/captureErrors.js.map +0 -1
- package/dist/diagnostics/contextLoss.js.map +0 -1
- package/dist/diagnostics/frameStats.js.map +0 -1
- package/dist/diagnostics/gpuInfo.js.map +0 -1
- package/dist/diagnostics/renderGuard.js.map +0 -1
- package/dist/ecs/SparseSet.js.map +0 -1
- package/dist/ecs/World.js.map +0 -1
- package/dist/ecs/fields.js.map +0 -1
- package/dist/engine/Engine.js.map +0 -1
- package/dist/engine/FixedLoop.js.map +0 -1
- package/dist/engine/Scene.js.map +0 -1
- package/dist/engine/SceneManager.js.map +0 -1
- package/dist/events/EventBus.js.map +0 -1
- package/dist/fx/FloatingText.js.map +0 -1
- package/dist/fx/hitStop.js.map +0 -1
- package/dist/fx/viewCull.js.map +0 -1
- package/dist/i18n/Translator.js.map +0 -1
- package/dist/i18n/extractStrings.js.map +0 -1
- package/dist/i18n/formatMessage.js.map +0 -1
- package/dist/input/Actions.js.map +0 -1
- package/dist/input/AimGesture.js.map +0 -1
- package/dist/input/Keyboard.js.map +0 -1
- package/dist/input/Keymap.js.map +0 -1
- package/dist/input/TargetLock.js.map +0 -1
- package/dist/input/aimAxis.js.map +0 -1
- package/dist/input/keyChord.js.map +0 -1
- package/dist/input/longPress.js.map +0 -1
- package/dist/input/stickAxis.js.map +0 -1
- package/dist/input/touchDetect.js.map +0 -1
- package/dist/light/LightLayer.js.map +0 -1
- package/dist/nav/MinHeap.js.map +0 -1
- package/dist/nav/findPath.js.map +0 -1
- package/dist/nav/navGrid.js.map +0 -1
- package/dist/nav/nearestOpen.js.map +0 -1
- package/dist/nav/straightenPath.js.map +0 -1
- package/dist/net/Connection.js.map +0 -1
- package/dist/net/InterpolationBuffer.js.map +0 -1
- package/dist/net/PredictedMover.js.map +0 -1
- package/dist/net/Room.js.map +0 -1
- package/dist/net/RoomHost.js.map +0 -1
- package/dist/net/cadence.js.map +0 -1
- package/dist/net/constants.js.map +0 -1
- package/dist/net/lobby/LobbyClient.js.map +0 -1
- package/dist/net/lobby/LobbyHost.js.map +0 -1
- package/dist/net/lobby/LobbyRooms.js.map +0 -1
- package/dist/net/lobby/Matchmaker.js.map +0 -1
- package/dist/net/movement.js.map +0 -1
- package/dist/net/protocol.js.map +0 -1
- package/dist/net/serverClock.js.map +0 -1
- package/dist/net/snapshotCodec.js.map +0 -1
- package/dist/parallax/Parallax.js.map +0 -1
- package/dist/parallax/ParallaxLayer.js.map +0 -1
- package/dist/parallax/parallaxMath.js.map +0 -1
- package/dist/particles/Emitter.js.map +0 -1
- package/dist/particles/ParticleBudget.js.map +0 -1
- package/dist/particles/ParticleSim.js.map +0 -1
- package/dist/particles/particleCurves.js.map +0 -1
- package/dist/particles/particleSampling.js.map +0 -1
- package/dist/physics/Body.js.map +0 -1
- package/dist/physics/BroadPhase.js.map +0 -1
- package/dist/physics/Contact.js.map +0 -1
- package/dist/physics/ContactSolver.js.map +0 -1
- package/dist/physics/PhysicsWorld.js.map +0 -1
- package/dist/physics/SpatialHash.js.map +0 -1
- package/dist/physics/collide.js.map +0 -1
- package/dist/physics/collidePolygon.js.map +0 -1
- package/dist/physics/contactReport.js.map +0 -1
- package/dist/physics/drawColliders.js.map +0 -1
- package/dist/physics/physicsSystem.js.map +0 -1
- package/dist/physics/polygon.js.map +0 -1
- package/dist/physics/raycast.js.map +0 -1
- package/dist/physics/syncToBody.js.map +0 -1
- package/dist/physics/tileBodies.js.map +0 -1
- package/dist/physics/types.js.map +0 -1
- package/dist/pixel/Darkness.js.map +0 -1
- package/dist/pixel/DitherVignette.js.map +0 -1
- package/dist/pixel/Evaporation.js.map +0 -1
- package/dist/pixel/NightShade.js.map +0 -1
- package/dist/pixel/PixelLayer.js.map +0 -1
- package/dist/pixel/SwayProp.js.map +0 -1
- package/dist/pixel/lights.js.map +0 -1
- package/dist/pixel/nineSlice.js.map +0 -1
- package/dist/pixel/paintLayer.js.map +0 -1
- package/dist/pixel/palette.js.map +0 -1
- package/dist/pixel/pixel.js.map +0 -1
- package/dist/pixel/pixelPaint.js.map +0 -1
- package/dist/pixel/pixelTone.js.map +0 -1
- package/dist/pixel/propKit.js.map +0 -1
- package/dist/pixel/ramp.js.map +0 -1
- package/dist/pixel/selout.js.map +0 -1
- package/dist/pixel/spritePixels.js.map +0 -1
- package/dist/pixel/swayRig.js.map +0 -1
- package/dist/pixel/vignetteMask.js.map +0 -1
- package/dist/pixel/weatherArt.js.map +0 -1
- package/dist/pixel.js.map +0 -1
- package/dist/platform/touch.js.map +0 -1
- package/dist/postfx/BloomFilter.js.map +0 -1
- package/dist/postfx/ChromaticAberrationFilter.js.map +0 -1
- package/dist/postfx/ColorAdjustFilter.js.map +0 -1
- package/dist/postfx/CrtFilter.js.map +0 -1
- package/dist/postfx/PixelateFilter.js.map +0 -1
- package/dist/postfx/PostFx.js.map +0 -1
- package/dist/postfx/ShockwaveFilter.js.map +0 -1
- package/dist/postfx/VignetteFilter.js.map +0 -1
- package/dist/postfx/fxAnimator.js.map +0 -1
- package/dist/postfx/fxShader.js.map +0 -1
- package/dist/postfx.js.map +0 -1
- package/dist/react/ChoiceDialog.js.map +0 -1
- package/dist/react/ConfirmDialog.js.map +0 -1
- package/dist/react/Glyph.js.map +0 -1
- package/dist/react/Modal.js.map +0 -1
- package/dist/react/Panel.js.map +0 -1
- package/dist/react/PixelImage.js.map +0 -1
- package/dist/react/Select.js.map +0 -1
- package/dist/react/Slider.js.map +0 -1
- package/dist/react/SplitPane.js.map +0 -1
- package/dist/react/TabStrip.js.map +0 -1
- package/dist/react/TitleTips.js.map +0 -1
- package/dist/react/Tooltip.js.map +0 -1
- package/dist/react/UiProvider.js.map +0 -1
- package/dist/react/baseCss.js.map +0 -1
- package/dist/react/choiceStore.js.map +0 -1
- package/dist/react/classNames.js.map +0 -1
- package/dist/react/confirmStore.js.map +0 -1
- package/dist/react/labels.js.map +0 -1
- package/dist/react/placement.js.map +0 -1
- package/dist/react/useFillSheet.js.map +0 -1
- package/dist/react/useFitToViewport.js.map +0 -1
- package/dist/react/usePicked.js.map +0 -1
- package/dist/react/useStore.js.map +0 -1
- package/dist/react/useViewport.js.map +0 -1
- package/dist/react.js.map +0 -1
- package/dist/rig/PosePlayer.js.map +0 -1
- package/dist/rig/RigView.js.map +0 -1
- package/dist/rig/Skeleton.js.map +0 -1
- package/dist/rig/humanoid.js.map +0 -1
- package/dist/rig/humanoid.json.js.map +0 -1
- package/dist/rig/humanoidPoses.js.map +0 -1
- package/dist/rig/poseMath.js.map +0 -1
- package/dist/rig/poses.json.js.map +0 -1
- package/dist/save/Autosaver.js.map +0 -1
- package/dist/save/MemoryBackend.js.map +0 -1
- package/dist/save/SaveStore.js.map +0 -1
- package/dist/save/WebStorageBackend.js.map +0 -1
- package/dist/save/checksum.js.map +0 -1
- package/dist/save/migrateSave.js.map +0 -1
- package/dist/settings.js.map +0 -1
- package/dist/tilemap/TilemapView.js.map +0 -1
- package/dist/tilemap/collision.js.map +0 -1
- package/dist/tilemap/tiled.js.map +0 -1
- package/dist/transitions/CrossfadeTransition.js.map +0 -1
- package/dist/transitions/DitherWipeTransition.js.map +0 -1
- package/dist/transitions/FadeTransition.js.map +0 -1
- package/dist/transitions/Letterbox.js.map +0 -1
- package/dist/transitions/ditherMask.js.map +0 -1
- package/dist/transitions/transitionTo.js.map +0 -1
- package/dist/transitions/tweenDone.js.map +0 -1
- package/dist/ui/Bar.js.map +0 -1
- package/dist/ui/Button.js.map +0 -1
- package/dist/ui/Dialog.js.map +0 -1
- package/dist/ui/FocusManager.js.map +0 -1
- package/dist/ui/Label.js.map +0 -1
- package/dist/ui/List.js.map +0 -1
- package/dist/ui/Panel.js.map +0 -1
- package/dist/ui/ScrollView.js.map +0 -1
- package/dist/ui/Slider.js.map +0 -1
- package/dist/ui/Stack.js.map +0 -1
- package/dist/ui/Toggle.js.map +0 -1
- package/dist/ui/UiHost.js.map +0 -1
- package/dist/ui/UiPointerGate.js.map +0 -1
- package/dist/ui/UiRoot.js.map +0 -1
- package/dist/ui/Widget.js.map +0 -1
- package/dist/ui/dom/DomOverlay.js.map +0 -1
- package/dist/ui/dom/DomWorldAnchors.js.map +0 -1
- package/dist/ui/dom/InputShield.js.map +0 -1
- package/dist/ui/dom/createStore.js.map +0 -1
- package/dist/ui/dom/shieldDecision.js.map +0 -1
- package/dist/ui/dom/worldToScreen.js.map +0 -1
- package/dist/ui/layout/anchor.js.map +0 -1
- package/dist/ui/layout/barDrain.js.map +0 -1
- package/dist/ui/layout/fitToViewport.js.map +0 -1
- package/dist/ui/layout/focusNav.js.map +0 -1
- package/dist/ui/layout/geometry.js.map +0 -1
- package/dist/ui/layout/listWidth.js.map +0 -1
- package/dist/ui/layout/placeTooltip.js.map +0 -1
- package/dist/ui/layout/scroll.js.map +0 -1
- package/dist/ui/layout/slider.js.map +0 -1
- package/dist/ui/layout/stack.js.map +0 -1
- package/dist/ui/layout/wrapRow.js.map +0 -1
- package/dist/ui.js.map +0 -1
package/dist/react/Slider.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Slider.js","sources":["../../src/react/Slider.tsx"],"sourcesContent":["import { type KeyboardEvent, type PointerEvent, useRef } from 'react';\nimport { quantize, sliderFraction, sliderValueAt } from '../ui/layout/slider';\nimport { useCx } from './UiProvider';\n\nexport interface SliderProps {\n value: number;\n onChange: (value: number) => void;\n /** Spoken name (`aria-label`). */\n label: string;\n min?: number;\n max?: number;\n step?: number;\n /** What the value reads as, for the screen reader and the number beside the track (default: a percentage). */\n format?: (value: number) => string;\n /** Hides the number beside the track. */\n hideValue?: boolean;\n disabled?: boolean;\n className?: string;\n}\n\nconst percent = (v: number) => `${Math.round(v * 100)}%`;\n\n/** The value an arrow, Page or Home/End key moves a slider to; `undefined` for any other key. */\nexport function sliderKeyValue(key: string, value: number, min: number, max: number, step: number): number | undefined {\n const moves: Record<string, number> = {\n ArrowLeft: value - step,\n ArrowDown: value - step,\n ArrowRight: value + step,\n ArrowUp: value + step,\n PageDown: value - step * 4,\n PageUp: value + step * 4,\n Home: min,\n End: max,\n };\n const to = moves[key];\n return to === undefined ? undefined : quantize(to, min, max, step);\n}\n\n/**\n * A slider drawn by the theme instead of the browser's range: a sunk track, a fill and a square knob. Dragging,\n * pressing anywhere on the track and the arrow / Page / Home / End keys move it; it is a `role=\"slider\"` with its aria\n * values, so it reads like the native range it replaces. Focused, it keeps the arrow keys from the game.\n */\nexport function Slider({ value, onChange, label, min = 0, max = 1, step = 0.05, format = percent, hideValue, disabled, className }: SliderProps) {\n const cx = useCx();\n const track = useRef<HTMLDivElement>(null);\n const set = (v: number) => {\n if (v !== value) onChange(v);\n };\n const fromPointer = (clientX: number) => {\n const r = track.current?.getBoundingClientRect();\n if (r && r.width > 0) set(sliderValueAt(clientX, r.left, r.width, min, max, step));\n };\n const onPointerDown = (e: PointerEvent<HTMLDivElement>) => {\n if (disabled || e.button !== 0) return;\n e.currentTarget.setPointerCapture(e.pointerId);\n e.currentTarget.focus();\n fromPointer(e.clientX);\n e.preventDefault();\n };\n const onPointerMove = (e: PointerEvent<HTMLDivElement>) => {\n if (!disabled && e.currentTarget.hasPointerCapture(e.pointerId)) fromPointer(e.clientX);\n };\n const onKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n const to = disabled ? undefined : sliderKeyValue(e.key, value, min, max, step);\n if (to === undefined) return;\n e.preventDefault();\n e.stopPropagation();\n set(to);\n };\n const pct = sliderFraction(value, min, max) * 100;\n return (\n <div className={cx(`e1681-slider-row${className ? ` ${className}` : ''}`)}>\n <div\n className={cx('e1681-slider')}\n role=\"slider\"\n tabIndex={disabled ? -1 : 0}\n aria-label={label}\n aria-valuemin={min}\n aria-valuemax={max}\n aria-valuenow={value}\n aria-valuetext={format(value)}\n aria-disabled={disabled || undefined}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onKeyDown={onKeyDown}\n >\n <div className={cx('e1681-slider-track')} ref={track}>\n <div className={cx('e1681-slider-fill')} style={{ width: `${pct}%` }} />\n <div className={cx('e1681-slider-knob')} style={{ left: `${pct}%` }} />\n </div>\n </div>\n {!hideValue && <span className={cx('e1681-slider-value')}>{format(value)}</span>}\n </div>\n );\n}\n"],"names":["percent","v","sliderKeyValue","key","value","min","max","step","to","quantize","Slider","onChange","label","format","hideValue","disabled","className","cx","useCx","track","useRef","set","fromPointer","clientX","r","sliderValueAt","onPointerDown","e","onPointerMove","onKeyDown","pct","sliderFraction","jsxs","jsx"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SplitPane.js","sources":["../../src/react/SplitPane.tsx"],"sourcesContent":["import { type CSSProperties, type MouseEvent, type ReactNode, useRef, useState } from 'react';\nimport { splitModeFor, SPLIT_STACK_BELOW } from './placement';\nimport { useFillSheet } from './useFillSheet';\nimport { useUi, useCx } from './UiProvider';\nimport { useViewport } from './useViewport';\n\nexport interface SplitPaneProps {\n list: ReactNode;\n /** The right pane; `null`/`undefined`/`false` shows `empty` instead. */\n detail?: ReactNode;\n empty?: ReactNode;\n /** The list column's width (any CSS grid track); by default the two share the width evenly. */\n listWidth?: string;\n /** Height follows the content (capped) instead of the fixed window height: for panes with little in them. */\n fit?: boolean;\n /** Below this width a touch screen shows one pane at a time (default `SPLIT_STACK_BELOW`, 600). */\n stackBelow?: number;\n /** A click on an element matching this opens the detail when stacked (default `.e1681-pickable`). */\n pickSelector?: string;\n /** The back button's word (default: the provider's `backToList`). */\n backLabel?: ReactNode;\n className?: string;\n}\n\n/**\n * Two panes side by side: a list that scrolls on its own and the detail of what is picked, which stays in view\n * however long the list grows, so a window is as tall as `.e1681-split` says and never taller. On a touch screen\n * narrower than `stackBelow` only one pane shows at a time: tapping a pickable row (`.e1681-pickable`) opens its\n * detail, and the back button returns. In a touch sheet it fills the height left below what sits above it.\n */\nexport function SplitPane({ list, detail, empty, listWidth, fit, stackBelow = SPLIT_STACK_BELOW, pickSelector = '.e1681-pickable', backLabel, className }: SplitPaneProps) {\n const cx = useCx();\n const ui = useUi();\n const width = useViewport(ui.touch).width;\n const stack = splitModeFor(width, ui.touch, stackBelow) === 'stack';\n const [showDetail, setShowDetail] = useState(false);\n const box = useRef<HTMLDivElement>(null);\n useFillSheet(box, ui.touch, !!fit);\n const style = listWidth && !stack ? ({ '--e1681-split-list': listWidth } as CSSProperties) : undefined;\n const picked = (e: MouseEvent) => {\n if (stack && (e.target as HTMLElement).closest(pickSelector)) setShowDetail(true);\n };\n const mode = stack ? (showDetail ? ' stack show-detail' : ' stack show-list') : '';\n return (\n <div ref={box} className={cx(`e1681-split${fit ? ' fit' : ''}${mode}${className ? ` ${className}` : ''}`)} style={style}>\n <div className={cx('e1681-split-list')} onClickCapture={picked}>\n {list}\n </div>\n <div className={cx('e1681-split-detail')}>\n {stack && (\n <button type=\"button\" className={cx('e1681-button small e1681-split-back')} onClick={() => setShowDetail(false)}>\n {backLabel ?? ui.labels.backToList}\n </button>\n )}\n {detail || <div className={cx('e1681-split-empty')}>{empty ?? ui.labels.pickHint}</div>}\n </div>\n </div>\n );\n}\n"],"names":["SplitPane","list","detail","empty","listWidth","fit","stackBelow","SPLIT_STACK_BELOW","pickSelector","backLabel","className","cx","useCx","ui","useUi","width","useViewport","stack","splitModeFor","showDetail","setShowDetail","useState","box","useRef","useFillSheet","style","picked","e","jsxs","jsx"],"mappings":";;;;;;AA8BO,SAASA,EAAU,EAAE,MAAAC,GAAM,QAAAC,GAAQ,OAAAC,GAAO,WAAAC,GAAW,KAAAC,GAAK,YAAAC,IAAaC,GAAmB,cAAAC,IAAe,mBAAmB,WAAAC,GAAW,WAAAC,KAA6B;AACzK,QAAMC,IAAKC,EAAA,GACLC,IAAKC,EAAA,GACLC,IAAQC,EAAYH,EAAG,KAAK,EAAE,OAC9BI,IAAQC,EAAaH,GAAOF,EAAG,OAAOP,CAAU,MAAM,SACtD,CAACa,GAAYC,CAAa,IAAIC,EAAS,EAAK,GAC5CC,IAAMC,EAAuB,IAAI;AACvC,EAAAC,EAAaF,GAAKT,EAAG,OAAO,CAAC,CAACR,CAAG;AACjC,QAAMoB,IAAQrB,KAAa,CAACa,IAAS,EAAE,sBAAsBb,MAAgC,QACvFsB,IAAS,CAACC,MAAkB;AAChC,IAAIV,KAAUU,EAAE,OAAuB,QAAQnB,CAAY,OAAiB,EAAI;AAAA,EAClF;AAEA,SACE,gBAAAoB,EAAC,SAAI,KAAKN,GAAK,WAAWX,EAAG,cAAcN,IAAM,SAAS,EAAE,GAFjDY,IAASE,IAAa,uBAAuB,qBAAsB,EAEX,GAAGT,IAAY,IAAIA,CAAS,KAAK,EAAE,EAAE,GAAG,OAAAe,GACzG,UAAA;AAAA,IAAA,gBAAAI,EAAC,SAAI,WAAWlB,EAAG,kBAAkB,GAAG,gBAAgBe,GACrD,UAAAzB,GACH;AAAA,IACA,gBAAA2B,EAAC,OAAA,EAAI,WAAWjB,EAAG,oBAAoB,GACpC,UAAA;AAAA,MAAAM,uBACE,UAAA,EAAO,MAAK,UAAS,WAAWN,EAAG,qCAAqC,GAAG,SAAS,MAAMS,EAAc,EAAK,GAC3G,UAAAX,KAAaI,EAAG,OAAO,YAC1B;AAAA,MAEDX,KAAU,gBAAA2B,EAAC,OAAA,EAAI,WAAWlB,EAAG,mBAAmB,GAAI,UAAAR,KAASU,EAAG,OAAO,SAAA,CAAS;AAAA,IAAA,EAAA,CACnF;AAAA,EAAA,GACF;AAEJ;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TabStrip.js","sources":["../../src/react/TabStrip.tsx"],"sourcesContent":["import type { CSSProperties, PointerEvent, ReactNode } from 'react';\nimport { useCx } from './UiProvider';\n\nexport interface Tab<T extends string> {\n id: T;\n label: ReactNode;\n /** Why a disabled tab cannot be taken. */\n title?: string;\n disabled?: boolean;\n /** The tab's own colour for its label, kept on both states. */\n color?: string;\n}\n\nexport interface TabStripProps<T extends string> {\n tabs: readonly Tab<T>[];\n value: T;\n onChange: (id: T) => void;\n /** The short strip that sits over a field or in a row of controls. */\n small?: boolean;\n /** Breaks onto more rows instead of scrolling sideways (same as `className={cx('wrap')}`). */\n wrap?: boolean;\n className?: string;\n /** What the strip chooses, for a screen reader. */\n label?: string;\n /** Lets a strip beside a text field keep the keyboard open (`e.preventDefault()`). */\n onPointerDown?: (e: PointerEvent) => void;\n}\n\n/**\n * One strip of tabs: the chosen one lifted and open into the pane below, the rest a step lower. Every row of tabs —\n * a window's sections, a filter, a difficulty — goes through here so they all read the same; never a row of buttons\n * or a `<select>`. A strip too long for one row takes `wrap` (or `className={cx('wrap')}`).\n */\nexport function TabStrip<T extends string>({ tabs, value, onChange, small, wrap, className, label, onPointerDown }: TabStripProps<T>) {\n const cx = useCx();\n const classes = ['e1681-tabs', small ? 'small' : '', wrap ? 'wrap' : '', className ?? ''].filter(Boolean).join(' ');\n return (\n <div className={cx(classes)} role=\"tablist\" aria-label={label}>\n {tabs.map((t) => {\n const style: CSSProperties | undefined = t.color ? { color: t.color } : undefined;\n return (\n <button\n key={t.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={t.id === value}\n className={cx(t.id === value ? 'e1681-tab active' : 'e1681-tab')}\n style={style}\n disabled={t.disabled}\n title={t.title}\n // a mouse takes a tab the moment it is pressed, or it lags a beat behind the hand; a finger only on a tap\n // (the click, which the browser never sends after a scroll), since a finger also presses to scroll the strip\n onPointerDown={(e) => {\n onPointerDown?.(e);\n if (e.pointerType === 'mouse' && e.button === 0 && !t.disabled && t.id !== value) onChange(t.id);\n }}\n onClick={() => {\n if (t.id !== value) onChange(t.id);\n }}\n >\n {t.label}\n </button>\n );\n })}\n </div>\n );\n}\n"],"names":["TabStrip","tabs","value","onChange","small","wrap","className","label","onPointerDown","cx","useCx","classes","jsx","t","style"],"mappings":";;AAiCO,SAASA,EAA2B,EAAE,MAAAC,GAAM,OAAAC,GAAO,UAAAC,GAAU,OAAAC,GAAO,MAAAC,GAAM,WAAAC,GAAW,OAAAC,GAAO,eAAAC,KAAmC;AACpI,QAAMC,IAAKC,EAAA,GACLC,IAAU,CAAC,cAAcP,IAAQ,UAAU,IAAIC,IAAO,SAAS,IAAIC,KAAa,EAAE,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAClH,SACE,gBAAAM,EAAC,OAAA,EAAI,WAAWH,EAAGE,CAAO,GAAG,MAAK,WAAU,cAAYJ,GACrD,UAAAN,EAAK,IAAI,CAACY,MAAM;AACf,UAAMC,IAAmCD,EAAE,QAAQ,EAAE,OAAOA,EAAE,UAAU;AACxE,WACE,gBAAAD;AAAA,MAAC;AAAA,MAAA;AAAA,QAEC,MAAK;AAAA,QACL,MAAK;AAAA,QACL,iBAAeC,EAAE,OAAOX;AAAA,QACxB,WAAWO,EAAGI,EAAE,OAAOX,IAAQ,qBAAqB,WAAW;AAAA,QAC/D,OAAAY;AAAA,QACA,UAAUD,EAAE;AAAA,QACZ,OAAOA,EAAE;AAAA,QAGT,eAAe,CAAC,MAAM;AACpB,UAAAL,IAAgB,CAAC,GACb,EAAE,gBAAgB,WAAW,EAAE,WAAW,KAAK,CAACK,EAAE,YAAYA,EAAE,OAAOX,KAAOC,EAASU,EAAE,EAAE;AAAA,QACjG;AAAA,QACA,SAAS,MAAM;AACb,UAAIA,EAAE,OAAOX,KAAOC,EAASU,EAAE,EAAE;AAAA,QACnC;AAAA,QAEC,UAAAA,EAAE;AAAA,MAAA;AAAA,MAlBEA,EAAE;AAAA,IAAA;AAAA,EAqBb,CAAC,EAAA,CACH;AAEJ;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TitleTips.js","sources":["../../src/react/TitleTips.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { Tooltip } from './Tooltip';\nimport { useUi, useCx } from './UiProvider';\n\nexport interface TitleTipsProps {\n /** How long the pointer rests on an element before its tip shows (default 350 ms). */\n delayMs?: number;\n /** Only elements inside this one (default: the whole document). */\n root?: () => HTMLElement | null;\n className?: string;\n}\n\ntype Tip = { text: string; x: number; y: number };\n\n/**\n * Every `title=` drawn as the kit's `Tooltip` instead of the browser's. Mounted once: the element under the pointer\n * has its `title` taken off while the pointer stays on it (so the browser shows nothing) and put back when it leaves,\n * so components keep writing plain `title=` and React's diffing never sees a change. Pointer events, not mouse\n * events, because a disabled button fires only those. Desktop only: a touch screen never showed a title.\n */\nexport function TitleTips({ delayMs = 350, root, className }: TitleTipsProps) {\n const cx = useCx();\n const ui = useUi();\n const [tip, setTip] = useState<Tip | null>(null);\n\n useEffect(() => {\n if (ui.touch || typeof document === 'undefined') return;\n let el: HTMLElement | null = null;\n let text = '';\n let timer = 0;\n let shown = false;\n let at = { x: 0, y: 0 };\n const scope = () => root?.() ?? document.documentElement;\n\n // takes the title off `el` (again, if React wrote a new one meanwhile) and keeps its text\n const take = () => {\n if (!el) return;\n const t = el.getAttribute('title');\n if (t !== null) {\n el.removeAttribute('title');\n text = t;\n if (shown) setTip(text ? { text, ...at } : null);\n }\n };\n const release = () => {\n window.clearTimeout(timer);\n if (el && el.isConnected && !el.hasAttribute('title') && text) el.setAttribute('title', text);\n el = null;\n text = '';\n shown = false;\n setTip(null);\n };\n const over = (e: PointerEvent) => {\n if (e.pointerType === 'touch') return;\n const next = (e.target as Element | null)?.closest?.('[title]') as HTMLElement | null;\n if (!next || next === el || !scope().contains(next)) return;\n release();\n el = next;\n at = { x: e.clientX, y: e.clientY };\n take();\n if (!text) return;\n timer = window.setTimeout(() => {\n shown = true;\n setTip({ text, ...at });\n }, delayMs);\n };\n const move = (e: PointerEvent) => {\n if (!el) return;\n if (!el.isConnected) return release();\n at = { x: e.clientX, y: e.clientY };\n take();\n if (shown) setTip(text ? { text, ...at } : null);\n };\n const out = (e: PointerEvent) => {\n if (!el) return;\n const to = e.relatedTarget as Node | null;\n if (to && el.contains(to)) return;\n release();\n };\n // a press hides it, as the browser's own did, until the pointer leaves and comes back\n const press = () => {\n window.clearTimeout(timer);\n shown = false;\n setTip(null);\n };\n\n document.addEventListener('pointerover', over, true);\n document.addEventListener('pointermove', move, true);\n document.addEventListener('pointerout', out, true);\n document.addEventListener('pointerdown', press, true);\n window.addEventListener('blur', release);\n return () => {\n document.removeEventListener('pointerover', over, true);\n document.removeEventListener('pointermove', move, true);\n document.removeEventListener('pointerout', out, true);\n document.removeEventListener('pointerdown', press, true);\n window.removeEventListener('blur', release);\n release();\n };\n }, [ui.touch, delayMs, root]);\n\n if (!tip) return null;\n return (\n <Tooltip x={tip.x} y={tip.y} {...(className ? { className } : {})}>\n <div className={cx('e1681-title-tip')}>{tip.text}</div>\n </Tooltip>\n );\n}\n"],"names":["TitleTips","delayMs","root","className","cx","useCx","ui","useUi","tip","setTip","useState","useEffect","el","text","timer","shown","at","scope","take","t","release","over","next","move","out","to","press","jsx","Tooltip"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Tooltip.js","sources":["../../src/react/Tooltip.tsx"],"sourcesContent":["import { type ReactNode, useLayoutEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport type { Rect, Size } from '../ui/layout/geometry';\nimport { placeTooltip, type TipSide } from '../ui/layout/placeTooltip';\nimport { cursorTip } from './placement';\nimport { useUi, useCx } from './UiProvider';\n\nexport interface TooltipProps {\n /** By the cursor: its screen position. */\n x?: number;\n y?: number;\n /** Or beside an element: its screen rect (`getBoundingClientRect()`). Wins over `x`/`y`. */\n anchor?: Rect;\n /** Open over the cursor (or on this side of the anchor) first; it flips when there is no room. */\n above?: boolean;\n side?: TipSide;\n className?: string;\n children: ReactNode;\n}\n\n/**\n * A tooltip frame kept on screen by its own measured size (`placeTooltip`): under the cursor while it fits, over it\n * when it does not, slid along the edge, never taller than the viewport. Portalled to the `UiProvider`'s `portal`, so\n * a window's `overflow` or transform never clips or shifts it.\n */\nexport function Tooltip({ x = 0, y = 0, anchor, above, side, className, children }: TooltipProps) {\n const cx = useCx();\n const ui = useUi();\n const ref = useRef<HTMLDivElement>(null);\n const [size, setSize] = useState<Size>({ width: 0, height: 0 });\n useLayoutEffect(() => {\n const el = ref.current;\n if (el && (el.offsetWidth !== size.width || el.offsetHeight !== size.height)) setSize({ width: el.offsetWidth, height: el.offsetHeight });\n });\n const view = ui.viewport();\n const at = anchor\n ? placeTooltip(anchor, size, view, { side: side ?? (above ? 'above' : 'below'), margin: ui.margin, snap: ui.snap })\n : cursorTip(x, y, size, view, { above: above ?? side === 'above', margin: ui.margin, snap: ui.snap });\n const target = ui.portal();\n const measured = size.width > 0;\n const tip = (\n <div\n ref={ref}\n className={cx(`e1681-tooltip e1681-tip-frame${className ? ` ${className}` : ''}`)}\n role=\"tooltip\"\n style={{ left: at.x, top: at.y, maxHeight: Math.max(0, view.height - 2 * ui.margin), visibility: measured ? undefined : 'hidden' }}\n >\n {children}\n </div>\n );\n return target ? createPortal(tip, target) : tip;\n}\n"],"names":["Tooltip","x","y","anchor","above","side","className","children","cx","useCx","ui","useUi","ref","useRef","size","setSize","useState","useLayoutEffect","el","view","at","placeTooltip","cursorTip","target","measured","tip","jsx","createPortal"],"mappings":";;;;;;AAyBO,SAASA,EAAQ,EAAE,GAAAC,IAAI,GAAG,GAAAC,IAAI,GAAG,QAAAC,GAAQ,OAAAC,GAAO,MAAAC,GAAM,WAAAC,GAAW,UAAAC,EAAA,GAA0B;AAChG,QAAMC,IAAKC,EAAA,GACLC,IAAKC,EAAA,GACLC,IAAMC,EAAuB,IAAI,GACjC,CAACC,GAAMC,CAAO,IAAIC,EAAe,EAAE,OAAO,GAAG,QAAQ,GAAG;AAC9D,EAAAC,EAAgB,MAAM;AACpB,UAAMC,IAAKN,EAAI;AACf,IAAIM,MAAOA,EAAG,gBAAgBJ,EAAK,SAASI,EAAG,iBAAiBJ,EAAK,WAASC,EAAQ,EAAE,OAAOG,EAAG,aAAa,QAAQA,EAAG,cAAc;AAAA,EAC1I,CAAC;AACD,QAAMC,IAAOT,EAAG,SAAA,GACVU,IAAKjB,IACPkB,EAAalB,GAAQW,GAAMK,GAAM,EAAE,MAAMd,MAASD,IAAQ,UAAU,UAAU,QAAQM,EAAG,QAAQ,MAAMA,EAAG,KAAA,CAAM,IAChHY,EAAUrB,GAAGC,GAAGY,GAAMK,GAAM,EAAE,OAAOf,KAASC,MAAS,SAAS,QAAQK,EAAG,QAAQ,MAAMA,EAAG,MAAM,GAChGa,IAASb,EAAG,OAAA,GACZc,IAAWV,EAAK,QAAQ,GACxBW,IACJ,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAd;AAAA,MACA,WAAWJ,EAAG,gCAAgCF,IAAY,IAAIA,CAAS,KAAK,EAAE,EAAE;AAAA,MAChF,MAAK;AAAA,MACL,OAAO,EAAE,MAAMc,EAAG,GAAG,KAAKA,EAAG,GAAG,WAAW,KAAK,IAAI,GAAGD,EAAK,SAAS,IAAIT,EAAG,MAAM,GAAG,YAAYc,IAAW,SAAY,SAAA;AAAA,MAEvH,UAAAjB;AAAA,IAAA;AAAA,EAAA;AAGL,SAAOgB,IAASI,EAAaF,GAAKF,CAAM,IAAIE;AAC9C;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"UiProvider.js","sources":["../../src/react/UiProvider.tsx"],"sourcesContent":["import { createContext, useContext, useMemo, type ReactNode } from 'react';\nimport { isTouchUi } from '../platform/touch';\nimport type { Size } from '../ui/layout/geometry';\nimport { classJoiner, type ClassJoiner } from './classNames';\nimport { DEFAULT_LABELS, type UiLabels } from './labels';\n\n/** What every component of the kit reads: where it is, how big the screen is and the words it prints. */\nexport interface UiSettings {\n /** Fingers, not a mouse: windows become full-screen sheets, tooltips are not shown. Default: `isTouchUi()`. */\n touch: boolean;\n /**\n * The scale windows and dialogs are meant to be drawn at (`fitToViewport`'s `scale`): a whole multiple for a\n * pixel-art UI laid out on small units. Default 1.\n */\n scale: number;\n /** The smallest scale a window may shrink to before its body scrolls instead (default: `scale`, never shrinks). */\n minScale: number;\n /** A player's \"window size\" for touch sheets: laid out on a box `1 / sheetScale` the screen and drawn back down. */\n sheetScale: number;\n /** Kept clear between a window and the screen's edges (default 8). */\n margin: number;\n /** Positions and sizes land on whole pixels (default true): pixel art at x.5 reads blurred. */\n snap: boolean;\n labels: UiLabels;\n /** The box windows, tooltips and lists are placed in. Default: the browser window. */\n viewport: () => Size;\n /** Where tooltips, the select's list and dialogs are portalled. Default: `document.body`. */\n portal: () => HTMLElement | null;\n /**\n * A game's own class names hung beside the kit's, keyed by the kit's name without its prefix (`tabs`, `tab`,\n * `button`, `select-list`…) or by a state (`picked`, `open`): a game whose stylesheet already styles its own names\n * takes the kit without rewriting it. Default none.\n */\n classNames: Readonly<Record<string, string>>;\n /** Joins class names, adding the game's beside each of the kit's (`classNames`); what every component calls. */\n cx: ClassJoiner;\n}\n\nexport interface UiProviderProps extends Partial<Omit<UiSettings, 'labels' | 'cx'>> {\n labels?: Partial<UiLabels>;\n children?: ReactNode;\n}\n\nconst NO_CLASSES: Readonly<Record<string, string>> = {};\n\nfunction windowSize(): Size {\n return typeof window === 'undefined' ? { width: 0, height: 0 } : { width: window.innerWidth, height: window.innerHeight };\n}\n\nfunction body(): HTMLElement | null {\n return typeof document === 'undefined' ? null : document.body;\n}\n\n/** The settings a component sees with no `UiProvider` above it. */\nexport function defaultUiSettings(): UiSettings {\n return { touch: isTouchUi(), scale: 1, minScale: 1, sheetScale: 1, margin: 8, snap: true, labels: DEFAULT_LABELS, viewport: windowSize, portal: body, classNames: NO_CLASSES, cx: classJoiner(NO_CLASSES) };\n}\n\nconst UiContext = createContext<UiSettings | null>(null);\n\n/**\n * Optional: sets the touch mode, the window scale, the words and the box to place things in for every component\n * below it. A UI mounted in a `DomOverlay` passes the overlay's content size as `viewport` and the overlay's\n * content as `portal`, so tooltips and lists land inside its scaled box.\n */\nexport function UiProvider({ children, labels, touch, scale, minScale, sheetScale, margin, snap, viewport, portal, classNames }: UiProviderProps) {\n const outer = useContext(UiContext);\n const value = useMemo<UiSettings>(() => {\n const base = outer ?? defaultUiSettings();\n const s = scale ?? base.scale;\n const classes = classNames ? { ...base.classNames, ...classNames } : base.classNames;\n return {\n touch: touch ?? base.touch,\n scale: s,\n minScale: minScale ?? Math.min(base.minScale, s),\n sheetScale: sheetScale ?? base.sheetScale,\n margin: margin ?? base.margin,\n snap: snap ?? base.snap,\n labels: labels ? { ...base.labels, ...labels } : base.labels,\n viewport: viewport ?? base.viewport,\n portal: portal ?? base.portal,\n classNames: classes,\n cx: classes === base.classNames ? base.cx : classJoiner(classes),\n };\n }, [outer, labels, touch, scale, minScale, sheetScale, margin, snap, viewport, portal, classNames]);\n return <UiContext.Provider value={value}>{children}</UiContext.Provider>;\n}\n\nlet fallback: UiSettings | null = null;\n\n/** The kit's settings: the nearest `UiProvider`'s, or the defaults. */\nexport function useUi(): UiSettings {\n const ui = useContext(UiContext);\n if (ui) return ui;\n fallback ??= defaultUiSettings();\n return fallback;\n}\n\n/** The class joiner of the nearest `UiProvider` (`UiSettings.cx`). */\nexport function useCx(): ClassJoiner {\n return useUi().cx;\n}\n"],"names":["NO_CLASSES","windowSize","body","defaultUiSettings","isTouchUi","DEFAULT_LABELS","classJoiner","UiContext","createContext","UiProvider","children","labels","touch","scale","minScale","sheetScale","margin","snap","viewport","portal","classNames","outer","useContext","value","useMemo","base","s","classes","jsx","fallback","useUi","ui","useCx"],"mappings":";;;;;AA2CA,MAAMA,IAA+C,CAAA;AAErD,SAASC,IAAmB;AAC1B,SAAO,OAAO,SAAW,MAAc,EAAE,OAAO,GAAG,QAAQ,EAAA,IAAM,EAAE,OAAO,OAAO,YAAY,QAAQ,OAAO,YAAA;AAC9G;AAEA,SAASC,IAA2B;AAClC,SAAO,OAAO,WAAa,MAAc,OAAO,SAAS;AAC3D;AAGO,SAASC,IAAgC;AAC9C,SAAO,EAAE,OAAOC,EAAA,GAAa,OAAO,GAAG,UAAU,GAAG,YAAY,GAAG,QAAQ,GAAG,MAAM,IAAM,QAAQC,GAAgB,UAAUJ,GAAY,QAAQC,GAAM,YAAYF,GAAY,IAAIM,EAAYN,CAAU,EAAA;AAC1M;AAEA,MAAMO,IAAYC,EAAiC,IAAI;AAOhD,SAASC,EAAW,EAAE,UAAAC,GAAU,QAAAC,GAAQ,OAAAC,GAAO,OAAAC,GAAO,UAAAC,GAAU,YAAAC,GAAY,QAAAC,GAAQ,MAAAC,GAAM,UAAAC,GAAU,QAAAC,GAAQ,YAAAC,KAA+B;AAChJ,QAAMC,IAAQC,EAAWf,CAAS,GAC5BgB,IAAQC,EAAoB,MAAM;AACtC,UAAMC,IAAOJ,KAASlB,EAAA,GAChBuB,IAAIb,KAASY,EAAK,OAClBE,IAAUP,IAAa,EAAE,GAAGK,EAAK,YAAY,GAAGL,MAAeK,EAAK;AAC1E,WAAO;AAAA,MACL,OAAOb,KAASa,EAAK;AAAA,MACrB,OAAOC;AAAA,MACP,UAAUZ,KAAY,KAAK,IAAIW,EAAK,UAAUC,CAAC;AAAA,MAC/C,YAAYX,KAAcU,EAAK;AAAA,MAC/B,QAAQT,KAAUS,EAAK;AAAA,MACvB,MAAMR,KAAQQ,EAAK;AAAA,MACnB,QAAQd,IAAS,EAAE,GAAGc,EAAK,QAAQ,GAAGd,EAAA,IAAWc,EAAK;AAAA,MACtD,UAAUP,KAAYO,EAAK;AAAA,MAC3B,QAAQN,KAAUM,EAAK;AAAA,MACvB,YAAYE;AAAA,MACZ,IAAIA,MAAYF,EAAK,aAAaA,EAAK,KAAKnB,EAAYqB,CAAO;AAAA,IAAA;AAAA,EAEnE,GAAG,CAACN,GAAOV,GAAQC,GAAOC,GAAOC,GAAUC,GAAYC,GAAQC,GAAMC,GAAUC,GAAQC,CAAU,CAAC;AAClG,SAAO,gBAAAQ,EAACrB,EAAU,UAAV,EAAmB,OAAAgB,GAAe,UAAAb,EAAA,CAAS;AACrD;AAEA,IAAImB,IAA8B;AAG3B,SAASC,IAAoB;AAClC,QAAMC,IAAKT,EAAWf,CAAS;AAC/B,SAAIwB,MACJF,MAAa1B,EAAA,GACN0B;AACT;AAGO,SAASG,IAAqB;AACnC,SAAOF,IAAQ;AACjB;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"baseCss.js","sources":["../../src/react/baseCss.ts"],"sourcesContent":["/**\n * The kit's base stylesheet, as a string: `injectBaseCss()` puts it in the page once, or a game copies it into its\n * own CSS. Everything is themed by custom properties a game sets on `:root` (or any ancestor):\n *\n * | Property | What |\n * |---|---|\n * | `--e1681-font`, `--e1681-font-size` | the UI's font |\n * | `--e1681-text`, `--e1681-muted`, `--e1681-accent` | text, quiet text, the picked / lit colour |\n * | `--e1681-bg`, `--e1681-well`, `--e1681-control-bg` | a window's fill, a sunk pane's, a button's or field's |\n * | `--e1681-border`, `--e1681-border-width` | the plain border used while no frame image is given |\n * | `--e1681-frame`, `--e1681-frame-slice`, `--e1681-frame-width` | windows and dialogs: a 9-slice `border-image` (`url(…)`, `16 20 20 16 fill`, `16px 20px 20px 16px`) |\n * | `--e1681-tip-frame`, `-slice`, `-width` | tooltips and the select's list |\n * | `--e1681-control-frame`, `-slice`, `-width` | buttons, the select, text fields |\n * | `--e1681-tab-frame`, `--e1681-tab-active-frame`, `-slice`, `-width` | tabs |\n * | `--e1681-well-frame`, `-slice`, `-width` | the split pane's detail, the slider's track |\n * | `--e1681-backdrop` | behind a dialog |\n *\n * A `-width` may have one to four lengths, like `border-width`. The pixel rules hold here: no `translate(-50%)` centring and no `zoom` (the components place in whole pixels), no\n * blurred shadow, no gradient. A frame's middle shows through only when its slice says `fill`.\n */\nexport const baseCss = `\n:root {\n --e1681-font: inherit;\n --e1681-font-size: 16px;\n --e1681-text: #ece6d6;\n --e1681-muted: #9c968a;\n --e1681-accent: #e8c060;\n --e1681-bg: #1d242e;\n --e1681-well: #131a24;\n --e1681-control-bg: #2a323e;\n --e1681-border: #6a5634;\n --e1681-border-width: 2px;\n --e1681-backdrop: rgba(0, 0, 0, 0.5);\n --e1681-frame: none;\n --e1681-frame-slice: 0;\n --e1681-frame-width: var(--e1681-border-width);\n --e1681-tip-frame: var(--e1681-frame);\n --e1681-tip-frame-slice: var(--e1681-frame-slice);\n --e1681-tip-frame-width: var(--e1681-frame-width);\n --e1681-control-frame: none;\n --e1681-control-frame-slice: 0;\n --e1681-control-frame-width: var(--e1681-border-width);\n --e1681-tab-frame: var(--e1681-control-frame);\n --e1681-tab-active-frame: var(--e1681-tab-frame);\n --e1681-tab-frame-slice: var(--e1681-control-frame-slice);\n --e1681-tab-frame-width: var(--e1681-control-frame-width);\n --e1681-well-frame: none;\n --e1681-well-frame-slice: 0;\n --e1681-well-frame-width: var(--e1681-border-width);\n}\n\n.e1681-frame {\n color: var(--e1681-text);\n font-family: var(--e1681-font);\n font-size: var(--e1681-font-size);\n background: var(--e1681-bg) padding-box;\n border-style: solid; border-color: var(--e1681-border); border-width: var(--e1681-frame-width);\n border-image: var(--e1681-frame) var(--e1681-frame-slice) / var(--e1681-frame-width) stretch;\n box-sizing: border-box;\n}\n.e1681-tip-frame {\n color: var(--e1681-text);\n font-family: var(--e1681-font);\n font-size: var(--e1681-font-size);\n background: var(--e1681-bg) padding-box;\n border-style: solid; border-color: var(--e1681-border); border-width: var(--e1681-tip-frame-width);\n border-image: var(--e1681-tip-frame) var(--e1681-tip-frame-slice) / var(--e1681-tip-frame-width) stretch;\n box-sizing: border-box;\n}\n\n/* windows: placed by useFitToViewport in whole pixels; header kept, body scrolls */\n.e1681-panel {\n position: fixed;\n z-index: 10;\n display: flex;\n flex-direction: column;\n min-width: 0;\n overflow: hidden;\n pointer-events: auto;\n}\n.e1681-panel-header { display: flex; align-items: center; gap: 8px; flex: none; padding: 2px 4px 6px; }\n.e1681-panel-title { flex: 1; min-width: 0; margin: 0; font-size: 1.25em; font-weight: normal; color: var(--e1681-accent); }\n.e1681-panel-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden; padding: 0 4px 4px; }\n.e1681-panel-footer { flex: none; display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; padding: 6px 4px 0; }\n.e1681-close {\n flex: none; padding: 2px; border: 0; background: none; color: var(--e1681-text); cursor: pointer; line-height: 0;\n}\n.e1681-close:hover { color: var(--e1681-accent); }\n/* a touch screen's sheet: the whole screen inside the safe area */\n.e1681-panel.e1681-sheet {\n inset: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px) env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px);\n width: auto;\n height: auto;\n}\n.e1681-panel.e1681-sheet .e1681-panel-body { overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }\n\n/* controls */\n.e1681-button, .e1681-input, .e1681-select {\n color: var(--e1681-text);\n font: inherit;\n background: var(--e1681-control-bg) padding-box;\n border-style: solid; border-color: var(--e1681-border); border-width: var(--e1681-control-frame-width);\n border-image: var(--e1681-control-frame) var(--e1681-control-frame-slice) / var(--e1681-control-frame-width) stretch;\n box-shadow: none;\n}\n.e1681-button { padding: 2px 10px; cursor: pointer; min-height: 28px; }\n.e1681-button.small { padding: 0 6px; min-height: 22px; }\n.e1681-button.ghost { background-color: transparent; }\n.e1681-button:hover:not(:disabled) { color: var(--e1681-accent); }\n/* pressed: the same face a pixel lower, never a transform */\n.e1681-button:active:not(:disabled) { padding-top: 3px; padding-bottom: 1px; }\n.e1681-button:disabled { opacity: 0.55; cursor: default; }\n.e1681-input { padding: 2px 6px; min-width: 0; }\n.e1681-glyph { display: inline-block; flex: none; vertical-align: -1px; }\n\n/* tabs: a row that scrolls sideways, or wraps with .wrap */\n.e1681-tabs { display: flex; gap: 2px; align-items: flex-end; overflow-x: auto; overflow-y: hidden; scrollbar-width: none; margin-bottom: 8px; }\n.e1681-tabs::-webkit-scrollbar { display: none; }\n.e1681-tabs.wrap { flex-wrap: wrap; overflow-x: visible; row-gap: 4px; }\n.e1681-tab {\n flex: 0 0 auto;\n padding: 2px 10px;\n color: var(--e1681-muted);\n font: inherit;\n cursor: pointer;\n white-space: nowrap;\n background: var(--e1681-control-bg) padding-box;\n border-style: solid; border-color: var(--e1681-border); border-width: var(--e1681-tab-frame-width);\n border-image: var(--e1681-tab-frame) var(--e1681-tab-frame-slice) / var(--e1681-tab-frame-width) stretch;\n}\n.e1681-tab:hover:not(.active):not(:disabled) { color: var(--e1681-text); }\n.e1681-tab.active { color: var(--e1681-accent); padding-bottom: 6px; border-image-source: var(--e1681-tab-active-frame); }\n.e1681-tab:disabled { opacity: 0.55; cursor: default; }\n.e1681-tab:focus-visible { outline: 2px solid var(--e1681-accent); outline-offset: -4px; }\n.e1681-tabs.small { margin-bottom: 4px; }\n.e1681-tabs.small .e1681-tab { padding: 0 6px; }\n.e1681-tabs.small .e1681-tab.active { padding-bottom: 4px; }\n\n/* list beside detail */\n.e1681-split {\n display: grid;\n grid-template-columns: var(--e1681-split-list, minmax(0, 1fr)) minmax(0, 1fr);\n gap: 12px;\n height: min(58vh, 620px);\n}\n.e1681-split.fit { height: auto; max-height: min(58vh, 620px); }\n.e1681-split-list, .e1681-split-detail { min-height: 0; overflow-y: auto; overflow-x: hidden; }\n.e1681-split-list { padding-right: 4px; }\n.e1681-split-detail {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 8px 10px;\n background: var(--e1681-well) padding-box;\n border-style: solid; border-color: var(--e1681-border); border-width: var(--e1681-well-frame-width);\n border-image: var(--e1681-well-frame) var(--e1681-well-frame-slice) / var(--e1681-well-frame-width) stretch;\n}\n.e1681-split-empty { flex: 1; display: flex; align-items: center; justify-content: center; text-align: center; color: var(--e1681-muted); }\n.e1681-split.stack { grid-template-columns: minmax(0, 1fr); }\n.e1681-split.stack.show-list .e1681-split-detail, .e1681-split.stack.show-detail .e1681-split-list { display: none; }\n.e1681-split-back { align-self: flex-start; }\n.e1681-row { display: flex; align-items: center; gap: 8px; padding: 4px 6px; margin-bottom: 4px; border: var(--e1681-border-width) solid transparent; }\n.e1681-pickable { cursor: pointer; }\n.e1681-pickable:hover:not(.picked):not(.disabled) { border-color: var(--e1681-border); }\n.e1681-row.picked { border-color: var(--e1681-accent); color: var(--e1681-accent); }\n.e1681-row.disabled { opacity: 0.55; cursor: default; }\n\n/* the select: as wide as its longest label */\n.e1681-select { display: inline-flex; align-items: center; gap: 6px; flex: none; padding: 1px 4px; text-align: left; cursor: pointer; }\n.e1681-select:hover:not(:disabled), .e1681-select.open { color: var(--e1681-accent); }\n.e1681-select:disabled { opacity: 0.6; cursor: default; }\n.e1681-select.grow { flex: 1 1 auto; min-width: 0; }\n.e1681-select.grow .e1681-select-labels { flex: 1 1 auto; min-width: 0; overflow: hidden; }\n.e1681-select.grow .e1681-select-labels > span { overflow: hidden; text-overflow: ellipsis; }\n.e1681-select-labels { display: grid; white-space: nowrap; }\n.e1681-select-labels > span { grid-area: 1 / 1; visibility: hidden; }\n.e1681-select-labels > span.shown { visibility: visible; }\n.e1681-select-list {\n position: fixed;\n z-index: 10000;\n box-sizing: border-box;\n width: max-content;\n padding: 2px;\n overflow-y: auto;\n pointer-events: auto;\n}\n.e1681-select-option { padding: 2px 6px; white-space: nowrap; cursor: pointer; }\n.e1681-select-list.measuring .e1681-select-option { width: max-content; }\n.e1681-select-option.hot { background: var(--e1681-control-bg); }\n.e1681-select-option.picked { color: var(--e1681-accent); }\n\n/* tooltips */\n.e1681-tooltip { position: fixed; z-index: 10001; max-width: 360px; padding: 4px 8px; overflow: hidden; pointer-events: none; }\n.e1681-title-tip { white-space: pre-line; }\n\n/* slider: a square knob, flat fills */\n.e1681-slider-row { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; }\n.e1681-slider { flex: 1; min-width: 0; padding: 6px 7px; cursor: pointer; touch-action: none; user-select: none; pointer-events: auto; }\n.e1681-slider:focus { outline: none; }\n.e1681-slider:focus-visible { outline: 2px solid var(--e1681-accent); outline-offset: 2px; }\n.e1681-slider[aria-disabled='true'] { opacity: 0.55; cursor: default; }\n.e1681-slider-track {\n position: relative;\n height: 6px;\n background: var(--e1681-well) padding-box;\n border-style: solid; border-color: var(--e1681-border); border-width: var(--e1681-well-frame-width);\n border-image: var(--e1681-well-frame) var(--e1681-well-frame-slice) / var(--e1681-well-frame-width) stretch;\n}\n.e1681-slider-fill { position: absolute; left: 0; top: 0; bottom: 0; background: var(--e1681-accent); }\n/* the knob is offset by half its own whole-pixel width with a margin, so it stays on the pixel grid */\n.e1681-slider-knob { position: absolute; top: -6px; width: 10px; height: 14px; margin-left: -5px; background: var(--e1681-text); border: 2px solid var(--e1681-well); box-sizing: border-box; }\n.e1681-slider-value { min-width: 40px; text-align: right; font-variant-numeric: tabular-nums; }\n\n/* dialogs */\n.e1681-backdrop { position: fixed; inset: 0; z-index: 9000; background: var(--e1681-backdrop); pointer-events: auto; }\n.e1681-dialog { position: fixed; display: flex; flex-direction: column; overflow: hidden; padding: 8px 10px; }\n.e1681-dialog-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; }\n.e1681-dialog-actions { flex: none; display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; padding-top: 10px; }\n.e1681-confirm-text { white-space: pre-line; }\n.e1681-confirm-qty { display: flex; align-items: center; gap: 6px; margin-top: 10px; }\n.e1681-confirm-qty-input { width: 56px; text-align: right; }\n.e1681-choice-list { margin-top: 8px; }\n.e1681-choice-icon { flex: none; line-height: 0; }\n.e1681-choice-label { flex: 1; min-width: 0; }\n.e1681-choice-note { flex: none; margin-left: auto; color: var(--e1681-muted); }\n\n/* sprites at whole multiples */\n.e1681-pixel-image { display: inline-flex; align-items: center; justify-content: center; flex: none; overflow: hidden; }\n.e1681-pixel-image > img { image-rendering: pixelated; flex: none; }\n`;\n\nconst STYLE_ID = 'e1681-react-base';\n\n/**\n * Puts `baseCss` into the page once, first in `<head>` so the game's own rules (and its custom properties) win\n * without needing more specific selectors. Returns the `<style>` element; nothing on a server.\n */\nexport function injectBaseCss(doc: Document | undefined = typeof document === 'undefined' ? undefined : document): HTMLStyleElement | null {\n if (!doc) return null;\n const found = doc.getElementById(STYLE_ID);\n if (found) return found as HTMLStyleElement;\n const style = doc.createElement('style');\n style.id = STYLE_ID;\n style.textContent = baseCss;\n doc.head.insertBefore(style, doc.head.firstChild);\n return style;\n}\n"],"names":["baseCss","STYLE_ID","injectBaseCss","doc","found","style"],"mappings":"AAoBO,MAAMA,IAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAmNjBC,IAAW;AAMV,SAASC,EAAcC,IAA4B,OAAO,WAAa,MAAc,SAAY,UAAmC;AACzI,MAAI,CAACA,EAAK,QAAO;AACjB,QAAMC,IAAQD,EAAI,eAAeF,CAAQ;AACzC,MAAIG,EAAO,QAAOA;AAClB,QAAMC,IAAQF,EAAI,cAAc,OAAO;AACvC,SAAAE,EAAM,KAAKJ,GACXI,EAAM,cAAcL,GACpBG,EAAI,KAAK,aAAaE,GAAOF,EAAI,KAAK,UAAU,GACzCE;AACT;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"choiceStore.js","sources":["../../src/react/choiceStore.ts"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { createStore } from '../ui/dom/createStore';\n\n/** One row of a choice: a line of text, with an icon before it and a note after it if the game gives them. */\nexport interface ChoiceOption {\n id: string;\n label: ReactNode;\n icon?: ReactNode;\n note?: ReactNode;\n disabled?: boolean;\n}\n\nexport interface ChoiceQuestion {\n text: ReactNode;\n options: readonly ChoiceOption[];\n /** The confirm button's word; default the provider's `choose`. */\n confirm?: string;\n cancel?: string;\n /** The id picked, or null when cancelled. */\n resolve: (id: string | null) => void;\n}\n\n/** The open choice, read by `ChoiceDialog`. */\nexport const choiceStore = createStore<{ pending: ChoiceQuestion | null }>({ pending: null });\n\n/** Asks the player to pick one of `options`; resolves its id, or null when cancelled. */\nexport function askChoice(text: ReactNode, options: readonly ChoiceOption[], words: { confirm?: string; cancel?: string } = {}): Promise<string | null> {\n choiceStore.getState().pending?.resolve(null);\n return new Promise((resolve) => choiceStore.setState({ pending: { text, options, resolve, ...words } }));\n}\n\n/** Answers the open choice (`null` cancels). */\nexport function answerChoice(id: string | null): void {\n const pending = choiceStore.getState().pending;\n choiceStore.setState({ pending: null });\n pending?.resolve(id);\n}\n\n/** Cancels the open choice, if any (a phone's back button). Returns whether there was one. */\nexport function cancelChoice(): boolean {\n if (!choiceStore.getState().pending) return false;\n answerChoice(null);\n return true;\n}\n\n/** The option picked first: the first one that can be taken. */\nexport function firstChoice(options: readonly ChoiceOption[]): string | null {\n return options.find((o) => !o.disabled)?.id ?? null;\n}\n"],"names":["choiceStore","createStore","askChoice","text","options","words","resolve","answerChoice","id","pending","cancelChoice","firstChoice","o"],"mappings":";AAuBO,MAAMA,IAAcC,EAAgD,EAAE,SAAS,MAAM;AAGrF,SAASC,EAAUC,GAAiBC,GAAkCC,IAA+C,CAAA,GAA4B;AACtJ,SAAAL,EAAY,SAAA,EAAW,SAAS,QAAQ,IAAI,GACrC,IAAI,QAAQ,CAACM,MAAYN,EAAY,SAAS,EAAE,SAAS,EAAE,MAAAG,GAAM,SAAAC,GAAS,SAAAE,GAAS,GAAGD,EAAA,EAAM,CAAG,CAAC;AACzG;AAGO,SAASE,EAAaC,GAAyB;AACpD,QAAMC,IAAUT,EAAY,SAAA,EAAW;AACvC,EAAAA,EAAY,SAAS,EAAE,SAAS,KAAA,CAAM,GACtCS,GAAS,QAAQD,CAAE;AACrB;AAGO,SAASE,IAAwB;AACtC,SAAKV,EAAY,WAAW,WAC5BO,EAAa,IAAI,GACV,MAFqC;AAG9C;AAGO,SAASI,EAAYP,GAAiD;AAC3E,SAAOA,EAAQ,KAAK,CAACQ,MAAM,CAACA,EAAE,QAAQ,GAAG,MAAM;AACjD;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"classNames.js","sources":["../../src/react/classNames.ts"],"sourcesContent":["/** Joins class names, skipping empty ones. */\nexport type ClassJoiner = (...names: (string | false | null | undefined)[]) => string;\n\nconst PREFIX = 'e1681-';\n\n/**\n * A joiner that hangs a game's own class names beside the kit's: every name it is given (split on spaces) is kept,\n * and followed by `classes[name without the kit's prefix]` when the game mapped it. Pure, so it is tested under node.\n *\n * ```ts\n * classJoiner({ tabs: 'tabs', button: 'btn', picked: 'active' })('e1681-button small', 'picked') // 'e1681-button btn small picked active'\n * ```\n */\nexport function classJoiner(classes: Readonly<Record<string, string>>): ClassJoiner {\n const mapped = Object.keys(classes).length > 0;\n return (...names) => {\n const out: string[] = [];\n for (const n of names) {\n if (!n) continue;\n for (const part of n.split(' ')) {\n if (!part) continue;\n out.push(part);\n if (!mapped) continue;\n const extra = classes[part.startsWith(PREFIX) ? part.slice(PREFIX.length) : part];\n if (extra) out.push(extra);\n }\n }\n return out.join(' ');\n };\n}\n"],"names":["PREFIX","classJoiner","classes","mapped","names","out","n","part","extra"],"mappings":"AAGA,MAAMA,IAAS;AAUR,SAASC,EAAYC,GAAwD;AAClF,QAAMC,IAAS,OAAO,KAAKD,CAAO,EAAE,SAAS;AAC7C,SAAO,IAAIE,MAAU;AACnB,UAAMC,IAAgB,CAAA;AACtB,eAAWC,KAAKF;AACd,UAAKE;AACL,mBAAWC,KAAQD,EAAE,MAAM,GAAG,GAAG;AAG/B,cAFI,CAACC,MACLF,EAAI,KAAKE,CAAI,GACT,CAACJ,GAAQ;AACb,gBAAMK,IAAQN,EAAQK,EAAK,WAAWP,CAAM,IAAIO,EAAK,MAAMP,EAAO,MAAM,IAAIO,CAAI;AAChF,UAAIC,KAAOH,EAAI,KAAKG,CAAK;AAAA,QAC3B;AAEF,WAAOH,EAAI,KAAK,GAAG;AAAA,EACrB;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"confirmStore.js","sources":["../../src/react/confirmStore.ts"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { createStore } from '../ui/dom/createStore';\n\nexport interface ConfirmQuestion {\n text: ReactNode;\n /** The confirm button's word; default the provider's `ok`. */\n ok?: string;\n cancel?: string;\n /** Above 1 the dialog also asks how many (1..max), starting at all of them; a plain question has 0. */\n max: number;\n /** The amount chosen, or 0 when cancelled. */\n resolve: (qty: number) => void;\n}\n\n/** The open question, read by `ConfirmDialog`. */\nexport const confirmStore = createStore<{ pending: ConfirmQuestion | null }>({ pending: null });\n\nexport interface AskOptions {\n ok?: string;\n cancel?: string;\n}\n\nfunction ask(text: ReactNode, max: number, options: AskOptions): Promise<number> {\n // a new question cancels the one still open\n confirmStore.getState().pending?.resolve(0);\n return new Promise((resolve) => confirmStore.setState({ pending: { text, max, resolve, ...options } }));\n}\n\n/** A yes/no question in the kit's own dialog instead of `window.confirm`; resolves true when confirmed. */\nexport function askConfirm(text: ReactNode, options: AskOptions = {}): Promise<boolean> {\n return ask(text, 0, options).then((qty) => qty > 0);\n}\n\n/** The same question with a slider and a field for how many of `max`; resolves 0 when cancelled. */\nexport function askQuantity(text: ReactNode, max: number, options: AskOptions = {}): Promise<number> {\n return ask(text, Math.max(1, Math.floor(max)), options);\n}\n\n/** Answers the open question (`qty` 0 cancels). */\nexport function answerConfirm(qty: number): void {\n const pending = confirmStore.getState().pending;\n confirmStore.setState({ pending: null });\n pending?.resolve(qty);\n}\n\n/** Cancels the open question, if any (a phone's back button). Returns whether there was one. */\nexport function cancelConfirm(): boolean {\n if (!confirmStore.getState().pending) return false;\n answerConfirm(0);\n return true;\n}\n\n/** A typed quantity held to 1..max: digits only, anything else 1. */\nexport function parseQuantity(text: string, max: number): number {\n return Math.max(1, Math.min(max, Math.floor(Number(text.replace(/[^0-9]/g, '')) || 1)));\n}\n"],"names":["confirmStore","createStore","ask","text","max","options","resolve","askConfirm","qty","askQuantity","answerConfirm","pending","cancelConfirm","parseQuantity"],"mappings":";AAeO,MAAMA,IAAeC,EAAiD,EAAE,SAAS,MAAM;AAO9F,SAASC,EAAIC,GAAiBC,GAAaC,GAAsC;AAE/E,SAAAL,EAAa,SAAA,EAAW,SAAS,QAAQ,CAAC,GACnC,IAAI,QAAQ,CAACM,MAAYN,EAAa,SAAS,EAAE,SAAS,EAAE,MAAAG,GAAM,KAAAC,GAAK,SAAAE,GAAS,GAAGD,EAAA,EAAQ,CAAG,CAAC;AACxG;AAGO,SAASE,EAAWJ,GAAiBE,IAAsB,IAAsB;AACtF,SAAOH,EAAIC,GAAM,GAAGE,CAAO,EAAE,KAAK,CAACG,MAAQA,IAAM,CAAC;AACpD;AAGO,SAASC,EAAYN,GAAiBC,GAAaC,IAAsB,CAAA,GAAqB;AACnG,SAAOH,EAAIC,GAAM,KAAK,IAAI,GAAG,KAAK,MAAMC,CAAG,CAAC,GAAGC,CAAO;AACxD;AAGO,SAASK,EAAcF,GAAmB;AAC/C,QAAMG,IAAUX,EAAa,SAAA,EAAW;AACxC,EAAAA,EAAa,SAAS,EAAE,SAAS,KAAA,CAAM,GACvCW,GAAS,QAAQH,CAAG;AACtB;AAGO,SAASI,IAAyB;AACvC,SAAKZ,EAAa,WAAW,WAC7BU,EAAc,CAAC,GACR,MAFsC;AAG/C;AAGO,SAASG,EAAcV,GAAcC,GAAqB;AAC/D,SAAO,KAAK,IAAI,GAAG,KAAK,IAAIA,GAAK,KAAK,MAAM,OAAOD,EAAK,QAAQ,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;AACxF;"}
|
package/dist/react/labels.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"labels.js","sources":["../../src/react/labels.ts"],"sourcesContent":["/**\n * Every word the kit itself prints, in English. A game hands its own (translated) words to `UiProvider`'s `labels`, or\n * to a component's own prop; the engine never translates anything.\n */\nexport interface UiLabels {\n /** The close button's spoken name; `{0}` is the window's title. */\n close: string;\n /** The close button's tooltip. */\n closeHint: string;\n /** The button that takes a stacked `SplitPane` back from the detail to the list. */\n backToList: string;\n /** What an empty detail pane says. */\n pickHint: string;\n cancel: string;\n ok: string;\n /** The quantity slider's and field's spoken name. */\n quantity: string;\n /** The choice dialog's confirm button. */\n choose: string;\n}\n\nexport const DEFAULT_LABELS: UiLabels = {\n close: 'Close {0}',\n closeHint: 'Close (Esc)',\n backToList: '‹ List',\n pickHint: 'Pick an entry on the left.',\n cancel: 'Cancel',\n ok: 'OK',\n quantity: 'Quantity',\n choose: 'Choose',\n};\n\n/** `{0}`, `{1}`… replaced by the arguments, in order. */\nexport function fillLabel(label: string, ...args: readonly string[]): string {\n return label.replace(/\\{(\\d+)\\}/g, (all, i: string) => args[Number(i)] ?? all);\n}\n"],"names":["DEFAULT_LABELS","fillLabel","label","args","all","i"],"mappings":"AAqBO,MAAMA,IAA2B;AAAA,EACtC,OAAO;AAAA,EACP,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,IAAI;AAAA,EACJ,UAAU;AAAA,EACV,QAAQ;AACV;AAGO,SAASC,EAAUC,MAAkBC,GAAiC;AAC3E,SAAOD,EAAM,QAAQ,cAAc,CAACE,GAAKC,MAAcF,EAAK,OAAOE,CAAC,CAAC,KAAKD,CAAG;AAC/E;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"placement.js","sources":["../../src/react/placement.ts"],"sourcesContent":["/**\n * The kit's layout decisions as pure functions over the engine's layout maths (`src/ui/layout`), so they run and are\n * tested under node: the components only measure the DOM and apply what these return.\n */\nimport type { CSSProperties } from 'react';\nimport { fitToViewport, type FitOptions, type FitResult } from '../ui/layout/fitToViewport';\nimport type { Rect, Size } from '../ui/layout/geometry';\nimport { placeTooltip, type TooltipPlacement } from '../ui/layout/placeTooltip';\n\n/**\n * Where a window or dialog of `wanted` size stands: fitted into the viewport by `fitToViewport` (shrunk to its\n * `minScale`, then cut, then moved inside the edges), centred unless `wanted` has an `x`/`y`. The style places it in\n * whole pixels with `left`/`top`, never `translate(-50%)`, and caps its size so its body scrolls.\n */\nexport function windowStyle(wanted: Rect | Size, viewport: Size, options: FitOptions = {}): { fit: FitResult; style: CSSProperties } {\n const fit = fitToViewport(wanted, viewport, { snap: true, ...options, centre: options.centre ?? !('x' in wanted) });\n const style: CSSProperties = {\n left: fit.x,\n top: fit.y,\n maxWidth: fit.width,\n maxHeight: fit.height,\n transform: fit.scale === 1 ? undefined : `scale(${fit.scale})`,\n transformOrigin: fit.scale === 1 ? undefined : '0 0',\n };\n return { fit, style };\n}\n\n/**\n * A touch sheet at a player's \"window size\": laid out on a box `1 / scale` the screen and scaled back down to fill\n * it, so a smaller window shows more rows and still covers the screen. Nothing at 1.\n */\nexport function sheetScaleStyle(scale: number): CSSProperties | undefined {\n if (scale === 1 || !(scale > 0)) return undefined;\n return { width: `${100 / scale}%`, height: `${100 / scale}%`, transform: `scale(${scale})`, transformOrigin: '0 0' };\n}\n\nexport interface CursorTipOptions {\n /** Open over the cursor first (a bar pinned to the bottom edge), not under it. */\n above?: boolean;\n /** Between the cursor and the tip, up and down (default 12). */\n gap?: number;\n /** How far right of the cursor the tip starts, so the pointer never covers it (default 18). */\n offsetX?: number;\n margin?: number;\n snap?: boolean;\n}\n\n/** A tooltip of `tip` size by the cursor: under it while it fits, over it when not, slid to stay on screen. */\nexport function cursorTip(x: number, y: number, tip: Size, viewport: Size, options: CursorTipOptions = {}): TooltipPlacement {\n const anchor = { x: x + (options.offsetX ?? 18), y, width: 0, height: 0 };\n return placeTooltip(anchor, tip, viewport, {\n side: options.above ? 'above' : 'below',\n gap: options.gap ?? 12,\n margin: options.margin ?? 8,\n snap: options.snap ?? true,\n });\n}\n\nexport interface ListPlacement {\n x: number;\n y: number;\n /** The list's height: its own, or the room on its side when that is less (it scrolls). */\n height: number;\n side: 'below' | 'above';\n}\n\n/**\n * A drop-down list under its button (`anchor`), over it when the room below is short and there is more above; as\n * tall as it is or as the room on that side allows. `list.width` is already the measured `listWidth`.\n */\nexport function listPlacement(anchor: Rect, list: Size, viewport: Size, gap = 2, margin = 4): ListPlacement {\n const below = viewport.height - margin - (anchor.y + anchor.height + gap);\n const above = anchor.y - gap - margin;\n const height = Math.max(0, Math.min(list.height, Math.max(below, above)));\n const p = placeTooltip(anchor, { width: list.width, height }, viewport, { side: 'below', gap, margin, snap: true });\n return { x: p.x, y: p.y, height: Math.floor(height), side: p.side === 'above' ? 'above' : 'below' };\n}\n\n/**\n * A sprite of `w` × `h` art pixels at the largest **whole** multiple that fits the box (never past `maxScale`), or a\n * whole divisor when it is bigger than the box: `object-fit: contain` stretched a 26-px sprite by 2.77 and drew its\n * pixels two and three screen pixels wide by turns.\n */\nexport function pixelFit(w: number, h: number, boxW: number, boxH: number, maxScale = Infinity): Size & { scale: number } {\n if (!(w > 0) || !(h > 0)) return { width: 0, height: 0, scale: 1 };\n const k = Math.min(boxW / w, boxH / h, maxScale);\n const scale = k >= 1 ? Math.floor(k) : 1 / Math.ceil(1 / k);\n return { width: Math.round(w * scale), height: Math.round(h * scale), scale };\n}\n\n/** Below this width (CSS px) a touch screen shows one pane of a `SplitPane` at a time. */\nexport const SPLIT_STACK_BELOW = 600;\n\n/** Side by side, or one pane at a time: only a touch screen narrower than `stackBelow` stacks. */\nexport function splitModeFor(width: number, touch: boolean, stackBelow = SPLIT_STACK_BELOW): 'side' | 'stack' {\n return touch && width < stackBelow ? 'stack' : 'side';\n}\n"],"names":["windowStyle","wanted","viewport","options","fit","fitToViewport","style","sheetScaleStyle","scale","cursorTip","x","y","tip","anchor","placeTooltip","listPlacement","list","gap","margin","below","above","height","p","pixelFit","w","h","boxW","boxH","maxScale","k","SPLIT_STACK_BELOW","splitModeFor","width","touch","stackBelow"],"mappings":";;AAcO,SAASA,EAAYC,GAAqBC,GAAgBC,IAAsB,CAAA,GAA8C;AACnI,QAAMC,IAAMC,EAAcJ,GAAQC,GAAU,EAAE,MAAM,IAAM,GAAGC,GAAS,QAAQA,EAAQ,UAAU,EAAE,OAAOF,IAAS,GAC5GK,IAAuB;AAAA,IAC3B,MAAMF,EAAI;AAAA,IACV,KAAKA,EAAI;AAAA,IACT,UAAUA,EAAI;AAAA,IACd,WAAWA,EAAI;AAAA,IACf,WAAWA,EAAI,UAAU,IAAI,SAAY,SAASA,EAAI,KAAK;AAAA,IAC3D,iBAAiBA,EAAI,UAAU,IAAI,SAAY;AAAA,EAAA;AAEjD,SAAO,EAAE,KAAAA,GAAK,OAAAE,EAAA;AAChB;AAMO,SAASC,EAAgBC,GAA0C;AACxE,MAAI,EAAAA,MAAU,KAAK,EAAEA,IAAQ;AAC7B,WAAO,EAAE,OAAO,GAAG,MAAMA,CAAK,KAAK,QAAQ,GAAG,MAAMA,CAAK,KAAK,WAAW,SAASA,CAAK,KAAK,iBAAiB,MAAA;AAC/G;AAcO,SAASC,EAAUC,GAAWC,GAAWC,GAAWV,GAAgBC,IAA4B,IAAsB;AAC3H,QAAMU,IAAS,EAAE,GAAGH,KAAKP,EAAQ,WAAW,KAAK,GAAAQ,GAAG,OAAO,GAAG,QAAQ,EAAA;AACtE,SAAOG,EAAaD,GAAQD,GAAKV,GAAU;AAAA,IACzC,MAAMC,EAAQ,QAAQ,UAAU;AAAA,IAChC,KAAKA,EAAQ,OAAO;AAAA,IACpB,QAAQA,EAAQ,UAAU;AAAA,IAC1B,MAAMA,EAAQ,QAAQ;AAAA,EAAA,CACvB;AACH;AAcO,SAASY,EAAcF,GAAcG,GAAYd,GAAgBe,IAAM,GAAGC,IAAS,GAAkB;AAC1G,QAAMC,IAAQjB,EAAS,SAASgB,KAAUL,EAAO,IAAIA,EAAO,SAASI,IAC/DG,IAAQP,EAAO,IAAII,IAAMC,GACzBG,IAAS,KAAK,IAAI,GAAG,KAAK,IAAIL,EAAK,QAAQ,KAAK,IAAIG,GAAOC,CAAK,CAAC,CAAC,GAClEE,IAAIR,EAAaD,GAAQ,EAAE,OAAOG,EAAK,OAAO,QAAAK,EAAA,GAAUnB,GAAU,EAAE,MAAM,SAAS,KAAAe,GAAK,QAAAC,GAAQ,MAAM,IAAM;AAClH,SAAO,EAAE,GAAGI,EAAE,GAAG,GAAGA,EAAE,GAAG,QAAQ,KAAK,MAAMD,CAAM,GAAG,MAAMC,EAAE,SAAS,UAAU,UAAU,QAAA;AAC5F;AAOO,SAASC,EAASC,GAAWC,GAAWC,GAAcC,GAAcC,IAAW,OAAoC;AACxH,MAAI,EAAEJ,IAAI,MAAM,EAAEC,IAAI,GAAI,QAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,EAAA;AAC/D,QAAMI,IAAI,KAAK,IAAIH,IAAOF,GAAGG,IAAOF,GAAGG,CAAQ,GACzCpB,IAAQqB,KAAK,IAAI,KAAK,MAAMA,CAAC,IAAI,IAAI,KAAK,KAAK,IAAIA,CAAC;AAC1D,SAAO,EAAE,OAAO,KAAK,MAAML,IAAIhB,CAAK,GAAG,QAAQ,KAAK,MAAMiB,IAAIjB,CAAK,GAAG,OAAAA,EAAA;AACxE;AAGO,MAAMsB,IAAoB;AAG1B,SAASC,EAAaC,GAAeC,GAAgBC,IAAaJ,GAAqC;AAC5G,SAAOG,KAASD,IAAQE,IAAa,UAAU;AACjD;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useFillSheet.js","sources":["../../src/react/useFillSheet.ts"],"sourcesContent":["import { type RefObject, useEffect, useLayoutEffect } from 'react';\n\n/** Never shorter than this (CSS px), however much sits above it. */\nconst MIN_HEIGHT = 180;\n\n/**\n * Fills what is left of a touch sheet's scrolling body (`.e1681-panel-body`) below whatever sits above the element —\n * tabs, a note — so a list-and-detail fits without the body scrolling; a stylesheet's `100vh - 120px` guesses one\n * layout and scrolls in every other. Measured after every render and on every resize; with `fit` it caps the height\n * instead of setting it. Outside a panel body the stylesheet's height stands.\n */\nexport function useFillSheet(ref: RefObject<HTMLElement | null>, active: boolean, fit: boolean): void {\n const measure = () => {\n const el = ref.current;\n const body = el?.closest<HTMLElement>('.e1681-panel-body');\n if (!el || !body) return;\n const offset = el.getBoundingClientRect().top - body.getBoundingClientRect().top + body.scrollTop;\n const pad = Number.parseFloat(getComputedStyle(body).paddingBottom) || 0;\n const px = `${Math.max(MIN_HEIGHT, Math.floor(body.clientHeight - offset - pad))}px`;\n if (fit) el.style.maxHeight = px;\n else el.style.height = px;\n };\n useLayoutEffect(() => {\n if (active) measure();\n });\n useEffect(() => {\n if (!active) return;\n const body = ref.current?.closest<HTMLElement>('.e1681-panel-body');\n const ro = typeof ResizeObserver === 'undefined' || !body ? null : new ResizeObserver(() => measure());\n if (body) ro?.observe(body);\n window.addEventListener('resize', measure);\n return () => {\n ro?.disconnect();\n window.removeEventListener('resize', measure);\n };\n // measure reads the ref and `fit` fresh each time\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [active, fit]);\n}\n"],"names":["MIN_HEIGHT","useFillSheet","ref","active","fit","measure","el","body","offset","pad","px","useLayoutEffect","useEffect","ro"],"mappings":";AAGA,MAAMA,IAAa;AAQZ,SAASC,EAAaC,GAAoCC,GAAiBC,GAAoB;AACpG,QAAMC,IAAU,MAAM;AACpB,UAAMC,IAAKJ,EAAI,SACTK,IAAOD,GAAI,QAAqB,mBAAmB;AACzD,QAAI,CAACA,KAAM,CAACC,EAAM;AAClB,UAAMC,IAASF,EAAG,wBAAwB,MAAMC,EAAK,sBAAA,EAAwB,MAAMA,EAAK,WAClFE,IAAM,OAAO,WAAW,iBAAiBF,CAAI,EAAE,aAAa,KAAK,GACjEG,IAAK,GAAG,KAAK,IAAIV,GAAY,KAAK,MAAMO,EAAK,eAAeC,IAASC,CAAG,CAAC,CAAC;AAChF,IAAIL,IAAKE,EAAG,MAAM,YAAYI,IACzBJ,EAAG,MAAM,SAASI;AAAA,EACzB;AACA,EAAAC,EAAgB,MAAM;AACpB,IAAIR,KAAQE,EAAA;AAAA,EACd,CAAC,GACDO,EAAU,MAAM;AACd,QAAI,CAACT,EAAQ;AACb,UAAMI,IAAOL,EAAI,SAAS,QAAqB,mBAAmB,GAC5DW,IAAK,OAAO,iBAAmB,OAAe,CAACN,IAAO,OAAO,IAAI,eAAe,MAAMF,GAAS;AACrG,WAAIE,KAAMM,GAAI,QAAQN,CAAI,GAC1B,OAAO,iBAAiB,UAAUF,CAAO,GAClC,MAAM;AACX,MAAAQ,GAAI,WAAA,GACJ,OAAO,oBAAoB,UAAUR,CAAO;AAAA,IAC9C;AAAA,EAGF,GAAG,CAACF,GAAQC,CAAG,CAAC;AAClB;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useFitToViewport.js","sources":["../../src/react/useFitToViewport.ts"],"sourcesContent":["import { type RefObject, useEffect, useLayoutEffect } from 'react';\nimport type { Point } from '../ui/layout/geometry';\nimport { windowStyle } from './placement';\nimport { useUi } from './UiProvider';\n\nexport interface FitTarget {\n /** Off: the element is left to the stylesheet (a touch sheet). */\n enabled: boolean;\n /** The width it wants, in its own units; default: its laid-out width. */\n width?: number;\n /** Where it wants to stand (screen pixels); default: centred. */\n at?: Point;\n}\n\n/** The scrolling part of a window: what is cut when the window does not fit. */\nexport const SCROLL_CLASS = 'e1681-scroll';\n\n/**\n * Keeps a window or dialog inside the viewport (`windowStyle` → `fitToViewport`): measured after every render, on a\n * resize and when its content changes size, written straight onto the element's style so a placement never costs a\n * render. The natural height is the element's plus what its scrolling body (`.e1681-scroll`) hides, so the cap never\n * has to be lifted to measure, which would throw the body's scroll back to the top.\n */\nexport function useFitToViewport(ref: RefObject<HTMLElement | null>, target: FitTarget): void {\n const ui = useUi();\n const { enabled, width } = target;\n const atX = target.at?.x;\n const atY = target.at?.y;\n\n const place = () => {\n const el = ref.current;\n if (!el || !enabled) return;\n const body = el.querySelector<HTMLElement>(`.${SCROLL_CLASS}`);\n const hidden = body ? body.scrollHeight - body.clientHeight : 0;\n const wanted = {\n width: width ?? Math.max(el.offsetWidth, el.scrollWidth),\n height: el.offsetHeight + Math.max(0, hidden),\n ...(atX !== undefined && atY !== undefined ? { x: atX, y: atY } : {}),\n };\n const { style } = windowStyle(wanted, ui.viewport(), { scale: ui.scale, minScale: ui.minScale, margin: ui.margin, snap: ui.snap });\n const s = el.style;\n s.left = `${style.left}px`;\n s.top = `${style.top}px`;\n s.maxWidth = `${style.maxWidth}px`;\n s.maxHeight = `${style.maxHeight}px`;\n s.transform = (style.transform as string | undefined) ?? '';\n s.transformOrigin = (style.transformOrigin as string | undefined) ?? '';\n };\n\n useLayoutEffect(place);\n\n useEffect(() => {\n if (!enabled) return;\n const el = ref.current;\n const body = el?.querySelector<HTMLElement>(`.${SCROLL_CLASS}`);\n const ro = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(() => place());\n for (const child of Array.from(body?.children ?? [])) ro?.observe(child);\n window.addEventListener('resize', place);\n return () => {\n ro?.disconnect();\n window.removeEventListener('resize', place);\n };\n // `place` reads the ref and the settings fresh each time\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [enabled, width, atX, atY, ui]);\n}\n"],"names":["SCROLL_CLASS","useFitToViewport","ref","target","ui","useUi","enabled","width","atX","atY","place","el","body","hidden","wanted","style","windowStyle","s","useLayoutEffect","useEffect","ro","child"],"mappings":";;;AAeO,MAAMA,IAAe;AAQrB,SAASC,EAAiBC,GAAoCC,GAAyB;AAC5F,QAAMC,IAAKC,EAAA,GACL,EAAE,SAAAC,GAAS,OAAAC,EAAA,IAAUJ,GACrBK,IAAML,EAAO,IAAI,GACjBM,IAAMN,EAAO,IAAI,GAEjBO,IAAQ,MAAM;AAClB,UAAMC,IAAKT,EAAI;AACf,QAAI,CAACS,KAAM,CAACL,EAAS;AACrB,UAAMM,IAAOD,EAAG,cAA2B,IAAIX,CAAY,EAAE,GACvDa,IAASD,IAAOA,EAAK,eAAeA,EAAK,eAAe,GACxDE,IAAS;AAAA,MACb,OAAOP,KAAS,KAAK,IAAII,EAAG,aAAaA,EAAG,WAAW;AAAA,MACvD,QAAQA,EAAG,eAAe,KAAK,IAAI,GAAGE,CAAM;AAAA,MAC5C,GAAIL,MAAQ,UAAaC,MAAQ,SAAY,EAAE,GAAGD,GAAK,GAAGC,MAAQ,CAAA;AAAA,IAAC,GAE/D,EAAE,OAAAM,MAAUC,EAAYF,GAAQV,EAAG,SAAA,GAAY,EAAE,OAAOA,EAAG,OAAO,UAAUA,EAAG,UAAU,QAAQA,EAAG,QAAQ,MAAMA,EAAG,MAAM,GAC3Ha,IAAIN,EAAG;AACb,IAAAM,EAAE,OAAO,GAAGF,EAAM,IAAI,MACtBE,EAAE,MAAM,GAAGF,EAAM,GAAG,MACpBE,EAAE,WAAW,GAAGF,EAAM,QAAQ,MAC9BE,EAAE,YAAY,GAAGF,EAAM,SAAS,MAChCE,EAAE,YAAaF,EAAM,aAAoC,IACzDE,EAAE,kBAAmBF,EAAM,mBAA0C;AAAA,EACvE;AAEA,EAAAG,EAAgBR,CAAK,GAErBS,EAAU,MAAM;AACd,QAAI,CAACb,EAAS;AAEd,UAAMM,IADKV,EAAI,SACE,cAA2B,IAAIF,CAAY,EAAE,GACxDoB,IAAK,OAAO,iBAAmB,MAAc,OAAO,IAAI,eAAe,MAAMV,GAAO;AAC1F,eAAWW,KAAS,MAAM,KAAKT,GAAM,YAAY,CAAA,CAAE,EAAG,CAAAQ,GAAI,QAAQC,CAAK;AACvE,kBAAO,iBAAiB,UAAUX,CAAK,GAChC,MAAM;AACX,MAAAU,GAAI,WAAA,GACJ,OAAO,oBAAoB,UAAUV,CAAK;AAAA,IAC5C;AAAA,EAGF,GAAG,CAACJ,GAASC,GAAOC,GAAKC,GAAKL,CAAE,CAAC;AACnC;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"usePicked.js","sources":["../../src/react/usePicked.ts"],"sourcesContent":["import { useState } from 'react';\n\n/**\n * Which entry of a list is picked for a `SplitPane`'s detail, by key. While nothing is picked — or the picked entry\n * is gone (sold, used up) — the first one stands in, so the detail is never empty while the list is not; `auto: false`\n * leaves it empty instead (a letter must not be opened, and so marked read, without the player choosing it).\n */\nexport function usePicked<T>(items: readonly T[], key: (item: T) => string, auto = true): [T | null, (id: string | null) => void] {\n const [picked, setPicked] = useState<string | null>(null);\n return [pickedEntry(items, key, picked, auto), setPicked];\n}\n\n/** `usePicked`'s rule, pure. */\nexport function pickedEntry<T>(items: readonly T[], key: (item: T) => string, picked: string | null, auto = true): T | null {\n const found = picked === null ? undefined : items.find((it) => key(it) === picked);\n return found ?? (auto ? (items[0] ?? null) : null);\n}\n"],"names":["usePicked","items","key","auto","picked","setPicked","useState","pickedEntry","it"],"mappings":";AAOO,SAASA,EAAaC,GAAqBC,GAA0BC,IAAO,IAA+C;AAChI,QAAM,CAACC,GAAQC,CAAS,IAAIC,EAAwB,IAAI;AACxD,SAAO,CAACC,EAAYN,GAAOC,GAAKE,GAAQD,CAAI,GAAGE,CAAS;AAC1D;AAGO,SAASE,EAAeN,GAAqBC,GAA0BE,GAAuBD,IAAO,IAAgB;AAE1H,UADcC,MAAW,OAAO,SAAYH,EAAM,KAAK,CAACO,MAAON,EAAIM,CAAE,MAAMJ,CAAM,OAChED,IAAQF,EAAM,CAAC,KAAK,OAAQ;AAC/C;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useStore.js","sources":["../../src/react/useStore.ts"],"sourcesContent":["import { useCallback, useSyncExternalStore } from 'react';\nimport type { Store } from '../ui/dom/createStore';\n\n/**\n * Reads an engine `Store` (`createStore`) in a component and renders again when what it reads changes. `select`\n * must return something stable (a field, a number), not a new object each call, or React renders forever.\n */\nexport function useStore<T extends object>(store: Store<T>): T;\nexport function useStore<T extends object, S>(store: Store<T>, select: (state: T) => S): S;\nexport function useStore<T extends object, S>(store: Store<T>, select?: (state: T) => S): T | S {\n const get = useCallback(() => (select ? select(store.getState()) : store.getState()), [store, select]);\n return useSyncExternalStore(store.subscribe, get, get);\n}\n"],"names":["useStore","store","select","get","useCallback","useSyncExternalStore"],"mappings":";AASO,SAASA,EAA8BC,GAAiBC,GAAiC;AAC9F,QAAMC,IAAMC,EAAY,MAAOF,IAASA,EAAOD,EAAM,UAAU,IAAIA,EAAM,SAAA,GAAa,CAACA,GAAOC,CAAM,CAAC;AACrG,SAAOG,EAAqBJ,EAAM,WAAWE,GAAKA,CAAG;AACvD;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useViewport.js","sources":["../../src/react/useViewport.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport type { Size } from '../ui/layout/geometry';\nimport { useUi } from './UiProvider';\n\n/** The `UiProvider`'s viewport, read again on every resize (only while `active`). */\nexport function useViewport(active = true): Size {\n const ui = useUi();\n const [size, setSize] = useState<Size>(() => ui.viewport());\n useEffect(() => {\n if (!active) return;\n const on = () => {\n const next = ui.viewport();\n setSize((s) => (s.width === next.width && s.height === next.height ? s : next));\n };\n on();\n window.addEventListener('resize', on);\n return () => window.removeEventListener('resize', on);\n }, [active, ui]);\n return size;\n}\n"],"names":["useViewport","active","ui","useUi","size","setSize","useState","useEffect","on","next","s"],"mappings":";;AAKO,SAASA,EAAYC,IAAS,IAAY;AAC/C,QAAMC,IAAKC,EAAA,GACL,CAACC,GAAMC,CAAO,IAAIC,EAAe,MAAMJ,EAAG,UAAU;AAC1D,SAAAK,EAAU,MAAM;AACd,QAAI,CAACN,EAAQ;AACb,UAAMO,IAAK,MAAM;AACf,YAAMC,IAAOP,EAAG,SAAA;AAChB,MAAAG,EAAQ,CAACK,MAAOA,EAAE,UAAUD,EAAK,SAASC,EAAE,WAAWD,EAAK,SAASC,IAAID,CAAK;AAAA,IAChF;AACA,WAAAD,EAAA,GACA,OAAO,iBAAiB,UAAUA,CAAE,GAC7B,MAAM,OAAO,oBAAoB,UAAUA,CAAE;AAAA,EACtD,GAAG,CAACP,GAAQC,CAAE,CAAC,GACRE;AACT;"}
|
package/dist/react.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"react.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PosePlayer.js","sources":["../../src/rig/PosePlayer.ts"],"sourcesContent":["import { blendPose } from './poseMath';\nimport type { RigPose } from './rigTypes';\n\n/**\n * A clip of key poses: by name (looked up in the player's library) or inline. The player blends between them, so a\n * few keys make a smooth motion; `blend: false` steps from key to key instead (pixel-art timing, a tumble).\n */\nexport interface PoseClip {\n poses: readonly (string | RigPose)[];\n /** Keys per second (default 8). */\n fps?: number;\n /** Per-key lengths in ms, overriding `fps`. */\n durations?: readonly number[];\n /** Start over at the end, blending the last key into the first (default true). */\n loop?: boolean;\n /** Blend between keys (default true). */\n blend?: boolean;\n}\n\n/**\n * Plays pose clips over time and answers the pose of this moment, blended between keys, with a crossfade when the\n * clip changes. Pure: give it a library of poses and clips, call `update(dt)`, read `pose`.\n *\n * ```ts\n * const player = new PosePlayer(HUMANOID_POSES, HUMANOID_CLIPS, 'idle');\n * player.play('walk', { fadeMs: 120 });\n * player.update(dt);\n * view.setPose(player.pose);\n * ```\n */\nexport class PosePlayer<Name extends string = string> {\n private clip: PoseClip;\n private name: Name;\n private elapsed = 0;\n private ended = false;\n /** The pose being faded out of, and how long the fade lasts and has run. */\n private from: RigPose | null = null;\n private fadeMs = 0;\n private fadeAt = 0;\n private cached: RigPose | null = null;\n speed = 1;\n /** Called when a clip that does not loop reaches its end. */\n onComplete: ((clip: Name) => void) | null = null;\n\n constructor(\n private readonly library: Readonly<Record<string, RigPose>>,\n private readonly clips: Record<Name, PoseClip>,\n initial: NoInfer<Name>,\n ) {\n this.clip = this.clipOf(initial);\n this.name = initial;\n }\n\n get current(): Name {\n return this.name;\n }\n\n get finished(): boolean {\n return this.ended;\n }\n\n /** Time into the current clip, ms. */\n get time(): number {\n return this.elapsed;\n }\n\n /** Plays `name`, fading from the pose shown now over `fadeMs`. The clip already playing keeps going unless `restart`. */\n play(name: Name, options: { fadeMs?: number; restart?: boolean } = {}): this {\n if (name === this.name && !options.restart) return this;\n const fadeMs = options.fadeMs ?? 0;\n this.from = fadeMs > 0 ? this.pose : null;\n this.fadeMs = fadeMs;\n this.fadeAt = 0;\n this.clip = this.clipOf(name);\n this.name = name;\n this.elapsed = 0;\n this.ended = false;\n this.cached = null;\n return this;\n }\n\n /** Advances by `dt` seconds. */\n update(dt: number): void {\n if (!(dt > 0) || this.speed <= 0) return;\n const ms = dt * 1000 * this.speed;\n this.cached = null;\n if (this.from) {\n this.fadeAt += ms;\n if (this.fadeAt >= this.fadeMs) this.from = null;\n }\n if (this.ended) return;\n this.elapsed += ms;\n const total = this.totalMs();\n if (this.elapsed >= total) {\n if (this.clip.loop !== false) {\n this.elapsed %= total;\n } else {\n this.elapsed = total;\n this.ended = true;\n this.onComplete?.(this.name);\n }\n }\n }\n\n /** The pose to show now. */\n get pose(): RigPose {\n if (this.cached) return this.cached;\n let pose = this.sample(this.elapsed);\n if (this.from) pose = blendPose(this.from, pose, Math.min(1, this.fadeAt / this.fadeMs));\n this.cached = pose;\n return pose;\n }\n\n /** The current clip's pose at `ms` into it. */\n sample(ms: number): RigPose {\n const keys = this.clip.poses;\n const n = keys.length;\n let t = Math.max(0, ms);\n for (let i = 0; i < n; i++) {\n const len = this.lengthOf(i);\n if (t < len || i === n - 1) {\n const a = this.resolve(keys[i]!);\n const last = i === n - 1;\n if (this.clip.blend === false || n === 1 || (last && this.clip.loop === false)) return a;\n const b = this.resolve(keys[last ? 0 : i + 1]!);\n return blendPose(a, b, Math.min(1, t / len));\n }\n t -= len;\n }\n return this.resolve(keys[n - 1]!);\n }\n\n private totalMs(): number {\n let sum = 0;\n for (let i = 0; i < this.clip.poses.length; i++) sum += this.lengthOf(i);\n return sum;\n }\n\n private lengthOf(i: number): number {\n return Math.max(1, this.clip.durations?.[i] ?? 1000 / (this.clip.fps ?? 8));\n }\n\n private resolve(key: string | RigPose): RigPose {\n if (typeof key !== 'string') return key;\n const pose = this.library[key];\n if (!pose) throw new Error(`PosePlayer: no pose \"${key}\" in the library`);\n return pose;\n }\n\n private clipOf(name: Name): PoseClip {\n const clip = this.clips[name];\n if (!clip) throw new Error(`PosePlayer: no clip \"${name}\"`);\n if (clip.poses.length === 0) throw new Error(`PosePlayer: clip \"${name}\" has no poses`);\n for (const k of clip.poses) this.resolve(k);\n return clip;\n }\n}\n"],"names":["PosePlayer","library","clips","initial","name","options","fadeMs","dt","ms","total","pose","blendPose","keys","n","t","len","a","last","b","sum","i","key","clip","k"],"mappings":"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"}
|
package/dist/rig/RigView.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"RigView.js","sources":["../../src/rig/RigView.ts"],"sourcesContent":["import { Container, Graphics, Sprite } from 'pixi.js';\nimport type { TextureRegistry } from '../core/TextureRegistry';\nimport { Skeleton } from './Skeleton';\nimport type { Joints, Point, RigDef, RigPose } from './rigTypes';\n\n/** The parts a humanoid rig is drawn with, back to front in this order. */\nexport const RIG_PARTS = ['upperArmB', 'forearmB', 'thighB', 'shinB', 'torso', 'thighF', 'shinF', 'head', 'upperArmF', 'forearmF'] as const;\nexport type RigPart = (typeof RIG_PARTS)[number];\n\n/**\n * How one part looks. A textured part is an image drawn with its top-centre on the bone's root joint and turned to\n * follow the bone (cut-out animation, any art style); a vector part is a capsule (limbs), a rounded box (torso) or a\n * circle (head) in `color`.\n */\nexport interface PartSkin {\n /** Texture key (and frame) in the stage's registry; leave out for a vector part. */\n texture?: string;\n frame?: string | number;\n /** Stretch the image along the bone to the next joint instead of drawing it at its own size. */\n stretch?: boolean;\n /** Where on the image the root joint sits, as a share of its size (default 0.5, 0). */\n anchorX?: number;\n anchorY?: number;\n /** Vector fill (default by part). */\n color?: number;\n /** Vector thickness in grid px (default the rig's `limbWidth`; the torso's width; the head's diameter). */\n width?: number;\n /** Vector outline colour, or null for none (default a dark outline). */\n outline?: number | null;\n}\n\nexport type RigSkin = Partial<Record<RigPart, PartSkin>>;\n\nexport interface RigViewOptions {\n /** World px per grid px (default 1). */\n scale?: number;\n /** Head radius in grid px (default 5) and how far above the chest point its centre sits (default 5.5). */\n headRadius?: number;\n headRise?: number;\n /** A broader build: shoulders set wider. */\n bulky?: boolean;\n}\n\nconst DEFAULT_COLORS: Record<RigPart, number> = {\n upperArmB: 0x6a90d0,\n forearmB: 0xf0c8a0,\n thighB: 0x3a3048,\n shinB: 0x3a3048,\n torso: 0x4a6fb0,\n thighF: 0x3a3048,\n shinF: 0x3a3048,\n head: 0xf0c8a0,\n upperArmF: 0x6a90d0,\n forearmF: 0xf0c8a0,\n};\n\nconst BACK = new Set<RigPart>(['upperArmB', 'forearmB', 'thighB', 'shinB']);\n\n/** Which joints each limb part runs between. */\nconst BONES: Record<Exclude<RigPart, 'torso' | 'head'>, [keyof Joints, keyof Joints]> = {\n upperArmB: ['shoulderB', 'elbowB'],\n forearmB: ['elbowB', 'wristB'],\n thighB: ['hipB', 'kneeB'],\n shinB: ['kneeB', 'ankleB'],\n thighF: ['hipF', 'kneeF'],\n shinF: ['kneeF', 'ankleF'],\n upperArmF: ['shoulderF', 'elbowF'],\n forearmF: ['elbowF', 'wristF'],\n};\n\nfunction shadeOf(color: number, k: number): number {\n const r = Math.round(((color >> 16) & 255) * k);\n const g = Math.round(((color >> 8) & 255) * k);\n const b = Math.round((color & 255) * k);\n return (r << 16) | (g << 8) | b;\n}\n\n/**\n * A character drawn from a rig pose: give it a pose each frame (`setPose`, from a `PosePlayer`) and it places every\n * part. The container's origin is the character's feet (bottom centre of the rig's frame); `facing` mirrors it.\n */\nexport class RigView extends Container {\n private readonly skeleton: Skeleton;\n private readonly sprites = new Map<RigPart, Sprite>();\n private readonly layers = new Map<RigPart, Container>();\n private joints: Joints | null = null;\n private curl: [number, number] | undefined;\n private readonly scaleGrid: number;\n private readonly headRadius: number;\n private readonly headRise: number;\n private readonly bulky: boolean;\n\n constructor(\n private readonly def: RigDef,\n private readonly skin: RigSkin = {},\n textures?: TextureRegistry,\n options: RigViewOptions = {},\n ) {\n super();\n this.skeleton = new Skeleton(def);\n this.scaleGrid = options.scale ?? 1;\n this.headRadius = options.headRadius ?? 5;\n this.headRise = options.headRise ?? 5.5;\n this.bulky = options.bulky === true;\n // one container per part keeps the back-to-front order whatever mixes sprites and vectors\n const body = new Container();\n body.position.set(-def.width / 2, -def.height);\n this.addChild(body);\n for (const part of RIG_PARTS) {\n const layer = new Container();\n this.layers.set(part, layer);\n body.addChild(layer);\n const s = skin[part];\n if (s?.texture && textures) {\n const sprite = new Sprite(textures.get(s.texture, s.frame));\n sprite.anchor.set(s.anchorX ?? 0.5, s.anchorY ?? 0);\n // images are drawn at the world's scale, while the rig works in grid px\n sprite.scale.set(1 / this.scaleGrid);\n this.sprites.set(part, sprite);\n layer.addChild(sprite);\n }\n }\n // vector parts are drawn into a graphics in their own layer on each pose\n this.scale.set(this.scaleGrid);\n }\n\n /** 1 faces right, -1 faces left. */\n set facing(dir: 1 | -1) {\n this.scale.x = this.scaleGrid * dir;\n }\n\n get facing(): 1 | -1 {\n return this.scale.x < 0 ? -1 : 1;\n }\n\n /** The joints of the last pose, in grid px of the rig's frame (for attaching a held item, an effect). */\n get lastJoints(): Joints | null {\n return this.joints;\n }\n\n /** Where a joint of the last pose is in this view's parent, in world px. */\n jointInParent(name: keyof Omit<Joints, 'forearm'>): Point | null {\n if (!this.joints) return null;\n const j = this.joints[name];\n return { x: this.x + (j.x - this.def.width / 2) * this.scale.x, y: this.y + (j.y - this.def.height) * this.scale.y };\n }\n\n setPose(pose: RigPose): this {\n const j = this.skeleton.solve(pose, this.bulky);\n this.joints = j;\n this.curl = pose.curl;\n for (const part of RIG_PARTS) this.place(part, j);\n return this;\n }\n\n override destroy(options?: Parameters<Container['destroy']>[0]): void {\n super.destroy(options ?? { children: true });\n }\n\n private place(part: RigPart, j: Joints): void {\n const layer = this.layers.get(part)!;\n const skin = this.skin[part] ?? {};\n const sprite = this.sprites.get(part);\n const [a, b] = this.ends(part, j);\n const angle = Math.atan2(b.y - a.y, b.x - a.x);\n if (sprite) {\n sprite.position.set(a.x, a.y);\n sprite.rotation = angle - Math.PI / 2;\n if (skin.stretch && sprite.texture.height > 0) sprite.scale.y = Math.hypot(b.x - a.x, b.y - a.y) / sprite.texture.height;\n return;\n }\n let g = layer.children[0] as Graphics | undefined;\n if (!g) {\n g = new Graphics();\n layer.addChild(g);\n }\n g.clear();\n const base = skin.color ?? DEFAULT_COLORS[part];\n const color = BACK.has(part) ? shadeOf(base, part.endsWith('ArmB') || part === 'forearmB' ? this.def.backArmShade : this.def.backShade) : base;\n const outline = skin.outline === undefined ? 0x1a1424 : skin.outline;\n const stroke = outline === null ? null : { color: outline, width: 0.6 };\n if (part === 'head') {\n const r = skin.width !== undefined ? skin.width / 2 : this.headRadius;\n g.circle(a.x, a.y, r).fill(color);\n if (stroke) g.stroke(stroke);\n return;\n }\n if (part === 'torso') {\n const w = skin.width ?? Math.abs(this.def.shoulderF[0] - this.def.shoulderB[0]) + 1;\n if (this.curl) {\n g.circle(this.curl[0], this.curl[1], w / 2 + 1).fill(color);\n } else {\n const len = Math.hypot(b.x - a.x, b.y - a.y);\n g.roundRect(-w / 2, -0.5, w, len + 1, Math.min(2, w / 3)).fill(color);\n g.position.set(a.x, a.y);\n g.rotation = angle - Math.PI / 2;\n if (stroke) g.stroke(stroke);\n return;\n }\n g.position.set(0, 0);\n g.rotation = 0;\n if (stroke) g.stroke(stroke);\n return;\n }\n const w = skin.width ?? this.def.limbWidth;\n g.position.set(0, 0);\n g.rotation = 0;\n // the outline is a wider line in its colour drawn first, the limb over it\n if (stroke) g.moveTo(a.x, a.y).lineTo(b.x, b.y).stroke({ color: stroke.color, width: w + stroke.width * 2, cap: 'round' });\n g.moveTo(a.x, a.y).lineTo(b.x, b.y).stroke({ color, width: w, cap: 'round' });\n }\n\n /** The two points a part spans: a limb's joints, the torso from chest to hips, the head's centre (twice). */\n private ends(part: RigPart, j: Joints): [Point, Point] {\n if (part === 'head') {\n const c = { x: j.head.x, y: j.head.y - this.headRise };\n return [c, { x: c.x, y: c.y + 1 }];\n }\n if (part === 'torso') return [j.chest, { x: (j.hipF.x + j.hipB.x) / 2, y: (j.hipF.y + j.hipB.y) / 2 }];\n const [from, to] = BONES[part];\n return [j[from] as Point, j[to] as Point];\n }\n}\n"],"names":["RIG_PARTS","DEFAULT_COLORS","BACK","BONES","shadeOf","color","k","r","g","b","RigView","Container","def","skin","textures","options","Skeleton","body","part","layer","s","sprite","Sprite","dir","name","j","pose","a","angle","Graphics","base","outline","stroke","w","len","c","from","to"],"mappings":"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"}
|
package/dist/rig/Skeleton.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Skeleton.js","sources":["../../src/rig/Skeleton.ts"],"sourcesContent":["import type { Joints, Limb, Point, RigDef, RigPose } from './rigTypes';\n\nconst RAD = Math.PI / 180;\nconst DOWN: Limb = [90, 2, 0, 2];\n\nfunction add(p: Point, dx: number, dy: number): Point {\n return { x: p.x + dx, y: p.y + dy };\n}\n\n/** Elbow and wrist (or knee and ankle) of a limb hung from `root`. */\nfunction chain(root: Point, limb: Limb): [Point, Point, number] {\n const [a1, l1, a2, l2] = limb;\n const upper = a1 * RAD;\n const lower = (a1 + a2) * RAD;\n const mid = add(root, Math.cos(upper) * l1, Math.sin(upper) * l1);\n return [mid, add(mid, Math.cos(lower) * l2, Math.sin(lower) * l2), lower];\n}\n\n/** Turns a pose (bone angles and lengths) into joint positions on the character's 20x24 grid. */\nexport class Skeleton {\n private readonly def: RigDef;\n\n constructor(def: RigDef) {\n this.def = def;\n }\n\n solve(pose: RigPose, bulky = false): Joints {\n const d = this.def;\n const wide = bulky ? d.bulkyShoulder : 0;\n const chest = { x: d.chest[0], y: d.chest[1] + (pose.by ?? 0) };\n const shoulderF = add(chest, d.shoulderF[0] + wide, d.shoulderF[1]);\n const shoulderB = add(chest, d.shoulderB[0] - wide, d.shoulderB[1]);\n const hx = pose.hipDx ?? 0;\n const hy = pose.hipDy ?? 0;\n const hipF = { x: d.hipF[0] + hx, y: d.hipF[1] + hy };\n const hipB = { x: d.hipB[0] + hx, y: d.hipB[1] + hy };\n const [elbowF, wristF, forearm] = this.frontArm(shoulderF, pose.armF ?? DOWN);\n const [elbowB, wristB] = chain(shoulderB, pose.armB ?? DOWN);\n const [kneeF, ankleF] = chain(hipF, pose.legF ?? DOWN);\n const [kneeB, ankleB] = chain(hipB, pose.legB ?? DOWN);\n const head = add(chest, pose.headDx ?? 0, pose.headDy ?? 0);\n return { chest, head, shoulderF, elbowF, wristF, shoulderB, elbowB, wristB, hipF, kneeF, ankleF, hipB, kneeB, ankleB, forearm };\n }\n\n /**\n * Elbow, wrist and forearm direction of the front arm (or of its motion smear) hung from `shoulder`. The fist keeps\n * the frame's last column for its outline: an arm reaching past that (a swing, a thrust, a draw on a bulky body) is\n * pulled back whole, so the forearm — and the weapon laid along it — keeps its angle.\n */\n frontArm(shoulder: Point, limb: Limb): [Point, Point, number] {\n const d = this.def;\n const [elbow, wrist, forearm] = chain(shoulder, limb);\n const over = Math.max(0, wrist.x - (d.width - d.limbWidth - 1));\n return [add(elbow, -over, 0), add(wrist, -over, 0), forearm];\n }\n}\n"],"names":["RAD","DOWN","add","p","dx","dy","chain","root","limb","a1","l1","a2","l2","upper","lower","mid","Skeleton","def","pose","bulky","d","wide","chest","shoulderF","shoulderB","hx","hy","hipF","hipB","elbowF","wristF","forearm","elbowB","wristB","kneeF","ankleF","kneeB","ankleB","head","shoulder","elbow","wrist","over"],"mappings":"AAEA,MAAMA,IAAM,KAAK,KAAK,KAChBC,IAAa,CAAC,IAAI,GAAG,GAAG,CAAC;AAE/B,SAASC,EAAIC,GAAUC,GAAYC,GAAmB;AACpD,SAAO,EAAE,GAAGF,EAAE,IAAIC,GAAI,GAAGD,EAAE,IAAIE,EAAA;AACjC;AAGA,SAASC,EAAMC,GAAaC,GAAoC;AAC9D,QAAM,CAACC,GAAIC,GAAIC,GAAIC,CAAE,IAAIJ,GACnBK,IAAQJ,IAAKT,GACbc,KAASL,IAAKE,KAAMX,GACpBe,IAAMb,EAAIK,GAAM,KAAK,IAAIM,CAAK,IAAIH,GAAI,KAAK,IAAIG,CAAK,IAAIH,CAAE;AAChE,SAAO,CAACK,GAAKb,EAAIa,GAAK,KAAK,IAAID,CAAK,IAAIF,GAAI,KAAK,IAAIE,CAAK,IAAIF,CAAE,GAAGE,CAAK;AAC1E;AAGO,MAAME,EAAS;AAAA,EACH;AAAA,EAEjB,YAAYC,GAAa;AACvB,SAAK,MAAMA;AAAA,EACb;AAAA,EAEA,MAAMC,GAAeC,IAAQ,IAAe;AAC1C,UAAMC,IAAI,KAAK,KACTC,IAAOF,IAAQC,EAAE,gBAAgB,GACjCE,IAAQ,EAAE,GAAGF,EAAE,MAAM,CAAC,GAAG,GAAGA,EAAE,MAAM,CAAC,KAAKF,EAAK,MAAM,GAAA,GACrDK,IAAYrB,EAAIoB,GAAOF,EAAE,UAAU,CAAC,IAAIC,GAAMD,EAAE,UAAU,CAAC,CAAC,GAC5DI,IAAYtB,EAAIoB,GAAOF,EAAE,UAAU,CAAC,IAAIC,GAAMD,EAAE,UAAU,CAAC,CAAC,GAC5DK,IAAKP,EAAK,SAAS,GACnBQ,IAAKR,EAAK,SAAS,GACnBS,IAAO,EAAE,GAAGP,EAAE,KAAK,CAAC,IAAIK,GAAI,GAAGL,EAAE,KAAK,CAAC,IAAIM,EAAA,GAC3CE,IAAO,EAAE,GAAGR,EAAE,KAAK,CAAC,IAAIK,GAAI,GAAGL,EAAE,KAAK,CAAC,IAAIM,EAAA,GAC3C,CAACG,GAAQC,GAAQC,CAAO,IAAI,KAAK,SAASR,GAAWL,EAAK,QAAQjB,CAAI,GACtE,CAAC+B,GAAQC,CAAM,IAAI3B,EAAMkB,GAAWN,EAAK,QAAQjB,CAAI,GACrD,CAACiC,GAAOC,CAAM,IAAI7B,EAAMqB,GAAMT,EAAK,QAAQjB,CAAI,GAC/C,CAACmC,GAAOC,CAAM,IAAI/B,EAAMsB,GAAMV,EAAK,QAAQjB,CAAI,GAC/CqC,IAAOpC,EAAIoB,GAAOJ,EAAK,UAAU,GAAGA,EAAK,UAAU,CAAC;AAC1D,WAAO,EAAE,OAAAI,GAAO,MAAAgB,GAAM,WAAAf,GAAW,QAAAM,GAAQ,QAAAC,GAAQ,WAAAN,GAAW,QAAAQ,GAAQ,QAAAC,GAAQ,MAAAN,GAAM,OAAAO,GAAO,QAAAC,GAAQ,MAAAP,GAAM,OAAAQ,GAAO,QAAAC,GAAQ,SAAAN,EAAA;AAAA,EACxH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAASQ,GAAiB/B,GAAoC;AAC5D,UAAMY,IAAI,KAAK,KACT,CAACoB,GAAOC,GAAOV,CAAO,IAAIzB,EAAMiC,GAAU/B,CAAI,GAC9CkC,IAAO,KAAK,IAAI,GAAGD,EAAM,KAAKrB,EAAE,QAAQA,EAAE,YAAY,EAAE;AAC9D,WAAO,CAAClB,EAAIsC,GAAO,CAACE,GAAM,CAAC,GAAGxC,EAAIuC,GAAO,CAACC,GAAM,CAAC,GAAGX,CAAO;AAAA,EAC7D;AACF;"}
|
package/dist/rig/humanoid.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"humanoid.js","sources":["../../src/rig/humanoid.ts"],"sourcesContent":["import type { RigDef } from './rigTypes';\nimport def from './humanoid.json';\n\n/** The proportions of the built-in 20×24 humanoid: chest, shoulders, hips and limb width. */\nexport const HUMANOID_RIG: RigDef = def as RigDef;\n"],"names":["HUMANOID_RIG","def"],"mappings":";AAIO,MAAMA,IAAuBC;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"humanoid.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"humanoidPoses.js","sources":["../../src/rig/humanoidPoses.ts"],"sourcesContent":["import type { RigPose } from './rigTypes';\nimport poses from './poses.json';\nimport type { PoseClip } from './PosePlayer';\n\n/**\n * The built-in humanoid's pose library: 72 poses for `HUMANOID_RIG`, made for a 20×24 character facing right, and\n * clips that string them into motion. Use them as they are, mix them (`overlay`, `upperOver`), or as the starting\n * point for your own.\n */\nexport const HUMANOID_POSES: Readonly<Record<string, RigPose>> = poses as unknown as Record<string, RigPose>;\n\nconst seq = (prefix: string, n: number) => Array.from({ length: n }, (_, i) => `${prefix}${i}`);\n\n/** Ready-made clips over `HUMANOID_POSES`. Looping clips blend from their last pose back into the first. */\nexport const HUMANOID_CLIPS = {\n idle: { poses: seq('BREATHE', 6), fps: 6 },\n walk: { poses: seq('STRIDE', 6), fps: 10 },\n run: { poses: seq('JOG', 8), fps: 14 },\n roll: { poses: seq('ROLL', 12), fps: 30, loop: false, blend: false },\n dash: { poses: ['DASH'], fps: 1, loop: false },\n hurt: { poses: ['HURT', 'BREATHE0'], fps: 5, loop: false },\n dizzy: { poses: ['DIZZY', 'BREATHE0'], fps: 2 },\n crouch: { poses: ['CROUCH'], fps: 1, loop: false },\n kneel: { poses: ['KNEEL'], fps: 1, loop: false },\n sit: { poses: ['SIT'], fps: 1, loop: false },\n sitGround: { poses: ['SIT_GROUND'], fps: 1, loop: false },\n pick: { poses: ['CROUCH', 'PICK', 'CROUCH'], fps: 6, loop: false },\n lookBack: { poses: ['LOOK_BACK'], fps: 1, loop: false },\n stretch: { poses: ['BREATHE0', 'STRETCH', 'STRETCH', 'BREATHE0'], fps: 3, loop: false },\n cheer: { poses: ['BREATHE0', 'CHEER', 'CHEER'], fps: 6, loop: false },\n wave: { poses: ['WAVE_A', 'WAVE_B'], fps: 4 },\n dance: { poses: ['DANCE_A', 'DANCE_B'], fps: 3 },\n slash: { poses: ['SLASH_READY', 'SLASH_WIND', 'SLASH_SWEEP', 'SLASH_FOLLOW', 'SLASH_END', 'SLASH_RECOVER'], durations: [60, 90, 40, 60, 90, 120], loop: false },\n thrust: { poses: ['THRUST_READY', 'THRUST_LUNGE', 'THRUST_MID', 'THRUST_END', 'THRUST_RECOVER'], durations: [80, 50, 60, 100, 120], loop: false },\n overhead: { poses: ['OVERHEAD_READY', 'OVERHEAD', 'OVERHEAD_MID', 'OVERHEAD_FALL'], durations: [90, 140, 50, 160], loop: false },\n cast: { poses: ['CAST_CHARGE', 'CAST', 'CAST_RELEASE'], durations: [220, 120, 200], loop: false },\n throw: { poses: ['THROW_READY', 'THROW_WIND', 'THROW_RELEASE', 'THROW_MID', 'THROW_RECOVER'], durations: [70, 110, 40, 70, 120], loop: false },\n} satisfies Record<string, PoseClip>;\n\nexport type HumanoidClip = keyof typeof HUMANOID_CLIPS;\n"],"names":["HUMANOID_POSES","poses","seq","prefix","n","_","i","HUMANOID_CLIPS"],"mappings":";AASO,MAAMA,IAAoDC,GAE3DC,IAAM,CAACC,GAAgBC,MAAc,MAAM,KAAK,EAAE,QAAQA,EAAA,GAAK,CAACC,GAAGC,MAAM,GAAGH,CAAM,GAAGG,CAAC,EAAE,GAGjFC,IAAiB;AAAA,EAC5B,MAAM,EAAE,OAAOL,EAAI,WAAW,CAAC,GAAG,KAAK,EAAA;AAAA,EACvC,MAAM,EAAE,OAAOA,EAAI,UAAU,CAAC,GAAG,KAAK,GAAA;AAAA,EACtC,KAAK,EAAE,OAAOA,EAAI,OAAO,CAAC,GAAG,KAAK,GAAA;AAAA,EAClC,MAAM,EAAE,OAAOA,EAAI,QAAQ,EAAE,GAAG,KAAK,IAAI,MAAM,IAAO,OAAO,GAAA;AAAA,EAC7D,MAAM,EAAE,OAAO,CAAC,MAAM,GAAG,KAAK,GAAG,MAAM,GAAA;AAAA,EACvC,MAAM,EAAE,OAAO,CAAC,QAAQ,UAAU,GAAG,KAAK,GAAG,MAAM,GAAA;AAAA,EACnD,OAAO,EAAE,OAAO,CAAC,SAAS,UAAU,GAAG,KAAK,EAAA;AAAA,EAC5C,QAAQ,EAAE,OAAO,CAAC,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAA;AAAA,EAC3C,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,KAAK,GAAG,MAAM,GAAA;AAAA,EACzC,KAAK,EAAE,OAAO,CAAC,KAAK,GAAG,KAAK,GAAG,MAAM,GAAA;AAAA,EACrC,WAAW,EAAE,OAAO,CAAC,YAAY,GAAG,KAAK,GAAG,MAAM,GAAA;AAAA,EAClD,MAAM,EAAE,OAAO,CAAC,UAAU,QAAQ,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAA;AAAA,EAC3D,UAAU,EAAE,OAAO,CAAC,WAAW,GAAG,KAAK,GAAG,MAAM,GAAA;AAAA,EAChD,SAAS,EAAE,OAAO,CAAC,YAAY,WAAW,WAAW,UAAU,GAAG,KAAK,GAAG,MAAM,GAAA;AAAA,EAChF,OAAO,EAAE,OAAO,CAAC,YAAY,SAAS,OAAO,GAAG,KAAK,GAAG,MAAM,GAAA;AAAA,EAC9D,MAAM,EAAE,OAAO,CAAC,UAAU,QAAQ,GAAG,KAAK,EAAA;AAAA,EAC1C,OAAO,EAAE,OAAO,CAAC,WAAW,SAAS,GAAG,KAAK,EAAA;AAAA,EAC7C,OAAO,EAAE,OAAO,CAAC,eAAe,cAAc,eAAe,gBAAgB,aAAa,eAAe,GAAG,WAAW,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,MAAM,GAAA;AAAA,EACxJ,QAAQ,EAAE,OAAO,CAAC,gBAAgB,gBAAgB,cAAc,cAAc,gBAAgB,GAAG,WAAW,CAAC,IAAI,IAAI,IAAI,KAAK,GAAG,GAAG,MAAM,GAAA;AAAA,EAC1I,UAAU,EAAE,OAAO,CAAC,kBAAkB,YAAY,gBAAgB,eAAe,GAAG,WAAW,CAAC,IAAI,KAAK,IAAI,GAAG,GAAG,MAAM,GAAA;AAAA,EACzH,MAAM,EAAE,OAAO,CAAC,eAAe,QAAQ,cAAc,GAAG,WAAW,CAAC,KAAK,KAAK,GAAG,GAAG,MAAM,GAAA;AAAA,EAC1F,OAAO,EAAE,OAAO,CAAC,eAAe,cAAc,iBAAiB,aAAa,eAAe,GAAG,WAAW,CAAC,IAAI,KAAK,IAAI,IAAI,GAAG,GAAG,MAAM,GAAA;AACzI;"}
|
package/dist/rig/poseMath.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"poseMath.js","sources":["../../src/rig/poseMath.ts"],"sourcesContent":["import type { Limb, RigPose } from './rigTypes';\n\nfunction lerp(a: number, b: number, k: number): number {\n return a + (b - a) * k;\n}\n\n/** Shortest turn from angle a to b, in degrees. */\nfunction lerpDeg(a: number, b: number, k: number): number {\n let d = (b - a) % 360;\n if (d > 180) d -= 360;\n if (d < -180) d += 360;\n return a + d * k;\n}\n\nfunction lerpLimb(a: Limb | undefined, b: Limb | undefined, k: number): Limb | undefined {\n if (!a || !b) return k < 1 ? a ?? b : b ?? a;\n return [lerpDeg(a[0], b[0], k), lerp(a[1], b[1], k), lerpDeg(a[2], b[2], k), lerp(a[3], b[3], k)];\n}\n\nfunction lerpOpt(a: number | undefined, b: number | undefined, k: number): number | undefined {\n if (a === undefined && b === undefined) return undefined;\n return lerp(a ?? 0, b ?? 0, k);\n}\n\n/**\n * The in-between of two poses at `k` (0 = a, 1 = b): bones turn along the shortest way and stretch, offsets slide.\n * What cannot be in between (face, cape trail, the curled torso) stays `a`'s until `b` is reached.\n */\nexport function blendPose(a: RigPose, b: RigPose, k: number): RigPose {\n if (k <= 0) return a;\n if (k >= 1) return b;\n const discrete = a;\n return {\n ...discrete,\n by: lerpOpt(a.by, b.by, k),\n armF: lerpLimb(a.armF, b.armF, k),\n armB: lerpLimb(a.armB, b.armB, k),\n legF: lerpLimb(a.legF, b.legF, k),\n legB: lerpLimb(a.legB, b.legB, k),\n hipDx: lerpOpt(a.hipDx, b.hipDx, k),\n hipDy: lerpOpt(a.hipDy, b.hipDy, k),\n headDx: lerpOpt(a.headDx, b.headDx, k),\n headDy: lerpOpt(a.headDy, b.headDy, k),\n smearArm: a.smearArm,\n curl: a.curl && b.curl ? [lerp(a.curl[0], b.curl[0], k), lerp(a.curl[1], b.curl[1], k)] : a.curl,\n };\n}\n\n/** `top` laid over `base`: every field `top` sets wins. */\nexport function overlay(base: RigPose, top: RigPose): RigPose {\n const out: RigPose = { ...base };\n for (const [key, value] of Object.entries(top)) if (value !== undefined) (out as Record<string, unknown>)[key] = value;\n return out;\n}\n\n/**\n * An action's upper body over a moving (or seated) body: the arms, head and face act while the legs keep the gait.\n * The chest takes both bobs, but rises at most a pixel so a tall hat keeps its headroom.\n */\nexport function upperOver(action: RigPose, legs: RigPose): RigPose {\n return {\n ...action,\n by: Math.max(-1, (action.by ?? 0) + (legs.by ?? 0)),\n legF: legs.legF,\n legB: legs.legB,\n hipDx: legs.hipDx,\n hipDy: legs.hipDy,\n // the far arm swings with the run, unless it holds the bow the action draws\n armB: action.armBFront ? action.armB : legs.armB,\n trail: legs.trail ?? action.trail,\n };\n}\n"],"names":["lerp","a","b","k","lerpDeg","d","lerpLimb","lerpOpt","blendPose","overlay","base","top","out","key","value","upperOver","action","legs"],"mappings":"AAEA,SAASA,EAAKC,GAAWC,GAAWC,GAAmB;AACrD,SAAOF,KAAKC,IAAID,KAAKE;AACvB;AAGA,SAASC,EAAQH,GAAWC,GAAWC,GAAmB;AACxD,MAAIE,KAAKH,IAAID,KAAK;AAClB,SAAII,IAAI,QAAKA,KAAK,MACdA,IAAI,SAAMA,KAAK,MACZJ,IAAII,IAAIF;AACjB;AAEA,SAASG,EAASL,GAAqBC,GAAqBC,GAA6B;AACvF,SAAI,CAACF,KAAK,CAACC,IAAUC,IAAI,IAAIF,KAAKC,IAAIA,KAAKD,IACpC,CAACG,EAAQH,EAAE,CAAC,GAAGC,EAAE,CAAC,GAAGC,CAAC,GAAGH,EAAKC,EAAE,CAAC,GAAGC,EAAE,CAAC,GAAGC,CAAC,GAAGC,EAAQH,EAAE,CAAC,GAAGC,EAAE,CAAC,GAAGC,CAAC,GAAGH,EAAKC,EAAE,CAAC,GAAGC,EAAE,CAAC,GAAGC,CAAC,CAAC;AAClG;AAEA,SAASI,EAAQN,GAAuBC,GAAuBC,GAA+B;AAC5F,MAAI,EAAAF,MAAM,UAAaC,MAAM;AAC7B,WAAOF,EAAKC,KAAK,GAAGC,KAAK,GAAGC,CAAC;AAC/B;AAMO,SAASK,EAAUP,GAAYC,GAAYC,GAAoB;AACpE,SAAIA,KAAK,IAAUF,IACfE,KAAK,IAAUD,IAEZ;AAAA,IACL,GAFeD;AAAA,IAGf,IAAIM,EAAQN,EAAE,IAAIC,EAAE,IAAIC,CAAC;AAAA,IACzB,MAAMG,EAASL,EAAE,MAAMC,EAAE,MAAMC,CAAC;AAAA,IAChC,MAAMG,EAASL,EAAE,MAAMC,EAAE,MAAMC,CAAC;AAAA,IAChC,MAAMG,EAASL,EAAE,MAAMC,EAAE,MAAMC,CAAC;AAAA,IAChC,MAAMG,EAASL,EAAE,MAAMC,EAAE,MAAMC,CAAC;AAAA,IAChC,OAAOI,EAAQN,EAAE,OAAOC,EAAE,OAAOC,CAAC;AAAA,IAClC,OAAOI,EAAQN,EAAE,OAAOC,EAAE,OAAOC,CAAC;AAAA,IAClC,QAAQI,EAAQN,EAAE,QAAQC,EAAE,QAAQC,CAAC;AAAA,IACrC,QAAQI,EAAQN,EAAE,QAAQC,EAAE,QAAQC,CAAC;AAAA,IACrC,UAAUF,EAAE;AAAA,IACZ,MAAMA,EAAE,QAAQC,EAAE,OAAO,CAACF,EAAKC,EAAE,KAAK,CAAC,GAAGC,EAAE,KAAK,CAAC,GAAGC,CAAC,GAAGH,EAAKC,EAAE,KAAK,CAAC,GAAGC,EAAE,KAAK,CAAC,GAAGC,CAAC,CAAC,IAAIF,EAAE;AAAA,EAAA;AAEhG;AAGO,SAASQ,EAAQC,GAAeC,GAAuB;AAC5D,QAAMC,IAAe,EAAE,GAAGF,EAAA;AAC1B,aAAW,CAACG,GAAKC,CAAK,KAAK,OAAO,QAAQH,CAAG,EAAG,CAAIG,MAAU,WAAYF,EAAgCC,CAAG,IAAIC;AACjH,SAAOF;AACT;AAMO,SAASG,EAAUC,GAAiBC,GAAwB;AACjE,SAAO;AAAA,IACL,GAAGD;AAAA,IACH,IAAI,KAAK,IAAI,KAAKA,EAAO,MAAM,MAAMC,EAAK,MAAM,EAAE;AAAA,IAClD,MAAMA,EAAK;AAAA,IACX,MAAMA,EAAK;AAAA,IACX,OAAOA,EAAK;AAAA,IACZ,OAAOA,EAAK;AAAA;AAAA,IAEZ,MAAMD,EAAO,YAAYA,EAAO,OAAOC,EAAK;AAAA,IAC5C,OAAOA,EAAK,SAASD,EAAO;AAAA,EAAA;AAEhC;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"poses.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Autosaver.js","sources":["../../src/save/Autosaver.ts"],"sourcesContent":["import type { SaveStore } from './SaveStore';\n\n/** The clock and timers the autosaver uses; tests hand in fake ones. */\nexport interface AutosaveTimers {\n now(): number;\n setTimeout(fn: () => void, ms: number): unknown;\n clearTimeout(handle: unknown): void;\n}\n\nexport interface AutosaverOptions {\n /** Least time between two saves, ms (default 30,000). */\n intervalMs?: number;\n /** Small summary stored beside each save (see `SaveStore.save`). */\n meta?: () => unknown;\n /** Told when a save fails; autosaving carries on. */\n onError?: (err: unknown) => void;\n timers?: AutosaveTimers;\n}\n\nconst REAL_TIMERS: AutosaveTimers = {\n now: () => Date.now(),\n setTimeout: (fn, ms) => setTimeout(fn, ms),\n clearTimeout: (h) => clearTimeout(h as ReturnType<typeof setTimeout>),\n};\n\n/**\n * Saves a slot when the game says something changed, but never more often than `intervalMs`: a burst of changes\n * (every pickup in a fight) becomes one save now and one trailing save at the end of the interval with the newest\n * state. `snapshot` is read when the save happens, not when it was asked for, so the trailing save is never stale.\n */\nexport class Autosaver {\n private readonly intervalMs: number;\n private readonly timers: AutosaveTimers;\n private lastSave = -Infinity;\n private dirty = false;\n private timer: unknown = null;\n private inFlight: Promise<void> | null = null;\n private disposed = false;\n\n constructor(\n private readonly store: SaveStore,\n private readonly slot: string,\n private readonly snapshot: () => unknown,\n private readonly options: AutosaverOptions = {},\n ) {\n this.intervalMs = options.intervalMs ?? 30_000;\n this.timers = options.timers ?? REAL_TIMERS;\n }\n\n /** Something worth saving changed. */\n request(): void {\n if (this.disposed) return;\n this.dirty = true;\n if (this.timer !== null || this.inFlight) return;\n const wait = this.lastSave + this.intervalMs - this.timers.now();\n if (wait <= 0) void this.run();\n else this.timer = this.timers.setTimeout(() => {\n this.timer = null;\n void this.run();\n }, wait);\n }\n\n /** Saves now if anything is waiting (leaving the game, a scene change); resolves when it is written. */\n async flush(): Promise<void> {\n if (this.timer !== null) {\n this.timers.clearTimeout(this.timer);\n this.timer = null;\n }\n if (this.inFlight) await this.inFlight;\n if (this.dirty) await this.run();\n }\n\n /** Stops autosaving; a pending trailing save is dropped (call `flush` first to keep it). */\n dispose(): void {\n this.disposed = true;\n if (this.timer !== null) this.timers.clearTimeout(this.timer);\n this.timer = null;\n }\n\n private run(): Promise<void> {\n this.dirty = false;\n this.lastSave = this.timers.now();\n const job = (async () => {\n try {\n await this.store.save(this.slot, this.snapshot(), this.options.meta?.() ?? null);\n } catch (err) {\n if (this.options.onError) this.options.onError(err);\n else console.error('[autosave] failed', err);\n }\n })();\n this.inFlight = job;\n void job.then(() => {\n this.inFlight = null;\n // changes made while the save was being written wait for the next interval\n if (this.dirty && !this.disposed) {\n this.dirty = false;\n this.request();\n }\n });\n return job;\n }\n}\n"],"names":["REAL_TIMERS","fn","ms","h","Autosaver","store","slot","snapshot","options","wait","job","err"],"mappings":"AAmBA,MAAMA,IAA8B;AAAA,EAClC,KAAK,MAAM,KAAK,IAAA;AAAA,EAChB,YAAY,CAACC,GAAIC,MAAO,WAAWD,GAAIC,CAAE;AAAA,EACzC,cAAc,CAACC,MAAM,aAAaA,CAAkC;AACtE;AAOO,MAAMC,EAAU;AAAA,EASrB,YACmBC,GACAC,GACAC,GACAC,IAA4B,CAAA,GAC7C;AAJiB,SAAA,QAAAH,GACA,KAAA,OAAAC,GACA,KAAA,WAAAC,GACA,KAAA,UAAAC,GAEjB,KAAK,aAAaA,EAAQ,cAAc,KACxC,KAAK,SAASA,EAAQ,UAAUR;AAAA,EAClC;AAAA,EAPmB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAZF;AAAA,EACA;AAAA,EACT,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAiB;AAAA,EACjB,WAAiC;AAAA,EACjC,WAAW;AAAA;AAAA,EAanB,UAAgB;AAGd,QAFI,KAAK,aACT,KAAK,QAAQ,IACT,KAAK,UAAU,QAAQ,KAAK,UAAU;AAC1C,UAAMS,IAAO,KAAK,WAAW,KAAK,aAAa,KAAK,OAAO,IAAA;AAC3D,IAAIA,KAAQ,IAAQ,KAAK,IAAA,IACpB,KAAK,QAAQ,KAAK,OAAO,WAAW,MAAM;AAC7C,WAAK,QAAQ,MACR,KAAK,IAAA;AAAA,IACZ,GAAGA,CAAI;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,QAAuB;AAC3B,IAAI,KAAK,UAAU,SACjB,KAAK,OAAO,aAAa,KAAK,KAAK,GACnC,KAAK,QAAQ,OAEX,KAAK,YAAU,MAAM,KAAK,UAC1B,KAAK,SAAO,MAAM,KAAK,IAAA;AAAA,EAC7B;AAAA;AAAA,EAGA,UAAgB;AACd,SAAK,WAAW,IACZ,KAAK,UAAU,aAAW,OAAO,aAAa,KAAK,KAAK,GAC5D,KAAK,QAAQ;AAAA,EACf;AAAA,EAEQ,MAAqB;AAC3B,SAAK,QAAQ,IACb,KAAK,WAAW,KAAK,OAAO,IAAA;AAC5B,UAAMC,KAAO,YAAY;AACvB,UAAI;AACF,cAAM,KAAK,MAAM,KAAK,KAAK,MAAM,KAAK,SAAA,GAAY,KAAK,QAAQ,OAAA,KAAY,IAAI;AAAA,MACjF,SAASC,GAAK;AACZ,QAAI,KAAK,QAAQ,UAAS,KAAK,QAAQ,QAAQA,CAAG,IAC7C,QAAQ,MAAM,qBAAqBA,CAAG;AAAA,MAC7C;AAAA,IACF,GAAA;AACA,gBAAK,WAAWD,GACXA,EAAI,KAAK,MAAM;AAClB,WAAK,WAAW,MAEZ,KAAK,SAAS,CAAC,KAAK,aACtB,KAAK,QAAQ,IACb,KAAK,QAAA;AAAA,IAET,CAAC,GACMA;AAAA,EACT;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MemoryBackend.js","sources":["../../src/save/MemoryBackend.ts"],"sourcesContent":["import type { SaveBackend } from './SaveBackend';\n\n/** Slots kept in a map for the life of the page: for tests, tools and a browser that refuses storage. */\nexport class MemoryBackend implements SaveBackend {\n readonly entries = new Map<string, string>();\n\n async read(key: string): Promise<string | null> {\n return this.entries.get(key) ?? null;\n }\n\n async write(key: string, value: string): Promise<void> {\n this.entries.set(key, value);\n }\n\n async remove(key: string): Promise<void> {\n this.entries.delete(key);\n }\n\n async keys(prefix: string): Promise<string[]> {\n return [...this.entries.keys()].filter((k) => k.startsWith(prefix));\n }\n}\n"],"names":["MemoryBackend","key","value","prefix","k"],"mappings":"AAGO,MAAMA,EAAqC;AAAA,EACvC,8BAAc,IAAA;AAAA,EAEvB,MAAM,KAAKC,GAAqC;AAC9C,WAAO,KAAK,QAAQ,IAAIA,CAAG,KAAK;AAAA,EAClC;AAAA,EAEA,MAAM,MAAMA,GAAaC,GAA8B;AACrD,SAAK,QAAQ,IAAID,GAAKC,CAAK;AAAA,EAC7B;AAAA,EAEA,MAAM,OAAOD,GAA4B;AACvC,SAAK,QAAQ,OAAOA,CAAG;AAAA,EACzB;AAAA,EAEA,MAAM,KAAKE,GAAmC;AAC5C,WAAO,CAAC,GAAG,KAAK,QAAQ,KAAA,CAAM,EAAE,OAAO,CAACC,MAAMA,EAAE,WAAWD,CAAM,CAAC;AAAA,EACpE;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SaveStore.js","sources":["../../src/save/SaveStore.ts"],"sourcesContent":["import { checksum } from './checksum';\nimport { MemoryBackend } from './MemoryBackend';\nimport { MigrationError, migrateSave, type Migrations } from './migrateSave';\nimport type { SaveBackend } from './SaveBackend';\nimport { WebStorageBackend } from './WebStorageBackend';\n\nexport interface SaveStoreOptions {\n /** The schema version this build writes; older saves go through `migrations` on load. */\n version: number;\n /** `migrations[n]` turns data of version n into version n + 1. */\n migrations?: Migrations;\n /** Where slots are kept (default `localStorage`, or memory where there is none). */\n backend?: SaveBackend;\n /** Prepended to every slot's key, so two games (or a game and its tools) on one origin never share slots. */\n prefix?: string;\n /** Keep the slot's previous good save beside it and fall back to it when the slot is corrupt (default true). */\n backup?: boolean;\n /** Wall clock for `savedAt` (default `Date.now`). */\n now?: () => number;\n}\n\n/** What a slot holds besides the game's data: shown on a load screen without loading the save. */\nexport interface SaveInfo {\n slot: string;\n version: number;\n savedAt: number;\n /** Whatever small summary the game passed to `save` (level reached, play time, a title). */\n meta: unknown;\n}\n\nexport type LoadFailure = 'missing' | 'corrupt' | 'too-new' | 'no-migration' | 'migration-failed';\n\nexport type LoadResult<T> =\n | { ok: true; data: T; info: SaveInfo; migrated: boolean; fromBackup: boolean }\n | { ok: false; slot: string; reason: LoadFailure; error?: unknown };\n\n/**\n * The stored form. `format` marks it as ours; `checksum` covers the JSON of `data`.\n *\n * The tag is `'lantern-save'`, from the engine's old name (Lantern Engine), and stays that on purpose: every save a\n * player already has carries it, and a save read under another tag is refused as corrupt. Changing it would lose them.\n */\ninterface Envelope {\n format: 'lantern-save';\n version: number;\n savedAt: number;\n meta: unknown;\n checksum: string;\n data: unknown;\n}\n\nconst BACKUP = '.bak';\n\n/**\n * Named save slots with a schema version, migrations from older versions, a checksum to catch a damaged slot, and a\n * backup of the previous save to fall back to. Data must be JSON-safe (plain objects, arrays, strings, finite\n * numbers, booleans, null).\n *\n * ```ts\n * const saves = new SaveStore({ version: 3, migrations: { 1: addInventory, 2: renameGold } });\n * await saves.save('slot1', state, { level: 4 });\n * const r = await saves.load<GameState>('slot1');\n * if (r.ok) start(r.data); else if (r.reason !== 'missing') warn(r.reason);\n * ```\n *\n * Writes to one slot are queued in order, so an autosave and a manual save never land the wrong way round.\n */\nexport class SaveStore {\n readonly version: number;\n private readonly backend: SaveBackend;\n private readonly prefix: string;\n private readonly migrations: Migrations;\n private readonly backup: boolean;\n private readonly now: () => number;\n private readonly queues = new Map<string, Promise<unknown>>();\n\n constructor(options: SaveStoreOptions) {\n if (!Number.isInteger(options.version) || options.version < 1) throw new Error('SaveStore: version must be a whole number from 1');\n this.version = options.version;\n this.backend = options.backend ?? defaultBackend();\n this.prefix = options.prefix ?? 'save:';\n this.migrations = options.migrations ?? {};\n this.backup = options.backup ?? true;\n this.now = options.now ?? Date.now;\n }\n\n /** Writes `data` to `slot` at the current version. Rejects when the backend does (a full disk, a quota). */\n save(slot: string, data: unknown, meta: unknown = null): Promise<SaveInfo> {\n return this.queued(slot, async () => {\n const key = this.key(slot);\n const json = JSON.stringify(data);\n if (json === undefined) throw new Error(`SaveStore: data for \"${slot}\" is not JSON`);\n const info: SaveInfo = { slot, version: this.version, savedAt: this.now(), meta };\n const envelope: Envelope = { format: 'lantern-save', version: info.version, savedAt: info.savedAt, meta, checksum: checksum(json), data };\n if (this.backup) {\n // only a save that still reads is worth keeping: a damaged one would overwrite the good backup\n const previous = await this.backend.read(key);\n if (previous !== null && verify(previous)) await this.backend.write(key + BACKUP, previous);\n }\n await this.backend.write(key, JSON.stringify(envelope));\n return info;\n });\n }\n\n /** Reads `slot`, brought to the current version. Never throws for a bad slot: the result says what went wrong. */\n load<T>(slot: string): Promise<LoadResult<T>> {\n return this.queued(slot, async () => {\n const key = this.key(slot);\n const main = await this.open<T>(slot, await this.backend.read(key), false);\n if (main.ok || !this.backup || main.reason !== 'corrupt') return main;\n const backup = await this.open<T>(slot, await this.backend.read(key + BACKUP), true);\n return backup.ok ? backup : main;\n });\n }\n\n /** Removes a slot and its backup. */\n delete(slot: string): Promise<void> {\n return this.queued(slot, async () => {\n const key = this.key(slot);\n await this.backend.remove(key);\n await this.backend.remove(key + BACKUP);\n });\n }\n\n /** Every slot that reads, newest first, without migrating its data. A damaged slot is left out. */\n async list(): Promise<SaveInfo[]> {\n const keys = await this.backend.keys(this.prefix);\n const out: SaveInfo[] = [];\n for (const key of keys) {\n if (key.endsWith(BACKUP)) continue;\n const raw = await this.backend.read(key);\n const env = raw === null ? null : parse(raw);\n if (env && verify(raw!)) out.push({ slot: key.slice(this.prefix.length), version: env.version, savedAt: env.savedAt, meta: env.meta });\n }\n return out.sort((a, b) => b.savedAt - a.savedAt);\n }\n\n private async open<T>(slot: string, raw: string | null, fromBackup: boolean): Promise<LoadResult<T>> {\n if (raw === null) return { ok: false, slot, reason: fromBackup ? 'corrupt' : 'missing' };\n const env = parse(raw);\n if (!env || checksum(JSON.stringify(env.data)) !== env.checksum) return { ok: false, slot, reason: 'corrupt' };\n try {\n const data = migrateSave(env.data, env.version, this.version, this.migrations) as T;\n const info: SaveInfo = { slot, version: env.version, savedAt: env.savedAt, meta: env.meta };\n return { ok: true, data, info, migrated: env.version !== this.version, fromBackup };\n } catch (err) {\n if (err instanceof MigrationError) return { ok: false, slot, reason: err.reason, error: err };\n throw err;\n }\n }\n\n private key(slot: string): string {\n if (!slot || slot.endsWith(BACKUP)) throw new Error(`SaveStore: \"${slot}\" is not a usable slot name`);\n return this.prefix + slot;\n }\n\n private queued<R>(slot: string, job: () => Promise<R>): Promise<R> {\n const run = (this.queues.get(slot) ?? Promise.resolve()).then(job);\n const tail = run.catch(() => undefined);\n this.queues.set(slot, tail);\n // forget the chain once it is idle, or a game with many slots keeps a promise per slot for good\n void tail.then(() => {\n if (this.queues.get(slot) === tail) this.queues.delete(slot);\n });\n return run;\n }\n}\n\nfunction parse(raw: string): Envelope | null {\n try {\n const v = JSON.parse(raw) as Partial<Envelope> | null;\n if (!v || v.format !== 'lantern-save' || typeof v.version !== 'number' || typeof v.checksum !== 'string' || !('data' in v)) return null;\n return v as Envelope;\n } catch {\n return null;\n }\n}\n\nfunction verify(raw: string): boolean {\n const env = parse(raw);\n return env !== null && checksum(JSON.stringify(env.data)) === env.checksum;\n}\n\nfunction defaultBackend(): SaveBackend {\n try {\n if (typeof globalThis.localStorage !== 'undefined') return new WebStorageBackend(globalThis.localStorage);\n } catch {\n // a sandboxed frame throws on merely reading localStorage\n }\n return new MemoryBackend();\n}\n"],"names":["BACKUP","SaveStore","options","defaultBackend","slot","data","meta","key","json","info","envelope","checksum","previous","verify","main","backup","keys","out","raw","env","parse","a","b","fromBackup","migrateSave","err","MigrationError","job","run","tail","v","WebStorageBackend","MemoryBackend"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"WebStorageBackend.js","sources":["../../src/save/WebStorageBackend.ts"],"sourcesContent":["import type { SaveBackend } from './SaveBackend';\n\n/**\n * Slots in a Web Storage area, `localStorage` by default. Small and synchronous, so keep saves to a few hundred KB:\n * browsers allow about 5 MB a site, and a write over that throws (reported to the caller of `save`).\n */\nexport class WebStorageBackend implements SaveBackend {\n constructor(private readonly storage: Storage = globalThis.localStorage) {\n if (!storage) throw new Error('WebStorageBackend: no Web Storage here; pass one or use another backend');\n }\n\n async read(key: string): Promise<string | null> {\n return this.storage.getItem(key);\n }\n\n async write(key: string, value: string): Promise<void> {\n this.storage.setItem(key, value);\n }\n\n async remove(key: string): Promise<void> {\n this.storage.removeItem(key);\n }\n\n async keys(prefix: string): Promise<string[]> {\n const out: string[] = [];\n for (let i = 0; i < this.storage.length; i++) {\n const k = this.storage.key(i);\n if (k !== null && k.startsWith(prefix)) out.push(k);\n }\n return out;\n }\n}\n"],"names":["WebStorageBackend","storage","key","value","prefix","out","i","k"],"mappings":"AAMO,MAAMA,EAAyC;AAAA,EACpD,YAA6BC,IAAmB,WAAW,cAAc;AACvE,QAD2B,KAAA,UAAAA,GACvB,CAACA,EAAS,OAAM,IAAI,MAAM,yEAAyE;AAAA,EACzG;AAAA,EAF6B;AAAA,EAI7B,MAAM,KAAKC,GAAqC;AAC9C,WAAO,KAAK,QAAQ,QAAQA,CAAG;AAAA,EACjC;AAAA,EAEA,MAAM,MAAMA,GAAaC,GAA8B;AACrD,SAAK,QAAQ,QAAQD,GAAKC,CAAK;AAAA,EACjC;AAAA,EAEA,MAAM,OAAOD,GAA4B;AACvC,SAAK,QAAQ,WAAWA,CAAG;AAAA,EAC7B;AAAA,EAEA,MAAM,KAAKE,GAAmC;AAC5C,UAAMC,IAAgB,CAAA;AACtB,aAASC,IAAI,GAAGA,IAAI,KAAK,QAAQ,QAAQA,KAAK;AAC5C,YAAMC,IAAI,KAAK,QAAQ,IAAID,CAAC;AAC5B,MAAIC,MAAM,QAAQA,EAAE,WAAWH,CAAM,KAAGC,EAAI,KAAKE,CAAC;AAAA,IACpD;AACA,WAAOF;AAAA,EACT;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"checksum.js","sources":["../../src/save/checksum.ts"],"sourcesContent":["/**\n * FNV-1a over the UTF-16 code units of `text`, as 8 hex digits. Not a defence against tampering (anyone can\n * recompute it); it catches a slot cut short by a full disk, a crash mid-write or a hand edit gone wrong, which is\n * what a save needs to tell before it loads half a world.\n */\nexport function checksum(text: string): string {\n let h = 0x811c9dc5;\n for (let i = 0; i < text.length; i++) {\n h ^= text.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return (h >>> 0).toString(16).padStart(8, '0');\n}\n"],"names":["checksum","text","h","i"],"mappings":"AAKO,SAASA,EAASC,GAAsB;AAC7C,MAAIC,IAAI;AACR,WAASC,IAAI,GAAGA,IAAIF,EAAK,QAAQE;AAC/B,IAAAD,KAAKD,EAAK,WAAWE,CAAC,GACtBD,IAAI,KAAK,KAAKA,GAAG,QAAU;AAE7B,UAAQA,MAAM,GAAG,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC/C;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"migrateSave.js","sources":["../../src/save/migrateSave.ts"],"sourcesContent":["/** Steps that bring saved data forward one schema version each: `migrations[n]` turns version n into n + 1. */\nexport type Migrations = Readonly<Record<number, (data: unknown) => unknown>>;\n\n/** Why data could not be brought to the current version. */\nexport class MigrationError extends Error {\n constructor(\n message: string,\n readonly reason: 'too-new' | 'no-migration' | 'migration-failed',\n readonly fromVersion: number,\n options?: { cause?: unknown },\n ) {\n super(message, options);\n this.name = 'MigrationError';\n }\n}\n\n/**\n * Runs the chain from `from` up to `to`, one step at a time, so a save three versions old goes through every step\n * written since rather than needing a direct path from each old version. Data newer than the game (a save from a\n * later build, then a downgrade) is refused rather than read wrong.\n */\nexport function migrateSave(data: unknown, from: number, to: number, migrations: Migrations): unknown {\n if (!Number.isInteger(from) || from > to) {\n throw new MigrationError(`save version ${from} is newer than this build reads (${to})`, 'too-new', from);\n }\n let value = data;\n for (let v = from; v < to; v++) {\n const step = migrations[v];\n if (!step) throw new MigrationError(`no migration from save version ${v} to ${v + 1}`, 'no-migration', from);\n try {\n value = step(value);\n } catch (err) {\n throw new MigrationError(`migration from save version ${v} failed`, 'migration-failed', from, { cause: err });\n }\n }\n return value;\n}\n"],"names":["MigrationError","message","reason","fromVersion","options","migrateSave","data","from","to","migrations","value","v","step","err"],"mappings":"AAIO,MAAMA,UAAuB,MAAM;AAAA,EACxC,YACEC,GACSC,GACAC,GACTC,GACA;AACA,UAAMH,GAASG,CAAO,GAJb,KAAA,SAAAF,GACA,KAAA,cAAAC,GAIT,KAAK,OAAO;AAAA,EACd;AAAA,EANW;AAAA,EACA;AAMb;AAOO,SAASE,EAAYC,GAAeC,GAAcC,GAAYC,GAAiC;AACpG,MAAI,CAAC,OAAO,UAAUF,CAAI,KAAKA,IAAOC;AACpC,UAAM,IAAIR,EAAe,gBAAgBO,CAAI,oCAAoCC,CAAE,KAAK,WAAWD,CAAI;AAEzG,MAAIG,IAAQJ;AACZ,WAASK,IAAIJ,GAAMI,IAAIH,GAAIG,KAAK;AAC9B,UAAMC,IAAOH,EAAWE,CAAC;AACzB,QAAI,CAACC,EAAM,OAAM,IAAIZ,EAAe,kCAAkCW,CAAC,OAAOA,IAAI,CAAC,IAAI,gBAAgBJ,CAAI;AAC3G,QAAI;AACF,MAAAG,IAAQE,EAAKF,CAAK;AAAA,IACpB,SAASG,GAAK;AACZ,YAAM,IAAIb,EAAe,+BAA+BW,CAAC,WAAW,oBAAoBJ,GAAM,EAAE,OAAOM,EAAA,CAAK;AAAA,IAC9G;AAAA,EACF;AACA,SAAOH;AACT;"}
|
package/dist/settings.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"settings.js","sources":["../src/settings.ts"],"sourcesContent":["/**\n * The few player settings the engine itself reads. A game keeps its own settings store and mirrors these fields\n * into this one (`engineSettings.set`) whenever they change; the engine never reads the game's store.\n */\nexport interface EngineSettings {\n /** Fewer particles and lighter effects. */\n reducedFx: boolean;\n /** Turns off everything that only costs battery: transient lights, heavy particles. */\n batterySaver: boolean;\n /** Short-lived lights a fight throws into the dark (`pixel/lights.ts`). */\n combatLights: boolean;\n /** 0–1, the combat sounds' volume (`audio/SoundEngine.ts`). */\n sfxVolume: number;\n /** 0–1, the music's volume. */\n musicVolume: number;\n}\n\nexport type SettingsListener = (s: EngineSettings) => void;\n\nconst DEFAULTS: EngineSettings = {\n reducedFx: false,\n batterySaver: false,\n combatLights: true,\n sfxVolume: 0.8,\n musicVolume: 0.6,\n};\n\nlet state: EngineSettings = { ...DEFAULTS };\nconst listeners = new Set<SettingsListener>();\n\n/** Same shape as the zustand stores the game used (`getState`, `subscribe`), so moved code reads it unchanged. */\nexport const engineSettings = {\n getState(): EngineSettings {\n return state;\n },\n set(patch: Partial<EngineSettings>): void {\n state = { ...state, ...patch };\n for (const l of [...listeners]) l(state);\n },\n subscribe(listener: SettingsListener): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n reset(): void {\n engineSettings.set(DEFAULTS);\n },\n};\n"],"names":["DEFAULTS","state","listeners","engineSettings","patch","l","listener"],"mappings":"AAmBA,MAAMA,IAA2B;AAAA,EAC/B,WAAW;AAAA,EACX,cAAc;AAAA,EACd,cAAc;AAAA,EACd,WAAW;AAAA,EACX,aAAa;AACf;AAEA,IAAIC,IAAwB,EAAE,GAAGD,EAAA;AACjC,MAAME,wBAAgB,IAAA,GAGTC,IAAiB;AAAA,EAC5B,WAA2B;AACzB,WAAOF;AAAA,EACT;AAAA,EACA,IAAIG,GAAsC;AACxC,IAAAH,IAAQ,EAAE,GAAGA,GAAO,GAAGG,EAAA;AACvB,eAAWC,KAAK,CAAC,GAAGH,CAAS,KAAKD,CAAK;AAAA,EACzC;AAAA,EACA,UAAUK,GAAwC;AAChD,WAAAJ,EAAU,IAAII,CAAQ,GACf,MAAMJ,EAAU,OAAOI,CAAQ;AAAA,EACxC;AAAA,EACA,QAAc;AACZ,IAAAH,EAAe,IAAIH,CAAQ;AAAA,EAC7B;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TilemapView.js","sources":["../../src/tilemap/TilemapView.ts"],"sourcesContent":["import { Container, Sprite } from 'pixi.js';\nimport type { Stage } from '../core/Stage';\nimport { FLIP_D, FLIP_H, FLIP_V, tileAt, tileRect, type TileLayer, type TileMap, type Tileset } from './tiled';\n\nexport interface TilemapViewOptions {\n /** The texture key holding a tileset's image (loaded with `AssetLoader.image`, or painted). Default: `tileset.name`. */\n textureFor?: (tileset: Tileset) => string;\n /** Tiles per chunk side (default 32): each chunk is painted once into a texture of its own. */\n chunkTiles?: number;\n /** Only these tile layers, by name; default every visible tile layer. */\n layers?: string[];\n /**\n * Depth of the first layer in the world layer; each layer after it one higher. Bodies are usually drawn at their y,\n * so the default keeps the map under every one of them. A layer with a numeric `depth` property is drawn there\n * instead (a roof layer over the bodies: `depth` = 1e6).\n */\n baseDepth?: number;\n /** Where the layers go (default the stage's world layer); a scene passes its `root` so they leave with it. */\n parent?: Container;\n}\n\nlet nextView = 0;\n\n/**\n * Draws a Tiled map's tile layers. Each layer is cut into chunks painted once into textures; a frame only toggles\n * which chunks are in view (`update`), so a map of any size costs a handful of sprites a frame.\n *\n * Animated tiles are drawn on their first frame.\n */\nexport class TilemapView {\n /** One container per drawn tile layer, by name, each at its own depth among the bodies. */\n readonly layers = new Map<string, Container>();\n private readonly chunks: { sprite: Sprite; x: number; y: number; w: number; h: number; key: string }[] = [];\n private readonly id = nextView++;\n\n constructor(\n private readonly stage: Stage,\n readonly map: TileMap,\n options: TilemapViewOptions = {},\n ) {\n const textureFor = options.textureFor ?? ((ts: Tileset) => ts.name);\n const chunk = Math.max(1, options.chunkTiles ?? 32);\n const base = options.baseDepth ?? -1_000_000;\n const wanted = options.layers ? new Set(options.layers) : null;\n const layers = map.layers.filter((l): l is TileLayer => l.kind === 'tiles' && (wanted ? wanted.has(l.name) : l.visible));\n const parent = options.parent ?? stage.world;\n layers.forEach((layer, n) => {\n const depth = typeof layer.properties.depth === 'number' ? layer.properties.depth : base + n;\n const holder = new Container();\n holder.zIndex = depth;\n holder.alpha = layer.opacity;\n holder.position.set(layer.offsetX, layer.offsetY);\n this.layers.set(layer.name, holder);\n parent.addChild(holder);\n for (let cy = 0; cy < map.height; cy += chunk) {\n for (let cx = 0; cx < map.width; cx += chunk) {\n const cw = Math.min(chunk, map.width - cx);\n const ch = Math.min(chunk, map.height - cy);\n this.paintChunk(layer, holder, cx, cy, cw, ch, textureFor);\n }\n }\n });\n }\n\n /** Shows only the chunks in the camera's view (plus a margin in world px). Call once a frame. */\n update(margin = 32): void {\n const v = this.stage.cameras.main.worldView;\n for (const c of this.chunks) {\n c.sprite.visible = c.x < v.right + margin && c.x + c.w > v.x - margin && c.y < v.bottom + margin && c.y + c.h > v.y - margin;\n }\n }\n\n /** World size of the map in px. */\n get widthPx(): number {\n return this.map.width * this.map.tileWidth;\n }\n\n get heightPx(): number {\n return this.map.height * this.map.tileHeight;\n }\n\n destroy(): void {\n for (const c of this.chunks) this.stage.textures.remove(c.key);\n this.chunks.length = 0;\n for (const c of this.layers.values()) c.destroy({ children: true });\n this.layers.clear();\n }\n\n private paintChunk(layer: TileLayer, holder: Container, cx: number, cy: number, cw: number, ch: number, textureFor: (ts: Tileset) => string): void {\n const { map } = this;\n const tw = map.tileWidth;\n const th = map.tileHeight;\n let any = false;\n const key = `tilemap:${this.id}:${layer.name}:${cx},${cy}`;\n // tiles taller than the grid (trees on a 16 px grid) overhang the cell above, so the chunk has room for them\n const tallest = Math.max(th, ...map.tilesets.map((t) => t.tileHeight));\n const pad = tallest - th;\n const tex = this.stage.textures.createCanvas(key, cw * tw, ch * th + pad);\n const ctx = tex.context;\n ctx.imageSmoothingEnabled = false;\n for (let y = 0; y < ch; y++) {\n for (let x = 0; x < cw; x++) {\n const raw = layer.data[(cy + y) * map.width + (cx + x)]!;\n const t = tileAt(map, raw);\n if (!t) continue;\n const src = this.stage.textures.exists(textureFor(t.tileset)) ? this.stage.textures.get(textureFor(t.tileset)).source.resource : null;\n if (!src) continue;\n const r = tileRect(t.tileset, t.local);\n // a tile is drawn on its cell's bottom-left corner, as Tiled does\n const dx = x * tw;\n const dy = pad + (y + 1) * th - r.h;\n ctx.save();\n ctx.translate(dx + r.w / 2, dy + r.h / 2);\n if (raw & FLIP_D) ctx.transform(0, 1, 1, 0, 0, 0);\n ctx.scale(raw & FLIP_H ? -1 : 1, raw & FLIP_V ? -1 : 1);\n ctx.drawImage(src as CanvasImageSource, r.x, r.y, r.w, r.h, -r.w / 2, -r.h / 2, r.w, r.h);\n ctx.restore();\n any = true;\n }\n }\n if (!any) {\n this.stage.textures.remove(key);\n return;\n }\n tex.refresh();\n const sprite = new Sprite(tex.whole);\n sprite.position.set(cx * tw, cy * th - pad);\n holder.addChild(sprite);\n this.chunks.push({ sprite, x: sprite.x + layer.offsetX, y: sprite.y + layer.offsetY, w: cw * tw, h: ch * th + pad, key });\n }\n}\n"],"names":["nextView","TilemapView","stage","map","options","textureFor","ts","chunk","base","wanted","layers","l","parent","layer","n","depth","holder","Container","cy","cx","cw","ch","margin","v","c","tw","th","any","key","pad","t","tex","ctx","y","x","raw","tileAt","src","r","tileRect","dx","dy","FLIP_D","FLIP_H","FLIP_V","sprite","Sprite"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"collision.js","sources":["../../src/tilemap/collision.ts"],"sourcesContent":["import { createCollisionGrid, type CollisionGrid, type CollisionGridOptions } from '../net/movement';\nimport { gidOf, tileAt, type TileMap } from './tiled';\n\nexport interface TiledCollisionOptions extends Omit<CollisionGridOptions, 'ground'> {\n /**\n * What makes a cell solid:\n * - a layer name: every non-empty cell of that tile layer is solid (a hidden \"collision\" layer is the usual way);\n * - `{ property }`: a cell is solid when its tile has that custom property set to true, on any tile layer.\n * Default: the layer named \"collision\" if there is one, else tiles with a `solid` property.\n */\n solid?: string | { property: string };\n}\n\n/** The `CollisionGrid` of a Tiled map, for `stepMover`; cells are the map's tiles. */\nexport function collisionFromTiled(map: TileMap, options: TiledCollisionOptions = {}): CollisionGrid {\n if (map.tileWidth !== map.tileHeight) throw new Error(`Tiled map: collision needs square tiles, not ${map.tileWidth}x${map.tileHeight}`);\n const rule = options.solid ?? (map.layers.some((l) => l.kind === 'tiles' && l.name === 'collision') ? 'collision' : { property: 'solid' });\n const cells = map.width * map.height;\n const solid = new Uint8Array(cells);\n if (typeof rule === 'string') {\n const layer = map.layers.find((l) => l.kind === 'tiles' && l.name === rule);\n if (!layer || layer.kind !== 'tiles') throw new Error(`Tiled map: no tile layer named \"${rule}\"`);\n for (let i = 0; i < cells; i++) if (gidOf(layer.data[i]!) !== 0) solid[i] = 1;\n } else {\n for (const layer of map.layers) {\n if (layer.kind !== 'tiles') continue;\n for (let i = 0; i < cells; i++) {\n const t = tileAt(map, layer.data[i]!);\n if (t && t.tileset.tileProperties.get(t.local)?.[rule.property] === true) solid[i] = 1;\n }\n }\n }\n let s = '';\n for (let i = 0; i < cells; i++) s += solid[i] ? '1' : '0';\n const { solid: _rule, ...gridOptions } = options;\n return createCollisionGrid(map.width, map.height, map.tileWidth, s, gridOptions);\n}\n"],"names":["collisionFromTiled","map","options","rule","l","cells","solid","layer","i","gidOf","t","tileAt","s","_rule","gridOptions","createCollisionGrid"],"mappings":";;AAcO,SAASA,EAAmBC,GAAcC,IAAiC,IAAmB;AACnG,MAAID,EAAI,cAAcA,EAAI,WAAY,OAAM,IAAI,MAAM,gDAAgDA,EAAI,SAAS,IAAIA,EAAI,UAAU,EAAE;AACvI,QAAME,IAAOD,EAAQ,UAAUD,EAAI,OAAO,KAAK,CAACG,MAAMA,EAAE,SAAS,WAAWA,EAAE,SAAS,WAAW,IAAI,cAAc,EAAE,UAAU,YAC1HC,IAAQJ,EAAI,QAAQA,EAAI,QACxBK,IAAQ,IAAI,WAAWD,CAAK;AAClC,MAAI,OAAOF,KAAS,UAAU;AAC5B,UAAMI,IAAQN,EAAI,OAAO,KAAK,CAACG,MAAMA,EAAE,SAAS,WAAWA,EAAE,SAASD,CAAI;AAC1E,QAAI,CAACI,KAASA,EAAM,SAAS,eAAe,IAAI,MAAM,mCAAmCJ,CAAI,GAAG;AAChG,aAASK,IAAI,GAAGA,IAAIH,GAAOG,IAAK,CAAIC,EAAMF,EAAM,KAAKC,CAAC,CAAE,MAAM,MAAGF,EAAME,CAAC,IAAI;AAAA,EAC9E;AACE,eAAWD,KAASN,EAAI;AACtB,UAAIM,EAAM,SAAS;AACnB,iBAASC,IAAI,GAAGA,IAAIH,GAAOG,KAAK;AAC9B,gBAAME,IAAIC,EAAOV,GAAKM,EAAM,KAAKC,CAAC,CAAE;AACpC,UAAIE,KAAKA,EAAE,QAAQ,eAAe,IAAIA,EAAE,KAAK,IAAIP,EAAK,QAAQ,MAAM,OAAMG,EAAME,CAAC,IAAI;AAAA,QACvF;AAGJ,MAAII,IAAI;AACR,WAASJ,IAAI,GAAGA,IAAIH,GAAOG,IAAK,CAAAI,KAAKN,EAAME,CAAC,IAAI,MAAM;AACtD,QAAM,EAAE,OAAOK,GAAO,GAAGC,MAAgBZ;AACzC,SAAOa,EAAoBd,EAAI,OAAOA,EAAI,QAAQA,EAAI,WAAWW,GAAGE,CAAW;AACjF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tiled.js","sources":["../../src/tilemap/tiled.ts"],"sourcesContent":["/**\n * Maps made in Tiled (https://www.mapeditor.org), read from its JSON format (`.tmj`, or `.json`). Supported:\n * orthogonal, finite maps; tile layers as CSV arrays or uncompressed base64; object layers; group layers (flattened);\n * custom properties; flipped tiles. Not supported (refused with a message saying so): infinite maps, compressed layer\n * data, isometric and hexagonal maps, external `.tsj` tilesets (embed them: Map → Embed Tilesets).\n *\n * Pure: no Pixi, no DOM, so a server can read the same map for collision.\n */\n\n/** The three flags Tiled keeps in a gid's high bits; the rest is the tile id. */\nexport const FLIP_H = 0x80000000;\nexport const FLIP_V = 0x40000000;\nexport const FLIP_D = 0x20000000;\nconst GID_MASK = 0x1fffffff;\n\nexport type TiledPropertyValue = string | number | boolean;\nexport type Properties = Record<string, TiledPropertyValue>;\n\nexport interface TileLayer {\n kind: 'tiles';\n name: string;\n /** Row-major gids (0 = empty), flip flags kept. Read the tile id with `gidOf`. */\n data: Uint32Array;\n visible: boolean;\n opacity: number;\n offsetX: number;\n offsetY: number;\n properties: Properties;\n}\n\nexport interface MapObject {\n id: number;\n name: string;\n type: string;\n x: number;\n y: number;\n width: number;\n height: number;\n rotation: number;\n /** A tile object's gid, if it is one. */\n gid?: number;\n point: boolean;\n ellipse: boolean;\n polygon?: { x: number; y: number }[];\n properties: Properties;\n}\n\nexport interface ObjectLayer {\n kind: 'objects';\n name: string;\n objects: MapObject[];\n visible: boolean;\n properties: Properties;\n}\n\nexport type MapLayer = TileLayer | ObjectLayer;\n\nexport interface Tileset {\n name: string;\n firstGid: number;\n /** The image's path as written in the map, relative to the map file. */\n image: string;\n imageWidth: number;\n imageHeight: number;\n tileWidth: number;\n tileHeight: number;\n columns: number;\n tileCount: number;\n margin: number;\n spacing: number;\n /** Per local tile id: its custom properties. */\n tileProperties: Map<number, Properties>;\n /** Per local tile id: an animation (local tile ids and how long each shows, ms). */\n animations: Map<number, { tileId: number; duration: number }[]>;\n properties: Properties;\n}\n\nexport interface TileMap {\n width: number;\n height: number;\n tileWidth: number;\n tileHeight: number;\n layers: MapLayer[];\n tilesets: Tileset[];\n properties: Properties;\n backgroundColor?: string;\n}\n\n/** The tile id of a gid, without its flip flags. */\nexport const gidOf = (raw: number): number => raw & GID_MASK;\n\n/** Which tileset a gid belongs to, and the tile's id inside it; null for an empty cell or an unknown gid. */\nexport function tileAt(map: TileMap, raw: number): { tileset: Tileset; local: number } | null {\n const gid = gidOf(raw);\n if (gid === 0) return null;\n let found: Tileset | null = null;\n for (const ts of map.tilesets) if (ts.firstGid <= gid && (!found || ts.firstGid > found.firstGid)) found = ts;\n if (!found || gid - found.firstGid >= found.tileCount) return null;\n return { tileset: found, local: gid - found.firstGid };\n}\n\n/** Where tile `local` sits in its tileset image. */\nexport function tileRect(ts: Tileset, local: number): { x: number; y: number; w: number; h: number } {\n const col = local % ts.columns;\n const row = Math.floor(local / ts.columns);\n return { x: ts.margin + col * (ts.tileWidth + ts.spacing), y: ts.margin + row * (ts.tileHeight + ts.spacing), w: ts.tileWidth, h: ts.tileHeight };\n}\n\nexport function layerByName(map: TileMap, name: string): MapLayer | undefined {\n return map.layers.find((l) => l.name === name);\n}\n\n/** Every object of every object layer (optionally only those of one `type`/class). */\nexport function objectsOf(map: TileMap, type?: string): MapObject[] {\n const out: MapObject[] = [];\n for (const l of map.layers) if (l.kind === 'objects') for (const o of l.objects) if (type === undefined || o.type === type) out.push(o);\n return out;\n}\n\n// ── Parsing ─────────────────────────────────────────────────────────────────────────────────────────────────────────\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- the input is untrusted JSON, checked field by field below */\ntype Json = any;\n\nclass TiledError extends Error {\n constructor(message: string) {\n super(`Tiled map: ${message}`);\n }\n}\n\nfunction props(list: Json): Properties {\n const out: Properties = {};\n if (!Array.isArray(list)) return out;\n for (const p of list) if (p && typeof p.name === 'string' && ['string', 'number', 'boolean'].includes(typeof p.value)) out[p.name] = p.value;\n return out;\n}\n\nfunction num(v: Json, what: string): number {\n if (typeof v !== 'number' || !Number.isFinite(v)) throw new TiledError(`${what} must be a number`);\n return v;\n}\n\nfunction decodeBase64(text: string): Uint8Array {\n if (typeof atob === 'function') {\n const bin = atob(text.trim());\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n }\n return new Uint8Array((globalThis as Json).Buffer.from(text, 'base64'));\n}\n\nfunction layerData(l: Json, cells: number): Uint32Array {\n if (l.compression) throw new TiledError(`layer \"${l.name}\" is ${l.compression}-compressed; save it as CSV or uncompressed base64`);\n let data: Uint32Array;\n if (l.encoding === 'base64') {\n const bytes = decodeBase64(String(l.data));\n if (bytes.length !== cells * 4) throw new TiledError(`layer \"${l.name}\" holds ${bytes.length / 4} cells, not ${cells}`);\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n data = new Uint32Array(cells);\n for (let i = 0; i < cells; i++) data[i] = view.getUint32(i * 4, true);\n } else {\n if (!Array.isArray(l.data) || l.data.length !== cells) throw new TiledError(`layer \"${l.name}\" must hold ${cells} cells`);\n data = Uint32Array.from(l.data as number[]);\n }\n return data;\n}\n\nfunction parseObject(o: Json): MapObject {\n const obj: MapObject = {\n id: num(o.id, 'object id'),\n name: String(o.name ?? ''),\n type: String(o.type ?? o.class ?? ''),\n x: num(o.x, 'object x'),\n y: num(o.y, 'object y'),\n width: Number(o.width ?? 0),\n height: Number(o.height ?? 0),\n rotation: Number(o.rotation ?? 0),\n point: o.point === true,\n ellipse: o.ellipse === true,\n properties: props(o.properties),\n };\n if (typeof o.gid === 'number') obj.gid = o.gid;\n if (Array.isArray(o.polygon)) obj.polygon = o.polygon.map((p: Json) => ({ x: num(p.x, 'polygon x'), y: num(p.y, 'polygon y') }));\n return obj;\n}\n\nfunction parseLayers(list: Json, map: { width: number; height: number }, out: MapLayer[], parent = { visible: true, opacity: 1, x: 0, y: 0 }): void {\n if (!Array.isArray(list)) return;\n for (const l of list) {\n const visible = parent.visible && l.visible !== false;\n const opacity = parent.opacity * (typeof l.opacity === 'number' ? l.opacity : 1);\n const offsetX = parent.x + Number(l.offsetx ?? 0);\n const offsetY = parent.y + Number(l.offsety ?? 0);\n if (l.type === 'tilelayer') {\n out.push({ kind: 'tiles', name: String(l.name ?? ''), data: layerData(l, map.width * map.height), visible, opacity, offsetX, offsetY, properties: props(l.properties) });\n } else if (l.type === 'objectgroup') {\n out.push({ kind: 'objects', name: String(l.name ?? ''), objects: (l.objects ?? []).map(parseObject), visible, properties: props(l.properties) });\n } else if (l.type === 'group') {\n parseLayers(l.layers, map, out, { visible, opacity, x: offsetX, y: offsetY });\n }\n // image layers are ignored: draw backgrounds yourself\n }\n}\n\nfunction parseTileset(t: Json): Tileset {\n if (typeof t.source === 'string') throw new TiledError(`tileset \"${t.source}\" is external; embed it in the map (Map → Embed Tilesets)`);\n if (typeof t.image !== 'string') throw new TiledError(`tileset \"${t.name}\" is a collection of images; use a single-image tileset`);\n const tileProperties = new Map<number, Properties>();\n const animations = new Map<number, { tileId: number; duration: number }[]>();\n for (const tile of t.tiles ?? []) {\n const id = num(tile.id, 'tile id');\n if (tile.properties) tileProperties.set(id, props(tile.properties));\n if (Array.isArray(tile.animation)) animations.set(id, tile.animation.map((f: Json) => ({ tileId: num(f.tileid, 'animation tileid'), duration: num(f.duration, 'animation duration') })));\n }\n return {\n name: String(t.name ?? ''),\n firstGid: num(t.firstgid, 'tileset firstgid'),\n image: t.image,\n imageWidth: num(t.imagewidth, 'tileset imagewidth'),\n imageHeight: num(t.imageheight, 'tileset imageheight'),\n tileWidth: num(t.tilewidth, 'tileset tilewidth'),\n tileHeight: num(t.tileheight, 'tileset tileheight'),\n columns: num(t.columns, 'tileset columns'),\n tileCount: num(t.tilecount, 'tileset tilecount'),\n margin: Number(t.margin ?? 0),\n spacing: Number(t.spacing ?? 0),\n tileProperties,\n animations,\n properties: props(t.properties),\n };\n}\n\n/** Reads a Tiled JSON map (already parsed with `JSON.parse`). Throws a `Tiled map: …` error saying what is wrong. */\nexport function parseTiled(json: unknown): TileMap {\n const m = json as Json;\n if (!m || typeof m !== 'object') throw new TiledError('not a JSON object');\n if (m.infinite) throw new TiledError('infinite maps are not supported; uncheck Map → Map Properties → Infinite');\n if (m.orientation && m.orientation !== 'orthogonal') throw new TiledError(`${m.orientation} maps are not supported, only orthogonal`);\n const width = num(m.width, 'width');\n const height = num(m.height, 'height');\n const layers: MapLayer[] = [];\n parseLayers(m.layers, { width, height }, layers);\n const map: TileMap = {\n width,\n height,\n tileWidth: num(m.tilewidth, 'tilewidth'),\n tileHeight: num(m.tileheight, 'tileheight'),\n layers,\n tilesets: (m.tilesets ?? []).map(parseTileset),\n properties: props(m.properties),\n };\n if (typeof m.backgroundcolor === 'string') map.backgroundColor = m.backgroundcolor;\n return map;\n}\n/* eslint-enable @typescript-eslint/no-explicit-any */\n"],"names":["FLIP_H","FLIP_V","FLIP_D","gidOf","raw","tileAt","map","gid","found","ts","tileRect","local","col","row","layerByName","name","l","objectsOf","type","out","o","TiledError","message","props","list","p","num","v","what","decodeBase64","text","bin","i","layerData","cells","data","bytes","view","parseObject","obj","parseLayers","parent","visible","opacity","offsetX","offsetY","parseTileset","t","tileProperties","animations","tile","id","f","parseTiled","json","m","width","height","layers"],"mappings":"AAUO,MAAMA,IAAS,YACTC,IAAS,YACTC,IAAS;AA6Ef,MAAMC,IAAQ,CAACC,MAAwBA,IAAM;AAG7C,SAASC,EAAOC,GAAcF,GAAyD;AAC5F,QAAMG,IAAMJ,EAAMC,CAAG;AACrB,MAAIG,MAAQ,EAAG,QAAO;AACtB,MAAIC,IAAwB;AAC5B,aAAWC,KAAMH,EAAI,SAAU,CAAIG,EAAG,YAAYF,MAAQ,CAACC,KAASC,EAAG,WAAWD,EAAM,cAAWA,IAAQC;AAC3G,SAAI,CAACD,KAASD,IAAMC,EAAM,YAAYA,EAAM,YAAkB,OACvD,EAAE,SAASA,GAAO,OAAOD,IAAMC,EAAM,SAAA;AAC9C;AAGO,SAASE,EAASD,GAAaE,GAA+D;AACnG,QAAMC,IAAMD,IAAQF,EAAG,SACjBI,IAAM,KAAK,MAAMF,IAAQF,EAAG,OAAO;AACzC,SAAO,EAAE,GAAGA,EAAG,SAASG,KAAOH,EAAG,YAAYA,EAAG,UAAU,GAAGA,EAAG,SAASI,KAAOJ,EAAG,aAAaA,EAAG,UAAU,GAAGA,EAAG,WAAW,GAAGA,EAAG,WAAA;AACvI;AAEO,SAASK,EAAYR,GAAcS,GAAoC;AAC5E,SAAOT,EAAI,OAAO,KAAK,CAACU,MAAMA,EAAE,SAASD,CAAI;AAC/C;AAGO,SAASE,EAAUX,GAAcY,GAA4B;AAClE,QAAMC,IAAmB,CAAA;AACzB,aAAWH,KAAKV,EAAI,OAAQ,KAAIU,EAAE,SAAS;AAAW,eAAWI,KAAKJ,EAAE,QAAS,EAAIE,MAAS,UAAaE,EAAE,SAASF,MAAMC,EAAI,KAAKC,CAAC;AACtI,SAAOD;AACT;AAOA,MAAME,UAAmB,MAAM;AAAA,EAC7B,YAAYC,GAAiB;AAC3B,UAAM,cAAcA,CAAO,EAAE;AAAA,EAC/B;AACF;AAEA,SAASC,EAAMC,GAAwB;AACrC,QAAML,IAAkB,CAAA;AACxB,MAAI,CAAC,MAAM,QAAQK,CAAI,EAAG,QAAOL;AACjC,aAAWM,KAAKD,EAAM,CAAIC,KAAK,OAAOA,EAAE,QAAS,YAAY,CAAC,UAAU,UAAU,SAAS,EAAE,SAAS,OAAOA,EAAE,KAAK,MAAGN,EAAIM,EAAE,IAAI,IAAIA,EAAE;AACvI,SAAON;AACT;AAEA,SAASO,EAAIC,GAASC,GAAsB;AAC1C,MAAI,OAAOD,KAAM,YAAY,CAAC,OAAO,SAASA,CAAC,EAAG,OAAM,IAAIN,EAAW,GAAGO,CAAI,mBAAmB;AACjG,SAAOD;AACT;AAEA,SAASE,EAAaC,GAA0B;AAC9C,MAAI,OAAO,QAAS,YAAY;AAC9B,UAAMC,IAAM,KAAKD,EAAK,KAAA,CAAM,GACtBX,IAAM,IAAI,WAAWY,EAAI,MAAM;AACrC,aAASC,IAAI,GAAGA,IAAID,EAAI,QAAQC,IAAK,CAAAb,EAAIa,CAAC,IAAID,EAAI,WAAWC,CAAC;AAC9D,WAAOb;AAAA,EACT;AACA,SAAO,IAAI,WAAY,WAAoB,OAAO,KAAKW,GAAM,QAAQ,CAAC;AACxE;AAEA,SAASG,EAAUjB,GAASkB,GAA4B;AACtD,MAAIlB,EAAE,YAAa,OAAM,IAAIK,EAAW,UAAUL,EAAE,IAAI,QAAQA,EAAE,WAAW,oDAAoD;AACjI,MAAImB;AACJ,MAAInB,EAAE,aAAa,UAAU;AAC3B,UAAMoB,IAAQP,EAAa,OAAOb,EAAE,IAAI,CAAC;AACzC,QAAIoB,EAAM,WAAWF,IAAQ,EAAG,OAAM,IAAIb,EAAW,UAAUL,EAAE,IAAI,WAAWoB,EAAM,SAAS,CAAC,eAAeF,CAAK,EAAE;AACtH,UAAMG,IAAO,IAAI,SAASD,EAAM,QAAQA,EAAM,YAAYA,EAAM,UAAU;AAC1E,IAAAD,IAAO,IAAI,YAAYD,CAAK;AAC5B,aAASF,IAAI,GAAGA,IAAIE,GAAOF,IAAK,CAAAG,EAAKH,CAAC,IAAIK,EAAK,UAAUL,IAAI,GAAG,EAAI;AAAA,EACtE,OAAO;AACL,QAAI,CAAC,MAAM,QAAQhB,EAAE,IAAI,KAAKA,EAAE,KAAK,WAAWkB,EAAO,OAAM,IAAIb,EAAW,UAAUL,EAAE,IAAI,eAAekB,CAAK,QAAQ;AACxH,IAAAC,IAAO,YAAY,KAAKnB,EAAE,IAAgB;AAAA,EAC5C;AACA,SAAOmB;AACT;AAEA,SAASG,EAAYlB,GAAoB;AACvC,QAAMmB,IAAiB;AAAA,IACrB,IAAIb,EAAIN,EAAE,IAAI,WAAW;AAAA,IACzB,MAAM,OAAOA,EAAE,QAAQ,EAAE;AAAA,IACzB,MAAM,OAAOA,EAAE,QAAQA,EAAE,SAAS,EAAE;AAAA,IACpC,GAAGM,EAAIN,EAAE,GAAG,UAAU;AAAA,IACtB,GAAGM,EAAIN,EAAE,GAAG,UAAU;AAAA,IACtB,OAAO,OAAOA,EAAE,SAAS,CAAC;AAAA,IAC1B,QAAQ,OAAOA,EAAE,UAAU,CAAC;AAAA,IAC5B,UAAU,OAAOA,EAAE,YAAY,CAAC;AAAA,IAChC,OAAOA,EAAE,UAAU;AAAA,IACnB,SAASA,EAAE,YAAY;AAAA,IACvB,YAAYG,EAAMH,EAAE,UAAU;AAAA,EAAA;AAEhC,SAAI,OAAOA,EAAE,OAAQ,aAAUmB,EAAI,MAAMnB,EAAE,MACvC,MAAM,QAAQA,EAAE,OAAO,QAAO,UAAUA,EAAE,QAAQ,IAAI,CAACK,OAAa,EAAE,GAAGC,EAAID,EAAE,GAAG,WAAW,GAAG,GAAGC,EAAID,EAAE,GAAG,WAAW,EAAA,EAAI,IACxHc;AACT;AAEA,SAASC,EAAYhB,GAAYlB,GAAwCa,GAAiBsB,IAAS,EAAE,SAAS,IAAM,SAAS,GAAG,GAAG,GAAG,GAAG,KAAW;AAClJ,MAAK,MAAM,QAAQjB,CAAI;AACvB,eAAWR,KAAKQ,GAAM;AACpB,YAAMkB,IAAUD,EAAO,WAAWzB,EAAE,YAAY,IAC1C2B,IAAUF,EAAO,WAAW,OAAOzB,EAAE,WAAY,WAAWA,EAAE,UAAU,IACxE4B,IAAUH,EAAO,IAAI,OAAOzB,EAAE,WAAW,CAAC,GAC1C6B,IAAUJ,EAAO,IAAI,OAAOzB,EAAE,WAAW,CAAC;AAChD,MAAIA,EAAE,SAAS,cACbG,EAAI,KAAK,EAAE,MAAM,SAAS,MAAM,OAAOH,EAAE,QAAQ,EAAE,GAAG,MAAMiB,EAAUjB,GAAGV,EAAI,QAAQA,EAAI,MAAM,GAAG,SAAAoC,GAAS,SAAAC,GAAS,SAAAC,GAAS,SAAAC,GAAS,YAAYtB,EAAMP,EAAE,UAAU,EAAA,CAAG,IAC9JA,EAAE,SAAS,gBACpBG,EAAI,KAAK,EAAE,MAAM,WAAW,MAAM,OAAOH,EAAE,QAAQ,EAAE,GAAG,UAAUA,EAAE,WAAW,CAAA,GAAI,IAAIsB,CAAW,GAAG,SAAAI,GAAS,YAAYnB,EAAMP,EAAE,UAAU,EAAA,CAAG,IACtIA,EAAE,SAAS,WACpBwB,EAAYxB,EAAE,QAAQV,GAAKa,GAAK,EAAE,SAAAuB,GAAS,SAAAC,GAAS,GAAGC,GAAS,GAAGC,EAAA,CAAS;AAAA,IAGhF;AACF;AAEA,SAASC,EAAaC,GAAkB;AACtC,MAAI,OAAOA,EAAE,UAAW,SAAU,OAAM,IAAI1B,EAAW,YAAY0B,EAAE,MAAM,2DAA2D;AACtI,MAAI,OAAOA,EAAE,SAAU,SAAU,OAAM,IAAI1B,EAAW,YAAY0B,EAAE,IAAI,yDAAyD;AACjI,QAAMC,wBAAqB,IAAA,GACrBC,wBAAiB,IAAA;AACvB,aAAWC,KAAQH,EAAE,SAAS,CAAA,GAAI;AAChC,UAAMI,IAAKzB,EAAIwB,EAAK,IAAI,SAAS;AACjC,IAAIA,EAAK,cAAYF,EAAe,IAAIG,GAAI5B,EAAM2B,EAAK,UAAU,CAAC,GAC9D,MAAM,QAAQA,EAAK,SAAS,KAAGD,EAAW,IAAIE,GAAID,EAAK,UAAU,IAAI,CAACE,OAAa,EAAE,QAAQ1B,EAAI0B,EAAE,QAAQ,kBAAkB,GAAG,UAAU1B,EAAI0B,EAAE,UAAU,oBAAoB,EAAA,EAAI,CAAC;AAAA,EACzL;AACA,SAAO;AAAA,IACL,MAAM,OAAOL,EAAE,QAAQ,EAAE;AAAA,IACzB,UAAUrB,EAAIqB,EAAE,UAAU,kBAAkB;AAAA,IAC5C,OAAOA,EAAE;AAAA,IACT,YAAYrB,EAAIqB,EAAE,YAAY,oBAAoB;AAAA,IAClD,aAAarB,EAAIqB,EAAE,aAAa,qBAAqB;AAAA,IACrD,WAAWrB,EAAIqB,EAAE,WAAW,mBAAmB;AAAA,IAC/C,YAAYrB,EAAIqB,EAAE,YAAY,oBAAoB;AAAA,IAClD,SAASrB,EAAIqB,EAAE,SAAS,iBAAiB;AAAA,IACzC,WAAWrB,EAAIqB,EAAE,WAAW,mBAAmB;AAAA,IAC/C,QAAQ,OAAOA,EAAE,UAAU,CAAC;AAAA,IAC5B,SAAS,OAAOA,EAAE,WAAW,CAAC;AAAA,IAC9B,gBAAAC;AAAA,IACA,YAAAC;AAAA,IACA,YAAY1B,EAAMwB,EAAE,UAAU;AAAA,EAAA;AAElC;AAGO,SAASM,EAAWC,GAAwB;AACjD,QAAMC,IAAID;AACV,MAAI,CAACC,KAAK,OAAOA,KAAM,SAAU,OAAM,IAAIlC,EAAW,mBAAmB;AACzE,MAAIkC,EAAE,SAAU,OAAM,IAAIlC,EAAW,0EAA0E;AAC/G,MAAIkC,EAAE,eAAeA,EAAE,gBAAgB,aAAc,OAAM,IAAIlC,EAAW,GAAGkC,EAAE,WAAW,0CAA0C;AACpI,QAAMC,IAAQ9B,EAAI6B,EAAE,OAAO,OAAO,GAC5BE,IAAS/B,EAAI6B,EAAE,QAAQ,QAAQ,GAC/BG,IAAqB,CAAA;AAC3B,EAAAlB,EAAYe,EAAE,QAAQ,EAAE,OAAAC,GAAO,QAAAC,EAAA,GAAUC,CAAM;AAC/C,QAAMpD,IAAe;AAAA,IACnB,OAAAkD;AAAA,IACA,QAAAC;AAAA,IACA,WAAW/B,EAAI6B,EAAE,WAAW,WAAW;AAAA,IACvC,YAAY7B,EAAI6B,EAAE,YAAY,YAAY;AAAA,IAC1C,QAAAG;AAAA,IACA,WAAWH,EAAE,YAAY,CAAA,GAAI,IAAIT,CAAY;AAAA,IAC7C,YAAYvB,EAAMgC,EAAE,UAAU;AAAA,EAAA;AAEhC,SAAI,OAAOA,EAAE,mBAAoB,aAAUjD,EAAI,kBAAkBiD,EAAE,kBAC5DjD;AACT;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CrossfadeTransition.js","sources":["../../src/transitions/CrossfadeTransition.ts"],"sourcesContent":["import { RenderTexture, Sprite } from 'pixi.js';\nimport type { Transition, TransitionContext } from './Transition';\nimport { tweenDone } from './tweenDone';\n\nexport interface CrossfadeOptions {\n /** Ms the old scene takes to fade out over the new one (default 400). */\n duration?: number;\n}\n\n/**\n * The old scene's last frame laid over the new scene and faded out. Two live scenes cannot both be drawn (the old\n * one is gone once the new one enters), so the old frame is captured into one screen-sized render texture, freed\n * when the transition ends. The new scene's `onEnter` runs under the still frame, so a long load reads as a pause.\n */\nexport class CrossfadeTransition implements Transition {\n private still: Sprite | null = null;\n private texture: RenderTexture | null = null;\n\n constructor(private readonly options: CrossfadeOptions = {}) {}\n\n async cover(ctx: TransitionContext): Promise<void> {\n const { app } = ctx;\n const texture = RenderTexture.create({ width: app.screen.width, height: app.screen.height, resolution: app.renderer.resolution });\n app.renderer.render({ container: app.stage, target: texture });\n this.texture = texture;\n this.still = new Sprite(texture);\n ctx.layer.addChild(this.still);\n }\n\n async reveal(ctx: TransitionContext): Promise<void> {\n if (!this.still) return;\n await tweenDone(ctx.stage, { targets: this.still, alpha: 0, duration: this.options.duration ?? 400, ease: 'Sine.InOut' });\n }\n\n dispose(): void {\n this.still?.destroy();\n this.still = null;\n this.texture?.destroy(true);\n this.texture = null;\n }\n}\n"],"names":["CrossfadeTransition","options","ctx","app","texture","RenderTexture","Sprite","tweenDone"],"mappings":";;AAcO,MAAMA,EAA0C;AAAA,EAIrD,YAA6BC,IAA4B,IAAI;AAAhC,SAAA,UAAAA;AAAA,EAAiC;AAAA,EAAjC;AAAA,EAHrB,QAAuB;AAAA,EACvB,UAAgC;AAAA,EAIxC,MAAM,MAAMC,GAAuC;AACjD,UAAM,EAAE,KAAAC,MAAQD,GACVE,IAAUC,EAAc,OAAO,EAAE,OAAOF,EAAI,OAAO,OAAO,QAAQA,EAAI,OAAO,QAAQ,YAAYA,EAAI,SAAS,YAAY;AAChI,IAAAA,EAAI,SAAS,OAAO,EAAE,WAAWA,EAAI,OAAO,QAAQC,GAAS,GAC7D,KAAK,UAAUA,GACf,KAAK,QAAQ,IAAIE,EAAOF,CAAO,GAC/BF,EAAI,MAAM,SAAS,KAAK,KAAK;AAAA,EAC/B;AAAA,EAEA,MAAM,OAAOA,GAAuC;AAClD,IAAK,KAAK,SACV,MAAMK,EAAUL,EAAI,OAAO,EAAE,SAAS,KAAK,OAAO,OAAO,GAAG,UAAU,KAAK,QAAQ,YAAY,KAAK,MAAM,cAAc;AAAA,EAC1H;AAAA,EAEA,UAAgB;AACd,SAAK,OAAO,QAAA,GACZ,KAAK,QAAQ,MACb,KAAK,SAAS,QAAQ,EAAI,GAC1B,KAAK,UAAU;AAAA,EACjB;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DitherWipeTransition.js","sources":["../../src/transitions/DitherWipeTransition.ts"],"sourcesContent":["import { CanvasSource, Container, Texture, TilingSprite } from 'pixi.js';\nimport type { Transition, TransitionContext } from './Transition';\nimport { DITHER_LEVELS, ditherCovered, wipeLevel } from './ditherMask';\nimport { tweenDone } from './tweenDone';\n\nexport interface DitherWipeOptions {\n /** `0xrrggbb` (default black). */\n color?: number;\n /** Ms for each half (default 450). */\n duration?: number;\n /** The way the wipe travels, or 'none' for the whole screen dissolving at once (default 'right'). */\n direction?: 'left' | 'right' | 'up' | 'down' | 'none';\n /** Screen pixels per dither cell (default the main camera's zoom, so a cell is one world pixel). */\n cell?: number;\n /** Share of the screen the dithered edge spans (default 0.35). */\n softness?: number;\n}\n\n/** One 4×4 tile per dither level, white on clear, shared by every wipe (17 tiny textures for the page's life). */\nlet patterns: Texture[] | null = null;\n\nfunction patternTextures(): Texture[] {\n if (patterns) return patterns;\n patterns = [];\n for (let level = 0; level <= DITHER_LEVELS; level++) {\n const canvas = document.createElement('canvas');\n canvas.width = 4;\n canvas.height = 4;\n const g = canvas.getContext('2d')!;\n g.fillStyle = '#ffffff';\n for (let y = 0; y < 4; y++) for (let x = 0; x < 4; x++) if (ditherCovered(level, x, y)) g.fillRect(x, y, 1, 1);\n patterns.push(new Texture({ source: new CanvasSource({ resource: canvas, scaleMode: 'nearest' }) }));\n }\n return patterns;\n}\n\n/**\n * A pixel-friendly wipe: the screen fills with an ordered-dither pattern whose density sweeps across it, in hard\n * whole cells rather than a smooth gradient. The screen is cut into bands along the direction of travel, each a\n * tiling sprite of one dither level, so a frame costs a texture swap per band rather than a rect per cell (a\n * 1080p screen at 2-px cells is half a million cells). Bands are whole tiles wide so the pattern runs on unbroken.\n */\nexport class DitherWipeTransition implements Transition {\n private readonly bands: TilingSprite[] = [];\n private root: Container | null = null;\n private stopResize: (() => void) | null = null;\n private readonly state = { p: 0 };\n private reversed = false;\n\n constructor(private readonly options: DitherWipeOptions = {}) {}\n\n async cover(ctx: TransitionContext): Promise<void> {\n this.root = new Container();\n ctx.layer.addChild(this.root);\n this.layout(ctx);\n this.stopResize = ctx.stage.onResize(() => this.layout(ctx));\n this.state.p = 0;\n this.reversed = false;\n await this.run(ctx, 1);\n }\n\n async reveal(ctx: TransitionContext): Promise<void> {\n if (!this.root) return;\n // the covered part retreats the way the wipe came in, so the sweep reads as one motion across the swap\n this.reversed = true;\n await this.run(ctx, 0);\n }\n\n dispose(): void {\n this.stopResize?.();\n this.stopResize = null;\n this.root?.destroy({ children: true });\n this.root = null;\n this.bands.length = 0;\n }\n\n private run(ctx: TransitionContext, to: number): Promise<void> {\n return tweenDone(ctx.stage, {\n targets: this.state,\n p: to,\n duration: this.options.duration ?? 450,\n ease: 'Linear',\n onUpdate: () => this.paint(),\n });\n }\n\n private layout(ctx: TransitionContext): void {\n const root = this.root!;\n for (const b of root.removeChildren()) b.destroy();\n this.bands.length = 0;\n const cell = Math.max(1, Math.round(this.options.cell ?? ctx.stage.cameras.main.zoom));\n const tile = cell * 4;\n const w = ctx.stage.width;\n const h = ctx.stage.height;\n const dir = this.options.direction ?? 'right';\n const horizontal = dir === 'left' || dir === 'right';\n const span = dir === 'none' ? 0 : horizontal ? w : h;\n // about 32 bands across, each a whole number of tiles\n const bandSize = dir === 'none' ? 0 : Math.max(tile, Math.ceil(span / 32 / tile) * tile);\n const count = dir === 'none' ? 1 : Math.ceil(span / bandSize);\n for (let i = 0; i < count; i++) {\n const bw = dir === 'none' || !horizontal ? w : bandSize;\n const bh = dir === 'none' || horizontal ? h : bandSize;\n const s = new TilingSprite({ texture: patternTextures()[0]!, width: bw, height: bh });\n s.tileScale.set(cell, cell);\n if (dir !== 'none') {\n if (horizontal) s.x = i * bandSize;\n else s.y = i * bandSize;\n }\n s.tint = this.options.color ?? 0x000000;\n root.addChild(s);\n this.bands.push(s);\n }\n this.paint();\n }\n\n private paint(): void {\n const dir = this.options.direction ?? 'right';\n const soft = dir === 'none' ? 1 : (this.options.softness ?? 0.35);\n const textures = patternTextures();\n const n = this.bands.length;\n for (let i = 0; i < n; i++) {\n let u = dir === 'none' ? 0.5 : (i + 0.5) / n;\n if (dir === 'left' || dir === 'up') u = 1 - u;\n if (this.reversed) u = 1 - u;\n const level = dir === 'none' ? Math.round(this.state.p * DITHER_LEVELS) : wipeLevel(this.state.p, u, soft);\n const band = this.bands[i]!;\n band.visible = level > 0;\n band.texture = textures[level]!;\n }\n }\n}\n"],"names":["patterns","patternTextures","level","DITHER_LEVELS","canvas","g","y","x","ditherCovered","Texture","CanvasSource","DitherWipeTransition","options","ctx","Container","to","tweenDone","root","b","cell","tile","w","h","dir","horizontal","span","bandSize","count","i","bw","bh","s","TilingSprite","soft","textures","n","u","wipeLevel","band"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FadeTransition.js","sources":["../../src/transitions/FadeTransition.ts"],"sourcesContent":["import { Sprite, Texture } from 'pixi.js';\nimport type { Transition, TransitionContext } from './Transition';\nimport { tweenDone } from './tweenDone';\n\nexport interface FadeOptions {\n /** `0xrrggbb` (default black). */\n color?: number;\n /** Ms for each half (default 300). */\n duration?: number;\n /** Ms to rest fully covered before revealing (default 0). */\n hold?: number;\n}\n\n/** Fades to a colour, swaps the scene, fades back. */\nexport class FadeTransition implements Transition {\n private sheet: Sprite | null = null;\n private stopResize: (() => void) | null = null;\n\n constructor(private readonly options: FadeOptions = {}) {}\n\n async cover(ctx: TransitionContext): Promise<void> {\n const sheet = new Sprite(Texture.WHITE);\n sheet.tint = this.options.color ?? 0x000000;\n sheet.alpha = 0;\n this.sheet = sheet;\n ctx.layer.addChild(sheet);\n const fit = () => {\n sheet.width = ctx.stage.width;\n sheet.height = ctx.stage.height;\n };\n fit();\n this.stopResize = ctx.stage.onResize(fit);\n await tweenDone(ctx.stage, { targets: sheet, alpha: 1, duration: this.options.duration ?? 300, ease: 'Sine.InOut' });\n }\n\n async reveal(ctx: TransitionContext): Promise<void> {\n if (!this.sheet) return;\n await tweenDone(ctx.stage, {\n targets: this.sheet,\n alpha: 0,\n delay: this.options.hold ?? 0,\n duration: this.options.duration ?? 300,\n ease: 'Sine.InOut',\n });\n }\n\n dispose(): void {\n this.stopResize?.();\n this.stopResize = null;\n this.sheet?.destroy();\n this.sheet = null;\n }\n}\n"],"names":["FadeTransition","options","ctx","sheet","Sprite","Texture","fit","tweenDone"],"mappings":";;AAcO,MAAMA,EAAqC;AAAA,EAIhD,YAA6BC,IAAuB,IAAI;AAA3B,SAAA,UAAAA;AAAA,EAA4B;AAAA,EAA5B;AAAA,EAHrB,QAAuB;AAAA,EACvB,aAAkC;AAAA,EAI1C,MAAM,MAAMC,GAAuC;AACjD,UAAMC,IAAQ,IAAIC,EAAOC,EAAQ,KAAK;AACtC,IAAAF,EAAM,OAAO,KAAK,QAAQ,SAAS,GACnCA,EAAM,QAAQ,GACd,KAAK,QAAQA,GACbD,EAAI,MAAM,SAASC,CAAK;AACxB,UAAMG,IAAM,MAAM;AAChB,MAAAH,EAAM,QAAQD,EAAI,MAAM,OACxBC,EAAM,SAASD,EAAI,MAAM;AAAA,IAC3B;AACA,IAAAI,EAAA,GACA,KAAK,aAAaJ,EAAI,MAAM,SAASI,CAAG,GACxC,MAAMC,EAAUL,EAAI,OAAO,EAAE,SAASC,GAAO,OAAO,GAAG,UAAU,KAAK,QAAQ,YAAY,KAAK,MAAM,cAAc;AAAA,EACrH;AAAA,EAEA,MAAM,OAAOD,GAAuC;AAClD,IAAK,KAAK,SACV,MAAMK,EAAUL,EAAI,OAAO;AAAA,MACzB,SAAS,KAAK;AAAA,MACd,OAAO;AAAA,MACP,OAAO,KAAK,QAAQ,QAAQ;AAAA,MAC5B,UAAU,KAAK,QAAQ,YAAY;AAAA,MACnC,MAAM;AAAA,IAAA,CACP;AAAA,EACH;AAAA,EAEA,UAAgB;AACd,SAAK,aAAA,GACL,KAAK,aAAa,MAClB,KAAK,OAAO,QAAA,GACZ,KAAK,QAAQ;AAAA,EACf;AACF;"}
|