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
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pixelPaint.js","sources":["../../src/pixel/pixelPaint.ts"],"sourcesContent":["import { batchFor, beginOutline, capturing, currentCoverage, dithered, endOutline, fillRuns, flushBatch, grade, pixelKey, ramp, record, stageRun } from './pixelTone';\nimport { PixelLayer } from './PixelLayer';\nimport type { Graphics } from '../core/Graphics';\n\n/*\n * Pixel painting shared by every kit's effects: every shape lands on the 1px world grid — the pixel size of the\n * character, prop and tile art, so an effect never looks chunkier than the body it sits on — and is redrawn at 12\n * frames per second. Every colour is pulled into the world's palette on the way out (`grade`). Shapes shade themselves from their colour's ramp (pixelTone.ts): rings are\n * brighter on the near side, thick arcs and discs carry a light edge and a deep underside, and strokes taper. Every\n * layer thins out with an ordered dither in its last frames instead of vanishing.\n */\n\nexport type Point = { x: number; y: number };\nexport const WHITE = 0xfff4d6;\nexport const snap = (n: number) => Math.round(n);\n\n/** A `size`-px square (1 by default: one art pixel) on the world grid, graded into the world palette. */\nexport function dot(g: Graphics, x: number, y: number, color: number, size = 1): void {\n const px = snap(x);\n const py = snap(y);\n if (dithered(px, py)) return;\n const c = grade(color);\n if (capturing()) {\n for (let dy = 0; dy < size; dy++) for (let dx = 0; dx < size; dx++) record(px + dx, py + dy, c);\n return;\n }\n const held = batchFor(g);\n if (held) {\n for (let dy = 0; dy < size; dy++) stageRun(held, px, py + dy, size, c);\n return;\n }\n g.fillStyle(c, 1).fillRect(px, py, size, size);\n}\n\n/**\n * Pixels a shape plots, the last plot of a pixel winning as it would on screen, laid when the shape is done as runs of\n * one tone (`fillRuns`) instead of a rect a pixel. A dither fade drops the same pixels `dot` would, and inside an\n * outline capture they are recorded like any `dot`.\n */\nclass Plot {\n private readonly at = new Map<number, number>();\n\n set(x: number, y: number, color: number): void {\n this.at.set(pixelKey(snap(x), snap(y)), color);\n }\n\n lay(g: Graphics): void {\n if (capturing()) {\n for (const [k, c] of this.at) dot(g, (k >>> 16) - 32768, (k & 0xffff) - 32768, c);\n return;\n }\n const held = batchFor(g);\n if (held instanceof PixelLayer) {\n // straight into the layer's staging: the keys are already one a pixel\n for (const [k, c] of this.at) {\n const x = (k >>> 16) - 32768;\n const y = (k & 0xffff) - 32768;\n if (!dithered(x, y)) held.stage(x, y, 1, grade(c));\n }\n return;\n }\n const graded = new Map<number, number>();\n for (const [k, c] of this.at) if (!dithered((k >>> 16) - 32768, (k & 0xffff) - 32768)) graded.set(k, grade(c));\n fillRuns(g, graded);\n }\n}\n\n/** A horizontal run of `w` px from (x, y), one pixel tall; drawn pixel by pixel while a dither fade or outline is on. */\nexport function hline(g: Graphics, x: number, y: number, w: number, color: number): void {\n const x0 = snap(x);\n const width = snap(w);\n if (width <= 0) return;\n if (!capturing() && !dithered(x0, y) && !dithered(x0 + 1, y) && !dithered(x0 + 2, y + 1) && !dithered(x0 + 3, y + 1)) {\n const held = batchFor(g);\n if (held) {\n stageRun(held, x0, snap(y), width, grade(color));\n return;\n }\n g.fillStyle(grade(color), 1).fillRect(x0, snap(y), width, 1);\n return;\n }\n for (let i = 0; i < width; i++) dot(g, x0 + i, y, color);\n}\n\n/** Paints `draw` with a dark 1px outline round everything it drew, like the outline on character and prop sprites. */\nexport function outlined(g: Graphics, outline: number, draw: () => void): void {\n beginOutline();\n try {\n draw();\n } finally {\n endOutline(g, outline);\n }\n}\n\n/**\n * A line `width` px thick built from 1px rows: the row on one side is lit, the far row deep, and a long stroke tapers\n * to the deep tone at both ends and catches a glint a third of the way along.\n */\nexport function stroke(g: Graphics, a: Point, b: Point, color: number, width = 2): void {\n const len = Math.hypot(b.x - a.x, b.y - a.y);\n const steps = Math.max(1, Math.ceil(len));\n const nx = len ? -(b.y - a.y) / len : 0;\n const ny = len ? (b.x - a.x) / len : 1;\n const r = ramp(color);\n const rows = Math.max(1, Math.round(width));\n for (let i = 0; i <= steps; i++) {\n const end = steps >= 10 && (i <= 1 || i >= steps - 1);\n const glint = steps >= 16 && Math.abs(i - Math.round(steps / 3)) <= 1;\n const x = a.x + ((b.x - a.x) * i) / steps;\n const y = a.y + ((b.y - a.y) * i) / steps;\n for (let k = 0; k < rows; k++) {\n const off = k - (rows - 1) / 2;\n const edge = rows >= 2 ? (k === 0 ? r.light : k === rows - 1 ? r.deep : color) : color;\n dot(g, x + nx * off, y + ny * off, end ? r.deep : glint && k === 0 ? r.hi : edge);\n }\n }\n}\n\n/** A broken ellipse of radius `r` in the ground's 2:1 perspective, lit on the near side, turning with `phase`. */\nexport function ring(g: Graphics, r: number, color: number, phase = 0, cy = 0): void {\n const tones = ramp(color);\n const n = Math.max(32, Math.ceil(r * 5));\n const plot = new Plot();\n for (let i = 0; i < n; i++) {\n const seg = ((((i * 64) / n + phase) % 16) + 16) % 16;\n if (seg < 3) continue;\n const a = (i * Math.PI * 2) / n;\n const near = Math.sin(a);\n // the near side of the ellipse is lit and a pixel thicker; the far side sinks into the deep tone\n const tone = seg < 4 ? tones.hi : near > 0.35 ? tones.light : near < -0.35 ? tones.deep : color;\n const x = Math.cos(a) * r;\n const y = cy + near * r * 0.5;\n plot.set(x, y, tone);\n if (near > 0.5) plot.set(x, y + 1, tones.deep);\n }\n plot.lay(g);\n}\n\n/** A faceted gem: light left face, base right face, deep lower tip and a glint on top once it is big enough. */\nexport function diamond(g: Graphics, x: number, y: number, r: number, color: number): void {\n const tones = ramp(color);\n for (let row = -r; row <= r; row++) {\n const width = Math.max(1, snap(r - Math.abs(row)));\n const left = snap(x - width / 2);\n if (row > r * 0.45) {\n hline(g, left, y + row, width, tones.deep);\n continue;\n }\n const half = Math.floor(width / 2);\n if (r < 3 || half < 1) hline(g, left, y + row, width, color);\n else {\n hline(g, left, y + row, half, tones.light);\n hline(g, left + half, y + row, width - half, color);\n }\n }\n if (r >= 5) dot(g, x - 1, y - r + 2, tones.hi);\n}\n\nexport function shards(g: Graphics, t: number, r: number, color: number, inward = false): void {\n for (let i = 0; i < 12; i++) {\n const a = i * Math.PI / 6 + (inward ? t * 1.8 : 0);\n const d = r * (inward ? 1 - t : t);\n dot(g, Math.cos(a) * d, -8 + Math.sin(a) * d * 0.6 - (inward ? 0 : Math.sin(t * Math.PI) * 8), i % 3 ? color : WHITE, i % 3 ? 1 : 2);\n }\n}\n\n/** A filled stepped disc of radius `r` (smoke, gas, puddles), in the ground's 2:1 perspective when `flat`. */\nexport function blob(g: Graphics, x: number, y: number, r: number, color: number, flat = false): void {\n const ry = Math.max(1, Math.round(flat ? r * 0.5 : r));\n const tones = ramp(color);\n for (let row = -ry; row <= ry; row++) {\n const half = snap(r * Math.sqrt(Math.max(0, 1 - (row / ry) ** 2)));\n if (half <= 0) continue;\n const down = (row + ry) / (2 * ry || 1);\n hline(g, x - half, y + row, half * 2, down > 0.7 && r >= 3 ? tones.deep : color);\n // a lit cap on the upper left\n if (r >= 3 && down < 0.35 && half >= 2) hline(g, x - half + 1, y + row, snap(half * 0.6), tones.light);\n }\n if (r >= 4) dot(g, x - r * 0.4, y - ry * 0.55, tones.hi);\n}\n\n/** A droplet thrown from (x, y) with velocity (vx, vy) px/s under gravity, `ms` after it left; null once it lands. */\nexport function drop(x: number, y: number, vx: number, vy: number, ms: number, ground = y + 8): Point | null {\n const t = ms / 1000;\n const py = y + vy * t + 260 * t * t;\n return py > ground && vy * t + 260 * t * t > 0 ? null : { x: x + vx * t, y: py };\n}\n\n/** A pixel X, `r` from its centre to a tip. */\nexport function xMark(g: Graphics, x: number, y: number, r: number, color: number): void {\n stroke(g, { x: x - r, y: y - r }, { x: x + r, y: y + r }, color);\n stroke(g, { x: x - r, y: y + r }, { x: x + r, y: y - r }, color);\n}\n\n/** A dash streak from `a` to `b`: a dark body with a bright core, eaten away from the start as `t` goes 0 to 1. */\nexport function dashStreak(g: Graphics, a: Point, b: Point, t: number, edge: number, core: number): void {\n const from = { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };\n stroke(g, from, b, edge, 4);\n stroke(g, from, b, core, 2);\n}\n\n/**\n * A puff of smoke or gas: a small stepped disc whose rim is dithered (every other pixel) so it reads soft, with a\n * lighter cap on its upper left. Solid discs the same size read as boulders or bushes.\n */\nexport function puffBlob(g: Graphics, x: number, y: number, r: number, body: number, light: number, frame = 0, opacity = 0.7): void {\n r = Math.round(r);\n if (r < 2) return;\n const tones = ramp(body);\n const ry = Math.max(2, Math.round(r * 0.85));\n // smoke is translucent: overlapping puffs thicken toward the middle of a cloud and the ground shows through its edge\n const alpha = opacity * currentCoverage();\n flushBatch();\n const x0 = snap(x);\n for (let row = -ry; row <= ry; row++) {\n const half = r * Math.sqrt(Math.max(0, 1 - (row / (ry + 0.5)) ** 2));\n // the same pixels as one rect each, laid as runs of one tone and alpha\n let runAt = 0;\n let runW = 0;\n let runTone = -1;\n let runAlpha = 0;\n const end = Math.ceil(half);\n for (let dx = -end; dx <= end; dx++) {\n let tone = -1;\n let a = 0;\n if (dx < end) {\n const edge = half - Math.abs(dx + 0.5);\n const noise = ((dx * 73856093) ^ (row * 19349663) ^ (frame * 83492791)) >>> 0;\n if (!(edge < 1 && noise % 2 === 0)) {\n const lit = row < -ry * 0.3 && dx < half * 0.2;\n tone = lit ? light : row > ry * 0.4 ? tones.deep : noise % 9 === 0 ? tones.light : body;\n a = edge < 1 ? alpha * 0.6 : alpha;\n }\n }\n if (tone === runTone && a === runAlpha && tone !== -1) {\n runW++;\n continue;\n }\n if (runTone !== -1) g.fillStyle(grade(runTone), runAlpha).fillRect(x0 + runAt, snap(y + row), runW, 1);\n runAt = dx;\n runW = 1;\n runTone = tone;\n runAlpha = a;\n }\n }\n}\n\n/** An arc of dots on an ellipse from angle `a0` to `a1` (flat: in the ground's 2:1 perspective), `thick` rows deep. */\nexport function arc(g: Graphics, x: number, y: number, r: number, a0: number, a1: number, color: number, flat = true, thick = 1): void {\n const steps = Math.max(2, Math.ceil(Math.abs(a1 - a0) * r));\n const tones = ramp(color);\n // `thick` counts the old 2px rows, so an arc keeps its weight; it is built from 1px rows\n thick *= 2;\n const plot = new Plot();\n for (let t = 0; t < thick; t++) {\n const rr = r - t;\n // a thick arc is lit on its outer edge and sinks to the deep tone inside; a thin one fades in from its tail\n const row = t === 0 ? tones.light : t === thick - 1 ? tones.deep : color;\n for (let i = 0; i <= steps; i++) {\n const a = a0 + ((a1 - a0) * i) / steps;\n const tone = i >= steps - 1 ? tones.hi : thick <= 2 && steps >= 12 && i < steps * 0.2 ? tones.deep : row;\n plot.set(x + Math.cos(a) * rr, y + Math.sin(a) * rr * (flat ? 0.5 : 1), tone);\n }\n }\n plot.lay(g);\n}\n\n/** Pale stepped dust puffs kicked up around (x, y) and settling over `t` 0..1. */\nexport function dust(g: Graphics, x: number, y: number, t: number, spread = 10, color = 0xc8b89a): void {\n for (let i = 0; i < 4; i++) {\n const a = i * 1.7 + 0.3;\n const size = Math.round(4 * (1 - t));\n if (size >= 1) blob(g, x + Math.cos(a) * spread * t, y - 2 + Math.sin(a) * spread * 0.4 * t - t * 4, size, i % 2 ? color : 0xe0d4bc);\n }\n}\n\n/** Pixel chunks flung out of (x, y) under gravity (rock, splinters): `ms` after the blow. */\nexport function debris(g: Graphics, x: number, y: number, count: number, speed: number, ms: number, colors: number[]): void {\n for (let i = 0; i < count; i++) {\n const a = (i / count) * Math.PI * 2 + 0.3;\n const v = speed * (0.6 + ((i * 7) % 5) * 0.1);\n const p = drop(x, y, Math.cos(a) * v, -70 - ((i * 17) % 40), ms, y + 6);\n if (!p) continue;\n const c = colors[i % colors.length]!;\n const py = p.y + (Math.sin(a) * v * 0.3 * ms) / 1000;\n if (i % 3) dot(g, p.x, py, c);\n else {\n // a chunk: body, deep underside and a glint\n dot(g, p.x, py, c, 2);\n dot(g, p.x, py + 2, ramp(c).deep, 2);\n dot(g, p.x, py, ramp(c).hi);\n }\n }\n}\n\n"],"names":["WHITE","snap","n","dot","g","x","y","color","size","px","py","dithered","c","grade","capturing","dy","dx","record","held","batchFor","stageRun","Plot","pixelKey","k","PixelLayer","graded","fillRuns","hline","w","x0","width","outlined","outline","draw","beginOutline","endOutline","stroke","a","b","len","steps","nx","ny","r","ramp","rows","i","end","glint","off","edge","ring","phase","cy","tones","plot","seg","near","tone","diamond","row","left","half","shards","t","inward","d","blob","flat","ry","down","drop","vx","vy","ms","ground","xMark","dashStreak","core","from","puffBlob","body","light","frame","opacity","alpha","currentCoverage","flushBatch","runAt","runW","runTone","runAlpha","noise","arc","a0","a1","thick","rr","dust","spread","debris","count","speed","colors","v","p"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pixelTone.js","sources":["../../src/pixel/pixelTone.ts"],"sourcesContent":["import type { Graphics } from '../core/Graphics';\nimport { PixelLayer } from './PixelLayer';\n\n/*\n * Colour ramps and ordered dithering for the pixel effects. A ramp turns one colour into five tones the way pixel\n * artists shade: shadows shift toward blue-violet and lose saturation, highlights shift toward yellow. The dither fade\n * drops pixels in a 4x4 Bayer order, so an effect thins out pixel by pixel instead of vanishing in one frame.\n */\n\nexport interface Ramp {\n /** Darkest: under-shade and outline pixels. */\n shade: number;\n deep: number;\n base: number;\n light: number;\n /** Near-white glint. */\n hi: number;\n}\n\nconst RAMPS = new Map<number, Ramp>();\n\nfunction toHsl(c: number): [number, number, number] {\n const r = ((c >> 16) & 255) / 255;\n const g = ((c >> 8) & 255) / 255;\n const b = (c & 255) / 255;\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const l = (max + min) / 2;\n if (max === min) return [0, 0, l];\n const d = max - min;\n const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n const h = max === r ? (g - b) / d + (g < b ? 6 : 0) : max === g ? (b - r) / d + 2 : (r - g) / d + 4;\n return [h / 6, s, l];\n}\n\nfunction fromHsl(h: number, s: number, l: number): number {\n const hue = (p: number, q: number, t: number) => {\n const u = (t + 1) % 1;\n if (u < 1 / 6) return p + (q - p) * 6 * u;\n if (u < 1 / 2) return q;\n if (u < 2 / 3) return p + (q - p) * (2 / 3 - u) * 6;\n return p;\n };\n const q = l < 0.5 ? l * (1 + s) : l + s - l * s;\n const p = 2 * l - q;\n const ch = (t: number) => Math.round(Math.max(0, Math.min(1, s === 0 ? l : hue(p, q, t))) * 255);\n return (ch(h + 1 / 3) << 16) | (ch(h) << 8) | ch(h - 1 / 3);\n}\n\n/** Hue moved `amount` (turns) toward `target`, the short way round. */\nfunction drift(h: number, target: number, amount: number): number {\n let d = target - h;\n if (d > 0.5) d -= 1;\n if (d < -0.5) d += 1;\n return (h + Math.sign(d) * Math.min(Math.abs(d), amount) + 1) % 1;\n}\n\n/**\n * Ramps picked by hand for the colours a game's effects are built on (`registerHandRamps`): an effect paints its\n * crescents, rings and trails in one material colour and lets the shapes shade themselves, so a hand ramp is how a\n * game's own colours get shades better than the formula's. `ramp` of one of these colours returns its hand ramp;\n * every other colour gets `derivedRamp`. `base` is always the colour itself.\n */\nconst HAND = new Map<number, Ramp>();\n\n/** Gives these colours ramps picked by hand (darkest `shade` … brightest `hi`); a colour registered again is replaced. */\nexport function registerHandRamps(ramps: Iterable<readonly [number, Omit<Ramp, 'base'>]> | Record<number, Omit<Ramp, 'base'>>): void {\n const entries = Symbol.iterator in ramps ? ramps : Object.entries(ramps).map(([c, r]) => [Number(c), r] as const);\n for (const [c, r] of entries) HAND.set(c, { ...r, base: c });\n}\n\nexport function ramp(color: number): Ramp {\n return HAND.get(color) ?? derivedRamp(color);\n}\n\n/** The formula's five tones for any colour: shadows cool and desaturate, highlights warm. */\nexport function derivedRamp(color: number): Ramp {\n let r = RAMPS.get(color);\n if (r) return r;\n const [h, s, l] = toHsl(color);\n const cool = drift(h, 0.72, 0.06);\n const warm = drift(h, 0.14, 0.04);\n r = {\n shade: fromHsl(cool, Math.min(1, s * 0.85), Math.max(0.08, l * 0.38)),\n deep: fromHsl(drift(h, 0.72, 0.03), s, Math.max(0.14, l * 0.66)),\n base: color,\n light: fromHsl(warm, s * 0.9, Math.min(0.9, l + (1 - l) * 0.45)),\n hi: fromHsl(warm, s * 0.5, Math.min(0.97, l + (1 - l) * 0.8)),\n };\n RAMPS.set(color, r);\n return r;\n}\n\nconst GRADED = new Map<number, number>();\n\n/**\n * The colour a pixel is actually painted with: effects are authored in bright, saturated colours, and this pulls them\n * into the world's palette (muted earth, grass and wood) — saturation down by a quarter, near-whites toned down and\n * warmed, so an effect sits in the scene instead of glowing on top of it. Glints keep most of their lift.\n */\nexport function grade(color: number): number {\n let out = GRADED.get(color);\n if (out !== undefined) return out;\n const [h, s, l] = toHsl(color);\n const lift = l > 0.82 ? 0.82 + (l - 0.82) * 0.55 : l;\n out = fromHsl(l > 0.82 ? drift(h, 0.12, 0.05) : h, s * (l > 0.82 ? 0.55 : 0.74), lift * 0.97);\n GRADED.set(color, out);\n return out;\n}\n\n/*\n * Outline capture: while one is open, `dot` and `hline` record pixels instead of painting them, and `endOutline`\n * paints a dark 1px rim round the recorded silhouette before the pixels themselves — the same outline the character\n * and prop sprites carry, for effects that have a shape (a shield, a skull, a flask, a bolt).\n */\nlet capture: Map<number, number> | null = null;\nconst key = (x: number, y: number) => ((x + 32768) << 16) | ((y + 32768) & 0xffff);\n\nexport function capturing(): Map<number, number> | null {\n return capture;\n}\n\n/** The four sides a rim pixel may lie on, as dx, dy pairs. */\nconst RIM_STEPS = [1, 0, -1, 0, 0, 1, 0, -1];\n\nexport function beginOutline(): void {\n capture = new Map();\n}\n\nexport function endOutline(g: Graphics, outline: number): void {\n const pixels = capture;\n capture = null;\n if (!pixels) return;\n // each rim pixel once, however many silhouette pixels it touches\n const rim = new Set<number>();\n for (const k of pixels.keys()) {\n const x = (k >>> 16) - 32768;\n const y = (k & 0xffff) - 32768;\n for (let i = 0; i < 8; i += 2) {\n const n = key(x + RIM_STEPS[i]!, y + RIM_STEPS[i + 1]!);\n if (!pixels.has(n)) rim.add(n);\n }\n }\n const ink = grade(outline);\n fillRuns(g, new Map([...rim].map((k) => [k, ink])));\n fillRuns(g, pixels);\n}\n\n/**\n * Lays pixels (keyed by `key`, each with its colour already graded) row by row, one rect for every run of one colour:\n * the same pixels as one rect each, at a fraction of the rects (single-pixel rects ran to 5,000–9,000 a frame for one\n * big ring or smoke, enough to be felt as lag).\n */\nexport function fillRuns(g: Graphics, pixels: Map<number, number>): void {\n if (batchG !== null && g === batchG) {\n if (direct) for (const [k, c] of pixels) direct.stage((k >>> 16) - 32768, (k & 0xffff) - 32768, 1, c);\n else for (const [k, c] of pixels) batch!.set(k, c);\n return;\n }\n layRuns(g, pixels);\n}\n\n/*\n * A frame's batch (`beginBatch` / `endBatch`): while one is open every solid pixel `dot`, `hline` and `fillRuns` paint goes into it,\n * the last write of a pixel winning as it would on screen, and it is laid as runs when the frame is done — so an\n * effect that plots a pixel at a time costs a rect a run, not a rect a pixel. A translucent fill (smoke, a heat wash)\n * calls `flushBatch` first, so what was painted before it stays under it.\n *\n * On a `PixelLayer` the batch is the layer's own staging: each pixel goes\n * straight into its list in the order painted and `commit` lays them, the later winning — the same pixels the map,\n * its sort and its runs gave, without them. Any other Graphics keeps the map.\n */\nlet batch: Map<number, number> | null = null;\nlet direct: PixelLayer | null = null;\nlet batchG: Graphics | null = null;\n/** Batches opened under this one (a layer painted from inside another's paint), each laid and closed on its own. */\nconst outer: { batch: Map<number, number> | null; direct: PixelLayer | null; g: Graphics | null }[] = [];\n\nexport function beginBatch(g: Graphics): void {\n outer.push({ batch, direct, g: batchG });\n if (g instanceof PixelLayer) {\n batch = null;\n direct = g;\n } else {\n batch = new Map();\n direct = null;\n }\n batchG = g;\n}\n\nexport function endBatch(): void {\n flushBatch();\n const was = outer.pop();\n batch = was?.batch ?? null;\n direct = was?.direct ?? null;\n batchG = was?.g ?? null;\n}\n\n/**\n * Where a solid pixel painted on `g` goes while its batch is open: the layer's staging, the batch's map, or nowhere\n * (paint it now). `stagePixel` writes into whichever it is.\n */\nexport function batchFor(g: Graphics): Map<number, number> | PixelLayer | null {\n if (batchG === null || g !== batchG) return null;\n return direct ?? batch;\n}\n\n/** Writes a solid run of `w` pixels at (x, y) into what `batchFor` returned. */\nexport function stageRun(held: Map<number, number> | PixelLayer, x: number, y: number, w: number, color: number): void {\n if (held instanceof PixelLayer) {\n held.stage(x, y, w, color);\n return;\n }\n for (let i = 0; i < w; i++) held.set(key(x + i, y), color);\n}\n\n/** Lays what the batch holds now, so a translucent fill that follows lies over it. */\nexport function flushBatch(): void {\n if (direct) {\n direct.commit();\n return;\n }\n if (!batch || !batchG || batch.size === 0) return;\n const held = batch;\n batch = new Map();\n layRuns(batchG, held);\n}\n\nfunction layRuns(g: Graphics, pixels: Map<number, number>): void {\n if (g instanceof PixelLayer) {\n // the layer lays its own runs; nothing between here and its commit is drawn\n for (const [k, c] of pixels) g.stage((k >>> 16) - 32768, (k & 0xffff) - 32768, 1, c);\n g.commit();\n return;\n }\n const keys = [...pixels.keys()].sort((a, b) => (a & 0xffff) - (b & 0xffff) || (a >>> 16) - (b >>> 16));\n let runX = 0;\n let runY = 0;\n let runW = 0;\n let runC = -1;\n for (const k of keys) {\n const x = (k >>> 16) - 32768;\n const y = (k & 0xffff) - 32768;\n const c = pixels.get(k)!;\n if (runW > 0 && y === runY && x === runX + runW && c === runC) {\n runW++;\n continue;\n }\n if (runW > 0) g.fillStyle(runC, 1).fillRect(runX, runY, runW, 1);\n runX = x;\n runY = y;\n runW = 1;\n runC = c;\n }\n if (runW > 0) g.fillStyle(runC, 1).fillRect(runX, runY, runW, 1);\n}\n\n/** The key `capturing()` and `fillRuns` file a world pixel under. */\nexport function pixelKey(x: number, y: number): number {\n return key(x, y);\n}\n\nexport function record(x: number, y: number, color: number): void {\n capture?.set(key(x, y), color);\n}\n\nconst BAYER = [0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5];\n\n/** 1 draws every pixel, 0 none; set around each paint so every shape thins out together. */\nlet coverage = 1;\n\nexport function setCoverage(value: number): void {\n coverage = value;\n}\n\n/** The current dither fade (1 while nothing fades), for translucent fills that fade with their layer. */\nexport function currentCoverage(): number {\n return coverage;\n}\n\n/** Whether the world pixel at (x, y) is dropped by the current dither fade. */\nexport function dithered(x: number, y: number): boolean {\n if (coverage >= 1) return false;\n const cx = (Math.round(x) & 3) >>> 0;\n const cy = (Math.round(y) & 3) >>> 0;\n return BAYER[cy * 4 + cx]! / 16 >= coverage;\n}\n"],"names":["RAMPS","toHsl","c","r","g","b","max","min","l","d","s","fromHsl","h","hue","p","q","t","u","ch","drift","target","amount","HAND","registerHandRamps","ramps","entries","ramp","color","derivedRamp","cool","warm","GRADED","grade","out","lift","capture","key","x","y","capturing","RIM_STEPS","beginOutline","endOutline","outline","pixels","rim","k","i","n","ink","fillRuns","batchG","direct","batch","layRuns","outer","beginBatch","PixelLayer","endBatch","flushBatch","was","batchFor","stageRun","held","w","keys","a","runX","runY","runW","runC","pixelKey","record","BAYER","coverage","setCoverage","value","currentCoverage","dithered","cx","cy"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"propKit.js","sources":["../../src/pixel/propKit.ts"],"sourcesContent":["import { P } from './palette';\nimport { type Color, PixelGrid, rgba } from './pixel';\nimport { hueShade, outlineOf } from './ramp';\n\n/*\n * Pieces for painting map props (trees, rocks, buildings) in code. A prop is anchored at the bottom-centre of its tile\n * footprint: `anchorX/anchorY` are pixel coordinates inside its canvas.\n */\n\nexport interface PropArt {\n canvas: HTMLCanvasElement;\n anchorX: number;\n anchorY: number;\n}\n\nexport function art(g: PixelGrid, anchorX: number, anchorY: number): PropArt {\n return { canvas: g.toCanvas(), anchorX, anchorY };\n}\n\n/**\n * The selective outline a prop takes, lit from the upper left like the map: the same ring a plain outline lays, but\n * full dark only where the silhouette meets the ground (under it) and on its shadow side (the right); along the top\n * and the left it is the darkest tone of the fill it borders (`outlineOf`), so a lit edge reads as the thing's own\n * shade rather than a sticker's line. Translucent pixels (a cast\n * shadow on the ground) are not part of the silhouette.\n */\nexport function litOutline(g: PixelGrid, dark: Color = P.outline): void {\n const { w, h } = g;\n const on = new Uint8Array(w * h);\n for (let i = 0; i < w * h; i++) on[i] = g.data[i]! >>> 24 === 255 ? 1 : 0;\n const at = (x: number, y: number) => x >= 0 && y >= 0 && x < w && y < h && on[y * w + x] === 1;\n for (let y = 0; y < h; y++)\n for (let x = 0; x < w; x++) {\n if (on[y * w + x]) continue;\n const above = at(x, y - 1);\n const left = at(x - 1, y);\n const right = at(x + 1, y);\n const below = at(x, y + 1);\n if (!(above || left || right || below)) continue;\n g.px(x, y, above || left ? dark : outlineOf(g.get(right ? x + 1 : x, right ? y : y + 1)));\n }\n}\n\n/**\n * A soft shadow thrown down and to the right onto the ground, laid only on empty pixels (call it after `litOutline`,\n * or the outline would ring the shadow): a darker `core` and a fainter `rim`, by default a translucent violet.\n */\nexport function groundShadow(g: PixelGrid, cx: number, cy: number, rx: number, ry: number, core: Color = SHADOW_CORE, rim: Color = SHADOW_RIM): void {\n for (let y = Math.floor(cy - ry); y <= Math.ceil(cy + ry); y++)\n for (let x = Math.floor(cx - rx); x <= Math.ceil(cx + rx); x++) {\n if (!g.inBounds(x, y) || g.alphaAt(x, y)) continue;\n const dx = (x + 0.5 - cx) / rx;\n const dy = (y + 0.5 - cy) / ry;\n const d = dx * dx + dy * dy;\n if (d <= 0.55) g.px(x, y, core);\n else if (d <= 1) g.px(x, y, rim);\n }\n}\n\nconst SHADOW_CORE = rgba(26, 22, 44, 78);\nconst SHADOW_RIM = rgba(26, 22, 44, 40);\n/*\n * ── Ramp-shaded pieces ──\n * `shadeBox` shades with hue-shifted ramps (`ramp.ts`) instead of `shade`; with `litOutline` above, a prop painted\n * with them looks like rig-drawn people standing next to it.\n */\n\n/**\n * Moves every opaque pixel of a box one step into shadow (or, with `f > 1`, into light) along its own hue: the eave's\n * shadow on a wall, the dark side of a chimney, a porch's shade. `mask` limits it to some pixels of the box.\n */\nexport function shadeBox(g: PixelGrid, x: number, y: number, w: number, h: number, f: number, mask?: (x: number, y: number) => boolean): void {\n for (let yy = y; yy < y + h; yy++)\n for (let xx = x; xx < x + w; xx++) {\n if (!g.inBounds(xx, yy)) continue;\n const c = g.get(xx, yy);\n if (!(c >>> 24) || (mask && !mask(xx, yy))) continue;\n g.px(xx, yy, hueShade(c, f, 'shadow'));\n }\n}\n"],"names":["art","g","anchorX","anchorY","litOutline","dark","P","w","h","on","i","at","x","y","above","left","right","below","outlineOf","groundShadow","cx","cy","rx","ry","core","SHADOW_CORE","rim","SHADOW_RIM","dx","dy","d","rgba","shadeBox","f","mask","yy","xx","c","hueShade"],"mappings":";;;AAeO,SAASA,EAAIC,GAAcC,GAAiBC,GAA0B;AAC3E,SAAO,EAAE,QAAQF,EAAE,SAAA,GAAY,SAAAC,GAAS,SAAAC,EAAA;AAC1C;AASO,SAASC,EAAWH,GAAcI,IAAcC,EAAE,SAAe;AACtE,QAAM,EAAE,GAAAC,GAAG,GAAAC,EAAA,IAAMP,GACXQ,IAAK,IAAI,WAAWF,IAAIC,CAAC;AAC/B,WAASE,IAAI,GAAGA,IAAIH,IAAIC,GAAGE,IAAK,CAAAD,EAAGC,CAAC,IAAIT,EAAE,KAAKS,CAAC,MAAO,OAAO,MAAM,IAAI;AACxE,QAAMC,IAAK,CAACC,GAAWC,MAAcD,KAAK,KAAKC,KAAK,KAAKD,IAAIL,KAAKM,IAAIL,KAAKC,EAAGI,IAAIN,IAAIK,CAAC,MAAM;AAC7F,WAASC,IAAI,GAAGA,IAAIL,GAAGK;AACrB,aAASD,IAAI,GAAGA,IAAIL,GAAGK,KAAK;AAC1B,UAAIH,EAAGI,IAAIN,IAAIK,CAAC,EAAG;AACnB,YAAME,IAAQH,EAAGC,GAAGC,IAAI,CAAC,GACnBE,IAAOJ,EAAGC,IAAI,GAAGC,CAAC,GAClBG,IAAQL,EAAGC,IAAI,GAAGC,CAAC,GACnBI,IAAQN,EAAGC,GAAGC,IAAI,CAAC;AACzB,OAAMC,KAASC,KAAQC,KAASC,MAChChB,EAAE,GAAGW,GAAGC,GAAGC,KAASC,IAAOV,IAAOa,EAAUjB,EAAE,IAAIe,IAAQJ,IAAI,IAAIA,GAAGI,IAAQH,IAAIA,IAAI,CAAC,CAAC,CAAC;AAAA,IAC1F;AACJ;AAMO,SAASM,EAAalB,GAAcmB,GAAYC,GAAYC,GAAYC,GAAYC,IAAcC,GAAaC,IAAaC,GAAkB;AACnJ,WAASd,IAAI,KAAK,MAAMQ,IAAKE,CAAE,GAAGV,KAAK,KAAK,KAAKQ,IAAKE,CAAE,GAAGV;AACzD,aAASD,IAAI,KAAK,MAAMQ,IAAKE,CAAE,GAAGV,KAAK,KAAK,KAAKQ,IAAKE,CAAE,GAAGV,KAAK;AAC9D,UAAI,CAACX,EAAE,SAASW,GAAGC,CAAC,KAAKZ,EAAE,QAAQW,GAAGC,CAAC,EAAG;AAC1C,YAAMe,KAAMhB,IAAI,MAAMQ,KAAME,GACtBO,KAAMhB,IAAI,MAAMQ,KAAME,GACtBO,IAAIF,IAAKA,IAAKC,IAAKA;AACzB,MAAIC,KAAK,OAAM7B,EAAE,GAAGW,GAAGC,GAAGW,CAAI,IACrBM,KAAK,KAAG7B,EAAE,GAAGW,GAAGC,GAAGa,CAAG;AAAA,IACjC;AACJ;AAEA,MAAMD,IAAcM,EAAK,IAAI,IAAI,IAAI,EAAE,GACjCJ,IAAaI,EAAK,IAAI,IAAI,IAAI,EAAE;AAW/B,SAASC,EAAS/B,GAAcW,GAAWC,GAAWN,GAAWC,GAAWyB,GAAWC,GAAgD;AAC5I,WAASC,IAAKtB,GAAGsB,IAAKtB,IAAIL,GAAG2B;AAC3B,aAASC,IAAKxB,GAAGwB,IAAKxB,IAAIL,GAAG6B,KAAM;AACjC,UAAI,CAACnC,EAAE,SAASmC,GAAID,CAAE,EAAG;AACzB,YAAME,IAAIpC,EAAE,IAAImC,GAAID,CAAE;AACtB,MAAI,EAAEE,MAAM,OAAQH,KAAQ,CAACA,EAAKE,GAAID,CAAE,KACxClC,EAAE,GAAGmC,GAAID,GAAIG,EAASD,GAAGJ,GAAG,QAAQ,CAAC;AAAA,IACvC;AACJ;"}
|
package/dist/pixel/ramp.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ramp.js","sources":["../../src/pixel/ramp.ts"],"sourcesContent":["import { type Color, channels, mix, rgba, shade } from './pixel';\n\n/*\n * Hue-shifted ramps: the one place a character's colours get their shadows and lights. A small pixel body cannot get\n * detail from extra pixels, so it gets it from colour — a shadow that only darkens reads as dirt, a light that only\n * whitens reads as chalk. Here a shadow drifts toward its material's cool (or, for skin and leather, warm red) hue\n * and gains a little saturation; a light drifts toward warm yellow and loses a little. Everything the rig paints\n * goes through `hueShade`.\n *\n * The materials below are built in; a game adds its own (glass, slime, bark…) with `registerMaterial`, or changes how a\n * built-in one shades by registering it again. A material nobody registered shades as cloth.\n */\n\n/** The materials every game starts with. */\nexport type BuiltinMaterial = 'cloth' | 'leather' | 'mail' | 'plate' | 'dragon' | 'fur' | 'skin' | 'hair' | 'gold' | 'gem' | 'shadow';\n/** A built-in material or one a game registered (`registerMaterial`). */\nexport type Material = BuiltinMaterial | (string & {});\n\n/** How a material's shadows and lights move its hue. */\nexport interface MaterialLight {\n /** Hue (degrees) a shadow drifts toward, and how far it drifts at a factor of 0.5. */\n cool: number;\n coolDeg: number;\n /** Saturation a shadow gains at a factor of 0.5. */\n coolSat: number;\n /** Hue a light drifts toward, and how far it drifts at a factor of 1.3. */\n warm: number;\n warmDeg: number;\n}\n\nconst LIGHT: Record<string, MaterialLight> = {\n // wool and linen: violet shadows, faintly warm lights\n cloth: { cool: 255, coolDeg: 14, coolSat: 0.06, warm: 50, warmDeg: 6 },\n // hide: its shadows go red-brown, never grey\n leather: { cool: 8, coolDeg: 12, coolSat: 0.08, warm: 45, warmDeg: 8 },\n // steel: blue shadows, lights close to white\n mail: { cool: 232, coolDeg: 16, coolSat: 0.07, warm: 52, warmDeg: 4 },\n plate: { cool: 235, coolDeg: 18, coolSat: 0.08, warm: 50, warmDeg: 6 },\n // scale: crimson-violet shadows, ember-orange lights\n dragon: { cool: 330, coolDeg: 16, coolSat: 0.06, warm: 36, warmDeg: 12 },\n fur: { cool: 262, coolDeg: 10, coolSat: 0.04, warm: 48, warmDeg: 6 },\n // skin: shadows go to blood-red, never to grey or green\n skin: { cool: 350, coolDeg: 14, coolSat: 0.09, warm: 42, warmDeg: 5 },\n hair: { cool: 262, coolDeg: 14, coolSat: 0.05, warm: 46, warmDeg: 10 },\n gold: { cool: 14, coolDeg: 14, coolSat: 0.05, warm: 58, warmDeg: 8 },\n gem: { cool: 262, coolDeg: 8, coolSat: 0.02, warm: 50, warmDeg: 2 },\n // the side turned away from the light, whatever it is made of (the far limbs)\n shadow: { cool: 255, coolDeg: 12, coolSat: 0.04, warm: 50, warmDeg: 0 },\n};\n\n/** Tones of one colour in one material, darkest to brightest. `base` is the colour itself. */\nexport interface Ramp {\n /** Under-edges, rivets, seams: the darkest tone that is still the material. */\n deep: Color;\n dark: Color;\n mid: Color;\n base: Color;\n light: Color;\n /** One specular pixel: a plate's glint, a strand of hair, a buckle. */\n glint: Color;\n}\n\n/** Five shading factors, darkest to brightest. */\nexport type RampFactors = [number, number, number, number, number];\n\n/** The factors each material's ramp is built from: metal is contrasty, fur and skin are soft. */\nconst RAMP_FACTORS: Record<string, RampFactors> = {\n cloth: [0.5, 0.66, 0.82, 1.12, 1.25],\n leather: [0.46, 0.62, 0.8, 1.16, 1.3],\n mail: [0.44, 0.6, 0.78, 1.22, 1.42],\n plate: [0.44, 0.56, 0.72, 1.2, 1.45],\n dragon: [0.42, 0.56, 0.74, 1.14, 1.32],\n fur: [0.56, 0.7, 0.84, 1.15, 1.26],\n skin: [0.6, 0.72, 0.86, 1.12, 1.24],\n hair: [0.5, 0.66, 0.8, 1.2, 1.36],\n gold: [0.42, 0.62, 0.8, 1.3, 1.5],\n gem: [0.32, 0.5, 0.78, 1.45, 1.65],\n shadow: [0.5, 0.66, 0.82, 1.12, 1.25],\n};\n\n/** Every registered material, built-in ones first. */\nexport const MATERIALS: readonly Material[] = Object.keys(LIGHT);\n\n/** Every factor a ramp or a painter shades by — what a recolour or a fine-pass mask has to recognise. */\nexport const RAMP_FACTOR_SET: readonly number[] = [...new Set(Object.values(RAMP_FACTORS).flat())];\n\n/**\n * Adds a material, or changes a built-in one: how its shadows and lights drift (`light`) and the five factors its\n * ramp is built from, darkest to brightest (`factors`, around 1: below darkens, above lifts). Register before painting\n * with it; tones already worked out for that material are forgotten.\n */\nexport function registerMaterial(name: string, material: { light: MaterialLight; factors: RampFactors }): void {\n LIGHT[name] = material.light;\n RAMP_FACTORS[name] = material.factors;\n if (!(MATERIALS as Material[]).includes(name)) (MATERIALS as Material[]).push(name);\n const factors = RAMP_FACTOR_SET as number[];\n for (const f of material.factors) if (!factors.includes(f)) factors.push(f);\n for (const cache of [CACHE, RAMPS] as Map<string, unknown>[]) for (const key of [...cache.keys()]) if (key.endsWith(`:${name}`)) cache.delete(key);\n}\n\nconst lightOf = (m: Material): MaterialLight => LIGHT[m] ?? LIGHT.cloth!;\nconst factorsOf = (m: Material): RampFactors => RAMP_FACTORS[m] ?? RAMP_FACTORS.cloth!;\n\nfunction toHsv(c: Color): [number, number, number] {\n const [r, g, b] = channels(c);\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const d = max - min;\n let h = 0;\n if (d > 0) {\n if (max === r) h = ((g - b) / d + 6) % 6;\n else if (max === g) h = (b - r) / d + 2;\n else h = (r - g) / d + 4;\n }\n return [h * 60, max === 0 ? 0 : d / max, max / 255];\n}\n\nfunction fromHsv(h: number, s: number, v: number, a: number): Color {\n const hh = (((h % 360) + 360) % 360) / 60;\n const i = Math.floor(hh);\n const f = hh - i;\n const p = v * (1 - s);\n const q = v * (1 - s * f);\n const t = v * (1 - s * (1 - f));\n const [r, g, b] = [[v, t, p], [q, v, p], [p, v, t], [p, q, v], [t, p, v], [v, p, q]][i % 6]!;\n return rgba(r! * 255, g! * 255, b! * 255, a);\n}\n\n/** Hue `h` moved up to `deg` degrees toward `target`, the short way round. */\nfunction drift(h: number, target: number, deg: number): number {\n let d = target - h;\n if (d > 180) d -= 360;\n if (d < -180) d += 360;\n return h + Math.sign(d) * Math.min(Math.abs(d), deg);\n}\n\nconst clamp01 = (x: number) => Math.max(0, Math.min(1, x));\nconst CACHE = new Map<string, Color>();\n\n/**\n * `c` shaded by `f` the way `shade` would (f < 1 darkens, f > 1 lifts toward white), with the hue moved the way\n * `material` moves it. `f === 1` returns `c` itself. A colour with too little saturation to have a hue (steel,\n * grey wool, white linen) takes the material's hue outright, faintly, so a grey coat still has a violet shadow.\n */\nexport function hueShade(c: Color, f: number, material: Material): Color {\n if (f === 1) return c;\n const key = `${c}:${f}:${material}`;\n let out = CACHE.get(key);\n if (out !== undefined) return out;\n const L = lightOf(material);\n const plain = shade(c, f);\n const [h, s, v] = toHsv(plain);\n const a = plain >>> 24;\n // how grey the colour is: a grey has no hue of its own to move, so it is tinted with the target hue instead —\n // drifting a grey's accidental hue partway round the wheel is how steel turned green in its lights\n const grey = clamp01(1 - toHsv(c)[1] / 0.14);\n let coloured: Color;\n let tinted: Color;\n if (f < 1) {\n const k = (1 - f) / 0.5;\n coloured = fromHsv(drift(h, L.cool, L.coolDeg * k), clamp01(s + L.coolSat * k), v, a);\n tinted = fromHsv(L.cool, clamp01(s + L.coolSat * k * 1.2), v, a);\n } else {\n const k = Math.min(1.5, (f - 1) / 0.3);\n // lifting toward white bleaches; a warm light keeps a little of its colour back\n coloured = fromHsv(drift(h, L.warm, L.warmDeg * k), clamp01(s * 1.08), v, a);\n tinted = fromHsv(L.warm, clamp01(s + 0.03 * k), v, a);\n }\n out = grey > 0 ? mix(coloured, tinted, grey) : coloured;\n CACHE.set(key, out);\n return out;\n}\n\nconst RAMPS = new Map<string, Ramp>();\n\nexport function ramp(c: Color, material: Material): Ramp {\n const key = `${c}:${material}`;\n let r = RAMPS.get(key);\n if (!r) {\n const [deep, dark, mid, light, glint] = factorsOf(material).map((f) => hueShade(c, f, material)) as [Color, Color, Color, Color, Color];\n r = { deep, dark, mid, base: c, light, glint };\n RAMPS.set(key, r);\n }\n return r;\n}\n\n/** Every tone of `c`'s ramp, for masking a material cue to that material. */\nexport function rampTones(c: Color, material: Material): Color[] {\n const r = ramp(c, material);\n return [r.deep, r.dark, r.mid, r.base, r.light, r.glint];\n}\n\nconst DEEP = new Map<Color, Color>();\n\n/**\n * The selective-outline tone of a fill: its own hue, cooled and pushed dark enough to hold an edge against grass\n * (value at most 0.3), so the outline on the lit side is the fill's own darkest shade instead of flat black.\n */\nexport function outlineOf(c: Color): Color {\n let d = DEEP.get(c);\n if (d === undefined) {\n const [h, s, v] = toHsv(hueShade(c, 0.42, 'shadow'));\n d = fromHsv(h, clamp01(s * 1.1 + 0.08), Math.min(v, 0.3), 255);\n DEEP.set(c, d);\n }\n return d;\n}\n"],"names":["LIGHT","RAMP_FACTORS","MATERIALS","RAMP_FACTOR_SET","registerMaterial","name","material","factors","f","cache","CACHE","RAMPS","key","lightOf","m","factorsOf","toHsv","c","r","g","b","channels","max","min","d","h","fromHsv","s","v","a","hh","i","p","q","t","rgba","drift","target","deg","clamp01","x","hueShade","out","L","plain","shade","grey","coloured","tinted","k","mix","ramp","deep","dark","mid","light","glint","rampTones","DEEP","outlineOf"],"mappings":"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"}
|
package/dist/pixel/selout.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"selout.js","sources":["../../src/pixel/selout.ts"],"sourcesContent":["import { P } from './palette';\nimport type { Color, PixelGrid } from './pixel';\nimport { outlineOf } from './ramp';\n\n/*\n * The selective outline (\"selout\") round an assembled body. It lands on exactly the pixels a plain outline would —\n * one ring outside the silhouette per grid pixel of `width` — so nothing about the silhouette changes, only the\n * colour of the ring: where the body turns away from the light (the back, facing left of a body facing right) and\n * where it stands on the ground (under the feet, under an arm) the ring stays full dark; on the lit side (the front\n * and the top of the head and shoulders) it takes the dark tone of the fill it borders (`outlineOf`). `dark` is the\n * full-dark ring's colour (the default palette's outline unless a game passes its own). A hard black\n * line all round a small pixel body is a sticker; a line that is the coat's own darkest shade on its lit side is a body.\n */\nexport function selectiveOutline(g: PixelGrid, width = 1, dark: Color = P.outline): void {\n const { w, h, data } = g;\n const solid = new Uint8Array(data.length);\n let x0 = w, y0 = h, x1 = -1, y1 = -1;\n for (let y = 0; y < h; y++) {\n const row = y * w;\n for (let x = 0; x < w; x++)\n if (data[row + x]! >>> 24) {\n solid[row + x] = 1;\n if (x < x0) x0 = x;\n if (x > x1) x1 = x;\n if (y < y0) y0 = y;\n y1 = y;\n }\n }\n if (x1 < 0) return;\n let frontier: number[] = [];\n for (let y = Math.max(0, y0 - 1); y <= Math.min(h - 1, y1 + 1); y++)\n for (let x = Math.max(0, x0 - 1); x <= Math.min(w - 1, x1 + 1); x++) {\n const i = y * w + x;\n if (solid[i]) continue;\n const above = y > 0 && solid[i - w] === 1;\n const right = x + 1 < w && solid[i + 1] === 1;\n const left = x > 0 && solid[i - 1] === 1;\n const below = y + 1 < h && solid[i + w] === 1;\n if (!(above || right || left || below)) continue;\n // under the body or behind it: full dark; in front of it or over it: the fill's own darkest tone\n data[i] = above || right ? dark : outlineOf(data[left ? i - 1 : i + w]!);\n frontier.push(i);\n }\n for (const i of frontier) solid[i] = 1;\n // a thicker outline (tools that paint above 1x) grows each ring from the one before it, keeping its colour\n for (let step = 1; step < width && frontier.length; step++) {\n const next: number[] = [];\n for (const i of frontier) {\n const x = i % w;\n const y = (i / w) | 0;\n const c = data[i]!;\n const grow = (ok: boolean, k: number) => {\n if (!ok || solid[k]) return;\n solid[k] = 1;\n data[k] = c;\n next.push(k);\n };\n grow(x > 0, i - 1);\n grow(x + 1 < w, i + 1);\n grow(y > 0, i - w);\n grow(y + 1 < h, i + w);\n }\n frontier = next;\n }\n}\n"],"names":["selectiveOutline","g","width","dark","P","w","h","data","solid","x0","y0","x1","y1","y","row","x","frontier","i","above","right","left","below","outlineOf","step","next","c","grow","ok","k"],"mappings":";;AAaO,SAASA,EAAiBC,GAAcC,IAAQ,GAAGC,IAAcC,EAAE,SAAe;AACvF,QAAM,EAAE,GAAAC,GAAG,GAAAC,GAAG,MAAAC,EAAA,IAASN,GACjBO,IAAQ,IAAI,WAAWD,EAAK,MAAM;AACxC,MAAIE,IAAKJ,GAAGK,IAAKJ,GAAGK,IAAK,IAAIC,IAAK;AAClC,WAASC,IAAI,GAAGA,IAAIP,GAAGO,KAAK;AAC1B,UAAMC,IAAMD,IAAIR;AAChB,aAASU,IAAI,GAAGA,IAAIV,GAAGU;AACrB,MAAIR,EAAKO,IAAMC,CAAC,MAAO,OACrBP,EAAMM,IAAMC,CAAC,IAAI,GACbA,IAAIN,MAAIA,IAAKM,IACbA,IAAIJ,MAAIA,IAAKI,IACbF,IAAIH,MAAIA,IAAKG,IACjBD,IAAKC;AAAA,EAEX;AACA,MAAIF,IAAK,EAAG;AACZ,MAAIK,IAAqB,CAAA;AACzB,WAASH,IAAI,KAAK,IAAI,GAAGH,IAAK,CAAC,GAAGG,KAAK,KAAK,IAAIP,IAAI,GAAGM,IAAK,CAAC,GAAGC;AAC9D,aAASE,IAAI,KAAK,IAAI,GAAGN,IAAK,CAAC,GAAGM,KAAK,KAAK,IAAIV,IAAI,GAAGM,IAAK,CAAC,GAAGI,KAAK;AACnE,YAAME,IAAIJ,IAAIR,IAAIU;AAClB,UAAIP,EAAMS,CAAC,EAAG;AACd,YAAMC,IAAQL,IAAI,KAAKL,EAAMS,IAAIZ,CAAC,MAAM,GAClCc,IAAQJ,IAAI,IAAIV,KAAKG,EAAMS,IAAI,CAAC,MAAM,GACtCG,IAAOL,IAAI,KAAKP,EAAMS,IAAI,CAAC,MAAM,GACjCI,IAAQR,IAAI,IAAIP,KAAKE,EAAMS,IAAIZ,CAAC,MAAM;AAC5C,OAAMa,KAASC,KAASC,KAAQC,OAEhCd,EAAKU,CAAC,IAAIC,KAASC,IAAQhB,IAAOmB,EAAUf,EAAKa,IAAOH,IAAI,IAAIA,IAAIZ,CAAC,CAAE,GACvEW,EAAS,KAAKC,CAAC;AAAA,IACjB;AACF,aAAWA,KAAKD,EAAU,CAAAR,EAAMS,CAAC,IAAI;AAErC,WAASM,IAAO,GAAGA,IAAOrB,KAASc,EAAS,QAAQO,KAAQ;AAC1D,UAAMC,IAAiB,CAAA;AACvB,eAAWP,KAAKD,GAAU;AACxB,YAAMD,IAAIE,IAAIZ,GACRQ,IAAKI,IAAIZ,IAAK,GACdoB,IAAIlB,EAAKU,CAAC,GACVS,IAAO,CAACC,GAAaC,MAAc;AACvC,QAAI,CAACD,KAAMnB,EAAMoB,CAAC,MAClBpB,EAAMoB,CAAC,IAAI,GACXrB,EAAKqB,CAAC,IAAIH,GACVD,EAAK,KAAKI,CAAC;AAAA,MACb;AACA,MAAAF,EAAKX,IAAI,GAAGE,IAAI,CAAC,GACjBS,EAAKX,IAAI,IAAIV,GAAGY,IAAI,CAAC,GACrBS,EAAKb,IAAI,GAAGI,IAAIZ,CAAC,GACjBqB,EAAKb,IAAI,IAAIP,GAAGW,IAAIZ,CAAC;AAAA,IACvB;AACA,IAAAW,IAAWQ;AAAA,EACb;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"spritePixels.js","sources":["../../src/pixel/spritePixels.ts"],"sourcesContent":["import type { Image } from '../core/Image';\n\n/** One opaque world pixel of a sprite frame: offset from the frame's feet (origin 0.5, 1) and its colour. */\nexport interface SpritePixel {\n dx: number;\n dy: number;\n color: number;\n}\n\n/**\n * A frame's opaque pixels packed in typed arrays (12 bytes a pixel instead of an object each). Iterating hands out one\n * reused `SpritePixel`, so read it in the loop and keep nothing of it; `at(i)` reads one into a given object.\n */\nexport class PackedPixels implements Iterable<SpritePixel> {\n constructor(\n readonly dx: Float32Array,\n readonly dy: Float32Array,\n readonly color: Uint32Array,\n readonly length: number,\n ) {}\n\n *[Symbol.iterator](): Iterator<SpritePixel> {\n const p: SpritePixel = { dx: 0, dy: 0, color: 0 };\n for (let i = 0; i < this.length; i++) {\n p.dx = this.dx[i]!;\n p.dy = this.dy[i]!;\n p.color = this.color[i]!;\n yield p;\n }\n }\n}\n\nconst EMPTY = new PackedPixels(new Float32Array(0), new Float32Array(0), new Uint32Array(0), 0);\n/** No pixels: what a body hands over when there is nothing of it worth lifting (a death with nothing to evaporate). */\nexport const NO_PIXELS = EMPTY;\n/**\n * The largest frame read, in image pixels: well over any sheet's (192x180 is about the largest). An image with no frame shows\n * its whole atlas page, and reading that — 4096x3520, 4.4 million pixels, ~110 MB a death — is what took mobile web views\n * down.\n */\nconst MAX_FRAME_AREA = 512 * 512;\n/** Frames kept, the least recently read going first once either bound is passed. */\nconst MAX_FRAMES = 256;\nconst MAX_PIXELS = 300_000;\nconst CACHE = new Map<string, PackedPixels>();\nlet cachedPixels = 0;\n\nfunction remember(key: string, packed: PackedPixels): void {\n CACHE.set(key, packed);\n cachedPixels += packed.length;\n while (CACHE.size > MAX_FRAMES || cachedPixels > MAX_PIXELS) {\n const oldest = CACHE.keys().next();\n if (oldest.done) break;\n cachedPixels -= CACHE.get(oldest.value)!.length;\n CACHE.delete(oldest.value);\n }\n}\n\n/**\n * The opaque pixels of an image's current frame, one per **world** pixel (`artScale` image pixels to one), as offsets\n * from the frame's feet in unscaled, unflipped world pixels. Read straight off the texture's canvas, so it is the\n * frame actually on screen. Atlas frames are shared by every body of one kind and cached (least recently read out\n * first); a texture repainted while in use (a rig body) is read fresh.\n */\nexport function spritePixels(img: Image, artScale: number, live: boolean): PackedPixels {\n const cacheKey = `${img.textureKey}|${img.frameName}|${artScale}`;\n if (!live) {\n const hit = CACHE.get(cacheKey);\n if (hit) {\n // most recently read goes to the back\n CACHE.delete(cacheKey);\n CACHE.set(cacheKey, hit);\n return hit;\n }\n }\n const tex = img.texture;\n const canvas = tex.source.resource as unknown;\n if (!(canvas instanceof HTMLCanvasElement)) return EMPTY;\n const f = tex.frame;\n const fw = Math.round(f.width);\n const fh = Math.round(f.height);\n if (fw <= 0 || fh <= 0 || fw * fh > MAX_FRAME_AREA) return EMPTY;\n let data: Uint8ClampedArray;\n try {\n data = canvas.getContext('2d')!.getImageData(Math.round(f.x), Math.round(f.y), fw, fh).data;\n } catch {\n return EMPTY;\n }\n const s = Math.max(1, Math.round(artScale));\n const ww = Math.floor(fw / s);\n const wh = Math.floor(fh / s);\n const half = Math.floor(s / 2);\n let n = 0;\n for (let wy = 0; wy < wh; wy++) for (let wx = 0; wx < ww; wx++) if (data[((wy * s + half) * fw + wx * s + half) * 4 + 3]! >= 128) n++;\n const dx = new Float32Array(n);\n const dy = new Float32Array(n);\n const color = new Uint32Array(n);\n let k = 0;\n for (let wy = 0; wy < wh; wy++) {\n for (let wx = 0; wx < ww; wx++) {\n const i = ((wy * s + half) * fw + wx * s + half) * 4;\n if (data[i + 3]! < 128) continue;\n dx[k] = wx - ww / 2;\n dy[k] = wy - wh;\n color[k] = (data[i]! << 16) | (data[i + 1]! << 8) | data[i + 2]!;\n k++;\n }\n }\n const out = new PackedPixels(dx, dy, color, n);\n if (!live) remember(cacheKey, out);\n return out;\n}\n"],"names":["PackedPixels","dx","dy","color","length","p","i","EMPTY","NO_PIXELS","MAX_FRAME_AREA","MAX_FRAMES","MAX_PIXELS","CACHE","cachedPixels","remember","key","packed","oldest","spritePixels","img","artScale","live","cacheKey","hit","tex","canvas","fw","fh","data","ww","wh","half","n","wy","wx","k","out"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"swayRig.js","sources":["../../src/pixel/swayRig.ts"],"sourcesContent":["/*\n * How a swaying sprite is cut up, and how far each cut moves. Pure (no Pixi, no canvas), so it is unit-tested.\n *\n * A tree, a bush, a reed bank or a banner is never repainted to sway: its frame — painted or loaded alike — is sliced\n * into rects. The `pieces` stay where they are; each `strip` is the same pixels moved sideways by a whole number of\n * world pixels. The strips partition the moving region and the pieces partition the rest, so no pixel is ever drawn\n * twice and none is lost; a strip that moves one pixel leaves a one-pixel step against its neighbour, which is how a\n * pixel artist draws a bough bending. `SwayProp` puts a rig on screen.\n */\n\n/**\n * - `crown`: a tree; the canopy above the trunk sways, the trunk found in the pixels (`trunkPivot`);\n * - `top`: the top `top` share of the frame sways (a bush, a reed clump);\n * - `hang`: a cloth hanging off a bar (`cloth`), each pair of rows swinging wider and later down it;\n * - `tufts`: only the given rects move (reeds painted into a bigger sprite), per variant.\n */\nexport type SwayMode = 'crown' | 'top' | 'hang' | 'tufts';\n\n/** [x0, y0, x1, y1) in the frame's pixels, and an optional weight (share of the full reach, default 1). */\nexport type TuftRect = readonly [number, number, number, number] | readonly [number, number, number, number, number];\n\nexport interface SwayDef {\n mode: SwayMode;\n /** `top`: the share of the frame's height, from its top, that moves. */\n top?: number;\n /** `hang`: the cloth, [x0, y0, x1, y1) in the frame's pixels. */\n cloth?: readonly [number, number, number, number];\n /** `tufts`: the moving rects per variant (\"*\" for every variant without its own). */\n tufts?: Readonly<Record<string, readonly TuftRect[]>>;\n /** Whole world pixels of reach at wind 1. */\n amp: number;\n /** Sway cycles a second. */\n speed: number;\n}\nexport interface Rect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nexport interface SwayStrip {\n rect: Rect;\n /** Share of the full reach this strip moves (the crown's top moves furthest, a banner's hem most). */\n weight: number;\n /** How far behind the top of the prop this strip swings, in cycles: the wave runs down a banner. */\n lag: number;\n}\n\nexport interface SwayRig {\n pieces: Rect[];\n strips: SwayStrip[];\n}\n\n/**\n * The first row of a tree's trunk (the canopy is every row above it), from the width of the opaque run on each row\n * of its sprite (0 = empty). The trunk is the longest run of narrow rows in the lower part of the sprite, \"narrow\"\n * meaning within two pixels of the narrowest row there — so a root flare at the foot does not end it. Null when\n * the sprite has no trunk worth the name (a bush, a heap of reeds).\n */\nexport function trunkPivot(widths: readonly number[]): number | null {\n const n = widths.length;\n let minW = Infinity;\n for (let y = Math.floor(n * 0.4); y < n; y++) if (widths[y]! > 0) minW = Math.min(minW, widths[y]!);\n if (!Number.isFinite(minW)) return null;\n const lim = minW + 2;\n let best: [number, number] | null = null;\n let start = -1;\n for (let y = Math.floor(n * 0.3); y <= n; y++) {\n const narrow = y < n && widths[y]! > 0 && widths[y]! <= lim;\n if (narrow && start < 0) start = y;\n if (!narrow && start >= 0) {\n if (!best || y - start > best[1] - best[0]) best = [start, y];\n start = -1;\n }\n }\n if (!best || best[1] - best[0] < 3) return null;\n let crown = 0;\n for (let y = 0; y < best[0]; y++) crown = Math.max(crown, widths[y]!);\n return crown >= lim * 1.5 ? best[0] : null;\n}\n\n/** A row band split in two: the top moving full reach, the lower half-way. One band when it is short. */\nfunction bands(from: number, to: number, w: number): SwayStrip[] {\n const h = to - from;\n if (h <= 0) return [];\n if (h < 20) return [{ rect: { x: 0, y: from, w, h }, weight: 1, lag: 0 }];\n const split = from + Math.round(h * 0.5);\n return [\n { rect: { x: 0, y: from, w, h: split - from }, weight: 1, lag: 0 },\n { rect: { x: 0, y: split, w, h: to - split }, weight: 0.55, lag: 0.04 },\n ];\n}\n\n/**\n * The frame `w`x`h` less the given rects (which must not overlap), as rects: cut into row bands at every rect's\n * top and bottom, and each band into the runs no rect covers.\n */\nexport function complement(w: number, h: number, holes: Rect[]): Rect[] {\n const ys = [...new Set([0, h, ...holes.flatMap((r) => [r.y, r.y + r.h])])].filter((y) => y >= 0 && y <= h).sort((a, b) => a - b);\n const out: Rect[] = [];\n for (let i = 0; i + 1 < ys.length; i++) {\n const y = ys[i]!;\n const bh = ys[i + 1]! - y;\n const cover = holes.filter((r) => r.y <= y && r.y + r.h >= y + bh).sort((a, b) => a.x - b.x);\n let x = 0;\n for (const r of cover) {\n if (r.x > x) out.push({ x, y, w: r.x - x, h: bh });\n x = Math.max(x, r.x + r.w);\n }\n if (x < w) out.push({ x, y, w: w - x, h: bh });\n }\n // the biggest still piece first: it becomes the placed image the map culls and fades\n return out.sort((a, b) => b.w * b.h - a.w * a.h);\n}\n\n/** The tuft rects of one variant (its own list, else the \"*\" one), clipped to the frame. */\nfunction tuftStrips(def: SwayDef, w: number, h: number, variant: number): SwayStrip[] {\n const list = def.tufts?.[String(variant)] ?? def.tufts?.['*'] ?? [];\n const strips: SwayStrip[] = [];\n for (const [x0, y0, x1raw, y1raw, weight = 1] of list) {\n const x1 = Math.min(w, x1raw);\n const y1 = Math.min(h, y1raw);\n if (x1 <= x0 || y1 <= y0) continue;\n // a reed's lower half follows its tip a beat late\n strips.push({ rect: { x: x0, y: y0, w: x1 - x0, h: y1 - y0 }, weight, lag: (1 - weight) * 0.08 });\n }\n return strips;\n}\n\n/**\n * The rig for one prop frame `w`x`h`. `rowWidths` is only read by a `crown`, whose pivot comes from the pixels\n * (without it, or without a trunk in them, the crown falls back to the top 60%). `variant` is only read by\n * `tufts`, whose rects are drawn per variant.\n */\nexport function buildRig(def: SwayDef, w: number, h: number, rowWidths?: readonly number[], variant = 0): SwayRig {\n if (def.mode === 'tufts') {\n const strips = tuftStrips(def, w, h, variant);\n return { pieces: strips.length ? complement(w, h, strips.map((s) => s.rect)) : [], strips };\n }\n if (def.mode === 'hang' && def.cloth) {\n const [x0, y0, x1raw, y1raw] = def.cloth;\n const x1 = Math.min(w, x1raw);\n const y1 = Math.min(h, y1raw);\n // the top third of the cloth hangs straight off its bar; below that each pair of rows swings wider\n const ys = y0 + Math.round((y1 - y0) * 0.3);\n const strips: SwayStrip[] = [];\n for (let y = ys; y < y1; y += 2) {\n const sh = Math.min(2, y1 - y);\n strips.push({ rect: { x: x0, y, w: x1 - x0, h: sh }, weight: (y + sh - ys) / (y1 - ys), lag: (y - ys) * 0.03 });\n }\n const pieces: Rect[] = [\n { x: 0, y: 0, w, h: ys },\n { x: 0, y: y1, w, h: h - y1 },\n { x: 0, y: ys, w: x0, h: y1 - ys },\n { x: x1, y: ys, w: w - x1, h: y1 - ys },\n ].filter((r) => r.w > 0 && r.h > 0);\n return { pieces, strips };\n }\n let pivot: number | null = null;\n if (def.mode === 'crown' && rowWidths) pivot = trunkPivot(rowWidths);\n if (pivot === null) pivot = Math.round(h * Math.min(0.9, def.mode === 'top' ? (def.top ?? 0.5) : 0.6));\n pivot = Math.max(1, Math.min(h - 1, pivot));\n return { pieces: [{ x: 0, y: pivot, w, h: h - pivot }], strips: bands(0, pivot, w) };\n}\n\n/** A stable [0, 1) per tile (or any two integers), so each prop keeps its own phase and pace from one visit to the next. */\nexport function tileHash(tx: number, ty: number, salt = 0): number {\n let h = Math.imul(tx + 0x9e37, 0x85ebca6b) ^ Math.imul(ty + 0x79b9 + salt * 131, 0xc2b2ae35);\n h ^= h >>> 15;\n h = Math.imul(h, 0x27d4eb2f);\n h ^= h >>> 13;\n return (h >>> 0) / 4294967296;\n}\n\n/** A gust rolling across the world from the west: every tree it passes bends a little further, then less. */\nexport function gustAt(tSec: number, worldX: number, gustSeconds: number, gustPx: number): number {\n return 0.65 + 0.35 * Math.sin(2 * Math.PI * (tSec / gustSeconds - worldX / gustPx));\n}\n\n/**\n * How many whole pixels a strip stands off at `tSec`: a main swing with a slower second one riding it (so no two\n * trees ever meet in step), leaning a little downwind as the wind rises, scaled by the reach, the wind, the gust\n * and the strip's weight, and rounded — the sway moves in pixel steps, 0 → 1 → 0, never smeared between them.\n *\n * `minReach` is the least that reach × wind × gust may come to while any wind blows (a still place's\n * floor, chosen by the game): without it a faint breeze never rounds to a whole pixel and the trees stand dead still, and\n * rounding is the rule, so the floor is what lets the crest of a swing tick one pixel over and back.\n */\nexport function swayOffset(\n tSec: number,\n speed: number,\n phase: number,\n lag: number,\n amp: number,\n wind: number,\n gust: number,\n weight: number,\n minReach = 0,\n): number {\n if (wind <= 0) return 0;\n const c = tSec * speed + phase - lag;\n const wave = 0.3 * wind + 0.72 * Math.sin(2 * Math.PI * c) + 0.28 * Math.sin(2 * Math.PI * (c * 0.43 + phase * 1.7));\n const reach = Math.max(amp * wind * gust, minReach);\n const v = Math.round(reach * weight * wave);\n return v === 0 ? 0 : v;\n}\n"],"names":["trunkPivot","widths","n","minW","y","lim","best","start","narrow","crown","bands","from","to","w","h","split","complement","holes","ys","r","a","b","out","i","bh","cover","x","tuftStrips","def","variant","list","strips","x0","y0","x1raw","y1raw","weight","x1","y1","buildRig","rowWidths","s","sh","pivot","tileHash","tx","ty","salt","gustAt","tSec","worldX","gustSeconds","gustPx","swayOffset","speed","phase","lag","amp","wind","gust","minReach","c","wave","reach","v"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vignetteMask.js","sources":["../../src/pixel/vignetteMask.ts"],"sourcesContent":["/**\n * The pure half of `DitherVignette`: which pixels of a `w`×`h` screen (in world pixels) the edge sets, its falloff an\n * ordered 4×4 Bayer dither rather than a gradient — the grammar `Darkness` lights the night in, so the edge reads as\n * pixel art and not as a blur over it.\n */\n\nconst BAYER = [0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5].map((v) => (v + 0.5) / 16);\n\n/** How strongly a pixel `d` of the way from the centre to the corner is darkened: 0 inside `from`, 1 at the corner. */\nfunction edge(d: number, from: number): number {\n if (d <= from) return 0;\n const t = Math.min(1, (d - from) / (1 - from));\n return t * t;\n}\n\n/**\n * One byte a pixel, 1 where the edge is set. `strength` (0..1) is the share of pixels set at the very corner; `from`\n * (0..1) is how far from the centre toward a corner the edge begins.\n */\nexport function vignetteMask(w: number, h: number, strength: number, from = 0.55): Uint8Array {\n const tw = Math.max(1, Math.ceil(w));\n const th = Math.max(1, Math.ceil(h));\n const out = new Uint8Array(tw * th);\n for (let y = 0; y < th; y++)\n for (let x = 0; x < tw; x++) {\n const dx = (x + 0.5) / tw - 0.5;\n const dy = (y + 0.5) / th - 0.5;\n // squared distance against squared bounds: no root for the many pixels inside `from`\n const d2 = (dx * dx + dy * dy) * 2;\n if (d2 <= from * from) continue;\n if (edge(Math.sqrt(d2), from) * strength <= BAYER[(y & 3) * 4 + (x & 3)]!) continue;\n out[y * tw + x] = 1;\n }\n return out;\n}\n\n/**\n * A pulse's opacity at `now` (ms): a quick rise and a slower fall, a beat every `slowMs` at `urgency` 0 quickening to\n * every `fastMs` at 1, deeper as it quickens, in `steps` levels rather than a smooth swell (0 for smooth).\n */\nexport function heartbeat(now: number, urgency: number, steps = 6, slowMs = 1000, fastMs = 500): number {\n const u = Math.max(0, Math.min(1, urgency));\n const period = slowMs - u * (slowMs - fastMs);\n const t = (((now % period) + period) % period) / period;\n const beat = t < 0.15 ? t / 0.15 : Math.max(0, 1 - (t - 0.15) / 0.55);\n const v = (0.35 + u * 0.4) * (0.45 + 0.55 * beat);\n return steps > 0 ? Math.round(v * steps) / steps : v;\n}\n"],"names":["BAYER","v","edge","d","from","t","vignetteMask","w","h","strength","tw","th","out","y","x","dx","dy","d2","heartbeat","now","urgency","steps","slowMs","fastMs","u","period","beat"],"mappings":"AAMA,MAAMA,IAAQ,CAAC,GAAG,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC,EAAE,IAAI,CAACC,OAAOA,IAAI,OAAO,EAAE;AAG9F,SAASC,EAAKC,GAAWC,GAAsB;AAC7C,MAAID,KAAKC,EAAM,QAAO;AACtB,QAAMC,IAAI,KAAK,IAAI,IAAIF,IAAIC,MAAS,IAAIA,EAAK;AAC7C,SAAOC,IAAIA;AACb;AAMO,SAASC,EAAaC,GAAWC,GAAWC,GAAkBL,IAAO,MAAkB;AAC5F,QAAMM,IAAK,KAAK,IAAI,GAAG,KAAK,KAAKH,CAAC,CAAC,GAC7BI,IAAK,KAAK,IAAI,GAAG,KAAK,KAAKH,CAAC,CAAC,GAC7BI,IAAM,IAAI,WAAWF,IAAKC,CAAE;AAClC,WAASE,IAAI,GAAGA,IAAIF,GAAIE;AACtB,aAASC,IAAI,GAAGA,IAAIJ,GAAII,KAAK;AAC3B,YAAMC,KAAMD,IAAI,OAAOJ,IAAK,KACtBM,KAAMH,IAAI,OAAOF,IAAK,KAEtBM,KAAMF,IAAKA,IAAKC,IAAKA,KAAM;AACjC,MAAIC,KAAMb,IAAOA,KACbF,EAAK,KAAK,KAAKe,CAAE,GAAGb,CAAI,IAAIK,KAAYT,GAAOa,IAAI,KAAK,KAAKC,IAAI,EAAE,MACvEF,EAAIC,IAAIH,IAAKI,CAAC,IAAI;AAAA,IACpB;AACF,SAAOF;AACT;AAMO,SAASM,EAAUC,GAAaC,GAAiBC,IAAQ,GAAGC,IAAS,KAAMC,IAAS,KAAa;AACtG,QAAMC,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,GAAGJ,CAAO,CAAC,GACpCK,IAASH,IAASE,KAAKF,IAASC,IAChC,KAAOJ,IAAMM,IAAUA,KAAUA,IAAUA,GAC3CC,IAAO,IAAI,OAAO,IAAI,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,QAAQ,IAAI,GAC9DzB,KAAK,OAAOuB,IAAI,QAAQ,OAAO,OAAOE;AAC5C,SAAOL,IAAQ,IAAI,KAAK,MAAMpB,IAAIoB,CAAK,IAAIA,IAAQpB;AACrD;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"weatherArt.js","sources":["../../src/pixel/weatherArt.ts"],"sourcesContent":["import type { TextureRegistry } from '../core/TextureRegistry';\nimport type { WeatherLook } from '../ambient/WeatherLayer';\n\n/** A pixel of weather art: x, y and a CSS colour. */\nexport type WeatherPixel = readonly [x: number, y: number, color: string];\n\n/** The colours the weather is painted in; every one may be replaced to suit a game's palette. */\nexport interface WeatherColors {\n rainHead: string;\n rainBody: string;\n rainTail: string;\n snow: string;\n snowShade: string;\n ashLight: string;\n ash: string;\n ashDark: string;\n ember: string;\n emberDeep: string;\n mist: string;\n}\n\nexport const WEATHER_COLORS: WeatherColors = {\n rainHead: 'rgba(214,230,248,0.85)',\n rainBody: 'rgba(168,190,216,0.7)',\n rainTail: 'rgba(128,150,180,0.5)',\n snow: '#f6f9ff',\n snowShade: '#b8c8e0',\n ashLight: '#8a827c',\n ash: '#5c5652',\n ashDark: '#3e3a38',\n ember: '#ffa040',\n emberDeep: '#c0501c',\n mist: 'rgba(226,232,222,0.62)',\n};\n\n/** A bank of mist: pixels on a checkerboard, denser toward its middle, ragged at every edge. Pure and seeded. */\nexport function mistPixels(w: number, h: number, seed: number, color: string): WeatherPixel[] {\n const px: WeatherPixel[] = [];\n let r = seed;\n const rand = () => {\n r = (Math.imul(r, 1103515245) + 12345) & 0x7fffffff;\n return r / 0x7fffffff;\n };\n for (let y = 0; y < h; y++)\n for (let x = 0; x < w; x++) {\n if ((x + y) % 2) continue;\n const nx = (x - w / 2) / (w / 2);\n const ny = (y - h / 2) / (h / 2);\n const d = 1 - (nx * nx + ny * ny);\n if (d > 0 && rand() < d * 0.9) px.push([x, y, color]);\n }\n return px;\n}\n\nfunction paint(textures: TextureRegistry, key: string, w: number, h: number, pixels: readonly WeatherPixel[]): void {\n if (textures.exists(key)) return;\n const c = document.createElement('canvas');\n c.width = w;\n c.height = h;\n const g = c.getContext('2d');\n if (!g) return;\n for (const [x, y, color] of pixels) {\n g.fillStyle = color;\n g.fillRect(x, y, 1, 1);\n }\n textures.addCanvas(key, c);\n}\n\n/**\n * Paints a set of weather in whole world pixels, flat colours and never a soft glow (a rain streak is the size of a\n * pixel of the roof it falls past; light from the up-left, so a flake's lit pixel is its top-left one), and returns\n * looks ready for `WeatherLayer`: `rain`, `snow`, `ash` (with an ember now and then) and `fog`. Painted once; a key\n * already in the registry is kept.\n */\nexport function paintWeatherArt(\n textures: TextureRegistry,\n colors: Partial<WeatherColors> = {},\n prefix = 'wx_',\n): Record<'rain' | 'snow' | 'ash' | 'fog', WeatherLook> {\n const c = { ...WEATHER_COLORS, ...colors };\n const k = (name: string) => prefix + name;\n paint(textures, k('rain_0'), 2, 5, [[1, 0, c.rainHead], [1, 1, c.rainBody], [1, 2, c.rainBody], [0, 3, c.rainTail], [0, 4, c.rainTail]]);\n paint(textures, k('rain_1'), 2, 4, [[1, 0, c.rainHead], [1, 1, c.rainBody], [0, 2, c.rainBody], [0, 3, c.rainTail]]);\n // the splash: a drop's crown, then two beads thrown wide\n paint(textures, k('splash_0'), 5, 2, [[2, 0, c.rainHead], [1, 1, c.rainBody], [3, 1, c.rainBody]]);\n paint(textures, k('splash_1'), 5, 2, [[0, 0, c.rainTail], [4, 0, c.rainTail], [2, 1, c.rainTail]]);\n paint(textures, k('snow_0'), 1, 1, [[0, 0, c.snow]]);\n paint(textures, k('snow_1'), 2, 2, [[0, 0, c.snow], [1, 0, c.snow], [0, 1, c.snow], [1, 1, c.snowShade]]);\n paint(textures, k('snow_2'), 3, 3, [[1, 0, c.snow], [0, 1, c.snow], [1, 1, c.snow], [2, 1, c.snowShade], [1, 2, c.snowShade]]);\n paint(textures, k('ash_0'), 2, 1, [[0, 0, c.ashLight], [1, 0, c.ash]]);\n paint(textures, k('ash_1'), 2, 2, [[0, 0, c.ash], [1, 1, c.ashDark]]);\n paint(textures, k('ash_2'), 1, 2, [[0, 0, c.ember], [0, 1, c.emberDeep]]);\n paint(textures, k('fog_0'), 72, 14, mistPixels(72, 14, 7, c.mist));\n paint(textures, k('fog_1'), 56, 12, mistPixels(56, 12, 31, c.mist));\n return {\n rain: { motion: 'fall', max: 170, keys: [k('rain_0'), k('rain_1')], splash: [k('splash_0'), k('splash_1')], tint: 0x40506a, tintAlpha: 0.1 },\n // the big flake one in five, the big ash an ember one in seven\n snow: { motion: 'flake', max: 120, keys: [k('snow_2'), k('snow_1'), k('snow_0'), k('snow_1'), k('snow_0')], sway: 5, tint: 0xd8e4f0, tintAlpha: 0.08 },\n ash: {\n motion: 'flake',\n max: 90,\n keys: [k('ash_2'), k('ash_1'), k('ash_0'), k('ash_1'), k('ash_0'), k('ash_1'), k('ash_0')],\n vx: [12, 24],\n vy: [10, 22],\n sway: 3,\n tint: 0x5a4a40,\n tintAlpha: 0.08,\n },\n fog: { motion: 'bank', max: 12, keys: [k('fog_0'), k('fog_1')], tint: 0xc8d0c4, tintAlpha: 0.24 },\n };\n}\n"],"names":["WEATHER_COLORS","mistPixels","w","h","seed","color","px","r","rand","y","x","nx","ny","d","paint","textures","key","pixels","c","g","paintWeatherArt","colors","prefix","k","name"],"mappings":"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"}
|
package/dist/pixel.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pixel.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"touch.js","sources":["../../src/platform/touch.ts"],"sourcesContent":["import { detectTouch, type TouchEnv } from '../input/touchDetect';\n\nlet cached: boolean | null = null;\nlet nativeShell = false;\nlet overrideKey = 'engine.touch';\n\n/** A native shell (Capacitor, Electron touch kiosk…) tells the engine it is one; browsers leave it false. */\nexport function setNativeShell(native: boolean): void {\n nativeShell = native;\n cached = null;\n}\n\n/**\n * The `localStorage` key that forces the touch controls on or off (`'1'`/`'0'`, beside `?touch=1|0` in the URL), for\n * trying them on a desktop browser. A game names its own so its players' saved choice carries over.\n */\nexport function setTouchOverrideKey(key: string): void {\n overrideKey = key;\n cached = null;\n}\n\nfunction readOverride(): string | null {\n try {\n const fromUrl = new URLSearchParams(window.location.search).get('touch');\n if (fromUrl !== null) return fromUrl;\n return localStorage.getItem(overrideKey);\n } catch {\n return null;\n }\n}\n\nfunction readEnv(): TouchEnv {\n return {\n native: nativeShell,\n coarse: typeof matchMedia === 'function' && matchMedia('(pointer: coarse)').matches,\n touchPoints: typeof navigator !== 'undefined' ? navigator.maxTouchPoints ?? 0 : 0,\n override: readOverride(),\n };\n}\n\n/** True when the game is played with fingers (decided once per launch, `detectTouch`). */\nexport function isTouchUi(): boolean {\n if (cached === null) cached = typeof window !== 'undefined' && detectTouch(readEnv());\n return cached;\n}\n"],"names":["cached","nativeShell","overrideKey","setNativeShell","native","setTouchOverrideKey","key","readOverride","fromUrl","readEnv","isTouchUi","detectTouch"],"mappings":";AAEA,IAAIA,IAAyB,MACzBC,IAAc,IACdC,IAAc;AAGX,SAASC,EAAeC,GAAuB;AACpD,EAAAH,IAAcG,GACdJ,IAAS;AACX;AAMO,SAASK,EAAoBC,GAAmB;AACrD,EAAAJ,IAAcI,GACdN,IAAS;AACX;AAEA,SAASO,IAA8B;AACrC,MAAI;AACF,UAAMC,IAAU,IAAI,gBAAgB,OAAO,SAAS,MAAM,EAAE,IAAI,OAAO;AACvE,WAAIA,MAAY,OAAaA,IACtB,aAAa,QAAQN,CAAW;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAASO,IAAoB;AAC3B,SAAO;AAAA,IACL,QAAQR;AAAA,IACR,QAAQ,OAAO,cAAe,cAAc,WAAW,mBAAmB,EAAE;AAAA,IAC5E,aAAa,OAAO,YAAc,MAAc,UAAU,kBAAkB,IAAI;AAAA,IAChF,UAAUM,EAAA;AAAA,EAAa;AAE3B;AAGO,SAASG,IAAqB;AACnC,SAAIV,MAAW,SAAMA,IAAS,OAAO,SAAW,OAAeW,EAAYF,GAAS,IAC7ET;AACT;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"BloomFilter.js","sources":["../../src/postfx/BloomFilter.ts"],"sourcesContent":["import { BlurFilter, TexturePool, type FilterSystem, type RenderSurface, type Texture } from 'pixi.js';\nimport { FxFilter } from './fxShader';\n\nexport interface BloomOptions {\n /** Brightness (0–1, the brightest channel) from which a pixel glows (default 0.7). */\n threshold?: number;\n /** How gradually the glow comes in above the threshold (default 0.2). */\n knee?: number;\n /** How strongly the glow is added back (default 1). */\n intensity?: number;\n /** Blur strength of the glow, pixels (default 8). */\n blur?: number;\n /** Blur quality: passes (default 4). */\n quality?: number;\n}\n\n/** The bright pass: what is above the threshold, the rest black. */\nclass BrightPass extends FxFilter {\n constructor(threshold: number, knee: number) {\n super({\n name: 'bloom-bright',\n uniforms: { uThreshold: { type: 'f32', value: threshold }, uKnee: { type: 'f32', value: knee } },\n glsl: `\n vec3 c = src.a > 0.0 ? src.rgb / src.a : src.rgb;\n float l = max(c.r, max(c.g, c.b));\n float k = smoothstep(uThreshold, uThreshold + max(uKnee, 0.0001), l);\n result = src * k;`,\n wgsl: `\n var c = src.rgb;\n if (src.a > 0.0) { c = src.rgb / src.a; }\n let l = max(c.r, max(c.g, c.b));\n let k = smoothstep(fx.uThreshold, fx.uThreshold + max(fx.uKnee, 0.0001), l);\n result = src * k;`,\n });\n }\n}\n\n/**\n * Bright things glow: the pixels above a threshold are blurred and added back over the picture. Three passes (bright,\n * blur, combine) on textures from Pixi's pool; costlier than the one-pass effects, so give it a `filterArea` or put it\n * on the world layer only.\n */\nexport class BloomFilter extends FxFilter {\n private readonly bright: BrightPass;\n private readonly blurPass: BlurFilter;\n\n constructor(options: BloomOptions = {}) {\n super({\n name: 'bloom',\n map: true,\n uniforms: { uIntensity: { type: 'f32', value: options.intensity ?? 1 } },\n glsl: `\n result = src + sampleMap(coord) * uIntensity;`,\n wgsl: `\n result = src + sampleMap(coord) * fx.uIntensity;`,\n });\n this.bright = new BrightPass(options.threshold ?? 0.7, options.knee ?? 0.2);\n this.blurPass = new BlurFilter({ strength: options.blur ?? 8, quality: options.quality ?? 4 });\n }\n\n get intensity(): number {\n return this.num('uIntensity');\n }\n set intensity(v: number) {\n this.set('uIntensity', v);\n }\n get threshold(): number {\n return this.bright.get('uThreshold') as number;\n }\n set threshold(v: number) {\n this.bright.set('uThreshold', v);\n }\n get blur(): number {\n return this.blurPass.strength;\n }\n set blur(v: number) {\n this.blurPass.strength = v;\n }\n\n override apply(filterManager: FilterSystem, input: Texture, output: RenderSurface, clearMode: boolean): void {\n const light = TexturePool.getSameSizeTexture(input);\n const glow = TexturePool.getSameSizeTexture(input);\n this.bright.apply(filterManager, input, light, true);\n this.blurPass.apply(filterManager, light, glow, true);\n this.resources.uMap = glow.source;\n filterManager.applyFilter(this, input, output, clearMode);\n TexturePool.returnTexture(glow);\n TexturePool.returnTexture(light);\n }\n\n override destroy(): void {\n this.bright.destroy();\n this.blurPass.destroy();\n super.destroy();\n }\n}\n"],"names":["BrightPass","FxFilter","threshold","knee","BloomFilter","options","BlurFilter","v","filterManager","input","output","clearMode","light","TexturePool","glow"],"mappings":";;AAiBA,MAAMA,UAAmBC,EAAS;AAAA,EAChC,YAAYC,GAAmBC,GAAc;AAC3C,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,UAAU,EAAE,YAAY,EAAE,MAAM,OAAO,OAAOD,KAAa,OAAO,EAAE,MAAM,OAAO,OAAOC,IAAK;AAAA,MAC7F,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,MAKN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAA,CAMP;AAAA,EACH;AACF;AAOO,MAAMC,UAAoBH,EAAS;AAAA,EACvB;AAAA,EACA;AAAA,EAEjB,YAAYI,IAAwB,IAAI;AACtC,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,KAAK;AAAA,MACL,UAAU,EAAE,YAAY,EAAE,MAAM,OAAO,OAAOA,EAAQ,aAAa,IAAE;AAAA,MACrE,MAAM;AAAA;AAAA,MAEN,MAAM;AAAA;AAAA,IAAA,CAEP,GACD,KAAK,SAAS,IAAIL,EAAWK,EAAQ,aAAa,KAAKA,EAAQ,QAAQ,GAAG,GAC1E,KAAK,WAAW,IAAIC,EAAW,EAAE,UAAUD,EAAQ,QAAQ,GAAG,SAASA,EAAQ,WAAW,EAAA,CAAG;AAAA,EAC/F;AAAA,EAEA,IAAI,YAAoB;AACtB,WAAO,KAAK,IAAI,YAAY;AAAA,EAC9B;AAAA,EACA,IAAI,UAAUE,GAAW;AACvB,SAAK,IAAI,cAAcA,CAAC;AAAA,EAC1B;AAAA,EACA,IAAI,YAAoB;AACtB,WAAO,KAAK,OAAO,IAAI,YAAY;AAAA,EACrC;AAAA,EACA,IAAI,UAAUA,GAAW;AACvB,SAAK,OAAO,IAAI,cAAcA,CAAC;AAAA,EACjC;AAAA,EACA,IAAI,OAAe;AACjB,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA,EACA,IAAI,KAAKA,GAAW;AAClB,SAAK,SAAS,WAAWA;AAAA,EAC3B;AAAA,EAES,MAAMC,GAA6BC,GAAgBC,GAAuBC,GAA0B;AAC3G,UAAMC,IAAQC,EAAY,mBAAmBJ,CAAK,GAC5CK,IAAOD,EAAY,mBAAmBJ,CAAK;AACjD,SAAK,OAAO,MAAMD,GAAeC,GAAOG,GAAO,EAAI,GACnD,KAAK,SAAS,MAAMJ,GAAeI,GAAOE,GAAM,EAAI,GACpD,KAAK,UAAU,OAAOA,EAAK,QAC3BN,EAAc,YAAY,MAAMC,GAAOC,GAAQC,CAAS,GACxDE,EAAY,cAAcC,CAAI,GAC9BD,EAAY,cAAcD,CAAK;AAAA,EACjC;AAAA,EAES,UAAgB;AACvB,SAAK,OAAO,QAAA,GACZ,KAAK,SAAS,QAAA,GACd,MAAM,QAAA;AAAA,EACR;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ChromaticAberrationFilter.js","sources":["../../src/postfx/ChromaticAberrationFilter.ts"],"sourcesContent":["import { FxFilter } from './fxShader';\n\n/**\n * Splits red and blue apart towards the edges, `offset` pixels at a corner: a hit's jolt, a dream, a cheap lens.\n * Nothing at the centre of the screen moves.\n */\nexport class ChromaticAberrationFilter extends FxFilter {\n constructor(offset = 2) {\n super({\n name: 'chromatic-aberration',\n uniforms: { uOffset: { type: 'f32', value: offset } },\n glsl: `\n vec2 off = (frameUv - 0.5) * 2.0 * uOffset * texel;\n result = vec4(sampleAt(coord + off).r, src.g, sampleAt(coord - off).b, src.a);`,\n wgsl: `\n let off = (frameUv - 0.5) * 2.0 * fx.uOffset * texel;\n result = vec4<f32>(sampleAt(coord + off).r, src.g, sampleAt(coord - off).b, src.a);`,\n });\n }\n\n get offset(): number {\n return this.num('uOffset');\n }\n set offset(v: number) {\n this.set('uOffset', v);\n }\n}\n"],"names":["ChromaticAberrationFilter","FxFilter","offset","v"],"mappings":";AAMO,MAAMA,UAAkCC,EAAS;AAAA,EACtD,YAAYC,IAAS,GAAG;AACtB,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,UAAU,EAAE,SAAS,EAAE,MAAM,OAAO,OAAOA,IAAO;AAAA,MAClD,MAAM;AAAA;AAAA;AAAA,MAGN,MAAM;AAAA;AAAA;AAAA,IAAA,CAGP;AAAA,EACH;AAAA,EAEA,IAAI,SAAiB;AACnB,WAAO,KAAK,IAAI,SAAS;AAAA,EAC3B;AAAA,EACA,IAAI,OAAOC,GAAW;AACpB,SAAK,IAAI,WAAWA,CAAC;AAAA,EACvB;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ColorAdjustFilter.js","sources":["../../src/postfx/ColorAdjustFilter.ts"],"sourcesContent":["import { FxFilter, rgb } from './fxShader';\n\nexport interface ColorAdjustOptions {\n /** Multiplies every channel (default 1). */\n brightness?: number;\n /** 1 unchanged, 0 flat grey, above 1 punchier (default 1). */\n contrast?: number;\n /** 1 unchanged, 0 greyscale (default 1). */\n saturation?: number;\n /** A colour multiplied in by `tintAmount` (default white, 0). */\n tint?: number;\n tintAmount?: number;\n}\n\n/**\n * Brightness, contrast, saturation and a tint, all animatable, plus a flash: the whole screen washed towards a colour\n * (`flash` 0–1, `flashColor`), for a hit, a pickup or lightning. `PostFx.flash` drives it.\n */\nexport class ColorAdjustFilter extends FxFilter {\n constructor(options: ColorAdjustOptions = {}) {\n super({\n name: 'color-adjust',\n uniforms: {\n uBrightness: { type: 'f32', value: options.brightness ?? 1 },\n uContrast: { type: 'f32', value: options.contrast ?? 1 },\n uSaturation: { type: 'f32', value: options.saturation ?? 1 },\n uTintAmount: { type: 'f32', value: options.tintAmount ?? 0 },\n uTint: { type: 'vec3<f32>', value: new Float32Array(rgb(options.tint ?? 0xffffff)) },\n uFlash: { type: 'f32', value: 0 },\n uFlashColor: { type: 'vec3<f32>', value: new Float32Array([1, 1, 1]) },\n },\n glsl: `\n vec3 c = src.a > 0.0 ? src.rgb / src.a : src.rgb;\n c *= uBrightness;\n c = (c - 0.5) * uContrast + 0.5;\n float l = dot(c, vec3(0.299, 0.587, 0.114));\n c = mix(vec3(l), c, uSaturation);\n c = mix(c, c * uTint, uTintAmount);\n c = mix(c, uFlashColor, uFlash);\n result = vec4(clamp(c, 0.0, 1.0) * src.a, src.a);`,\n wgsl: `\n var c = src.rgb;\n if (src.a > 0.0) { c = src.rgb / src.a; }\n c = c * fx.uBrightness;\n c = (c - 0.5) * fx.uContrast + 0.5;\n let l = dot(c, vec3<f32>(0.299, 0.587, 0.114));\n c = mix(vec3<f32>(l), c, fx.uSaturation);\n c = mix(c, c * fx.uTint, fx.uTintAmount);\n c = mix(c, fx.uFlashColor, fx.uFlash);\n result = vec4<f32>(clamp(c, vec3<f32>(0.0), vec3<f32>(1.0)) * src.a, src.a);`,\n });\n }\n\n get brightness(): number {\n return this.num('uBrightness');\n }\n set brightness(v: number) {\n this.set('uBrightness', v);\n }\n get contrast(): number {\n return this.num('uContrast');\n }\n set contrast(v: number) {\n this.set('uContrast', v);\n }\n get saturation(): number {\n return this.num('uSaturation');\n }\n set saturation(v: number) {\n this.set('uSaturation', v);\n }\n get flash(): number {\n return this.num('uFlash');\n }\n set flash(v: number) {\n this.set('uFlash', v);\n }\n set flashColor(c: number) {\n this.set('uFlashColor', ...rgb(c));\n }\n set tint(c: number) {\n this.set('uTint', ...rgb(c));\n }\n get tintAmount(): number {\n return this.num('uTintAmount');\n }\n set tintAmount(v: number) {\n this.set('uTintAmount', v);\n }\n}\n"],"names":["ColorAdjustFilter","FxFilter","options","rgb","v","c"],"mappings":";AAkBO,MAAMA,UAA0BC,EAAS;AAAA,EAC9C,YAAYC,IAA8B,IAAI;AAC5C,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,UAAU;AAAA,QACR,aAAa,EAAE,MAAM,OAAO,OAAOA,EAAQ,cAAc,EAAA;AAAA,QACzD,WAAW,EAAE,MAAM,OAAO,OAAOA,EAAQ,YAAY,EAAA;AAAA,QACrD,aAAa,EAAE,MAAM,OAAO,OAAOA,EAAQ,cAAc,EAAA;AAAA,QACzD,aAAa,EAAE,MAAM,OAAO,OAAOA,EAAQ,cAAc,EAAA;AAAA,QACzD,OAAO,EAAE,MAAM,aAAa,OAAO,IAAI,aAAaC,EAAID,EAAQ,QAAQ,QAAQ,CAAC,EAAA;AAAA,QACjF,QAAQ,EAAE,MAAM,OAAO,OAAO,EAAA;AAAA,QAC9B,aAAa,EAAE,MAAM,aAAa,OAAO,IAAI,aAAa,CAAC,GAAG,GAAG,CAAC,CAAC,EAAA;AAAA,MAAE;AAAA,MAEvE,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MASN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAA,CAUP;AAAA,EACH;AAAA,EAEA,IAAI,aAAqB;AACvB,WAAO,KAAK,IAAI,aAAa;AAAA,EAC/B;AAAA,EACA,IAAI,WAAWE,GAAW;AACxB,SAAK,IAAI,eAAeA,CAAC;AAAA,EAC3B;AAAA,EACA,IAAI,WAAmB;AACrB,WAAO,KAAK,IAAI,WAAW;AAAA,EAC7B;AAAA,EACA,IAAI,SAASA,GAAW;AACtB,SAAK,IAAI,aAAaA,CAAC;AAAA,EACzB;AAAA,EACA,IAAI,aAAqB;AACvB,WAAO,KAAK,IAAI,aAAa;AAAA,EAC/B;AAAA,EACA,IAAI,WAAWA,GAAW;AACxB,SAAK,IAAI,eAAeA,CAAC;AAAA,EAC3B;AAAA,EACA,IAAI,QAAgB;AAClB,WAAO,KAAK,IAAI,QAAQ;AAAA,EAC1B;AAAA,EACA,IAAI,MAAMA,GAAW;AACnB,SAAK,IAAI,UAAUA,CAAC;AAAA,EACtB;AAAA,EACA,IAAI,WAAWC,GAAW;AACxB,SAAK,IAAI,eAAe,GAAGF,EAAIE,CAAC,CAAC;AAAA,EACnC;AAAA,EACA,IAAI,KAAKA,GAAW;AAClB,SAAK,IAAI,SAAS,GAAGF,EAAIE,CAAC,CAAC;AAAA,EAC7B;AAAA,EACA,IAAI,aAAqB;AACvB,WAAO,KAAK,IAAI,aAAa;AAAA,EAC/B;AAAA,EACA,IAAI,WAAWD,GAAW;AACxB,SAAK,IAAI,eAAeA,CAAC;AAAA,EAC3B;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CrtFilter.js","sources":["../../src/postfx/CrtFilter.ts"],"sourcesContent":["import { FxFilter } from './fxShader';\n\nexport interface CrtOptions {\n /** 0–1, how dark the gaps between scanlines are (default 0.3). */\n scanlines?: number;\n /** Screen pixels per scanline (default 3). */\n lineHeight?: number;\n /** How much the glass bulges, 0 flat (default 0.08). */\n curvature?: number;\n /** 0–1, how much darker the bulged corners are (default 0.25). */\n cornerShade?: number;\n}\n\n/** An old television: scanlines, a bulging glass with black beyond its edge, darker corners. */\nexport class CrtFilter extends FxFilter {\n constructor(options: CrtOptions = {}) {\n super({\n name: 'crt',\n uniforms: {\n uScanlines: { type: 'f32', value: options.scanlines ?? 0.3 },\n uLineHeight: { type: 'f32', value: options.lineHeight ?? 3 },\n uCurvature: { type: 'f32', value: options.curvature ?? 0.08 },\n uCornerShade: { type: 'f32', value: options.cornerShade ?? 0.25 },\n },\n glsl: `\n vec2 c = frameUv * 2.0 - 1.0;\n c = c * (1.0 + uCurvature * (c.yx * c.yx));\n vec2 uv = c * 0.5 + 0.5;\n if (uv.x < 0.0 || uv.y < 0.0 || uv.x > 1.0 || uv.y > 1.0) {\n result = vec4(0.0, 0.0, 0.0, 1.0);\n } else {\n result = sampleAt(toCoord(uv));\n float line = 0.5 + 0.5 * cos(uv.y * framePx.y / max(uLineHeight, 1.0) * 6.2831853);\n float shade = 1.0 - uCornerShade * dot(c, c) * 0.5;\n result = vec4(result.rgb * mix(1.0, line, uScanlines) * shade, result.a);\n }`,\n wgsl: `\n var c = frameUv * 2.0 - 1.0;\n c = c * (1.0 + fx.uCurvature * (c.yx * c.yx));\n let uv2 = c * 0.5 + 0.5;\n if (uv2.x < 0.0 || uv2.y < 0.0 || uv2.x > 1.0 || uv2.y > 1.0) {\n result = vec4<f32>(0.0, 0.0, 0.0, 1.0);\n } else {\n result = sampleAt(toCoord(uv2));\n let line = 0.5 + 0.5 * cos(uv2.y * framePx.y / max(fx.uLineHeight, 1.0) * 6.2831853);\n let shade = 1.0 - fx.uCornerShade * dot(c, c) * 0.5;\n result = vec4<f32>(result.rgb * mix(1.0, line, fx.uScanlines) * shade, result.a);\n }`,\n });\n }\n}\n"],"names":["CrtFilter","FxFilter","options"],"mappings":";AAcO,MAAMA,UAAkBC,EAAS;AAAA,EACtC,YAAYC,IAAsB,IAAI;AACpC,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,UAAU;AAAA,QACR,YAAY,EAAE,MAAM,OAAO,OAAOA,EAAQ,aAAa,IAAA;AAAA,QACvD,aAAa,EAAE,MAAM,OAAO,OAAOA,EAAQ,cAAc,EAAA;AAAA,QACzD,YAAY,EAAE,MAAM,OAAO,OAAOA,EAAQ,aAAa,KAAA;AAAA,QACvD,cAAc,EAAE,MAAM,OAAO,OAAOA,EAAQ,eAAe,KAAA;AAAA,MAAK;AAAA,MAElE,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAYN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAA,CAYP;AAAA,EACH;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PixelateFilter.js","sources":["../../src/postfx/PixelateFilter.ts"],"sourcesContent":["import { FxFilter } from './fxShader';\n\n/** Shows the screen in blocks of `size` pixels: a transition's dissolve, a retro mode, a censor box. 1 is off. */\nexport class PixelateFilter extends FxFilter {\n constructor(size = 4) {\n super({\n name: 'pixelate',\n uniforms: { uSize: { type: 'f32', value: size } },\n glsl: `\n float s = max(uSize, 1.0);\n vec2 px = coord / texel;\n result = sampleAt((floor(px / s) + 0.5) * s * texel);`,\n wgsl: `\n let s = max(fx.uSize, 1.0);\n let px = coord / texel;\n result = sampleAt((floor(px / s) + 0.5) * s * texel);`,\n });\n }\n\n get size(): number {\n return this.num('uSize');\n }\n set size(v: number) {\n this.set('uSize', v);\n }\n}\n"],"names":["PixelateFilter","FxFilter","size","v"],"mappings":";AAGO,MAAMA,UAAuBC,EAAS;AAAA,EAC3C,YAAYC,IAAO,GAAG;AACpB,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,UAAU,EAAE,OAAO,EAAE,MAAM,OAAO,OAAOA,IAAK;AAAA,MAC9C,MAAM;AAAA;AAAA;AAAA;AAAA,MAIN,MAAM;AAAA;AAAA;AAAA;AAAA,IAAA,CAIP;AAAA,EACH;AAAA,EAEA,IAAI,OAAe;AACjB,WAAO,KAAK,IAAI,OAAO;AAAA,EACzB;AAAA,EACA,IAAI,KAAKC,GAAW;AAClB,SAAK,IAAI,SAASA,CAAC;AAAA,EACrB;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PostFx.js","sources":["../../src/postfx/PostFx.ts"],"sourcesContent":["import { Container, Rectangle, Sprite, Texture, type Filter } from 'pixi.js';\nimport { easeByName, type EaseFn } from '../core/eases';\nimport type { Engine } from '../engine/Engine';\nimport { engineSettings } from '../settings';\nimport { ColorAdjustFilter } from './ColorAdjustFilter';\nimport { FxAnimator } from './fxAnimator';\nimport { ShockwaveFilter, type ShockwaveOptions } from './ShockwaveFilter';\n\nexport interface PostFxOptions {\n /**\n * What the effects lie over: 'all' (default) the whole picture, HUD included; 'world' the camera's layer only, so\n * the HUD stays sharp; or any container.\n */\n layer?: 'all' | 'world' | Container;\n /**\n * Fills the screen behind the picture with this colour (default the renderer's background when `layer` is 'all').\n * Effects only see what is drawn: without a backdrop the empty background stays untouched by a vignette or a flash.\n * `false` for none.\n */\n backdrop?: number | false;\n /** Skip flashes and shockwaves while the player asked for reduced effects (default true). */\n honourReducedFx?: boolean;\n}\n\nexport interface FlashOptions {\n color?: number;\n /** Peak strength, 0–1 (default 0.6). */\n strength?: number;\n ms?: number;\n}\n\nexport interface ShockwaveRun extends ShockwaveOptions {\n /** How far the ring travels, as a share of the screen's height (default 0.6). */\n reach?: number;\n ms?: number;\n}\n\n/**\n * Full-screen effects over a game: a chain of filters (vignette, bloom, a colour grade, CRT, pixelate…) laid over the\n * picture in the order added, switched on and off one by one, and short effects that come and go — a flash, a\n * shockwave, any uniform animated over time. One per engine is plenty; give it to the scenes that want it.\n *\n * ```ts\n * const post = PostFx.forEngine(engine);\n * const vignette = post.add(new VignetteFilter({ strength: 0.4 }));\n * post.add(new BloomFilter({ threshold: 0.75 }));\n * post.flash({ color: 0xffffff, ms: 120 }); // a hit\n * post.shockwave(x, y); // screen pixels: an explosion\n * post.animate((v) => (vignette.strength = v), 0.4, 0.8, 300, 'Sine.InOut'); // a heartbeat\n * ```\n */\nexport class PostFx {\n readonly animator = new FxAnimator();\n private readonly chain: { filter: Filter; on: boolean }[] = [];\n private transient: Filter[] = [];\n private colorFx: ColorAdjustFilter | null = null;\n private backdropSprite: Sprite | null = null;\n private offFrame: (() => void) | null = null;\n /** The screen in the target's own coordinates: the filtered area, refreshed every frame as a camera moves. */\n private readonly area = new Rectangle();\n\n constructor(\n readonly target: Container,\n /** The screen rectangle, live (the renderer's `screen`): what is filtered, and how screen points map to it. */\n private readonly screen: Rectangle,\n private readonly options: PostFxOptions = {},\n ) {\n this.fitArea();\n target.filterArea = this.area;\n // the backdrop is laid in screen space: only over a target that is not moved by a camera\n if (options.backdrop !== false && options.backdrop !== undefined) this.setBackdrop(options.backdrop);\n }\n\n /** A `PostFx` over an engine's picture, stepped on its frames. */\n static forEngine(engine: Engine, options: PostFxOptions = {}): PostFx {\n const layer = options.layer ?? 'all';\n const target = layer === 'all' ? engine.app.stage : layer === 'world' ? engine.stage.world : layer;\n const backdrop = options.backdrop ?? (layer === 'all' ? engine.app.renderer.background.color.toNumber() : false);\n const fx = new PostFx(target, engine.app.screen, { ...options, backdrop });\n fx.offFrame = engine.events.on('frame', ({ dt }) => fx.update(dt * 1000));\n return fx;\n }\n\n /** Adds an effect at the end of the chain (or at `index`) and returns it. */\n add<T extends Filter>(filter: T, index = this.chain.length): T {\n this.chain.splice(index, 0, { filter, on: true });\n this.apply();\n return filter;\n }\n\n /** Takes an effect out (it is not destroyed: add it back later, or destroy it yourself). */\n remove(filter: Filter): void {\n const i = this.chain.findIndex((e) => e.filter === filter);\n if (i < 0) return;\n this.chain.splice(i, 1);\n this.apply();\n }\n\n /** Turns an effect on or off without taking it out of its place. */\n setEnabled(filter: Filter, on: boolean): void {\n const e = this.chain.find((x) => x.filter === filter);\n if (!e || e.on === on) return;\n e.on = on;\n this.apply();\n }\n\n isEnabled(filter: Filter): boolean {\n return this.chain.find((x) => x.filter === filter)?.on ?? false;\n }\n\n /** Every effect in the chain, in order. */\n get filters(): readonly Filter[] {\n return this.chain.map((e) => e.filter);\n }\n\n /** Animates any number (a uniform, an effect's property) from `from` to `to` over `ms`. */\n animate(set: (v: number) => void, from: number, to: number, ms: number, ease?: EaseFn | string, done?: () => void): void {\n this.animator.run({ set, from, to, ms, ease: typeof ease === 'string' ? easeByName(ease) : ease, done });\n }\n\n /** The whole picture washed towards a colour, fading out over `ms`. Skipped under reduced effects. */\n flash(options: FlashOptions = {}): void {\n if (this.reduced) return;\n const fx = this.ensureColor();\n fx.flashColor = options.color ?? 0xffffff;\n this.animator.run({ key: 'flash', set: (v) => (fx.flash = v), from: options.strength ?? 0.6, to: 0, ms: options.ms ?? 160, ease: easeByName('Quad.Out') });\n }\n\n /** A ring of bent picture spreading from a screen point (screen pixels). Skipped under reduced effects. */\n shockwave(x: number, y: number, options: ShockwaveRun = {}): void {\n if (this.reduced) return;\n const wave = new ShockwaveFilter(options);\n wave.setCenter((x - this.screen.x) / this.screen.width, (y - this.screen.y) / this.screen.height);\n const amplitude = wave.amplitude;\n this.transient.push(wave);\n this.apply();\n this.animator.run({\n set: (t) => {\n wave.radius = t * (options.reach ?? 0.6);\n wave.amplitude = amplitude * (1 - t);\n },\n from: 0,\n to: 1,\n ms: options.ms ?? 600,\n ease: easeByName('Quad.Out'),\n done: () => {\n this.transient = this.transient.filter((f) => f !== wave);\n this.apply();\n wave.destroy();\n },\n });\n }\n\n /** The colour adjustment `flash` uses, made and put first in the chain the first time: brightness, saturation, a tint. */\n get color(): ColorAdjustFilter {\n return this.ensureColor();\n }\n\n /** Steps the animations; `forEngine` calls it on every frame. */\n update(ms: number): void {\n if (this.animator.size > 0) this.animator.step(ms);\n this.fitArea();\n }\n\n destroy(): void {\n this.offFrame?.();\n this.offFrame = null;\n this.animator.finishAll();\n this.target.filters = [];\n for (const e of this.chain) e.filter.destroy();\n this.chain.length = 0;\n this.backdropSprite?.destroy();\n this.backdropSprite = null;\n }\n\n /** The screen rectangle mapped into the target's local space (a camera's layer is scaled and moved). */\n private fitArea(): void {\n const m = this.target.worldTransform;\n const det = m.a * m.d - m.b * m.c;\n if (det === 0) return;\n const inv = (x: number, y: number): [number, number] => {\n const dx = x - m.tx;\n const dy = y - m.ty;\n return [(m.d * dx - m.c * dy) / det, (m.a * dy - m.b * dx) / det];\n };\n const [x0, y0] = inv(this.screen.x, this.screen.y);\n const [x1, y1] = inv(this.screen.x + this.screen.width, this.screen.y + this.screen.height);\n this.area.x = Math.min(x0, x1);\n this.area.y = Math.min(y0, y1);\n this.area.width = Math.abs(x1 - x0);\n this.area.height = Math.abs(y1 - y0);\n }\n\n private get reduced(): boolean {\n return (this.options.honourReducedFx ?? true) && engineSettings.getState().reducedFx;\n }\n\n private ensureColor(): ColorAdjustFilter {\n if (!this.colorFx) this.colorFx = this.add(new ColorAdjustFilter(), 0);\n return this.colorFx;\n }\n\n private setBackdrop(color: number): void {\n const s = new Sprite(Texture.WHITE);\n s.tint = color;\n s.label = 'postfx-backdrop';\n this.backdropSprite = s;\n this.target.addChildAt(s, 0);\n const fit = () => {\n s.position.set(this.screen.x, this.screen.y);\n s.width = this.screen.width;\n s.height = this.screen.height;\n };\n fit();\n s.onRender = fit;\n }\n\n private apply(): void {\n const list: Filter[] = [];\n for (const e of this.chain) if (e.on) list.push(e.filter);\n // transient waves go under the lasting effects, so a CRT's curve bends the wave too\n this.target.filters = list.length + this.transient.length > 0 ? [...this.transient, ...list] : [];\n }\n}\n"],"names":["PostFx","target","screen","options","FxAnimator","Rectangle","engine","layer","backdrop","fx","dt","filter","index","i","e","on","x","set","from","to","ms","ease","done","easeByName","v","y","wave","ShockwaveFilter","amplitude","t","f","m","det","inv","dx","dy","x0","y0","x1","y1","engineSettings","ColorAdjustFilter","color","s","Sprite","Texture","fit","list"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ShockwaveFilter.js","sources":["../../src/postfx/ShockwaveFilter.ts"],"sourcesContent":["import { FxFilter } from './fxShader';\n\nexport interface ShockwaveOptions {\n /** Ring's width as a share of the screen's height (default 0.06). */\n width?: number;\n /** How far the ring pushes the picture, screen pixels (default 12). */\n amplitude?: number;\n}\n\n/**\n * A ring that bends the picture as it passes: an explosion's blast, a landing's thump. Set `centerX/Y` (0–1 across the\n * filtered area) and grow `radius` (a share of its height) over time; `PostFx.shockwave` does both and removes it.\n */\nexport class ShockwaveFilter extends FxFilter {\n constructor(options: ShockwaveOptions = {}) {\n super({\n name: 'shockwave',\n uniforms: {\n uCenter: { type: 'vec2<f32>', value: new Float32Array([0.5, 0.5]) },\n uRadius: { type: 'f32', value: 0 },\n uWidth: { type: 'f32', value: options.width ?? 0.06 },\n uAmplitude: { type: 'f32', value: options.amplitude ?? 12 },\n },\n glsl: `\n vec2 d = (frameUv - uCenter) * vec2(framePx.x / framePx.y, 1.0);\n float dist = length(d);\n float x = (dist - uRadius) / max(uWidth, 0.0001);\n if (abs(x) < 1.0 && dist > 0.0) {\n float wave = sin(x * 3.14159265) * (1.0 - abs(x));\n result = sampleAt(coord - (d / dist) * wave * uAmplitude * texel);\n }`,\n wgsl: `\n let d = (frameUv - fx.uCenter) * vec2<f32>(framePx.x / framePx.y, 1.0);\n let dist = length(d);\n let x = (dist - fx.uRadius) / max(fx.uWidth, 0.0001);\n if (abs(x) < 1.0 && dist > 0.0) {\n let wave = sin(x * 3.14159265) * (1.0 - abs(x));\n result = sampleAt(coord - (d / dist) * wave * fx.uAmplitude * texel);\n }`,\n });\n }\n\n setCenter(x: number, y: number): void {\n this.set('uCenter', x, y);\n }\n get radius(): number {\n return this.num('uRadius');\n }\n set radius(v: number) {\n this.set('uRadius', v);\n }\n get amplitude(): number {\n return this.num('uAmplitude');\n }\n set amplitude(v: number) {\n this.set('uAmplitude', v);\n }\n}\n"],"names":["ShockwaveFilter","FxFilter","options","x","y","v"],"mappings":";AAaO,MAAMA,UAAwBC,EAAS;AAAA,EAC5C,YAAYC,IAA4B,IAAI;AAC1C,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,UAAU;AAAA,QACR,SAAS,EAAE,MAAM,aAAa,OAAO,IAAI,aAAa,CAAC,KAAK,GAAG,CAAC,EAAA;AAAA,QAChE,SAAS,EAAE,MAAM,OAAO,OAAO,EAAA;AAAA,QAC/B,QAAQ,EAAE,MAAM,OAAO,OAAOA,EAAQ,SAAS,KAAA;AAAA,QAC/C,YAAY,EAAE,MAAM,OAAO,OAAOA,EAAQ,aAAa,GAAA;AAAA,MAAG;AAAA,MAE5D,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAA,CAQP;AAAA,EACH;AAAA,EAEA,UAAUC,GAAWC,GAAiB;AACpC,SAAK,IAAI,WAAWD,GAAGC,CAAC;AAAA,EAC1B;AAAA,EACA,IAAI,SAAiB;AACnB,WAAO,KAAK,IAAI,SAAS;AAAA,EAC3B;AAAA,EACA,IAAI,OAAOC,GAAW;AACpB,SAAK,IAAI,WAAWA,CAAC;AAAA,EACvB;AAAA,EACA,IAAI,YAAoB;AACtB,WAAO,KAAK,IAAI,YAAY;AAAA,EAC9B;AAAA,EACA,IAAI,UAAUA,GAAW;AACvB,SAAK,IAAI,cAAcA,CAAC;AAAA,EAC1B;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"VignetteFilter.js","sources":["../../src/postfx/VignetteFilter.ts"],"sourcesContent":["import { FxFilter, rgb } from './fxShader';\n\nexport interface VignetteOptions {\n /** 0–1, how dark the corners go (default 0.5). */\n strength?: number;\n /** Where the darkening starts, as a share of the way from the centre to a corner (default 0.55). */\n radius?: number;\n /** How far beyond `radius` it reaches full strength (default 0.45). */\n softness?: number;\n /** What the edges fade to (default black); a red one is a classic low-health warning. */\n color?: number;\n}\n\n/** Darkens the edges of the screen towards a colour, round whatever its shape. */\nexport class VignetteFilter extends FxFilter {\n constructor(options: VignetteOptions = {}) {\n super({\n name: 'vignette',\n uniforms: {\n uStrength: { type: 'f32', value: options.strength ?? 0.5 },\n uRadius: { type: 'f32', value: options.radius ?? 0.55 },\n uSoftness: { type: 'f32', value: options.softness ?? 0.45 },\n uColor: { type: 'vec3<f32>', value: new Float32Array(rgb(options.color ?? 0)) },\n },\n glsl: `\n vec2 aspect = vec2(framePx.x / framePx.y, 1.0);\n float dist = length((frameUv - 0.5) * aspect) / length(aspect * 0.5);\n float v = smoothstep(uRadius, uRadius + max(uSoftness, 0.0001), dist) * uStrength;\n result = vec4(mix(src.rgb, uColor * src.a, v), src.a);`,\n wgsl: `\n let aspect = vec2<f32>(framePx.x / framePx.y, 1.0);\n let dist = length((frameUv - 0.5) * aspect) / length(aspect * 0.5);\n let v = smoothstep(fx.uRadius, fx.uRadius + max(fx.uSoftness, 0.0001), dist) * fx.uStrength;\n result = vec4<f32>(mix(src.rgb, fx.uColor * src.a, v), src.a);`,\n });\n }\n\n get strength(): number {\n return this.num('uStrength');\n }\n set strength(v: number) {\n this.set('uStrength', v);\n }\n\n set color(c: number) {\n this.set('uColor', ...rgb(c));\n }\n}\n"],"names":["VignetteFilter","FxFilter","options","rgb","v","c"],"mappings":";AAcO,MAAMA,UAAuBC,EAAS;AAAA,EAC3C,YAAYC,IAA2B,IAAI;AACzC,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,UAAU;AAAA,QACR,WAAW,EAAE,MAAM,OAAO,OAAOA,EAAQ,YAAY,IAAA;AAAA,QACrD,SAAS,EAAE,MAAM,OAAO,OAAOA,EAAQ,UAAU,KAAA;AAAA,QACjD,WAAW,EAAE,MAAM,OAAO,OAAOA,EAAQ,YAAY,KAAA;AAAA,QACrD,QAAQ,EAAE,MAAM,aAAa,OAAO,IAAI,aAAaC,EAAID,EAAQ,SAAS,CAAC,CAAC,EAAA;AAAA,MAAE;AAAA,MAEhF,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,MAKN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,IAAA,CAKP;AAAA,EACH;AAAA,EAEA,IAAI,WAAmB;AACrB,WAAO,KAAK,IAAI,WAAW;AAAA,EAC7B;AAAA,EACA,IAAI,SAASE,GAAW;AACtB,SAAK,IAAI,aAAaA,CAAC;AAAA,EACzB;AAAA,EAEA,IAAI,MAAMC,GAAW;AACnB,SAAK,IAAI,UAAU,GAAGF,EAAIE,CAAC,CAAC;AAAA,EAC9B;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fxAnimator.js","sources":["../../src/postfx/fxAnimator.ts"],"sourcesContent":["import type { EaseFn } from '../core/eases';\n\n/** One running change of a number over time. */\nexport interface FxAnimation {\n /** Called with the eased value every frame, and with `to` at the end. */\n set: (value: number) => void;\n from: number;\n to: number;\n ms: number;\n ease: EaseFn;\n /** Runs once after the last `set`. */\n done?: () => void;\n elapsed: number;\n /** A key that cancels whatever animation already holds it (one flash at a time, say). */\n key?: string;\n}\n\nconst linear: EaseFn = (t) => t;\n\n/**\n * Advances numbers over time for the post effects: a flash fading, a shockwave growing, a vignette breathing. Pure,\n * so it is tested without a renderer; `PostFx` steps it on every frame.\n */\nexport class FxAnimator {\n private readonly list: FxAnimation[] = [];\n\n get size(): number {\n return this.list.length;\n }\n\n /** Starts an animation; one with the same `key` as a running one replaces it (the old one's `done` still runs). */\n run(a: Omit<FxAnimation, 'elapsed' | 'ease'> & { ease?: EaseFn }): void {\n if (a.key !== undefined) {\n const i = this.list.findIndex((x) => x.key === a.key);\n if (i >= 0) this.list.splice(i, 1)[0]!.done?.();\n }\n const anim: FxAnimation = { ...a, ease: a.ease ?? linear, elapsed: 0 };\n anim.set(anim.from);\n if (anim.ms <= 0) {\n anim.set(anim.to);\n anim.done?.();\n return;\n }\n this.list.push(anim);\n }\n\n /** Moves every animation on by `ms`; finished ones set their last value, call `done` and are dropped. */\n step(ms: number): void {\n for (let i = 0; i < this.list.length; ) {\n const a = this.list[i]!;\n a.elapsed += ms;\n const t = Math.min(1, a.elapsed / a.ms);\n a.set(a.from + (a.to - a.from) * a.ease(t));\n if (t >= 1) {\n this.list.splice(i, 1);\n a.done?.();\n } else i++;\n }\n }\n\n /** Ends everything at once, each at its last value. */\n finishAll(): void {\n for (const a of this.list.splice(0)) {\n a.set(a.to);\n a.done?.();\n }\n }\n}\n"],"names":["linear","t","FxAnimator","a","i","x","anim","ms"],"mappings":"AAiBA,MAAMA,IAAiB,CAACC,MAAMA;AAMvB,MAAMC,EAAW;AAAA,EACL,OAAsB,CAAA;AAAA,EAEvC,IAAI,OAAe;AACjB,WAAO,KAAK,KAAK;AAAA,EACnB;AAAA;AAAA,EAGA,IAAIC,GAAoE;AACtE,QAAIA,EAAE,QAAQ,QAAW;AACvB,YAAMC,IAAI,KAAK,KAAK,UAAU,CAACC,MAAMA,EAAE,QAAQF,EAAE,GAAG;AACpD,MAAIC,KAAK,KAAG,KAAK,KAAK,OAAOA,GAAG,CAAC,EAAE,CAAC,EAAG,OAAA;AAAA,IACzC;AACA,UAAME,IAAoB,EAAE,GAAGH,GAAG,MAAMA,EAAE,QAAQH,GAAQ,SAAS,EAAA;AAEnE,QADAM,EAAK,IAAIA,EAAK,IAAI,GACdA,EAAK,MAAM,GAAG;AAChB,MAAAA,EAAK,IAAIA,EAAK,EAAE,GAChBA,EAAK,OAAA;AACL;AAAA,IACF;AACA,SAAK,KAAK,KAAKA,CAAI;AAAA,EACrB;AAAA;AAAA,EAGA,KAAKC,GAAkB;AACrB,aAASH,IAAI,GAAGA,IAAI,KAAK,KAAK,UAAU;AACtC,YAAMD,IAAI,KAAK,KAAKC,CAAC;AACrB,MAAAD,EAAE,WAAWI;AACb,YAAMN,IAAI,KAAK,IAAI,GAAGE,EAAE,UAAUA,EAAE,EAAE;AACtC,MAAAA,EAAE,IAAIA,EAAE,QAAQA,EAAE,KAAKA,EAAE,QAAQA,EAAE,KAAKF,CAAC,CAAC,GACtCA,KAAK,KACP,KAAK,KAAK,OAAOG,GAAG,CAAC,GACrBD,EAAE,OAAA,KACGC;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAGA,YAAkB;AAChB,eAAWD,KAAK,KAAK,KAAK,OAAO,CAAC;AAChC,MAAAA,EAAE,IAAIA,EAAE,EAAE,GACVA,EAAE,OAAA;AAAA,EAEN;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fxShader.js","sources":["../../src/postfx/fxShader.ts"],"sourcesContent":["import { Filter, GlProgram, GpuProgram, Texture, UniformGroup } from 'pixi.js';\n\n/** The uniform types an effect may declare. */\nexport type FxUniformType = 'f32' | 'vec2<f32>' | 'vec3<f32>' | 'vec4<f32>';\n\nexport interface FxUniform {\n type: FxUniformType;\n value: number | Float32Array;\n}\n\nexport type FxUniforms = Record<string, FxUniform>;\n\n/**\n * One effect's shader: its uniforms, and the body of its fragment function in GLSL (WebGL 2) and WGSL (WebGPU).\n *\n * Both bodies are given the same helpers, so they read alike:\n * - `frameUv` (vec2): 0–1 across the filtered area, whatever the size of the texture Pixi hands over;\n * - `framePx` (vec2): the filtered area's size in pixels; `texel` (vec2): one input pixel in texture coordinates;\n * - `sampleAt(coord)`: the input at a texture coordinate, clamped inside the area; `toCoord(uv)`: frame uv back to one;\n * - `coord`: this fragment's texture coordinate; `src`: the input there (premultiplied alpha);\n * - `uMap` / `sampleMap(coord)` when the effect has a second texture (`map: true`).\n * GLSL reads its uniforms by name; WGSL through `fx.<name>`. Each body ends by writing `result`.\n */\nexport interface FxShaderSpec {\n name: string;\n uniforms: FxUniforms;\n glsl: string;\n wgsl: string;\n /** A second texture the effect samples (bloom's blurred light). */\n map?: boolean;\n}\n\nconst GLSL_TYPE: Record<FxUniformType, string> = { f32: 'float', 'vec2<f32>': 'vec2', 'vec3<f32>': 'vec3', 'vec4<f32>': 'vec4' };\n\nconst VERTEX_GLSL = `in vec2 aPosition;\nout vec2 vTextureCoord;\nuniform vec4 uInputSize;\nuniform vec4 uOutputFrame;\nuniform vec4 uOutputTexture;\nvoid main(void) {\n vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;\n position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;\n position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;\n gl_Position = vec4(position, 0.0, 1.0);\n vTextureCoord = aPosition * (uOutputFrame.zw * uInputSize.zw);\n}\n`;\n\n/** The GLSL fragment shader of an effect. */\nexport function glslSource(spec: FxShaderSpec): string {\n const decl = Object.entries(spec.uniforms)\n .map(([n, u]) => `uniform ${GLSL_TYPE[u.type]} ${n};`)\n .join('\\n');\n return `in vec2 vTextureCoord;\nout vec4 finalColor;\nuniform sampler2D uTexture;\n${spec.map ? 'uniform sampler2D uMap;' : ''}\nuniform vec4 uInputSize;\nuniform vec4 uInputClamp;\nuniform vec4 uOutputFrame;\n${decl}\nvec4 sampleAt(vec2 c) { return texture(uTexture, clamp(c, uInputClamp.xy, uInputClamp.zw)); }\n${spec.map ? 'vec4 sampleMap(vec2 c) { return texture(uMap, clamp(c, uInputClamp.xy, uInputClamp.zw)); }' : ''}\nvec2 toCoord(vec2 uv) { return uv * uOutputFrame.zw * uInputSize.zw; }\nvoid main(void) {\n vec2 coord = vTextureCoord;\n vec2 framePx = uOutputFrame.zw;\n vec2 frameUv = coord * uInputSize.xy / framePx;\n vec2 texel = uInputSize.zw;\n vec4 src = texture(uTexture, coord);\n vec4 result = src;\n${spec.glsl}\n finalColor = result;\n}\n`;\n}\n\n/** The WGSL module of an effect (vertex and fragment). */\nexport function wgslSource(spec: FxShaderSpec): string {\n const fields = Object.entries(spec.uniforms)\n .map(([n, u]) => ` ${n}: ${u.type},`)\n .join('\\n');\n return `struct GlobalFilterUniforms {\n uInputSize: vec4<f32>,\n uInputPixel: vec4<f32>,\n uInputClamp: vec4<f32>,\n uOutputFrame: vec4<f32>,\n uGlobalFrame: vec4<f32>,\n uOutputTexture: vec4<f32>,\n};\nstruct FxUniforms {\n${fields}\n};\n@group(0) @binding(0) var<uniform> gfu: GlobalFilterUniforms;\n@group(0) @binding(1) var uTexture: texture_2d<f32>;\n@group(0) @binding(2) var uSampler: sampler;\n@group(1) @binding(0) var<uniform> fx: FxUniforms;\n${spec.map ? '@group(1) @binding(1) var uMap: texture_2d<f32>;' : ''}\nstruct VSOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) uv: vec2<f32>,\n};\n@vertex\nfn mainVertex(@location(0) aPosition: vec2<f32>) -> VSOutput {\n var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;\n position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;\n position.y = position.y * (2.0 * gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;\n return VSOutput(vec4<f32>(position, 0.0, 1.0), aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw));\n}\nfn sampleAt(c: vec2<f32>) -> vec4<f32> { return textureSampleLevel(uTexture, uSampler, clamp(c, gfu.uInputClamp.xy, gfu.uInputClamp.zw), 0.0); }\n${spec.map ? 'fn sampleMap(c: vec2<f32>) -> vec4<f32> { return textureSampleLevel(uMap, uSampler, clamp(c, gfu.uInputClamp.xy, gfu.uInputClamp.zw), 0.0); }' : ''}\nfn toCoord(uv: vec2<f32>) -> vec2<f32> { return uv * gfu.uOutputFrame.zw * gfu.uInputSize.zw; }\n@fragment\nfn mainFragment(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {\n let coord = uv;\n let framePx = gfu.uOutputFrame.zw;\n let frameUv = coord * gfu.uInputSize.xy / framePx;\n let texel = gfu.uInputSize.zw;\n let src = textureSampleLevel(uTexture, uSampler, coord, 0.0);\n var result = src;\n${spec.wgsl}\n return result;\n}\n`;\n}\n\n/**\n * A full-screen effect built from an `FxShaderSpec`: a Pixi `Filter` whose uniforms are read and written by name\n * (`fx.set('strength', 0.5)`, or the subclasses' own properties). Works on both renderers.\n */\nexport class FxFilter extends Filter {\n readonly fxName: string;\n private readonly group: UniformGroup;\n\n constructor(spec: FxShaderSpec) {\n const uniforms: Record<string, { value: number | Float32Array; type: FxUniformType }> = {};\n for (const [n, u] of Object.entries(spec.uniforms)) uniforms[n] = { value: typeof u.value === 'number' ? u.value : new Float32Array(u.value), type: u.type };\n const group = new UniformGroup(uniforms);\n const wgsl = wgslSource(spec);\n super({\n // highp: the uniforms the vertex shader shares with it must match its precision, or the program will not link\n glProgram: GlProgram.from({ vertex: VERTEX_GLSL, fragment: glslSource(spec), name: `fx-${spec.name}`, preferredFragmentPrecision: 'highp' }),\n gpuProgram: GpuProgram.from({ vertex: { source: wgsl, entryPoint: 'mainVertex' }, fragment: { source: wgsl, entryPoint: 'mainFragment' } }),\n resources: spec.map ? { fxUniforms: group, uMap: Texture.WHITE.source } : { fxUniforms: group },\n // as sharp as the canvas: a pixel game's effects land on its own pixels\n resolution: 'inherit',\n });\n this.fxName = spec.name;\n this.group = group;\n }\n\n /** A uniform's value (a number, or the live array of a vector). */\n get(name: string): number | Float32Array {\n return this.group.uniforms[name] as number | Float32Array;\n }\n\n /** Sets a number uniform, or the components of a vector one. */\n set(name: string, ...values: number[]): this {\n const u = this.group.uniforms as Record<string, number | Float32Array>;\n const cur = u[name];\n if (cur === undefined) throw new Error(`${this.fxName}: no uniform '${name}'`);\n if (typeof cur === 'number') u[name] = values[0] ?? 0;\n else for (let i = 0; i < cur.length && i < values.length; i++) cur[i] = values[i]!;\n this.group.update();\n return this;\n }\n\n /** A number uniform, for the subclasses' getters. */\n protected num(name: string): number {\n return this.group.uniforms[name] as number;\n }\n}\n\n/** A 0xRRGGBB colour as three 0–1 channels. */\nexport function rgb(color: number): [number, number, number] {\n return [((color >> 16) & 0xff) / 255, ((color >> 8) & 0xff) / 255, (color & 0xff) / 255];\n}\n"],"names":["GLSL_TYPE","VERTEX_GLSL","glslSource","spec","decl","n","u","wgslSource","FxFilter","Filter","uniforms","group","UniformGroup","wgsl","GlProgram","GpuProgram","Texture","name","values","cur","i","rgb","color"],"mappings":"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"}
|
package/dist/postfx.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"postfx.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ChoiceDialog.js","sources":["../../src/react/ChoiceDialog.tsx"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\nimport { answerChoice, choiceStore, firstChoice, type ChoiceQuestion } from './choiceStore';\nimport { Modal } from './Modal';\nimport { useStore } from './useStore';\nimport { useUi, useCx } from './UiProvider';\n\nconst selectPending = (s: { pending: ChoiceQuestion | null }) => s.pending;\n\n/**\n * The open `askChoice`, if any: mounted once at the UI's root. A row per option, the first that can be taken picked;\n * Enter takes the picked one, Esc or a press outside cancels.\n */\nexport function ChoiceDialog({ width = 400 }: { width?: number }) {\n const cx = useCx();\n const ui = useUi();\n const pending = useStore(choiceStore, selectPending);\n const [picked, setPicked] = useState<string | null>(null);\n useEffect(() => setPicked(pending ? firstChoice(pending.options) : null), [pending]);\n const cancel = useCallback(() => answerChoice(null), []);\n const confirm = useCallback(() => {\n if (picked) answerChoice(picked);\n }, [picked]);\n if (!pending) return null;\n return (\n <Modal\n width={width}\n onCancel={cancel}\n onConfirm={confirm}\n className={cx('e1681-choice')}\n actions={\n <>\n <button type=\"button\" className={cx('e1681-button ghost')} onClick={cancel}>\n {pending.cancel ?? ui.labels.cancel}\n </button>\n <button type=\"button\" className={cx('e1681-button')} disabled={!picked} onClick={confirm}>\n {pending.confirm ?? ui.labels.choose}\n </button>\n </>\n }\n >\n <div className={cx('e1681-confirm-text')}>{pending.text}</div>\n <div className={cx('e1681-choice-list')} role=\"listbox\">\n {pending.options.map(({ id, label, icon, note, disabled }) => (\n <div\n key={id}\n role=\"option\"\n aria-selected={picked === id}\n aria-disabled={disabled || undefined}\n className={cx(`e1681-row e1681-pickable${picked === id ? ' picked' : ''}${disabled ? ' disabled' : ''}`)}\n onClick={() => !disabled && setPicked(id)}\n onDoubleClick={() => !disabled && answerChoice(id)}\n >\n {icon !== undefined && <span className={cx('e1681-choice-icon')}>{icon}</span>}\n <span className={cx('e1681-choice-label')}>{label}</span>\n {note !== undefined && <span className={cx('e1681-choice-note')}>{note}</span>}\n </div>\n ))}\n </div>\n </Modal>\n );\n}\n"],"names":["selectPending","s","ChoiceDialog","width","cx","useCx","ui","useUi","pending","useStore","choiceStore","picked","setPicked","useState","useEffect","firstChoice","cancel","useCallback","answerChoice","confirm","jsxs","Modal","Fragment","jsx","id","label","icon","note","disabled"],"mappings":";;;;;;AAMA,MAAMA,IAAgB,CAACC,MAA0CA,EAAE;AAM5D,SAASC,EAAa,EAAE,OAAAC,IAAQ,OAA2B;AAChE,QAAMC,IAAKC,EAAA,GACLC,IAAKC,EAAA,GACLC,IAAUC,EAASC,GAAaV,CAAa,GAC7C,CAACW,GAAQC,CAAS,IAAIC,EAAwB,IAAI;AACxD,EAAAC,EAAU,MAAMF,EAAUJ,IAAUO,EAAYP,EAAQ,OAAO,IAAI,IAAI,GAAG,CAACA,CAAO,CAAC;AACnF,QAAMQ,IAASC,EAAY,MAAMC,EAAa,IAAI,GAAG,CAAA,CAAE,GACjDC,IAAUF,EAAY,MAAM;AAChC,IAAIN,OAAqBA,CAAM;AAAA,EACjC,GAAG,CAACA,CAAM,CAAC;AACX,SAAKH,IAEH,gBAAAY;AAAA,IAACC;AAAA,IAAA;AAAA,MACC,OAAAlB;AAAA,MACA,UAAUa;AAAA,MACV,WAAWG;AAAA,MACX,WAAWf,EAAG,cAAc;AAAA,MAC5B,SACE,gBAAAgB,EAAAE,GAAA,EACE,UAAA;AAAA,QAAA,gBAAAC,EAAC,UAAA,EAAO,MAAK,UAAS,WAAWnB,EAAG,oBAAoB,GAAG,SAASY,GACjE,UAAAR,EAAQ,UAAUF,EAAG,OAAO,QAC/B;AAAA,0BACC,UAAA,EAAO,MAAK,UAAS,WAAWF,EAAG,cAAc,GAAG,UAAU,CAACO,GAAQ,SAASQ,GAC9E,YAAQ,WAAWb,EAAG,OAAO,OAAA,CAChC;AAAA,MAAA,GACF;AAAA,MAGF,UAAA;AAAA,QAAA,gBAAAiB,EAAC,SAAI,WAAWnB,EAAG,oBAAoB,GAAI,YAAQ,MAAK;AAAA,0BACvD,OAAA,EAAI,WAAWA,EAAG,mBAAmB,GAAG,MAAK,WAC3C,UAAAI,EAAQ,QAAQ,IAAI,CAAC,EAAE,IAAAgB,GAAI,OAAAC,GAAO,MAAAC,GAAM,MAAAC,GAAM,UAAAC,QAC7C,gBAAAR;AAAA,UAAC;AAAA,UAAA;AAAA,YAEC,MAAK;AAAA,YACL,iBAAeT,MAAWa;AAAA,YAC1B,iBAAeI,KAAY;AAAA,YAC3B,WAAWxB,EAAG,2BAA2BO,MAAWa,IAAK,YAAY,EAAE,GAAGI,IAAW,cAAc,EAAE,EAAE;AAAA,YACvG,SAAS,MAAM,CAACA,KAAYhB,EAAUY,CAAE;AAAA,YACxC,eAAe,MAAM,CAACI,KAAYV,EAAaM,CAAE;AAAA,YAEhD,UAAA;AAAA,cAAAE,MAAS,UAAa,gBAAAH,EAAC,QAAA,EAAK,WAAWnB,EAAG,mBAAmB,GAAI,UAAAsB,GAAK;AAAA,gCACtE,QAAA,EAAK,WAAWtB,EAAG,oBAAoB,GAAI,UAAAqB,GAAM;AAAA,cACjDE,MAAS,UAAa,gBAAAJ,EAAC,QAAA,EAAK,WAAWnB,EAAG,mBAAmB,GAAI,UAAAuB,EAAA,CAAK;AAAA,YAAA;AAAA,UAAA;AAAA,UAVlEH;AAAA,QAAA,CAYR,EAAA,CACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,IAnCiB;AAsCvB;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ConfirmDialog.js","sources":["../../src/react/ConfirmDialog.tsx"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\nimport { answerConfirm, confirmStore, parseQuantity, type ConfirmQuestion } from './confirmStore';\nimport { Glyph } from './Glyph';\nimport { Modal } from './Modal';\nimport { Slider } from './Slider';\nimport { useStore } from './useStore';\nimport { useUi, useCx } from './UiProvider';\n\nconst selectPending = (s: { pending: ConfirmQuestion | null }) => s.pending;\n\n/**\n * The open `askConfirm` / `askQuantity` question, if any: mounted once at the UI's root. Enter confirms, Esc or a\n * press outside cancels. A quantity is asked with a slider, − and + and a field for an exact number (a slider over 99\n * cannot land on 37 with a thumb).\n */\nexport function ConfirmDialog({ width = 360 }: { width?: number }) {\n const cx = useCx();\n const ui = useUi();\n const pending = useStore(confirmStore, selectPending);\n const [qty, setQty] = useState(1);\n useEffect(() => setQty(Math.max(1, pending?.max ?? 1)), [pending]);\n const many = (pending?.max ?? 0) > 1;\n const cancel = useCallback(() => answerConfirm(0), []);\n const confirm = useCallback(() => answerConfirm(many ? qty : 1), [many, qty]);\n if (!pending) return null;\n const max = pending.max;\n return (\n <Modal\n role=\"alertdialog\"\n width={width}\n onCancel={cancel}\n onConfirm={confirm}\n className={cx('e1681-confirm')}\n actions={\n <>\n <button type=\"button\" className={cx('e1681-button ghost')} onClick={cancel}>\n {pending.cancel ?? ui.labels.cancel}\n </button>\n <button type=\"button\" className={cx('e1681-button')} autoFocus={!ui.touch} onClick={confirm}>\n {pending.ok ?? ui.labels.ok}\n </button>\n </>\n }\n >\n <div className={cx('e1681-confirm-text')}>{pending.text}</div>\n {many && (\n <div className={cx('e1681-confirm-qty')}>\n <button type=\"button\" className={cx('e1681-button ghost small')} aria-label=\"-1\" onClick={() => setQty(Math.max(1, qty - 1))}>\n <Glyph id=\"minus\" />\n </button>\n <Slider value={qty} min={1} max={max} step={1} format={(v) => `x${v}`} label={ui.labels.quantity} onChange={setQty} />\n <button type=\"button\" className={cx('e1681-button ghost small')} aria-label=\"+1\" onClick={() => setQty(Math.min(max, qty + 1))}>\n <Glyph id=\"plus\" />\n </button>\n <input\n className={cx('e1681-input e1681-confirm-qty-input')}\n inputMode=\"numeric\"\n aria-label={ui.labels.quantity}\n value={qty}\n onChange={(e) => setQty(parseQuantity(e.target.value, max))}\n onFocus={(e) => e.target.select()}\n />\n </div>\n )}\n </Modal>\n );\n}\n"],"names":["selectPending","ConfirmDialog","width","cx","useCx","ui","useUi","pending","useStore","confirmStore","qty","setQty","useState","useEffect","many","cancel","useCallback","answerConfirm","confirm","max","jsxs","Modal","Fragment","jsx","Glyph","Slider","v","e","parseQuantity"],"mappings":";;;;;;;;AAQA,MAAMA,IAAgB,CAAC,MAA2C,EAAE;AAO7D,SAASC,EAAc,EAAE,OAAAC,IAAQ,OAA2B;AACjE,QAAMC,IAAKC,EAAA,GACLC,IAAKC,EAAA,GACLC,IAAUC,EAASC,GAAcT,CAAa,GAC9C,CAACU,GAAKC,CAAM,IAAIC,EAAS,CAAC;AAChC,EAAAC,EAAU,MAAMF,EAAO,KAAK,IAAI,GAAGJ,GAAS,OAAO,CAAC,CAAC,GAAG,CAACA,CAAO,CAAC;AACjE,QAAMO,KAAQP,GAAS,OAAO,KAAK,GAC7BQ,IAASC,EAAY,MAAMC,EAAc,CAAC,GAAG,CAAA,CAAE,GAC/CC,IAAUF,EAAY,MAAMC,EAAcH,IAAOJ,IAAM,CAAC,GAAG,CAACI,GAAMJ,CAAG,CAAC;AAC5E,MAAI,CAACH,EAAS,QAAO;AACrB,QAAMY,IAAMZ,EAAQ;AACpB,SACE,gBAAAa;AAAA,IAACC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,OAAAnB;AAAA,MACA,UAAUa;AAAA,MACV,WAAWG;AAAA,MACX,WAAWf,EAAG,eAAe;AAAA,MAC7B,SACE,gBAAAiB,EAAAE,GAAA,EACE,UAAA;AAAA,QAAA,gBAAAC,EAAC,UAAA,EAAO,MAAK,UAAS,WAAWpB,EAAG,oBAAoB,GAAG,SAASY,GACjE,UAAAR,EAAQ,UAAUF,EAAG,OAAO,QAC/B;AAAA,0BACC,UAAA,EAAO,MAAK,UAAS,WAAWF,EAAG,cAAc,GAAG,WAAW,CAACE,EAAG,OAAO,SAASa,GACjF,YAAQ,MAAMb,EAAG,OAAO,GAAA,CAC3B;AAAA,MAAA,GACF;AAAA,MAGF,UAAA;AAAA,QAAA,gBAAAkB,EAAC,SAAI,WAAWpB,EAAG,oBAAoB,GAAI,YAAQ,MAAK;AAAA,QACvDW,KACC,gBAAAM,EAAC,OAAA,EAAI,WAAWjB,EAAG,mBAAmB,GACpC,UAAA;AAAA,UAAA,gBAAAoB,EAAC,UAAA,EAAO,MAAK,UAAS,WAAWpB,EAAG,0BAA0B,GAAG,cAAW,MAAK,SAAS,MAAMQ,EAAO,KAAK,IAAI,GAAGD,IAAM,CAAC,CAAC,GACzH,UAAA,gBAAAa,EAACC,GAAA,EAAM,IAAG,QAAA,CAAQ,EAAA,CACpB;AAAA,UACA,gBAAAD,EAACE,KAAO,OAAOf,GAAK,KAAK,GAAG,KAAAS,GAAU,MAAM,GAAG,QAAQ,CAACO,MAAM,IAAIA,CAAC,IAAI,OAAOrB,EAAG,OAAO,UAAU,UAAUM,GAAQ;AAAA,UACpH,gBAAAY,EAAC,UAAA,EAAO,MAAK,UAAS,WAAWpB,EAAG,0BAA0B,GAAG,cAAW,MAAK,SAAS,MAAMQ,EAAO,KAAK,IAAIQ,GAAKT,IAAM,CAAC,CAAC,GAC3H,UAAA,gBAAAa,EAACC,GAAA,EAAM,IAAG,OAAA,CAAO,EAAA,CACnB;AAAA,UACA,gBAAAD;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAWpB,EAAG,qCAAqC;AAAA,cACnD,WAAU;AAAA,cACV,cAAYE,EAAG,OAAO;AAAA,cACtB,OAAOK;AAAA,cACP,UAAU,CAACiB,MAAMhB,EAAOiB,EAAcD,EAAE,OAAO,OAAOR,CAAG,CAAC;AAAA,cAC1D,SAAS,CAACQ,MAAMA,EAAE,OAAO,OAAA;AAAA,YAAO;AAAA,UAAA;AAAA,QAClC,EAAA,CACF;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIR;"}
|
package/dist/react/Glyph.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Glyph.js","sources":["../../src/react/Glyph.tsx"],"sourcesContent":["import { memo } from 'react';\nimport { useCx } from './UiProvider';\n\n/**\n * Small marks (close, check, arrows, a star…) as pixel art on a square grid, drawn in the text's colour. A pixel font\n * usually carries letters and punctuation only, so a ✕, ✓, ▾ or → typed into the UI falls through to a system font\n * and reads as a smooth vector glyph in the middle of pixel text. `scale` is screen pixels per glyph pixel: a whole\n * number, so the mark keeps its pixels square.\n *\n * Rows are strings, one character a pixel: `.` is empty, `X` the text's colour; any other letter takes its colour\n * from the glyph's `tones` (a coloured glyph, lit like the world's art).\n */\nexport interface GlyphArt {\n rows: readonly string[];\n tones?: Readonly<Record<string, string>>;\n}\n\nconst BUILT_IN = {\n close: ['X.....X', '.X...X.', '..X.X..', '...X...', '..X.X..', '.X...X.', 'X.....X'],\n check: ['.......', '......X', '.....XX', 'X...XX.', 'XX.XX..', '.XXX...', '..X....'],\n down: ['.......', '.......', 'XXXXXXX', '.XXXXX.', '..XXX..', '...X...', '.......'],\n up: ['.......', '...X...', '..XXX..', '.XXXXX.', 'XXXXXXX', '.......', '.......'],\n right: ['..X....', '..XX...', '..XXX..', '..XXXX.', '..XXX..', '..XX...', '..X....'],\n left: ['....X..', '...XX..', '..XXX..', '.XXXX..', '..XXX..', '...XX..', '....X..'],\n arrow: ['.......', '....X..', '.....X.', 'XXXXXXX', '.....X.', '....X..', '.......'],\n back: ['..X....', '.XX....', 'XXXXXX.', '.XX...X', '..X...X', '......X', '..XXXX.'],\n star: ['...X...', '...X...', 'XXXXXXX', '.XXXXX.', '..XXX..', '.XX.XX.', 'X.....X'],\n diamond: ['...X...', '..XXX..', '.XXXXX.', 'XXXXXXX', '.XXXXX.', '..XXX..', '...X...'],\n diamondOpen: ['...X...', '..X.X..', '.X...X.', 'X.....X', '.X...X.', '..X.X..', '...X...'],\n menu: ['.......', 'XXXXXXX', '.......', 'XXXXXXX', '.......', 'XXXXXXX', '.......'],\n plus: ['.......', '...X...', '...X...', '.XXXXX.', '...X...', '...X...', '.......'],\n minus: ['.......', '.......', '.......', '.XXXXX.', '.......', '.......', '.......'],\n warn: ['...X...', '..XXX..', '..X.X..', '.XX.XX.', '.X...X.', 'XX.X.XX', 'XXXXXXX'],\n spark: ['...X...', '...X...', '..XXX..', 'XXXXXXX', '..XXX..', '...X...', '...X...'],\n lock: ['..XXX..', '.X...X.', '.X...X.', 'XXXXXXX', 'XXX.XXX', 'XXX.XXX', 'XXXXXXX'],\n pin: ['.XXXXX.', '..XXX..', '..XXX..', '.XXXXX.', 'XXXXXXX', '...X...', '...X...'],\n mail: ['.......', 'XXXXXXX', 'XX...XX', 'X.X.X.X', 'X..X..X', 'X.....X', 'XXXXXXX'],\n} as const;\n\n/** The marks the engine draws itself; a game adds its own with `registerGlyph`. */\nexport type BuiltInGlyph = keyof typeof BUILT_IN;\nexport type GlyphId = BuiltInGlyph | (string & {});\n\n/** Each glyph as [fill, path] pairs, built once: one pair in the text's colour, or one per tone. */\nconst LAYERS = new Map<string, { size: number; layers: [string, string][] }>();\n\n/** The cells of one letter as one SVG path of 1x1 squares. */\nexport function glyphPath(rows: readonly string[], letter: string): string {\n return rows.flatMap((row, y) => [...row].flatMap((c, x) => (c === letter ? [`M${x} ${y}h1v1h-1z`] : []))).join('');\n}\n\n/** Adds a mark (or replaces one, a built-in included): a coin in the game's own colours, a class crest. */\nexport function registerGlyph(id: string, art: GlyphArt | readonly string[]): void {\n const { rows, tones } = Array.isArray(art) ? { rows: art as readonly string[], tones: undefined } : (art as GlyphArt);\n const size = Math.max(rows.length, ...rows.map((r) => r.length));\n const layers: [string, string][] = [];\n if (rows.some((r) => r.includes('X'))) layers.push(['currentColor', glyphPath(rows, 'X')]);\n for (const [letter, fill] of Object.entries(tones ?? {})) layers.push([fill, glyphPath(rows, letter)]);\n LAYERS.set(id, { size, layers });\n}\n\nfor (const [id, rows] of Object.entries(BUILT_IN)) registerGlyph(id, rows);\n\n/** Whether `id` has art (built in or registered). */\nexport function hasGlyph(id: string): boolean {\n return LAYERS.has(id);\n}\n\nexport interface GlyphProps {\n id: GlyphId;\n /** Screen pixels per glyph pixel (default 2). */\n scale?: number;\n className?: string;\n /** Spoken name; without it the mark is hidden from screen readers (it sits beside words that say it). */\n label?: string;\n}\n\nexport const Glyph = memo(function Glyph({ id, scale = 2, className, label }: GlyphProps) {\n const cx = useCx();\n const art = LAYERS.get(id);\n if (!art) return null;\n const px = art.size * Math.max(1, Math.round(scale));\n return (\n <svg\n className={cx(className ? `e1681-glyph ${className}` : 'e1681-glyph')}\n width={px}\n height={px}\n viewBox={`0 0 ${art.size} ${art.size}`}\n shapeRendering=\"crispEdges\"\n role={label ? 'img' : undefined}\n aria-label={label}\n aria-hidden={label ? undefined : true}\n >\n {art.layers.map(([fill, d]) => (\n <path key={fill} d={d} fill={fill} />\n ))}\n </svg>\n );\n});\n"],"names":["BUILT_IN","LAYERS","glyphPath","rows","letter","row","y","c","x","registerGlyph","id","art","tones","size","r","layers","fill","hasGlyph","Glyph","memo","scale","className","label","cx","useCx","px","jsx","d"],"mappings":"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"}
|
package/dist/react/Modal.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.js","sources":["../../src/react/Modal.tsx"],"sourcesContent":["import { type ReactNode, useEffect, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport { SCROLL_CLASS, useFitToViewport } from './useFitToViewport';\nimport { useUi, useCx } from './UiProvider';\n\nexport interface ModalProps {\n /** Esc, or a press on the backdrop. */\n onCancel: () => void;\n /** Enter (a text field's own Enter included). */\n onConfirm?: () => void;\n /** The box's preferred width, px. */\n width?: number;\n role?: 'dialog' | 'alertdialog';\n label?: string;\n className?: string;\n children: ReactNode;\n /** Under the scrolling body, never scrolled away: the dialog's buttons. */\n actions?: ReactNode;\n}\n\n/**\n * A dialog box over a backdrop, fitted into the viewport like a window (`fitToViewport`, centred in whole pixels) and\n * portalled to the `UiProvider`'s `portal`. Esc and Enter are caught on the window in the capture phase, so they do\n * not also close the window underneath or reach the game's keys.\n */\nexport function Modal({ onCancel, onConfirm, width, role = 'dialog', label, className, children, actions }: ModalProps) {\n const cx = useCx();\n const ui = useUi();\n const ref = useRef<HTMLDivElement>(null);\n useFitToViewport(ref, { enabled: true, ...(width !== undefined ? { width } : {}) });\n\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') onCancel();\n else if (e.key === 'Enter' && onConfirm) onConfirm();\n else return;\n e.preventDefault();\n e.stopImmediatePropagation();\n };\n window.addEventListener('keydown', onKey, true);\n return () => window.removeEventListener('keydown', onKey, true);\n }, [onCancel, onConfirm]);\n\n const target = ui.portal();\n const box = (\n <div className={cx('e1681-backdrop')} onPointerDown={(e) => e.stopPropagation()} onClick={onCancel}>\n <div\n ref={ref}\n className={cx(`e1681-dialog e1681-frame${className ? ` ${className}` : ''}`)}\n role={role}\n aria-modal=\"true\"\n aria-label={label}\n style={{ width }}\n onClick={(e) => e.stopPropagation()}\n >\n <div className={cx(`e1681-dialog-body ${SCROLL_CLASS}`)}>{children}</div>\n {actions !== undefined && <div className={cx('e1681-dialog-actions')}>{actions}</div>}\n </div>\n </div>\n );\n return target ? createPortal(box, target) : box;\n}\n"],"names":["Modal","onCancel","onConfirm","width","role","label","className","children","actions","cx","useCx","ui","useUi","ref","useRef","useFitToViewport","useEffect","onKey","e","target","box","jsx","jsxs","SCROLL_CLASS","createPortal"],"mappings":";;;;;AAyBO,SAASA,EAAM,EAAE,UAAAC,GAAU,WAAAC,GAAW,OAAAC,GAAO,MAAAC,IAAO,UAAU,OAAAC,GAAO,WAAAC,GAAW,UAAAC,GAAU,SAAAC,EAAA,GAAuB;AACtH,QAAMC,IAAKC,EAAA,GACLC,IAAKC,EAAA,GACLC,IAAMC,EAAuB,IAAI;AACvC,EAAAC,EAAiBF,GAAK,EAAE,SAAS,IAAM,GAAIV,MAAU,SAAY,EAAE,OAAAA,MAAU,CAAA,GAAK,GAElFa,EAAU,MAAM;AACd,UAAMC,IAAQ,CAACC,MAAqB;AAClC,UAAIA,EAAE,QAAQ,SAAU,CAAAjB,EAAA;AAAA,eACfiB,EAAE,QAAQ,WAAWhB,EAAW,CAAAA,EAAA;AAAA,UACpC;AACL,MAAAgB,EAAE,eAAA,GACFA,EAAE,yBAAA;AAAA,IACJ;AACA,kBAAO,iBAAiB,WAAWD,GAAO,EAAI,GACvC,MAAM,OAAO,oBAAoB,WAAWA,GAAO,EAAI;AAAA,EAChE,GAAG,CAAChB,GAAUC,CAAS,CAAC;AAExB,QAAMiB,IAASR,EAAG,OAAA,GACZS,IACJ,gBAAAC,EAAC,OAAA,EAAI,WAAWZ,EAAG,gBAAgB,GAAG,eAAe,CAAC,MAAM,EAAE,gBAAA,GAAmB,SAASR,GACxF,UAAA,gBAAAqB;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAT;AAAA,MACA,WAAWJ,EAAG,2BAA2BH,IAAY,IAAIA,CAAS,KAAK,EAAE,EAAE;AAAA,MAC3E,MAAAF;AAAA,MACA,cAAW;AAAA,MACX,cAAYC;AAAA,MACZ,OAAO,EAAE,OAAAF,EAAA;AAAA,MACT,SAAS,CAAC,MAAM,EAAE,gBAAA;AAAA,MAElB,UAAA;AAAA,QAAA,gBAAAkB,EAAC,SAAI,WAAWZ,EAAG,qBAAqBc,CAAY,EAAE,GAAI,UAAAhB,GAAS;AAAA,QAClEC,MAAY,UAAa,gBAAAa,EAAC,OAAA,EAAI,WAAWZ,EAAG,sBAAsB,GAAI,UAAAD,EAAA,CAAQ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,GAEnF;AAEF,SAAOW,IAASK,EAAaJ,GAAKD,CAAM,IAAIC;AAC9C;"}
|
package/dist/react/Panel.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Panel.js","sources":["../../src/react/Panel.tsx"],"sourcesContent":["import { type CSSProperties, type ReactNode, useRef } from 'react';\nimport type { Point } from '../ui/layout/geometry';\nimport { Glyph } from './Glyph';\nimport { fillLabel } from './labels';\nimport { sheetScaleStyle } from './placement';\nimport { SCROLL_CLASS, useFitToViewport } from './useFitToViewport';\nimport { useUi, useCx } from './UiProvider';\n\nexport interface PanelProps {\n title: ReactNode;\n /** The close button's action; no close button without it. */\n onClose?: () => void;\n children?: ReactNode;\n /** The width it wants, in px (its own units); default: as wide as its content. */\n width?: number;\n /** Where it wants to stand, screen px; default: centred. It is moved back inside the screen either way. */\n at?: Point;\n /** Under the scrolling body, never scrolled away (a row of buttons). */\n footer?: ReactNode;\n className?: string;\n style?: CSSProperties;\n /** The title as plain text, for the close button's spoken name when `title` is not a string. */\n titleText?: string;\n}\n\n/**\n * A window that never overflows the screen: fitted by `fitToViewport` (moved inside the edges, cut to what is left,\n * shrunk to `minScale` when the `UiProvider` allows it) with its header kept and its body scrolling under it. Placed\n * in whole pixels by `left`/`top`, never centred with `translate(-50%)`, which stood an odd-width window half a pixel\n * off the grid and smeared every icon in it.\n *\n * On a touch screen it is a full-screen sheet instead (`.e1681-sheet`): no width of its own, the header put and the\n * body scrolling, laid out at the provider's `sheetScale`. A press inside never reaches the world under it.\n */\nexport function Panel({ title, onClose, children, width, at, footer, className, style, titleText }: PanelProps) {\n const cx = useCx();\n const ui = useUi();\n const ref = useRef<HTMLDivElement>(null);\n useFitToViewport(ref, { enabled: !ui.touch, ...(width !== undefined ? { width } : {}), ...(at ? { at } : {}) });\n const name = titleText ?? (typeof title === 'string' ? title : '');\n const header = (\n <div className={cx('e1681-panel-header')}>\n <h3 className={cx('e1681-panel-title')}>{title}</h3>\n {onClose && (\n <button type=\"button\" className={cx('e1681-close')} onClick={onClose} title={ui.labels.closeHint} aria-label={fillLabel(ui.labels.close, name).trim()}>\n <Glyph id=\"close\" />\n </button>\n )}\n </div>\n );\n const classes = ['e1681-panel', 'e1681-frame', ui.touch ? 'e1681-sheet' : '', className ?? ''].filter(Boolean).join(' ');\n const own: CSSProperties = ui.touch ? { ...sheetScaleStyle(ui.sheetScale) } : { width };\n return (\n <div ref={ref} className={cx(classes)} style={{ ...own, ...style }} role=\"dialog\" aria-label={name || undefined} onPointerDown={(e) => e.stopPropagation()}>\n {header}\n <div className={cx(`e1681-panel-body ${SCROLL_CLASS}`)}>{children}</div>\n {footer !== undefined && <div className={cx('e1681-panel-footer')}>{footer}</div>}\n </div>\n );\n}\n"],"names":["Panel","title","onClose","children","width","at","footer","className","style","titleText","cx","useCx","ui","useUi","ref","useRef","useFitToViewport","name","header","jsxs","jsx","fillLabel","Glyph","classes","own","sheetScaleStyle","e","SCROLL_CLASS"],"mappings":";;;;;;;AAkCO,SAASA,EAAM,EAAE,OAAAC,GAAO,SAAAC,GAAS,UAAAC,GAAU,OAAAC,GAAO,IAAAC,GAAI,QAAAC,GAAQ,WAAAC,GAAW,OAAAC,GAAO,WAAAC,EAAA,GAAyB;AAC9G,QAAMC,IAAKC,EAAA,GACLC,IAAKC,EAAA,GACLC,IAAMC,EAAuB,IAAI;AACvC,EAAAC,EAAiBF,GAAK,EAAE,SAAS,CAACF,EAAG,OAAO,GAAIR,MAAU,SAAY,EAAE,OAAAA,MAAU,CAAA,GAAK,GAAIC,IAAK,EAAE,IAAAA,EAAA,IAAO,CAAA,GAAK;AAC9G,QAAMY,IAAOR,MAAc,OAAOR,KAAU,WAAWA,IAAQ,KACzDiB,IACJ,gBAAAC,EAAC,OAAA,EAAI,WAAWT,EAAG,oBAAoB,GACrC,UAAA;AAAA,IAAA,gBAAAU,EAAC,MAAA,EAAG,WAAWV,EAAG,mBAAmB,GAAI,UAAAT,GAAM;AAAA,IAC9CC,KACC,gBAAAkB,EAAC,UAAA,EAAO,MAAK,UAAS,WAAWV,EAAG,aAAa,GAAG,SAASR,GAAS,OAAOU,EAAG,OAAO,WAAW,cAAYS,EAAUT,EAAG,OAAO,OAAOK,CAAI,EAAE,KAAA,GAC7I,UAAA,gBAAAG,EAACE,GAAA,EAAM,IAAG,QAAA,CAAQ,EAAA,CACpB;AAAA,EAAA,GAEJ,GAEIC,IAAU,CAAC,eAAe,eAAeX,EAAG,QAAQ,gBAAgB,IAAIL,KAAa,EAAE,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GACjHiB,IAAqBZ,EAAG,QAAQ,EAAE,GAAGa,EAAgBb,EAAG,UAAU,MAAM,EAAE,OAAAR,EAAA;AAChF,SACE,gBAAAe,EAAC,OAAA,EAAI,KAAAL,GAAU,WAAWJ,EAAGa,CAAO,GAAG,OAAO,EAAE,GAAGC,GAAK,GAAGhB,KAAS,MAAK,UAAS,cAAYS,KAAQ,QAAW,eAAe,CAACS,MAAMA,EAAE,gBAAA,GACtI,UAAA;AAAA,IAAAR;AAAA,IACD,gBAAAE,EAAC,SAAI,WAAWV,EAAG,oBAAoBiB,CAAY,EAAE,GAAI,UAAAxB,GAAS;AAAA,IACjEG,MAAW,UAAa,gBAAAc,EAAC,OAAA,EAAI,WAAWV,EAAG,oBAAoB,GAAI,UAAAJ,EAAA,CAAO;AAAA,EAAA,GAC7E;AAEJ;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PixelImage.js","sources":["../../src/react/PixelImage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { pixelFit } from './placement';\nimport { useCx } from './UiProvider';\n\nexport interface PixelImageProps {\n src: string;\n /** The box, CSS px. */\n width: number;\n height: number;\n /** Image pixels the URL already spends on one art pixel (a sheet painted at 4x). */\n upscaled?: number;\n /** Caps the multiple, so a row of sprites of different sizes keeps their sizes against each other. */\n maxScale?: number;\n alt?: string;\n className?: string;\n}\n\n/**\n * A sprite whose size is not known ahead (a creature, a portrait), shown at the largest **whole** multiple of its\n * art that fits the box, centred (`pixelFit`); a sprite bigger than the box is shrunk by a whole divisor. Hidden until\n * it has loaded and been measured, so it never flashes at the wrong size.\n */\nexport function PixelImage({ src, width, height, upscaled = 1, maxScale, alt = '', className }: PixelImageProps) {\n const cx = useCx();\n const [art, setArt] = useState<{ src: string; w: number; h: number } | null>(null);\n const size = art?.src === src ? pixelFit(art.w, art.h, width, height, maxScale) : null;\n return (\n <span className={cx(className ? `e1681-pixel-image ${className}` : 'e1681-pixel-image')} style={{ width, height }}>\n <img\n src={src}\n alt={alt}\n draggable={false}\n style={size ? { width: size.width, height: size.height } : { visibility: 'hidden' }}\n onLoad={(e) => {\n const img = e.currentTarget;\n setArt({ src, w: img.naturalWidth / upscaled, h: img.naturalHeight / upscaled });\n }}\n />\n </span>\n );\n}\n"],"names":["PixelImage","src","width","height","upscaled","maxScale","alt","className","cx","useCx","art","setArt","useState","size","pixelFit","jsx","e","img"],"mappings":";;;;AAsBO,SAASA,EAAW,EAAE,KAAAC,GAAK,OAAAC,GAAO,QAAAC,GAAQ,UAAAC,IAAW,GAAG,UAAAC,GAAU,KAAAC,IAAM,IAAI,WAAAC,EAAA,GAA8B;AAC/G,QAAMC,IAAKC,EAAA,GACL,CAACC,GAAKC,CAAM,IAAIC,EAAuD,IAAI,GAC3EC,IAAOH,GAAK,QAAQT,IAAMa,EAASJ,EAAI,GAAGA,EAAI,GAAGR,GAAOC,GAAQE,CAAQ,IAAI;AAClF,SACE,gBAAAU,EAAC,QAAA,EAAK,WAAWP,EAAGD,IAAY,qBAAqBA,CAAS,KAAK,mBAAmB,GAAG,OAAO,EAAE,OAAAL,GAAO,QAAAC,KACvG,UAAA,gBAAAY;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAd;AAAA,MACA,KAAAK;AAAA,MACA,WAAW;AAAA,MACX,OAAOO,IAAO,EAAE,OAAOA,EAAK,OAAO,QAAQA,EAAK,OAAA,IAAW,EAAE,YAAY,SAAA;AAAA,MACzE,QAAQ,CAACG,MAAM;AACb,cAAMC,IAAMD,EAAE;AACd,QAAAL,EAAO,EAAE,KAAAV,GAAK,GAAGgB,EAAI,eAAeb,GAAU,GAAGa,EAAI,gBAAgBb,EAAA,CAAU;AAAA,MACjF;AAAA,IAAA;AAAA,EAAA,GAEJ;AAEJ;"}
|
package/dist/react/Select.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Select.js","sources":["../../src/react/Select.tsx"],"sourcesContent":["import { type KeyboardEvent, type ReactNode, useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { listWidth } from '../ui/layout/listWidth';\nimport { scrollIntoView } from '../ui/layout/scroll';\nimport { Glyph } from './Glyph';\nimport { listPlacement, type ListPlacement } from './placement';\nimport { useUi, useCx } from './UiProvider';\n\nexport interface SelectOption<T extends string | number> {\n value: T;\n label: ReactNode;\n}\n\nexport interface SelectProps<T extends string | number> {\n value: T;\n options: readonly SelectOption<T>[];\n onChange: (value: T) => void;\n /** What it chooses, for a screen reader. */\n label?: string;\n /** Fills its row, cutting the label with an ellipsis instead of growing. */\n grow?: boolean;\n className?: string;\n disabled?: boolean;\n}\n\n/** The next option along the list, wrapping round. */\nexport function stepOption(index: number, step: number, count: number): number {\n return count > 0 ? (((Math.max(0, index) + step) % count) + count) % count : 0;\n}\n\n/**\n * A drop-down drawn by the theme instead of the browser's `<select>`, whose list is the OS's own widget and whose box\n * clips its text at a fixed width. The button is as wide as its longest label (every label stacked in one cell, the\n * picked one shown). The list opens in a portal, so no window's `overflow` cuts it: as wide as its longest label\n * (`listWidth`), under the button or over it when the screen ends below (`listPlacement`). A press never takes the\n * focus, so a text field beside it keeps its keyboard.\n */\nexport function Select<T extends string | number>({ value, options, onChange, label, grow, className, disabled }: SelectProps<T>) {\n const cx = useCx();\n const ui = useUi();\n const [open, setOpen] = useState(false);\n const [place, setPlace] = useState<(ListPlacement & { width: number }) | null>(null);\n const [hot, setHot] = useState(0);\n const buttonRef = useRef<HTMLButtonElement>(null);\n const listRef = useRef<HTMLDivElement>(null);\n const picked = options.findIndex((o) => o.value === value);\n\n // first rendered hidden at its natural size, then measured and placed\n useLayoutEffect(() => {\n const list = listRef.current;\n const button = buttonRef.current;\n if (!open || !list || !button || place) return;\n const rows = Array.from(list.children) as HTMLElement[];\n const widest = rows.reduce((w, r) => Math.max(w, r.offsetWidth), 0);\n const view = ui.viewport();\n const width = listWidth(\n rows.map((_, i) => String(i)),\n (i) => rows[Number(i)]?.offsetWidth ?? 0,\n { padding: list.offsetWidth - widest, min: Math.round(button.offsetWidth), max: view.width - 2 * ui.margin },\n );\n const r = button.getBoundingClientRect();\n const at = listPlacement({ x: r.left, y: r.top, width: r.width, height: r.height }, { width, height: list.offsetHeight }, view, 2, ui.margin);\n setPlace({ ...at, width });\n }, [open, place, ui]);\n\n // the option moved to by the keyboard is scrolled into view, no further\n useEffect(() => {\n const list = listRef.current;\n const row = list?.children[hot] as HTMLElement | undefined;\n if (list && row) list.scrollTop = scrollIntoView(list.scrollTop, row.offsetTop, row.offsetHeight, list.clientHeight, list.scrollHeight);\n }, [hot, place]);\n\n useEffect(() => {\n if (!open) return;\n const away = (e: PointerEvent) => {\n const t = e.target as Node;\n if (!buttonRef.current?.contains(t) && !listRef.current?.contains(t)) setOpen(false);\n };\n const shut = () => setOpen(false);\n document.addEventListener('pointerdown', away, true);\n window.addEventListener('resize', shut);\n return () => {\n document.removeEventListener('pointerdown', away, true);\n window.removeEventListener('resize', shut);\n };\n }, [open]);\n\n const show = (on: boolean) => {\n setPlace(null);\n setOpen(on);\n };\n const choose = (i: number) => {\n const o = options[i];\n show(false);\n if (o && o.value !== value) onChange(o.value);\n };\n const toggle = () => {\n if (disabled) return;\n setHot(Math.max(0, picked));\n show(!open);\n };\n const onKey = (e: KeyboardEvent) => {\n // the game's own keys never see what is pressed here\n e.stopPropagation();\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {\n e.preventDefault();\n const step = e.key === 'ArrowDown' ? 1 : -1;\n if (open) setHot((h) => stepOption(h, step, options.length));\n else choose(stepOption(picked, step, options.length));\n } else if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n if (open) choose(hot);\n else toggle();\n } else if (e.key === 'Escape' && open) {\n e.preventDefault();\n show(false);\n }\n };\n\n const target = ui.portal();\n const list = (\n <div\n ref={listRef}\n className={cx(`e1681-select-list e1681-tip-frame${place ? '' : ' measuring'}`)}\n role=\"listbox\"\n aria-label={label}\n style={place ? { left: place.x, top: place.y, width: place.width, maxHeight: place.height } : { left: 0, top: 0, visibility: 'hidden' }}\n onPointerDown={(e) => e.preventDefault()}\n >\n {options.map((o, i) => (\n <div\n key={String(o.value)}\n role=\"option\"\n aria-selected={i === picked}\n className={cx(`e1681-select-option${i === picked ? ' picked' : ''}${i === hot ? ' hot' : ''}`)}\n onPointerEnter={() => setHot(i)}\n onClick={() => choose(i)}\n >\n {o.label}\n </div>\n ))}\n </div>\n );\n return (\n <>\n <button\n ref={buttonRef}\n type=\"button\"\n className={cx(`e1681-select${open ? ' open' : ''}${grow ? ' grow' : ''}${className ? ` ${className}` : ''}`)}\n aria-label={label}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n disabled={disabled}\n onPointerDown={(e) => e.preventDefault()}\n onClick={toggle}\n onKeyDown={onKey}\n >\n <span className={cx('e1681-select-labels')}>\n {options.map((o, i) => (\n <span key={String(o.value)} className={cx(i === picked ? 'shown' : undefined)} aria-hidden={i !== picked}>\n {o.label}\n </span>\n ))}\n </span>\n <Glyph id={open ? 'up' : 'down'} />\n </button>\n {open && (target ? createPortal(list, target) : list)}\n </>\n );\n}\n"],"names":["stepOption","index","step","count","Select","value","options","onChange","label","grow","className","disabled","cx","useCx","ui","useUi","open","setOpen","useState","place","setPlace","hot","setHot","buttonRef","useRef","listRef","picked","o","useLayoutEffect","list","button","rows","widest","r","view","width","listWidth","_","i","at","listPlacement","useEffect","row","scrollIntoView","away","e","t","shut","show","on","choose","toggle","onKey","h","target","jsx","jsxs","Fragment","Glyph","createPortal"],"mappings":"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"}
|