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":"collide.js","sources":["../../src/physics/collide.ts"],"sourcesContent":["import type { Body } from './Body';\nimport { circlePolygon, polygonPolygon } from './collidePolygon';\nimport { FACE_BOTTOM, FACE_LEFT, FACE_RIGHT, FACE_TOP, type BoxShape, type CircleShape } from './types';\n\n/**\n * The result of a narrow-phase test: the normal points from `a` to `b`, `depth` > 0 is overlap, < 0 a gap (the\n * deepest of the points). Up to two contact points, each with its own depth and a feature id that stays the same\n * while the same corners touch, so the solver can carry each point's impulse over from step to step.\n */\nexport interface Manifold {\n nx: number;\n ny: number;\n depth: number;\n count: number;\n readonly px: [number, number];\n readonly py: [number, number];\n readonly pd: [number, number];\n readonly id: [number, number];\n}\n\nexport function emptyManifold(): Manifold {\n return { nx: 0, ny: 0, depth: 0, count: 0, px: [0, 0], py: [0, 0], pd: [0, 0], id: [0, 0] };\n}\n\n/**\n * Narrow phase. Fills `out` and returns true when the two shapes overlap or lie closer than `margin`. The margin\n * keeps a resting contact alive while the solver holds the bodies a hair apart, so a body standing on the ground is\n * grounded on every step instead of flickering between touching and not.\n *\n * Two bodies that never turn and sit square (boxes at no angle, circles) take the axis-aligned tests, which know the\n * internal faces of merged tile boxes; anything turned or turning takes the polygon tests (`collidePolygon.ts`).\n */\nexport function collide(a: Body, b: Body, margin: number, out: Manifold): boolean {\n const sa = a.shape;\n const sb = b.shape;\n if (sa.kind === 'circle' && sb.kind === 'circle') {\n if (!circleCircle(a, sa, b, sb, margin, out)) return false;\n onePoint(out, a.x + out.nx * (sa.radius - out.depth / 2), a.y + out.ny * (sa.radius - out.depth / 2));\n return true;\n }\n if (sa.kind === 'circle') {\n if (!b.axisAligned) return circlePolygon(a, sa.radius, b, margin, true, out);\n if (!circleBox(a, sa, b, sb as BoxShape, margin, out)) return false;\n out.nx = -out.nx;\n out.ny = -out.ny;\n // the normal now runs from the circle (a) to the box: the circle's surface point is ahead of its centre\n onePoint(out, a.x + out.nx * (sa.radius - out.depth / 2), a.y + out.ny * (sa.radius - out.depth / 2));\n return true;\n }\n if (sb.kind === 'circle') {\n if (!a.axisAligned) return circlePolygon(b, sb.radius, a, margin, false, out);\n if (!circleBox(b, sb, a, sa as BoxShape, margin, out)) return false;\n // circleBox gives the normal from the box (a) to the circle (b): already a → b\n onePoint(out, b.x - out.nx * (sb.radius - out.depth / 2), b.y - out.ny * (sb.radius - out.depth / 2));\n return true;\n }\n if (a.axisAligned && b.axisAligned && a.invInertia === 0 && b.invInertia === 0) {\n if (!boxBox(a, sa as BoxShape, b, sb as BoxShape, margin, out)) return false;\n // neither turns, so where the point lies changes nothing: the middle of the overlap\n onePoint(out, (Math.max(a.minX, b.minX) + Math.min(a.maxX, b.maxX)) / 2, (Math.max(a.minY, b.minY) + Math.min(a.maxY, b.maxY)) / 2);\n return true;\n }\n return polygonPolygon(a, b, margin, out);\n}\n\nfunction onePoint(out: Manifold, x: number, y: number): void {\n out.count = 1;\n out.px[0] = x;\n out.py[0] = y;\n out.pd[0] = out.depth;\n out.id[0] = 0;\n}\n\n/** The narrow phase's three numbers, for the axis-aligned tests below. */\ninterface Normal {\n nx: number;\n ny: number;\n depth: number;\n}\n\n/** Whether a normal along x (sign sx, from a to b) may be used: not out of a face either box marks as internal. */\nfunction xAllowed(a: Body, b: Body, sx: number): boolean {\n return sx > 0 ? !(a.internalFaces & FACE_RIGHT) && !(b.internalFaces & FACE_LEFT) : !(a.internalFaces & FACE_LEFT) && !(b.internalFaces & FACE_RIGHT);\n}\n\nfunction yAllowed(a: Body, b: Body, sy: number): boolean {\n return sy > 0 ? !(a.internalFaces & FACE_BOTTOM) && !(b.internalFaces & FACE_TOP) : !(a.internalFaces & FACE_TOP) && !(b.internalFaces & FACE_BOTTOM);\n}\n\nfunction boxBox(a: Body, sa: BoxShape, b: Body, sb: BoxShape, margin: number, out: Normal): boolean {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n const px = sa.halfWidth + sb.halfWidth - Math.abs(dx);\n const py = sa.halfHeight + sb.halfHeight - Math.abs(dy);\n if (px <= -margin || py <= -margin) return false;\n // corner to corner across a gap: no face faces the other, and a contact here only snags bodies sliding past\n if (px < 0 && py < 0) return false;\n const sx = dx < 0 ? -1 : 1;\n const sy = dy < 0 ? -1 : 1;\n let useX: boolean;\n if (px < 0) useX = true;\n else if (py < 0) useX = false;\n else {\n const okX = xAllowed(a, b, sx);\n const okY = yAllowed(a, b, sy);\n useX = okX && okY ? px < py : okX ? true : okY ? false : px < py;\n }\n if (useX) {\n if (px < 0 && !xAllowed(a, b, sx)) return false;\n out.nx = sx;\n out.ny = 0;\n out.depth = px;\n } else {\n if (py < 0 && !yAllowed(a, b, sy)) return false;\n out.nx = 0;\n out.ny = sy;\n out.depth = py;\n }\n return true;\n}\n\nfunction circleCircle(a: Body, sa: CircleShape, b: Body, sb: CircleShape, margin: number, out: Normal): boolean {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n const r = sa.radius + sb.radius;\n const d2 = dx * dx + dy * dy;\n if (d2 >= (r + margin) * (r + margin)) return false;\n const d = Math.sqrt(d2);\n if (d > 1e-9) {\n out.nx = dx / d;\n out.ny = dy / d;\n } else {\n out.nx = 0;\n out.ny = 1;\n }\n out.depth = r - d;\n return true;\n}\n\n/**\n * Circle `c` against box `b`; the normal is written from the box to the circle. A face the box marks as internal\n * is never pushed out of, and at a corner beside an internal face the other face's axis is used instead, so a ball\n * rolls over the seam between two ground boxes without a bump.\n */\nfunction circleBox(c: Body, sc: CircleShape, b: Body, sb: BoxShape, margin: number, out: Normal): boolean {\n const hw = sb.halfWidth;\n const hh = sb.halfHeight;\n const r = sc.radius;\n const dx = c.x - b.x;\n const dy = c.y - b.y;\n const faces = b.internalFaces;\n const sx = dx < 0 ? -1 : 1;\n const sy = dy < 0 ? -1 : 1;\n const xFaceOpen = !(faces & (sx > 0 ? FACE_RIGHT : FACE_LEFT));\n const yFaceOpen = !(faces & (sy > 0 ? FACE_BOTTOM : FACE_TOP));\n const insideX = Math.abs(dx) <= hw;\n const insideY = Math.abs(dy) <= hh;\n if (insideX && insideY) {\n const penX = hw - Math.abs(dx) + r;\n const penY = hh - Math.abs(dy) + r;\n const useX = xFaceOpen && yFaceOpen ? penX < penY : xFaceOpen ? true : yFaceOpen ? false : penX < penY;\n out.nx = useX ? sx : 0;\n out.ny = useX ? 0 : sy;\n out.depth = useX ? penX : penY;\n return true;\n }\n if (insideY) {\n // beside a vertical face\n if (!xFaceOpen) return false;\n const gap = Math.abs(dx) - hw;\n if (gap >= r + margin) return false;\n out.nx = sx;\n out.ny = 0;\n out.depth = r - gap;\n return true;\n }\n if (insideX) {\n if (!yFaceOpen) return false;\n const gap = Math.abs(dy) - hh;\n if (gap >= r + margin) return false;\n out.nx = 0;\n out.ny = sy;\n out.depth = r - gap;\n return true;\n }\n // a corner region\n const ex = dx - sx * hw;\n const ey = dy - sy * hh;\n if (!xFaceOpen && !yFaceOpen) return false;\n if (!xFaceOpen || !yFaceOpen) {\n // the corner belongs to a longer surface: collide with that surface's line, not the corner's point\n const gap = !xFaceOpen ? Math.abs(ey) : Math.abs(ex);\n if (gap >= r + margin) return false;\n out.nx = !xFaceOpen ? 0 : sx;\n out.ny = !xFaceOpen ? sy : 0;\n out.depth = r - gap;\n return true;\n }\n const d2 = ex * ex + ey * ey;\n if (d2 >= (r + margin) * (r + margin)) return false;\n const d = Math.sqrt(d2);\n out.nx = d > 1e-9 ? ex / d : sx;\n out.ny = d > 1e-9 ? ey / d : 0;\n out.depth = r - d;\n return true;\n}\n"],"names":["emptyManifold","collide","a","b","margin","out","sa","sb","circleCircle","onePoint","circleBox","circlePolygon","boxBox","polygonPolygon","x","y","xAllowed","sx","FACE_RIGHT","FACE_LEFT","yAllowed","sy","FACE_BOTTOM","FACE_TOP","dx","dy","px","py","useX","okX","okY","r","d2","d","c","sc","hw","hh","faces","xFaceOpen","yFaceOpen","insideX","insideY","penX","penY","gap","ex","ey"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"collidePolygon.js","sources":["../../src/physics/collidePolygon.ts"],"sourcesContent":["import type { Body } from './Body';\nimport type { Manifold } from './collide';\nimport { FACE_BOTTOM, FACE_LEFT, FACE_RIGHT, FACE_TOP } from './types';\n\n/** Face bits of a box's corners order (`Body.updateBounds`): face 0 is the top, then right, bottom, left. */\nconst BOX_FACE_BITS = [FACE_TOP, FACE_RIGHT, FACE_BOTTOM, FACE_LEFT] as const;\n\n/** A reference face is kept unless the other side's is deeper by more than this (px): no flip-flopping between equals. */\nconst FLIP_TOLERANCE = 0.05;\n\n/**\n * Whether `body` has an internal face (one lying against more solid ground) whose outward normal is (nx, ny): only\n * a merged tile box at no angle has them.\n */\nfunction internalAlong(body: Body, nx: number, ny: number): boolean {\n if (!body.internalFaces || !body.axisAligned) return false;\n for (let i = 0; i < 4; i++) {\n if (body.internalFaces & BOX_FACE_BITS[i]! && body.wnx[i]! * nx + body.wny[i]! * ny > 0.999) return true;\n }\n return false;\n}\n\n/** A contact normal from a to b that pushes neither body out of an internal face. */\nfunction normalAllowed(a: Body, b: Body, nx: number, ny: number): boolean {\n return !internalAlong(a, nx, ny) && !internalAlong(b, -nx, -ny);\n}\n\n/** Separation of `other`'s corners from face `i` of `ref`: the most negative is how deep the deepest corner lies. */\nfunction faceSeparation(ref: Body, i: number, other: Body): number {\n const nx = ref.wnx[i]!;\n const ny = ref.wny[i]!;\n const vx = ref.wx[i]!;\n const vy = ref.wy[i]!;\n let min = Infinity;\n for (let j = 0; j < other.corners; j++) {\n const d = (other.wx[j]! - vx) * nx + (other.wy[j]! - vy) * ny;\n if (d < min) min = d;\n }\n return min;\n}\n\ninterface Best {\n sep: number;\n face: number;\n}\n\n/** The face of `ref` the two are furthest apart along, among the faces whose normal may be used; also the plain SAT maximum. */\nfunction bestFace(ref: Body, other: Body, refIsA: boolean, best: Best): number {\n let max = -Infinity;\n best.sep = -Infinity;\n best.face = -1;\n for (let i = 0; i < ref.corners; i++) {\n const s = faceSeparation(ref, i, other);\n if (s > max) max = s;\n const nx = refIsA ? ref.wnx[i]! : -ref.wnx[i]!;\n const ny = refIsA ? ref.wny[i]! : -ref.wny[i]!;\n const ok = refIsA ? normalAllowed(ref, other, nx, ny) : normalAllowed(other, ref, nx, ny);\n if (ok && s > best.sep) {\n best.sep = s;\n best.face = i;\n }\n }\n return max;\n}\n\nconst bestA: Best = { sep: 0, face: -1 };\nconst bestB: Best = { sep: 0, face: -1 };\nconst clipX = [0, 0];\nconst clipY = [0, 0];\nconst clipId = [0, 0];\n\n/**\n * Two convex shapes (boxes or polygons, turned or not) by the separating axis test, then the incident edge clipped\n * against the reference face's sides (Box2D's manifold), giving one or two points. A normal out of a merged tile box's\n * internal face is never used, so a turned crate slides over the seams of a tile floor like a square one does.\n */\nexport function polygonPolygon(a: Body, b: Body, margin: number, out: Manifold): boolean {\n const maxA = bestFace(a, b, true, bestA);\n if (maxA > margin) return false;\n const maxB = bestFace(b, a, false, bestB);\n if (maxB > margin) return false;\n let flip: boolean;\n if (bestA.face < 0 && bestB.face < 0) {\n // every axis is an internal face: fall back to the plain shallowest one, as the axis-aligned test does\n flip = maxB > maxA + FLIP_TOLERANCE;\n if (!flip) bestA.face = faceOfSeparation(a, b, maxA);\n else bestB.face = faceOfSeparation(b, a, maxB);\n } else if (bestA.face < 0) flip = true;\n else if (bestB.face < 0) flip = false;\n else flip = bestB.sep > bestA.sep + FLIP_TOLERANCE;\n const ref = flip ? b : a;\n const inc = flip ? a : b;\n const r = flip ? bestB.face : bestA.face;\n const nrx = ref.wnx[r]!;\n const nry = ref.wny[r]!;\n // the incident face: the one of the other body most against the reference normal\n let k = 0;\n let minDot = Infinity;\n for (let j = 0; j < inc.corners; j++) {\n const d = inc.wnx[j]! * nrx + inc.wny[j]! * nry;\n if (d < minDot) {\n minDot = d;\n k = j;\n }\n }\n const k2 = (k + 1) % inc.corners;\n clipX[0] = inc.wx[k]!;\n clipY[0] = inc.wy[k]!;\n clipId[0] = k;\n clipX[1] = inc.wx[k2]!;\n clipY[1] = inc.wy[k2]!;\n clipId[1] = k2;\n const r2 = (r + 1) % ref.corners;\n const r1x = ref.wx[r]!;\n const r1y = ref.wy[r]!;\n const r2x = ref.wx[r2]!;\n const r2y = ref.wy[r2]!;\n const tl = Math.hypot(r2x - r1x, r2y - r1y);\n const tx = (r2x - r1x) / tl;\n const ty = (r2y - r1y) / tl;\n // keep what lies between the reference face's two side planes\n if (!clip(-tx, -ty, -(tx * r1x + ty * r1y), 16)) return false;\n if (!clip(tx, ty, tx * r2x + ty * r2y, 17)) return false;\n out.nx = flip ? -nrx : nrx;\n out.ny = flip ? -nry : nry;\n out.count = 0;\n out.depth = -Infinity;\n for (let i = 0; i < 2; i++) {\n const sep = (clipX[i]! - r1x) * nrx + (clipY[i]! - r1y) * nry;\n if (sep > margin) continue;\n const n = out.count++;\n // halfway between the incident corner and the reference face\n out.px[n] = clipX[i]! - nrx * sep * 0.5;\n out.py[n] = clipY[i]! - nry * sep * 0.5;\n out.pd[n] = -sep;\n out.id[n] = ((flip ? 1 : 0) << 12) | (r << 6) | clipId[i]!;\n if (-sep > out.depth) out.depth = -sep;\n }\n return out.count > 0;\n}\n\n/** The first face of `ref` whose separation is `sep`. */\nfunction faceOfSeparation(ref: Body, other: Body, sep: number): number {\n for (let i = 0; i < ref.corners; i++) if (faceSeparation(ref, i, other) === sep) return i;\n return 0;\n}\n\n/** Clips the two points in `clipX/Y` to the half plane n·p ≤ offset; a cut point gets the feature id `cutId`. */\nfunction clip(nx: number, ny: number, offset: number, cutId: number): boolean {\n const d0 = nx * clipX[0]! + ny * clipY[0]! - offset;\n const d1 = nx * clipX[1]! + ny * clipY[1]! - offset;\n if (d0 > 0 && d1 > 0) return false;\n if (d0 <= 0 && d1 <= 0) return true;\n const t = d0 / (d0 - d1);\n const x = clipX[0]! + (clipX[1]! - clipX[0]!) * t;\n const y = clipY[0]! + (clipY[1]! - clipY[0]!) * t;\n if (d0 > 0) {\n clipX[0] = x;\n clipY[0] = y;\n clipId[0] = cutId;\n } else {\n clipX[1] = x;\n clipY[1] = y;\n clipId[1] = cutId;\n }\n return true;\n}\n\n/**\n * A circle against a box or polygon that is turned (or turns): the face the centre lies furthest out of, then that\n * face's middle or one of its corners. `circleIsA` says which way round the pair is, so the normal runs from a to b.\n */\nexport function circlePolygon(circle: Body, radius: number, poly: Body, margin: number, circleIsA: boolean, out: Manifold): boolean {\n const cx = circle.x;\n const cy = circle.y;\n let face = 0;\n let sep = -Infinity;\n for (let i = 0; i < poly.corners; i++) {\n const s = (cx - poly.wx[i]!) * poly.wnx[i]! + (cy - poly.wy[i]!) * poly.wny[i]!;\n if (s > sep) {\n sep = s;\n face = i;\n }\n }\n if (sep > radius + margin) return false;\n // normal from the polygon to the circle, and the distance from the polygon's surface to the circle's centre\n let nx = poly.wnx[face]!;\n let ny = poly.wny[face]!;\n let dist = sep;\n let id = face;\n if (sep > 0) {\n const f2 = (face + 1) % poly.corners;\n const v1x = poly.wx[face]!;\n const v1y = poly.wy[face]!;\n const v2x = poly.wx[f2]!;\n const v2y = poly.wy[f2]!;\n const u1 = (cx - v1x) * (v2x - v1x) + (cy - v1y) * (v2y - v1y);\n const u2 = (cx - v2x) * (v1x - v2x) + (cy - v2y) * (v1y - v2y);\n if (u1 <= 0 || u2 <= 0) {\n const vx = u1 <= 0 ? v1x : v2x;\n const vy = u1 <= 0 ? v1y : v2y;\n const d = Math.hypot(cx - vx, cy - vy);\n if (d > radius + margin) return false;\n if (d > 1e-9) {\n nx = (cx - vx) / d;\n ny = (cy - vy) / d;\n }\n dist = d;\n id = 32 + (u1 <= 0 ? face : f2);\n }\n }\n const depth = radius - dist;\n out.nx = circleIsA ? -nx : nx;\n out.ny = circleIsA ? -ny : ny;\n out.depth = depth;\n out.count = 1;\n out.px[0] = cx - nx * (radius - depth / 2);\n out.py[0] = cy - ny * (radius - depth / 2);\n out.pd[0] = depth;\n out.id[0] = id;\n return true;\n}\n"],"names":["BOX_FACE_BITS","FACE_TOP","FACE_RIGHT","FACE_BOTTOM","FACE_LEFT","FLIP_TOLERANCE","internalAlong","body","nx","ny","i","normalAllowed","a","b","faceSeparation","ref","other","vx","vy","min","j","d","bestFace","refIsA","best","max","bestA","bestB","clipX","clipY","clipId","polygonPolygon","margin","out","maxA","maxB","flip","faceOfSeparation","inc","r","nrx","nry","k","minDot","k2","r2","r1x","r1y","r2x","r2y","tl","tx","ty","clip","sep","n","offset","cutId","d0","d1","t","x","y","circlePolygon","circle","radius","poly","circleIsA","cx","cy","face","s","dist","id","f2","v1x","v1y","v2x","v2y","u1","u2","depth"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"contactReport.js","sources":["../../src/physics/contactReport.ts"],"sourcesContent":["import type { Body } from './Body';\nimport type { Contact } from './Contact';\nimport type { ContactInfo } from './types';\n\n/** Marks `body` as standing on `ground` through contact `c`; the first ground found in a step wins. */\nexport function setGround(body: Body, ground: Body, c: Contact): void {\n if (body.grounded) return;\n body.grounded = true;\n body.ground = ground;\n const s = body === c.b ? 1 : -1;\n body.groundNx = c.nx * s;\n body.groundNy = c.ny * s;\n}\n\n/** A copy for an event: the contact itself changes on the next step. */\nexport function info(c: Contact): ContactInfo {\n return { a: c.a, b: c.b, nx: c.nx, ny: c.ny, depth: c.depth, sensor: c.sensor, points: c.points };\n}\n"],"names":["setGround","body","ground","c","s","info"],"mappings":"AAKO,SAASA,EAAUC,GAAYC,GAAcC,GAAkB;AACpE,MAAIF,EAAK,SAAU;AACnB,EAAAA,EAAK,WAAW,IAChBA,EAAK,SAASC;AACd,QAAME,IAAIH,MAASE,EAAE,IAAI,IAAI;AAC7B,EAAAF,EAAK,WAAWE,EAAE,KAAKC,GACvBH,EAAK,WAAWE,EAAE,KAAKC;AACzB;AAGO,SAASC,EAAKF,GAAyB;AAC5C,SAAO,EAAE,GAAGA,EAAE,GAAG,GAAGA,EAAE,GAAG,IAAIA,EAAE,IAAI,IAAIA,EAAE,IAAI,OAAOA,EAAE,OAAO,QAAQA,EAAE,QAAQ,QAAQA,EAAE,OAAA;AAC3F;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"drawColliders.js","sources":["../../src/physics/drawColliders.ts"],"sourcesContent":["import type { Body } from './Body';\nimport type { PhysicsWorld } from './PhysicsWorld';\n\n/** Colours `drawColliders` gives each kind of body. */\nexport interface ColliderColors {\n static: number;\n kinematic: number;\n /** A dynamic body standing on something. */\n grounded: number;\n /** A dynamic body in the air. */\n airborne: number;\n /** A dynamic body asleep (only with `allowSleep`). */\n sleeping: number;\n sensor: number;\n}\n\nexport const COLLIDER_COLORS: Readonly<ColliderColors> = {\n static: 0x40ff60,\n kinematic: 0x40e0ff,\n grounded: 0xffff60,\n airborne: 0xff6060,\n sleeping: 0x808080,\n sensor: 0xff60e0,\n};\n\n/**\n * Draws one box in world coordinates. The same shape as `DebugOverlay`'s `BoxDrawer`, written out here so the physics\n * module stays free of anything that draws.\n */\nexport type ColliderBoxDrawer = (x: number, y: number, width: number, height: number, color?: number) => void;\n\n/** Draws one line in world coordinates (`DebugOverlay`'s `LineDrawer`). */\nexport type ColliderLineDrawer = (x0: number, y0: number, x1: number, y1: number, color?: number) => void;\n\n/**\n * Draws every body's bounding box, coloured by what it is: the usual body of a `DebugOverlay`'s `colliders` callback,\n * which otherwise made every game keep a list of its own bodies just to show them.\n *\n * ```ts\n * new DebugOverlay(engine, { colliders: (draw) => drawColliders(physics, draw) });\n * ```\n *\n * `colorOf` picks a game's own colour for a body (a one-way plank, a hazard), or returns undefined for the default.\n * A circle is drawn as its bounding box: the overlay draws boxes, and a box round a circle still shows where it is.\n * Given `line` (the overlay hands it over as its second argument), a turned box or a polygon is drawn as its own\n * outline, and a circle that rotates gets a spoke showing its angle.\n *\n * ```ts\n * new DebugOverlay(engine, { colliders: (draw, line) => drawColliders(physics, draw, {}, undefined, line) });\n * ```\n */\nexport function drawColliders(\n world: PhysicsWorld,\n draw: ColliderBoxDrawer,\n colors: Partial<ColliderColors> = {},\n colorOf?: (body: Body) => number | undefined,\n line?: ColliderLineDrawer,\n): void {\n const c = { ...COLLIDER_COLORS, ...colors };\n for (const b of world.bodies) {\n const color = colorOf?.(b) ?? colliderColor(b, c);\n if (line && b.corners > 0 && !b.axisAligned) {\n for (let i = 0; i < b.corners; i++) {\n const j = (i + 1) % b.corners;\n line(b.wx[i]!, b.wy[i]!, b.wx[j]!, b.wy[j]!, color);\n }\n continue;\n }\n draw(b.minX, b.minY, b.maxX - b.minX, b.maxY - b.minY, color);\n if (line && b.shape.kind === 'circle' && b.rotates) line(b.x, b.y, b.x + b.cos * b.shape.radius, b.y + b.sin * b.shape.radius, color);\n }\n}\n\n/** The default colour of one body. */\nexport function colliderColor(b: Body, c: ColliderColors = COLLIDER_COLORS): number {\n if (b.sensor) return c.sensor;\n if (b.type === 'static') return c.static;\n if (b.type === 'kinematic') return c.kinematic;\n if (!b.awake) return c.sleeping;\n return b.grounded ? c.grounded : c.airborne;\n}\n"],"names":["COLLIDER_COLORS","drawColliders","world","draw","colors","colorOf","line","c","b","color","colliderColor","j"],"mappings":"AAgBO,MAAMA,IAA4C;AAAA,EACvD,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,QAAQ;AACV;AA4BO,SAASC,EACdC,GACAC,GACAC,IAAkC,CAAA,GAClCC,GACAC,GACM;AACN,QAAMC,IAAI,EAAE,GAAGP,GAAiB,GAAGI,EAAA;AACnC,aAAWI,KAAKN,EAAM,QAAQ;AAC5B,UAAMO,IAAQJ,IAAUG,CAAC,KAAKE,EAAcF,GAAGD,CAAC;AAChD,QAAID,KAAQE,EAAE,UAAU,KAAK,CAACA,EAAE,aAAa;AAC3C,eAAS,IAAI,GAAG,IAAIA,EAAE,SAAS,KAAK;AAClC,cAAMG,KAAK,IAAI,KAAKH,EAAE;AACtB,QAAAF,EAAKE,EAAE,GAAG,CAAC,GAAIA,EAAE,GAAG,CAAC,GAAIA,EAAE,GAAGG,CAAC,GAAIH,EAAE,GAAGG,CAAC,GAAIF,CAAK;AAAA,MACpD;AACA;AAAA,IACF;AACA,IAAAN,EAAKK,EAAE,MAAMA,EAAE,MAAMA,EAAE,OAAOA,EAAE,MAAMA,EAAE,OAAOA,EAAE,MAAMC,CAAK,GACxDH,KAAQE,EAAE,MAAM,SAAS,YAAYA,EAAE,WAASF,EAAKE,EAAE,GAAGA,EAAE,GAAGA,EAAE,IAAIA,EAAE,MAAMA,EAAE,MAAM,QAAQA,EAAE,IAAIA,EAAE,MAAMA,EAAE,MAAM,QAAQC,CAAK;AAAA,EACtI;AACF;AAGO,SAASC,EAAcF,GAASD,IAAoBP,GAAyB;AAClF,SAAIQ,EAAE,SAAeD,EAAE,SACnBC,EAAE,SAAS,WAAiBD,EAAE,SAC9BC,EAAE,SAAS,cAAoBD,EAAE,YAChCC,EAAE,QACAA,EAAE,WAAWD,EAAE,WAAWA,EAAE,WADdA,EAAE;AAEzB;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"physicsSystem.js","sources":["../../src/physics/physicsSystem.ts"],"sourcesContent":["import { system, type System } from '../ecs/World';\nimport type { PhysicsWorld } from './PhysicsWorld';\n\n/** A `fixed` system that steps `physics` once per simulation step, by the loop's own dt. */\nexport function physicsSystem(physics: PhysicsWorld, name = 'physics'): System {\n return system(name, 'fixed', ({ dt }) => physics.step(dt));\n}\n"],"names":["physicsSystem","physics","name","system","dt"],"mappings":";AAIO,SAASA,EAAcC,GAAuBC,IAAO,WAAmB;AAC7E,SAAOC,EAAOD,GAAM,SAAS,CAAC,EAAE,IAAAE,QAASH,EAAQ,KAAKG,CAAE,CAAC;AAC3D;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"polygon.js","sources":["../../src/physics/polygon.ts"],"sourcesContent":["import type { BoxShape, PolygonShape, Vec2 } from './types';\n\n/** The most corners a polygon shape may have. */\nexport const MAX_POLYGON_VERTICES = 16;\n\n/** Twice the signed area: positive when the points run clockwise on screen (y down), the order shapes are kept in. */\nfunction doubleArea(points: readonly Vec2[]): number {\n let a = 0;\n for (let i = 0; i < points.length; i++) {\n const p = points[i]!;\n const q = points[(i + 1) % points.length]!;\n a += p.x * q.y - q.x * p.y;\n }\n return a;\n}\n\n/** The centroid of the convex hull of `points`: where to put the body so a `polygonShape(points)` lands on them. */\nexport function polygonCentroid(points: readonly Vec2[]): Vec2 {\n const hull = convexHull(points);\n let cx = 0;\n let cy = 0;\n let a = 0;\n for (let i = 0; i < hull.length; i++) {\n const p = hull[i]!;\n const q = hull[(i + 1) % hull.length]!;\n const cross = p.x * q.y - q.x * p.y;\n a += cross;\n cx += (p.x + q.x) * cross;\n cy += (p.y + q.y) * cross;\n }\n if (Math.abs(a) < 1e-9) throw new Error('polygonShape: the points enclose no area');\n return { x: cx / (3 * a), y: cy / (3 * a) };\n}\n\n/**\n * A convex polygon shape from any points: their convex hull, in the order the solver keeps, moved so its centroid is\n * the body's position. Give points round the origin, or anywhere and place the body at `polygonCentroid(points)`.\n */\nexport function polygonShape(points: readonly Vec2[]): PolygonShape {\n const hull = convexHull(points);\n if (hull.length < 3) throw new Error('polygonShape: needs at least three points not on one line');\n if (hull.length > MAX_POLYGON_VERTICES) throw new Error(`polygonShape: at most ${MAX_POLYGON_VERTICES} corners, got ${hull.length}`);\n const c = polygonCentroid(hull);\n return { kind: 'polygon', vertices: hull.map((p) => ({ x: p.x - c.x, y: p.y - c.y })) };\n}\n\n/** A regular polygon of `sides` corners on a circle of `radius`, its first corner straight up. */\nexport function regularPolygon(sides: number, radius: number): PolygonShape {\n const n = Math.max(3, Math.min(MAX_POLYGON_VERTICES, Math.round(sides)));\n const points: Vec2[] = [];\n for (let i = 0; i < n; i++) {\n const t = -Math.PI / 2 + (i * 2 * Math.PI) / n;\n points.push({ x: Math.cos(t) * radius, y: Math.sin(t) * radius });\n }\n return polygonShape(points);\n}\n\n/** Andrew's monotone chain, returned clockwise on screen (positive `doubleArea`), collinear points dropped. */\nfunction convexHull(points: readonly Vec2[]): Vec2[] {\n const pts = points.map((p) => ({ x: p.x, y: p.y })).sort((a, b) => a.x - b.x || a.y - b.y);\n if (pts.length < 3) return pts;\n const cross = (o: Vec2, a: Vec2, b: Vec2) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);\n const lower: Vec2[] = [];\n for (const p of pts) {\n while (lower.length >= 2 && cross(lower[lower.length - 2]!, lower[lower.length - 1]!, p) <= 1e-9) lower.pop();\n lower.push(p);\n }\n const upper: Vec2[] = [];\n for (let i = pts.length - 1; i >= 0; i--) {\n const p = pts[i]!;\n while (upper.length >= 2 && cross(upper[upper.length - 2]!, upper[upper.length - 1]!, p) <= 1e-9) upper.pop();\n upper.push(p);\n }\n const hull = lower.slice(0, -1).concat(upper.slice(0, -1));\n return doubleArea(hull) < 0 ? hull.reverse() : hull;\n}\n\n/** Local outward normals and the solver's mass terms of a polygon, worked out once per shape object. */\nexport interface PolygonData {\n readonly vertices: readonly Vec2[];\n readonly normals: readonly Vec2[];\n /** Second moment of area about the centroid per unit of area-mass: inertia = mass × this. */\n readonly inertiaPerMass: number;\n /** The furthest corner from the centre. */\n readonly radius: number;\n /** The nearest face to the centre: how far the body may move in one sub-step without passing through anything. */\n readonly inradius: number;\n}\n\nconst cache = new WeakMap<PolygonShape, PolygonData>();\n\n/** The data of a polygon shape, checked (convex, in order) the first time it is asked for. */\nexport function polygonData(shape: PolygonShape): PolygonData {\n let d = cache.get(shape);\n if (d) return d;\n const v = shape.vertices;\n if (v.length < 3 || v.length > MAX_POLYGON_VERTICES) throw new Error(`Body: a polygon needs 3–${MAX_POLYGON_VERTICES} corners, got ${v.length}`);\n if (doubleArea(v) <= 0) throw new Error('Body: polygon corners must run clockwise on screen (make the shape with polygonShape)');\n const normals: Vec2[] = [];\n let inertia = 0;\n let area = 0;\n let radius = 0;\n let inradius = Infinity;\n for (let i = 0; i < v.length; i++) {\n const p = v[i]!;\n const q = v[(i + 1) % v.length]!;\n const ex = q.x - p.x;\n const ey = q.y - p.y;\n const len = Math.hypot(ex, ey);\n if (len < 1e-9) throw new Error('Body: a polygon has two corners in one place');\n const n = { x: ey / len, y: -ex / len };\n normals.push(n);\n const r = v[(i + 2) % v.length]!;\n if ((r.x - p.x) * n.x + (r.y - p.y) * n.y > 1e-6) throw new Error('Body: a polygon must be convex (make the shape with polygonShape)');\n const cross = p.x * q.y - q.x * p.y;\n area += cross / 2;\n inertia += (cross * (p.x * p.x + p.x * q.x + q.x * q.x + p.y * p.y + p.y * q.y + q.y * q.y)) / 12;\n radius = Math.max(radius, Math.hypot(p.x, p.y));\n inradius = Math.min(inradius, p.x * n.x + p.y * n.y);\n }\n d = { vertices: v, normals, inertiaPerMass: inertia / area, radius, inradius };\n cache.set(shape, d);\n return d;\n}\n\n/** A box as the four corners the polygon tests use: top-left first, clockwise on screen, so face i is top, right, bottom, left. */\nexport function boxCorners(s: BoxShape): Vec2[] {\n return [\n { x: -s.halfWidth, y: -s.halfHeight },\n { x: s.halfWidth, y: -s.halfHeight },\n { x: s.halfWidth, y: s.halfHeight },\n { x: -s.halfWidth, y: s.halfHeight },\n ];\n}\n"],"names":["MAX_POLYGON_VERTICES","doubleArea","points","a","i","p","q","polygonCentroid","hull","convexHull","cx","cy","cross","polygonShape","c","regularPolygon","sides","radius","t","pts","b","o","lower","upper","cache","polygonData","shape","d","v","normals","inertia","area","inradius","ex","ey","len","n","r"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"raycast.js","sources":["../../src/physics/raycast.ts"],"sourcesContent":["import type { Body } from './Body';\nimport type { SpatialHash } from './SpatialHash';\nimport type { RayHit, RaycastOptions } from './types';\n\n/** Where a ray from (ox, oy) along (dx, dy) — not normalised, t = 1 at its end — first enters a body. */\ninterface ShapeHit {\n t: number;\n nx: number;\n ny: number;\n}\n\nconst scratch: ShapeHit = { t: 0, nx: 0, ny: 0 };\n\n/** A ray starting inside the shape misses it, so a body can cast from its own centre without hitting itself. */\nexport function rayShape(body: Body, ox: number, oy: number, dx: number, dy: number, maxT: number, out: ShapeHit): boolean {\n const s = body.shape;\n if (s.kind === 'circle') {\n const fx = ox - body.x;\n const fy = oy - body.y;\n const a = dx * dx + dy * dy;\n const c = fx * fx + fy * fy - s.radius * s.radius;\n if (c <= 0 || a === 0) return false;\n const b = fx * dx + fy * dy;\n const disc = b * b - a * c;\n if (disc < 0 || b > 0) return false;\n const t = (-b - Math.sqrt(disc)) / a;\n if (t < 0 || t > maxT) return false;\n out.t = t;\n out.nx = (fx + dx * t) / s.radius;\n out.ny = (fy + dy * t) / s.radius;\n return true;\n }\n if (!body.axisAligned) return rayPolygon(body, ox, oy, dx, dy, maxT, out);\n if (ox > body.minX && ox < body.maxX && oy > body.minY && oy < body.maxY) return false;\n let tmin = -Infinity;\n let tmax = Infinity;\n let nx = 0;\n let ny = 0;\n if (dx !== 0) {\n const t1 = (body.minX - ox) / dx;\n const t2 = (body.maxX - ox) / dx;\n const near = Math.min(t1, t2);\n if (near > tmin) {\n tmin = near;\n nx = dx > 0 ? -1 : 1;\n ny = 0;\n }\n tmax = Math.min(tmax, Math.max(t1, t2));\n } else if (ox < body.minX || ox > body.maxX) return false;\n if (dy !== 0) {\n const t1 = (body.minY - oy) / dy;\n const t2 = (body.maxY - oy) / dy;\n const near = Math.min(t1, t2);\n if (near > tmin) {\n tmin = near;\n nx = 0;\n ny = dy > 0 ? -1 : 1;\n }\n tmax = Math.min(tmax, Math.max(t1, t2));\n } else if (oy < body.minY || oy > body.maxY) return false;\n if (tmin > tmax || tmin < 0 || tmin > maxT) return false;\n out.t = tmin;\n out.nx = nx;\n out.ny = ny;\n return true;\n}\n\n/** Cyrus–Beck: the ray clipped by each face's half plane of a turned box or a polygon (world corners). */\nfunction rayPolygon(body: Body, ox: number, oy: number, dx: number, dy: number, maxT: number, out: ShapeHit): boolean {\n let tmin = 0;\n let tmax = maxT;\n let face = -1;\n let inside = true;\n for (let i = 0; i < body.corners; i++) {\n const nx = body.wnx[i]!;\n const ny = body.wny[i]!;\n // distance of the start outside face i, and how fast the ray closes on it\n const num = (ox - body.wx[i]!) * nx + (oy - body.wy[i]!) * ny;\n const den = dx * nx + dy * ny;\n if (num > 0) inside = false;\n if (den === 0) {\n if (num > 0) return false;\n continue;\n }\n const t = -num / den;\n if (den < 0) {\n if (t > tmin) {\n tmin = t;\n face = i;\n }\n } else if (t < tmax) tmax = t;\n if (tmin > tmax) return false;\n }\n if (inside || face < 0) return false;\n out.t = tmin;\n out.nx = body.wnx[face]!;\n out.ny = body.wny[face]!;\n return true;\n}\n\nlet rayCounter = 0;\n\n/**\n * The first body a segment from (x0, y0) to (x1, y1) meets, walking the broad phase's cells in the ray's order\n * (Amanatides–Woo) and stopping once the next cell lies beyond the best hit so far; a long ray over a large map\n * looks only at the cells it crosses.\n */\nexport function castRay(hashes: readonly SpatialHash[], x0: number, y0: number, x1: number, y1: number, options: RaycastOptions = {}): RayHit | null {\n const grid = hashes[0];\n if (!grid) return null;\n const mask = options.mask ?? 0xffffffff;\n const stamp = ++rayCounter;\n const dx = x1 - x0;\n const dy = y1 - y0;\n const size = grid.cellSize;\n let cx = grid.cellOf(x0);\n let cy = grid.cellOf(y0);\n const ex = grid.cellOf(x1);\n const ey = grid.cellOf(y1);\n const stepX = dx > 0 ? 1 : dx < 0 ? -1 : 0;\n const stepY = dy > 0 ? 1 : dy < 0 ? -1 : 0;\n const tDeltaX = stepX !== 0 ? size / Math.abs(dx) : Infinity;\n const tDeltaY = stepY !== 0 ? size / Math.abs(dy) : Infinity;\n let tMaxX = stepX > 0 ? ((cx + 1) * size - x0) / dx : stepX < 0 ? (cx * size - x0) / dx : Infinity;\n let tMaxY = stepY > 0 ? ((cy + 1) * size - y0) / dy : stepY < 0 ? (cy * size - y0) / dy : Infinity;\n let best: Body | null = null;\n let bestT = 1;\n let bestNx = 0;\n let bestNy = 0;\n // the cell count bounds the walk even if rounding ever kept it from landing on the end cell exactly\n let cellsLeft = Math.abs(ex - cx) + Math.abs(ey - cy) + 1;\n for (;;) {\n for (const hash of hashes) {\n const list = hash.at(cx, cy);\n if (!list) continue;\n for (const body of list) {\n if (body.rayStamp === stamp) continue;\n body.rayStamp = stamp;\n if (!(body.layer & mask)) continue;\n if (body.sensor && !options.includeSensors) continue;\n if (options.filter && !options.filter(body)) continue;\n if (rayShape(body, x0, y0, dx, dy, bestT, scratch) && (!best || scratch.t < bestT)) {\n best = body;\n bestT = scratch.t;\n bestNx = scratch.nx;\n bestNy = scratch.ny;\n }\n }\n }\n if (--cellsLeft <= 0) break;\n const next = Math.min(tMaxX, tMaxY);\n if (next > bestT) break;\n if (tMaxX < tMaxY) {\n cx += stepX;\n tMaxX += tDeltaX;\n } else {\n cy += stepY;\n tMaxY += tDeltaY;\n }\n }\n if (!best) return null;\n return { body: best, x: x0 + dx * bestT, y: y0 + dy * bestT, nx: bestNx, ny: bestNy, fraction: bestT };\n}\n"],"names":["scratch","rayShape","body","ox","oy","dx","dy","maxT","out","s","fx","fy","a","c","b","disc","t","rayPolygon","tmin","tmax","nx","ny","t1","t2","near","face","inside","i","num","den","rayCounter","castRay","hashes","x0","y0","x1","y1","options","grid","mask","stamp","size","cx","cy","ex","ey","stepX","stepY","tDeltaX","tDeltaY","tMaxX","tMaxY","best","bestT","bestNx","bestNy","cellsLeft","hash","list"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"syncToBody.js","sources":["../../src/physics/syncToBody.ts"],"sourcesContent":["import type { Body } from './Body';\n\n/** Anything with a settable position: a Pixi container, a sprite, a plain object. */\nexport interface Positioned {\n x: number;\n y: number;\n /** Set to the body's angle when the body rotates or stands at an angle; left alone otherwise. */\n rotation?: number;\n}\n\n/**\n * Places `view` where `body` is drawn this frame: between its last two step positions by the loop's `alpha`\n * (`SystemContext.alpha` in a `frame` system), so motion is smooth at any refresh rate even though physics steps\n * at a fixed rate. Pass `round` for pixel art, so the view lands on whole pixels. A body that rotates (or was placed\n * at an angle) turns the view too, by the shorter way between its last two angles; the offset is turned with it.\n */\nexport function syncToBody(view: Positioned, body: Body, alpha: number, offsetX = 0, offsetY = 0, round = false): void {\n let x = body.prevX + (body.x - body.prevX) * alpha;\n let y = body.prevY + (body.y - body.prevY) * alpha;\n if (body.rotates || body.angle !== 0) {\n let d = body.angle - body.prevAngle;\n if (d > Math.PI || d < -Math.PI) d -= Math.round(d / (2 * Math.PI)) * 2 * Math.PI;\n const angle = body.prevAngle + d * alpha;\n if (offsetX !== 0 || offsetY !== 0) {\n const c = Math.cos(angle);\n const s = Math.sin(angle);\n x += c * offsetX - s * offsetY;\n y += s * offsetX + c * offsetY;\n }\n view.rotation = angle;\n } else {\n x += offsetX;\n y += offsetY;\n }\n view.x = round ? Math.round(x) : x;\n view.y = round ? Math.round(y) : y;\n}\n"],"names":["syncToBody","view","body","alpha","offsetX","offsetY","round","x","y","d","angle","c","s"],"mappings":"AAgBO,SAASA,EAAWC,GAAkBC,GAAYC,GAAeC,IAAU,GAAGC,IAAU,GAAGC,IAAQ,IAAa;AACrH,MAAIC,IAAIL,EAAK,SAASA,EAAK,IAAIA,EAAK,SAASC,GACzCK,IAAIN,EAAK,SAASA,EAAK,IAAIA,EAAK,SAASC;AAC7C,MAAID,EAAK,WAAWA,EAAK,UAAU,GAAG;AACpC,QAAIO,IAAIP,EAAK,QAAQA,EAAK;AAC1B,KAAIO,IAAI,KAAK,MAAMA,IAAI,CAAC,KAAK,QAAIA,KAAK,KAAK,MAAMA,KAAK,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK;AAC/E,UAAMC,IAAQR,EAAK,YAAYO,IAAIN;AACnC,QAAIC,MAAY,KAAKC,MAAY,GAAG;AAClC,YAAMM,IAAI,KAAK,IAAID,CAAK,GAClBE,IAAI,KAAK,IAAIF,CAAK;AACxB,MAAAH,KAAKI,IAAIP,IAAUQ,IAAIP,GACvBG,KAAKI,IAAIR,IAAUO,IAAIN;AAAA,IACzB;AACA,IAAAJ,EAAK,WAAWS;AAAA,EAClB;AACE,IAAAH,KAAKH,GACLI,KAAKH;AAEP,EAAAJ,EAAK,IAAIK,IAAQ,KAAK,MAAMC,CAAC,IAAIA,GACjCN,EAAK,IAAIK,IAAQ,KAAK,MAAME,CAAC,IAAIA;AACnC;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tileBodies.js","sources":["../../src/physics/tileBodies.ts"],"sourcesContent":["import type { CollisionGrid } from '../net/movement';\nimport { Body } from './Body';\nimport { FACE_BOTTOM, FACE_LEFT, FACE_RIGHT, FACE_TOP } from './types';\n\nexport interface TileBodiesOptions {\n /** World position of tile (0, 0)'s top-left corner (default 0, 0). */\n originX?: number;\n originY?: number;\n friction?: number;\n restitution?: number;\n layer?: number;\n mask?: number;\n userData?: unknown;\n}\n\n/** A rectangle of solid tiles, in tiles, both ends inclusive. */\nexport interface TileRect {\n x0: number;\n y0: number;\n x1: number;\n y1: number;\n}\n\n/**\n * Covers the solid cells of a tile grid with as few rectangles as a cheap greedy pass finds: runs along each row,\n * then a run is grown downwards while the row below has exactly the same run. A floor becomes one box and a wall\n * one tall box, so a body sliding along either crosses no seam to snag on.\n */\nexport function mergeTileRects(width: number, height: number, solid: (tx: number, ty: number) => boolean): TileRect[] {\n const done: TileRect[] = [];\n /** Rectangles still growing, by the run they end in (\"x0,x1\"). */\n let open = new Map<number, TileRect>();\n for (let ty = 0; ty < height; ty++) {\n const next = new Map<number, TileRect>();\n let tx = 0;\n while (tx < width) {\n if (!solid(tx, ty)) {\n tx++;\n continue;\n }\n const x0 = tx;\n while (tx < width && solid(tx, ty)) tx++;\n const x1 = tx - 1;\n const key = x0 * 0x100000 + x1;\n const above = open.get(key);\n if (above) {\n above.y1 = ty;\n open.delete(key);\n next.set(key, above);\n } else next.set(key, { x0, y0: ty, x1, y1: ty });\n }\n for (const r of open.values()) done.push(r);\n open = next;\n }\n for (const r of open.values()) done.push(r);\n return done;\n}\n\n/**\n * Static boxes for the solid tiles of a grid, ready for `PhysicsWorld.add`. Each box marks the faces that lie\n * wholly against more solid tiles as internal, so a body is never pushed out through a face that is inside the\n * ground (the seam between a floor and the wall standing on it).\n *\n * Outside the grid counts as open, so the edges of the map collide like any other face.\n */\nexport function tileBodies(width: number, height: number, tileSize: number, solid: (tx: number, ty: number) => boolean, options: TileBodiesOptions = {}): Body[] {\n const inside = (tx: number, ty: number): boolean => tx >= 0 && ty >= 0 && tx < width && ty < height && solid(tx, ty);\n const ox = options.originX ?? 0;\n const oy = options.originY ?? 0;\n return mergeTileRects(width, height, inside).map((r) => {\n const w = (r.x1 - r.x0 + 1) * tileSize;\n const h = (r.y1 - r.y0 + 1) * tileSize;\n const body = new Body({\n type: 'static',\n shape: { kind: 'box', halfWidth: w / 2, halfHeight: h / 2 },\n x: ox + r.x0 * tileSize + w / 2,\n y: oy + r.y0 * tileSize + h / 2,\n friction: options.friction,\n restitution: options.restitution,\n layer: options.layer,\n mask: options.mask,\n userData: options.userData,\n });\n let faces = 0;\n if (rowSolid(inside, r.x0, r.x1, r.y0 - 1)) faces |= FACE_TOP;\n if (rowSolid(inside, r.x0, r.x1, r.y1 + 1)) faces |= FACE_BOTTOM;\n if (columnSolid(inside, r.x0 - 1, r.y0, r.y1)) faces |= FACE_LEFT;\n if (columnSolid(inside, r.x1 + 1, r.y0, r.y1)) faces |= FACE_RIGHT;\n body.internalFaces = faces;\n return body;\n });\n}\n\n/** `tileBodies` for the engine's `CollisionGrid` (from `createCollisionGrid` or a Tiled map's `collisionFromTiled`). */\nexport function tileBodiesFromGrid(grid: CollisionGrid, options: TileBodiesOptions = {}): Body[] {\n return tileBodies(grid.width, grid.height, grid.tileSize, (tx, ty) => grid.solid[ty * grid.width + tx] === 1, options);\n}\n\nfunction rowSolid(solid: (tx: number, ty: number) => boolean, x0: number, x1: number, ty: number): boolean {\n for (let tx = x0; tx <= x1; tx++) if (!solid(tx, ty)) return false;\n return true;\n}\n\nfunction columnSolid(solid: (tx: number, ty: number) => boolean, tx: number, y0: number, y1: number): boolean {\n for (let ty = y0; ty <= y1; ty++) if (!solid(tx, ty)) return false;\n return true;\n}\n"],"names":["mergeTileRects","width","height","solid","done","open","ty","next","tx","x0","x1","key","above","r","tileBodies","tileSize","options","inside","ox","oy","w","h","body","Body","faces","rowSolid","FACE_TOP","FACE_BOTTOM","columnSolid","FACE_LEFT","FACE_RIGHT","tileBodiesFromGrid","grid","y0","y1"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sources":["../../src/physics/types.ts"],"sourcesContent":["import type { Body } from './Body';\n\n/**\n * The shapes a body may have, each centred on the body's position. A body turns only when it is made with\n * `rotates: true`; a platformer's bodies need no rotation, and a body that never turns keeps the cheap axis-aligned\n * tests and the steadiest stacks.\n */\nexport type Shape = CircleShape | BoxShape | PolygonShape;\n\nexport interface CircleShape {\n readonly kind: 'circle';\n radius: number;\n}\n\nexport interface BoxShape {\n readonly kind: 'box';\n halfWidth: number;\n halfHeight: number;\n}\n\n/** A point or a vector in world or body pixels, y down. */\nexport interface Vec2 {\n x: number;\n y: number;\n}\n\n/**\n * A convex polygon, its vertices relative to the body's position, which must be the polygon's centroid (make one with\n * `polygonShape`, which orders the points and moves them there). Up to `MAX_POLYGON_VERTICES` corners.\n */\nexport interface PolygonShape {\n readonly kind: 'polygon';\n readonly vertices: readonly Vec2[];\n}\n\n/**\n * - `dynamic`: moved by gravity, forces and contacts;\n * - `static`: never moves (ground, walls); cheapest, kept in a broad phase of its own;\n * - `kinematic`: moved only by its velocity (a moving platform), pushes dynamic bodies and is never pushed.\n */\nexport type BodyType = 'dynamic' | 'static' | 'kinematic';\n\nexport interface BodyOptions {\n type?: BodyType;\n shape: Shape;\n x?: number;\n y?: number;\n vx?: number;\n vy?: number;\n /**\n * Whether contacts and torques turn the body (default false). A body that does not rotate may still be placed at an\n * `angle`, and keeps it.\n */\n rotates?: boolean;\n /** Radians, clockwise on screen (y grows downwards). */\n angle?: number;\n /** Radians per second. */\n angularVelocity?: number;\n /** Turning lost per second, as a share (default 0.05): a rolling ball slows down without rolling friction. */\n angularDamping?: number;\n /** Mass of a dynamic body (default 1). Ignored for static and kinematic bodies, whose mass is infinite. */\n mass?: number;\n /** Bounce, 0–1 (default 0). A pair bounces by the larger of its two. */\n restitution?: number;\n /** Coulomb friction coefficient (default 0.5). A pair uses the geometric mean of its two. */\n friction?: number;\n /** Velocity lost per second, as a share (default 0): air drag. */\n linearDamping?: number;\n /** Multiplies the world's gravity for this body (default 1; 0 floats). */\n gravityScale?: number;\n /** A sensor reports overlaps (contact events) but is never pushed and pushes nothing. */\n sensor?: boolean;\n /** Category bits this body is in (default 1). */\n layer?: number;\n /** Category bits this body collides with (default all). A pair collides when each one's layer is in the other's mask. */\n mask?: number;\n /** Anything the game wants to find again from a contact or a ray hit (an entity id, an object). */\n userData?: unknown;\n}\n\n/** Faces of a box that lie against more solid ground (bits), never used as a contact normal. */\nexport const FACE_LEFT = 1;\nexport const FACE_RIGHT = 2;\nexport const FACE_TOP = 4;\nexport const FACE_BOTTOM = 8;\n\n/** A contact as events and queries report it. The normal points from `a` towards `b`; y grows downwards. */\nexport interface ContactInfo {\n readonly a: Body;\n readonly b: Body;\n readonly nx: number;\n readonly ny: number;\n /** Overlap along the normal, px (slightly negative while resting just apart). */\n readonly depth: number;\n /** Either body is a sensor: reported, never resolved. */\n readonly sensor: boolean;\n /** Where the two touch, world px: one or two points (two where an edge lies along an edge). */\n readonly points: readonly Vec2[];\n}\n\nexport interface PhysicsEvents {\n /** Two bodies started touching (or a sensor started overlapping). Emitted after the step. */\n begin: ContactInfo;\n /** Two bodies stopped touching, or one of them was removed. Emitted after the step. */\n end: ContactInfo;\n}\n\nexport interface RayHit {\n body: Body;\n /** Where the ray met the body. */\n x: number;\n y: number;\n /** Surface normal there, pointing back towards the ray's start. */\n nx: number;\n ny: number;\n /** Share of the ray's length at the hit, 0–1. */\n fraction: number;\n}\n\nexport interface RaycastOptions {\n /** Only bodies whose layer shares a bit with this (default all). */\n mask?: number;\n /** Sensors are skipped unless this is true. */\n includeSensors?: boolean;\n /** Skip bodies this returns false for (the caster itself, say). */\n filter?: (body: Body) => boolean;\n}\n"],"names":["FACE_LEFT","FACE_RIGHT","FACE_TOP","FACE_BOTTOM"],"mappings":"AAiFO,MAAMA,IAAY,GACZC,IAAa,GACbC,IAAW,GACXC,IAAc;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Darkness.js","sources":["../../src/pixel/Darkness.ts"],"sourcesContent":["import { Container, RenderTexture, Sprite, Texture } from 'pixi.js';\nimport { TILE_SIZE } from '../net/constants';\nimport type { Stage } from '../core/Stage';\nimport { lightRadius, liveLights } from './lights';\nimport { isTouchUi } from '../platform/touch';\n\n/**\n * The dark underground, and the lights that are cut out of it.\n *\n * The obvious way to do this — a black sheet with glowing blobs added on top — is what makes a dungeon look\n * like it is standing in fog: adding light to black gives grey, so the floor inside the \"light\" is a washed-out\n * haze rather than lit stone. What reads as light is the opposite operation. The sheet of night and the lamps\n * are drawn into a texture of their own, the lamps **erasing** the night rather than adding to it, and that\n * texture is laid over the world. Inside a lamp there is simply no night left, so the art underneath shows at\n * its own colour; outside one there is nothing but black. That is the whole trick.\n *\n * It lives in screen space because the hole has to stay under the torch as the camera moves, and a texture the\n * size of the window is far cheaper than one the size of the map.\n */\n/** How often a flicker alone redraws the sheet (≈15 a second): a torch's wobble does not need 60. */\nconst FLICKER_MS = 66;\n/** The least time between two redraws on a phone (≈30 a second, what the phones run at by default). */\nconst PHONE_MS = 33;\n/** World pixels of night drawn past each edge of the window: room for a shake and for a phone's held redraw. */\nconst MARGIN = 8;\n\n/*\n * Lit the way pixel art lights a room: the night is drawn one texel to one **world** pixel, lined up\n * with the camera so its texels sit exactly on the art's pixels, and every light's falloff is a few levels of alpha\n * blended by an ordered (Bayer 4×4) dither in those world pixels — far off a stipple, close a solid fill — instead of\n * a smooth gradient laid over pixel art (which read as fog) or hard rings (which read as a target). A torch's\n * flicker is its radius stepping a whole pixel, not a sprite scaled by fractions.\n */\n\n/** How a light falls off with `d`, a share of its radius: a bright core and a long thinning tail. */\nconst holeAt = (d: number): number => Math.min(1, Math.pow(Math.max(0, 1 - d * d), 1.5) * 1.15);\n/** The colour a light lays on what it lights: strongest at the heart, gone well before its edge. */\nconst washAt = (d: number): number => Math.pow(Math.max(0, 1 - d), 2);\n/** Alpha levels the dither blends between: the hole's and the colour's. */\nconst HOLE_LEVELS = 4;\nconst WASH_LEVELS = 3;\n/** How strongly a light colours what it lights, before the night's own depth weighs it. */\nconst WASH_ALPHA = 0.3;\n/** A light's height over its width: the ground is seen at a slant, so a pool of light is an ellipse. */\nconst SQUASH = 0.85;\n/** A plain light's colour (a lamp nobody named one for, the light a body carries): warm, as flame is. */\nexport const WARM_LIGHT = 0xffc27a;\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/**\n * Radii are painted at every second pixel up to 24 and every eighth above, so a shrinking flash reuses a few textures\n * (each new one is a canvas and an upload; one per pixel made the first flashes hitch).\n */\nconst bucket = (r: number): number => (r <= 24 ? Math.max(2, Math.round(r / 2) * 2) : Math.round(r / 8) * 8);\n/** Every bucket a light can take, painted ahead one per task (`warm`), so no flash pays for its texture. */\nconst BUCKETS = [...new Set(Array.from({ length: 160 }, (_, r) => bucket(r + 1)))];\n/** How often transient lights alone redraw the sheet (≈30 a second): a flash fading needs no more. */\nconst FLASH_MS = 33;\n\nconst textures = new Map<string, Texture>();\n\nlet warming = false;\n\n/** Paints every falloff ahead, one a task, so the first flashes find theirs made. */\nfunction warm(): void {\n if (warming) return;\n warming = true;\n const todo = BUCKETS.flatMap((r) => [['hole', r], ['wash', r]] as const);\n const next = () => {\n const item = todo.shift();\n if (!item) return;\n falloff(item[0], item[1]);\n setTimeout(next, 0);\n };\n setTimeout(next, 0);\n}\n\n/** One falloff at radius `r` world px, one texel a world pixel, dithered in place. */\nfunction falloff(kind: 'hole' | 'wash', r: number): Texture {\n const key = `${kind}${r}`;\n const cached = textures.get(key);\n if (cached) return cached;\n const w = r * 2;\n const h = Math.max(2, Math.round(r * 2 * SQUASH));\n const levels = kind === 'hole' ? HOLE_LEVELS : WASH_LEVELS;\n const curve = kind === 'hole' ? holeAt : washAt;\n const canvas = document.createElement('canvas');\n canvas.width = w;\n canvas.height = h;\n // a browser out of canvas memory (iOS) gives no context: no light this time, and the next asks again\n const ctx = canvas.getContext('2d');\n if (!ctx) return Texture.EMPTY;\n const img = ctx.createImageData(w, h);\n for (let y = 0; y < h; y++)\n for (let x = 0; x < w; x++) {\n const d = Math.hypot((x + 0.5 - w / 2) / r, (y + 0.5 - h / 2) / (r * SQUASH));\n if (d >= 1) continue;\n const v = Math.min(levels, Math.floor(curve(d) * levels + BAYER[(y % 4) * 4 + (x % 4)]!)) / levels;\n const i = (y * w + x) * 4;\n img.data[i] = img.data[i + 1] = img.data[i + 2] = 255;\n img.data[i + 3] = Math.round(v * 255);\n }\n ctx.putImageData(img, 0, 0);\n const texture = Texture.from(canvas);\n texture.source.scaleMode = 'nearest';\n textures.set(key, texture);\n return texture;\n}\n\n/** A light's two marks on the night: the hole it erases, and the colour it lays over what shows through. */\ninterface LightSprites {\n hole: Sprite;\n wash: Sprite;\n}\n\nexport class Darkness {\n private readonly scratch = new Container();\n private readonly sheet: Sprite;\n /** Every hole is erased before any colour is laid, or a later hole would wipe an earlier light's colour away. */\n private readonly holes = new Container();\n private readonly washes = new Container();\n private readonly lamps: { sprites: LightSprites; x: number; y: number; r: number; flicker: number }[] = [];\n /** The transient lights (`lights.ts`), reused frame to frame. */\n private readonly flashes: LightSprites[] = [];\n private rt: RenderTexture;\n /**\n * On the screen layer, laid over the world with its exact transform by the camera (`Camera.follow`), shake included:\n * the night is drawn in world pixels round the unshaken camera and only this layer moves with a shake, so a shaking\n * screen does not redraw the night every frame.\n */\n private readonly layer = new Container();\n private readonly view: Sprite;\n /** The light the player carries: the only one that follows them, and the last one they lose. */\n private readonly carried: LightSprites;\n private readonly unsubscribeResize: () => void;\n /** Lamps that flicker: while one does (or the player carries a light) every frame differs. */\n private flickering = 0;\n /** Something other than the camera changed what the night looks like (a lamp added, dimmed, the window resized). */\n private dirty = true;\n /** What the last drawn frame was drawn from: the texture, the camera's texel and the shade. */\n private drawn = '';\n /** The carried light's place at the last redraw (it follows the body). */\n private carriedAt = '';\n /** When the sheet was last redrawn. */\n private drawnAt = -Infinity;\n /** The world pixel the night's top-left texel stands on. */\n private ox = 0;\n private oy = 0;\n /** The night texture's size in world pixels, and the zoom it was made for. */\n private rtKey = '';\n /** The lamps' colour turned down (`setLampsDimmed`). */\n private dimLamps = false;\n /** A phone redraws the night at most `PHONE_MS` apart, whatever moves (`isTouchUi`). */\n private readonly phone = isTouchUi();\n\n constructor(\n private readonly stage: Stage,\n private color: number,\n private alpha: number,\n ) {\n // a flat sheet of night over the whole window: one white pixel, tinted and stretched\n this.sheet = new Sprite({ texture: Texture.WHITE });\n this.scratch.addChild(this.sheet, this.holes, this.washes);\n this.carried = this.light(0xfff0dc);\n\n this.rt = RenderTexture.create({ width: 1, height: 1 });\n this.view = new Sprite({ texture: this.rt });\n this.layer.addChild(this.view);\n stage.screen.addChild(this.layer);\n stage.cameras.main.follow(this.layer);\n this.unsubscribeResize = stage.onResize(() => (this.dirty = true));\n warm();\n }\n\n /** A hole and its colour, added to the night's layers. */\n private light(color: number): LightSprites {\n const hole = new Sprite({ texture: falloff('hole', 8) });\n hole.blendMode = 'erase';\n this.holes.addChild(hole);\n const wash = new Sprite({ texture: falloff('wash', 8) });\n wash.tint = color;\n this.washes.addChild(wash);\n return { hole, wash };\n }\n\n /** A lamp fixed in the world: a torch on a wall, the seep in its chamber, a crystal in a seam, in its own colour. */\n addLamp(x: number, y: number, radius: number, flicker = 0, color = WARM_LIGHT): void {\n this.lamps.push({ sprites: this.light(color), x, y, r: radius, flicker });\n if (flicker) this.flickering++;\n this.dirty = true;\n }\n\n /**\n * Changes how dark and what colour the sheet is (the outdoor night, `NightShade`, deepens and lifts with the\n * world clock). A sheet with no alpha left is neither drawn nor redrawn, so a map at noon pays nothing for its night.\n */\n setShade(color: number, alpha: number): void {\n this.color = color;\n this.alpha = alpha;\n this.layer.visible = alpha > 0.004;\n }\n\n /**\n * Dimmed lamps lay only `LAMP_DIM` of their colour on the dark (a \"fewer ambient lights\" setting). The holes they\n * cut stay as wide, so a dark place is still seen by them.\n */\n setLampsDimmed(dim: boolean): void {\n if (dim === this.dimLamps) return;\n this.dimLamps = dim;\n this.dirty = true;\n }\n\n /**\n * The night's own texture, one texel a world pixel and `MARGIN` to spare on each side (a shake, or a camera that\n * moved while a phone waits for its next redraw, never shows its edge), remade when the window or the zoom changes:\n * at zoom 2 or 3 it is a quarter or a ninth of the screen's pixels. True when it was remade.\n */\n private fit(w: number, h: number, zoom: number): boolean {\n const tw = Math.ceil(w / zoom) + MARGIN * 2;\n const th = Math.ceil(h / zoom) + MARGIN * 2;\n const key = `${tw},${th},${zoom}`;\n if (key === this.rtKey) return false;\n this.rtKey = key;\n this.rt.destroy(true);\n this.rt = RenderTexture.create({ width: tw, height: th });\n this.rt.source.scaleMode = 'nearest';\n this.view.texture = this.rt;\n return true;\n }\n\n /** Redraws the night with this frame's holes in it, when anything in it changed. `px`/`py` is where the player stands. */\n update(now: number, px: number, py: number, carriedRadius: number): void {\n if (!this.layer.visible) return;\n const cam = this.stage.cameras.main;\n const zoom = cam.zoom || 1;\n const remade = this.fit(this.stage.width, this.stage.height, zoom);\n // the texel grid follows the camera (never its shake) in whole world pixels\n const ox = Math.floor(cam.scrollX) - MARGIN;\n const oy = Math.floor(cam.scrollY) - MARGIN;\n const key = `${ox},${oy},${this.rtKey},${this.color},${this.alpha}`;\n const lights = liveLights(now);\n const carriedAt = carriedRadius > 0 ? `${Math.round(px)},${Math.round(py)},${Math.round(carriedRadius)}` : '';\n // the light a body carries goes where it goes; a fight's flashes fade (`FLASH_MS`), torches wobble (`FLICKER_MS`)\n const since = now - this.drawnAt;\n const due =\n this.dirty ||\n key !== this.drawn ||\n carriedAt !== this.carriedAt ||\n (lights.length > 0 && since >= FLASH_MS) ||\n ((carriedRadius > 0 || this.flickering > 0) && since >= FLICKER_MS);\n if (!due) return;\n // a phone holds every redraw `PHONE_MS` apart (redrawing the night at 60 a second runs it hot); the night stays\n // pinned to the world meanwhile, and only a camera about to show the texture's edge cannot wait\n const edge = Math.abs(ox - this.ox) >= MARGIN - 1 || Math.abs(oy - this.oy) >= MARGIN - 1;\n if (this.phone && since < PHONE_MS && !remade && !edge) return;\n this.dirty = false;\n this.drawn = key;\n this.carriedAt = carriedAt;\n this.drawnAt = now;\n this.ox = ox;\n this.oy = oy;\n this.view.position.set(ox, oy);\n this.sheet.width = this.rt.width;\n this.sheet.height = this.rt.height;\n this.sheet.position.set(0, 0);\n this.sheet.tint = this.color;\n this.sheet.alpha = this.alpha;\n\n // the colour a light lays shows as much as the night is deep: a dungeon's lamps colour their stone fully, a dusk\n // street's only a little\n const wash = WASH_ALPHA * Math.min(1, this.alpha * 1.4);\n if (carriedRadius <= 0) this.hide(this.carried);\n else this.place(this.carried, px, py - 6, carriedRadius * (1 + Math.sin(now / 260) * 0.03 + Math.sin(now / 90) * 0.015), wash * 0.5);\n const lampWash = this.dimLamps ? wash * LAMP_DIM : wash;\n for (const l of this.lamps) {\n // torchlight is never still: each one wobbles on its own phase so a corridor of them does not pulse as one,\n // and its colour breathes with it\n const wobble = l.flicker ? 1 + Math.sin(now / 190 + l.flicker) * 0.05 + Math.sin(now / 71 + l.flicker) * 0.03 : 1;\n this.place(l.sprites, l.x, l.y, l.r * wobble, lampWash * (l.flicker ? 0.9 + (wobble - 1) * 2 : 1));\n }\n while (this.flashes.length < lights.length) this.flashes.push(this.light(WARM_LIGHT));\n for (let i = 0; i < this.flashes.length; i++) {\n const l = lights[i];\n const f = this.flashes[i]!;\n if (!l) {\n this.hide(f);\n continue;\n }\n f.wash.tint = l.color;\n // a flash colours what it lights more than a lamp does: it is the brightest thing in the room for its moment\n this.place(f, l.x, l.y, lightRadius(l, now), Math.min(0.6, wash * 1.6));\n }\n\n this.stage.app.renderer.render({ container: this.scratch, target: this.rt, clear: true });\n }\n\n private hide(l: LightSprites): void {\n l.hole.visible = false;\n l.wash.visible = false;\n }\n\n /** Lays light `l` at world (wx, wy), `r` px across its radius, in the night's world-pixel grid. */\n private place(l: LightSprites, wx: number, wy: number, r: number, wash: number): void {\n const R = bucket(r);\n const cx = Math.round(wx) - this.ox;\n const cy = Math.round(wy) - this.oy;\n // a light wholly off the texture cannot be seen, so it is not drawn\n if (r < 1 || cx < -R || cy < -R || cx > this.rt.width + R || cy > this.rt.height + R) {\n this.hide(l);\n return;\n }\n l.hole.texture = falloff('hole', R);\n l.wash.texture = falloff('wash', R);\n for (const sprite of [l.hole, l.wash]) {\n sprite.visible = true;\n sprite.position.set(cx - R, cy - Math.round(R * SQUASH));\n }\n l.wash.alpha = wash;\n l.wash.visible = wash > 0.01;\n }\n\n destroy(): void {\n this.unsubscribeResize();\n this.stage.cameras.main.unfollow(this.layer);\n this.layer.destroy({ children: true });\n this.rt.destroy(true);\n this.scratch.destroy({ children: true, context: true });\n }\n}\n\n/** What is left of a lamp's glow and colour while lamps are dimmed (`setLampsDimmed`). */\nexport const LAMP_DIM = 0.35;\n\n/** World units of light the player carries underground — about five tiles of certainty in any direction. */\nexport const CARRIED_LIGHT = TILE_SIZE * 5;\n"],"names":["FLICKER_MS","PHONE_MS","MARGIN","holeAt","d","washAt","HOLE_LEVELS","WASH_LEVELS","WASH_ALPHA","SQUASH","WARM_LIGHT","BAYER","v","bucket","r","BUCKETS","_","FLASH_MS","textures","warming","warm","todo","next","item","falloff","kind","key","cached","w","h","levels","curve","canvas","ctx","Texture","img","y","x","i","texture","Darkness","stage","color","alpha","Sprite","RenderTexture","Container","isTouchUi","hole","wash","radius","flicker","dim","zoom","tw","th","now","px","py","carriedRadius","cam","remade","ox","oy","lights","liveLights","carriedAt","since","edge","lampWash","LAMP_DIM","l","wobble","f","lightRadius","wx","wy","R","cx","cy","sprite","CARRIED_LIGHT","TILE_SIZE"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DitherVignette.js","sources":["../../src/pixel/DitherVignette.ts"],"sourcesContent":["import { Sprite, Texture } from 'pixi.js';\nimport type { Stage } from '../core/Stage';\nimport { vignetteMask } from './vignetteMask';\n\nconst CACHE = new Map<string, Texture>();\n/** Each texture is a whole screen's worth of pixels: keep only a few sizes. */\nconst CACHE_MAX = 12;\n\n/**\n * A white dithered edge for a screen of `w`×`h` world pixels, one texel a world pixel and scaled up `'nearest'`;\n * the sprite that draws it tints it. Made once per size and strength. Returns `Texture.EMPTY` when the browser has\n * no canvas memory to spare (iOS), so the caller asks again later.\n */\nexport function ditherVignette(w: number, h: number, strength = 1, from = 0.55): Texture {\n const tw = Math.max(1, Math.ceil(w));\n const th = Math.max(1, Math.ceil(h));\n const key = `${tw}x${th}|${strength.toFixed(2)}|${from}`;\n const hit = CACHE.get(key);\n if (hit) return hit;\n const canvas = document.createElement('canvas');\n canvas.width = tw;\n canvas.height = th;\n const ctx = canvas.getContext('2d');\n if (!ctx) return Texture.EMPTY;\n const mask = vignetteMask(tw, th, strength, from);\n const img = ctx.createImageData(tw, th);\n for (let i = 0; i < mask.length; i++) if (mask[i]) img.data.fill(255, i * 4, i * 4 + 4);\n ctx.putImageData(img, 0, 0);\n const texture = Texture.from(canvas);\n texture.source.scaleMode = 'nearest';\n if (CACHE.size >= CACHE_MAX) {\n for (const t of CACHE.values()) t.destroy(true);\n CACHE.clear();\n }\n CACHE.set(key, texture);\n return texture;\n}\n\nexport interface DitherVignetteOptions {\n /** The edge's colour (default black). */\n color?: number;\n /** How far from the centre toward a corner the edge begins, 0..1 (default 0.55). */\n from?: number;\n}\n\n/**\n * A dithered pixel edge round the screen, on the screen layer: a darkened border, or tinted red and beaten with\n * `heartbeat` while a player is low on health. The texture is made at full strength, off the frame that first needs\n * it (a full-screen dither is a few ms on a phone), and remade only when the window or the zoom changes; `show`\n * changes how much of it shows.\n */\nexport class DitherVignette {\n private readonly edge = new Sprite();\n private madeFor = '';\n private making: ReturnType<typeof setTimeout> | null = null;\n private readonly from: number;\n\n constructor(\n private readonly stage: Stage,\n options: DitherVignetteOptions = {},\n ) {\n this.from = options.from ?? 0.55;\n this.edge.tint = options.color ?? 0x000000;\n this.edge.visible = false;\n stage.screen.addChild(this.edge);\n this.making = setTimeout(() => {\n this.making = null;\n this.make();\n }, 0);\n }\n\n setColor(color: number): void {\n this.edge.tint = color;\n }\n\n private make(): void {\n if (this.edge.destroyed) return;\n const z = Math.max(1, this.stage.cameras.main.zoom);\n const w = this.stage.width / z;\n const h = this.stage.height / z;\n const key = `${Math.ceil(w)}x${Math.ceil(h)}`;\n if (key === this.madeFor) return;\n const tex = ditherVignette(w, h, 1, this.from);\n this.edge.texture = tex;\n // an empty texture leaves the size unmarked, so the next call asks again\n if (tex !== Texture.EMPTY) this.madeFor = key;\n }\n\n /** Shows the edge at `alpha` (0..1); call each frame it shows. */\n show(alpha: number): void {\n this.edge.visible = alpha > 0;\n if (!this.edge.visible) return;\n this.make();\n this.edge.alpha = alpha;\n this.edge.width = this.stage.width;\n this.edge.height = this.stage.height;\n }\n\n hide(): void {\n this.edge.visible = false;\n }\n\n destroy(): void {\n if (this.making) clearTimeout(this.making);\n this.edge.destroy();\n }\n}\n"],"names":["CACHE","CACHE_MAX","ditherVignette","w","h","strength","from","tw","th","key","hit","canvas","ctx","Texture","mask","vignetteMask","img","i","texture","t","DitherVignette","stage","options","Sprite","color","z","tex","alpha"],"mappings":";;AAIA,MAAMA,wBAAY,IAAA,GAEZC,IAAY;AAOX,SAASC,EAAeC,GAAWC,GAAWC,IAAW,GAAGC,IAAO,MAAe;AACvF,QAAMC,IAAK,KAAK,IAAI,GAAG,KAAK,KAAKJ,CAAC,CAAC,GAC7BK,IAAK,KAAK,IAAI,GAAG,KAAK,KAAKJ,CAAC,CAAC,GAC7BK,IAAM,GAAGF,CAAE,IAAIC,CAAE,IAAIH,EAAS,QAAQ,CAAC,CAAC,IAAIC,CAAI,IAChDI,IAAMV,EAAM,IAAIS,CAAG;AACzB,MAAIC,EAAK,QAAOA;AAChB,QAAMC,IAAS,SAAS,cAAc,QAAQ;AAC9C,EAAAA,EAAO,QAAQJ,GACfI,EAAO,SAASH;AAChB,QAAMI,IAAMD,EAAO,WAAW,IAAI;AAClC,MAAI,CAACC,EAAK,QAAOC,EAAQ;AACzB,QAAMC,IAAOC,EAAaR,GAAIC,GAAIH,GAAUC,CAAI,GAC1CU,IAAMJ,EAAI,gBAAgBL,GAAIC,CAAE;AACtC,WAASS,IAAI,GAAGA,IAAIH,EAAK,QAAQG,KAASH,EAAKG,CAAC,KAAGD,EAAI,KAAK,KAAK,KAAKC,IAAI,GAAGA,IAAI,IAAI,CAAC;AACtF,EAAAL,EAAI,aAAaI,GAAK,GAAG,CAAC;AAC1B,QAAME,IAAUL,EAAQ,KAAKF,CAAM;AAEnC,MADAO,EAAQ,OAAO,YAAY,WACvBlB,EAAM,QAAQC,GAAW;AAC3B,eAAWkB,KAAKnB,EAAM,OAAA,EAAU,CAAAmB,EAAE,QAAQ,EAAI;AAC9C,IAAAnB,EAAM,MAAA;AAAA,EACR;AACA,SAAAA,EAAM,IAAIS,GAAKS,CAAO,GACfA;AACT;AAeO,MAAME,EAAe;AAAA,EAM1B,YACmBC,GACjBC,IAAiC,IACjC;AAFiB,SAAA,QAAAD,GAGjB,KAAK,OAAOC,EAAQ,QAAQ,MAC5B,KAAK,KAAK,OAAOA,EAAQ,SAAS,GAClC,KAAK,KAAK,UAAU,IACpBD,EAAM,OAAO,SAAS,KAAK,IAAI,GAC/B,KAAK,SAAS,WAAW,MAAM;AAC7B,WAAK,SAAS,MACd,KAAK,KAAA;AAAA,IACP,GAAG,CAAC;AAAA,EACN;AAAA,EAXmB;AAAA,EANF,OAAO,IAAIE,EAAA;AAAA,EACpB,UAAU;AAAA,EACV,SAA+C;AAAA,EACtC;AAAA,EAgBjB,SAASC,GAAqB;AAC5B,SAAK,KAAK,OAAOA;AAAA,EACnB;AAAA,EAEQ,OAAa;AACnB,QAAI,KAAK,KAAK,UAAW;AACzB,UAAMC,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,KAAK,IAAI,GAC5CtB,IAAI,KAAK,MAAM,QAAQsB,GACvB,IAAI,KAAK,MAAM,SAASA,GACxBhB,IAAM,GAAG,KAAK,KAAKN,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC;AAC3C,QAAIM,MAAQ,KAAK,QAAS;AAC1B,UAAMiB,IAAMxB,EAAeC,GAAG,GAAG,GAAG,KAAK,IAAI;AAC7C,SAAK,KAAK,UAAUuB,GAEhBA,MAAQb,EAAQ,UAAO,KAAK,UAAUJ;AAAA,EAC5C;AAAA;AAAA,EAGA,KAAKkB,GAAqB;AAExB,IADA,KAAK,KAAK,UAAUA,IAAQ,GACvB,KAAK,KAAK,YACf,KAAK,KAAA,GACL,KAAK,KAAK,QAAQA,GAClB,KAAK,KAAK,QAAQ,KAAK,MAAM,OAC7B,KAAK,KAAK,SAAS,KAAK,MAAM;AAAA,EAChC;AAAA,EAEA,OAAa;AACX,SAAK,KAAK,UAAU;AAAA,EACtB;AAAA,EAEA,UAAgB;AACd,IAAI,KAAK,UAAQ,aAAa,KAAK,MAAM,GACzC,KAAK,KAAK,QAAA;AAAA,EACZ;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Evaporation.js","sources":["../../src/pixel/Evaporation.ts"],"sourcesContent":["import { addPixelLayer, type PixelLayer } from './PixelLayer';\nimport type { Stage } from '../core/Stage';\nimport { engineSettings } from '../settings';\nimport type { WorldObject } from '../core/types';\nimport { ramp } from './pixelTone';\nimport type { PackedPixels } from './spritePixels';\n\n/** What a dying body hands over: its feet in the world, how it was drawn, and the pixels of its last frame. */\nexport interface DeathPixels {\n x: number;\n y: number;\n /** World pixels per sprite pixel (the body's `scale`), negative when the body faced left. */\n scaleX: number;\n scaleY: number;\n pixels: PackedPixels;\n}\n\nconst FRAME_MS = 1000 / 12;\n/** Pixels in flight across every cloud at once: a crowd dying together shares this, it never adds to it. */\nconst BUDGET = 1400;\nconst PER_BODY = 150;\nconst PER_BIG = 420;\n/** Even a body dying into a full budget leaves a few of its pixels, or it would just blink out. */\nconst MIN_PER_BODY = 16;\n/** The same three under reduced effects or the battery saver. */\nconst THIN = { budget: 500, body: 60, big: 160 };\n\ninterface Cloud {\n g: PixelLayer;\n x: number;\n y: number;\n born: number;\n end: number;\n size: number;\n n: number;\n /** Per particle: start offset, release delay, lifetime, drift speed, wobble phase. */\n x0: Float32Array;\n y0: Float32Array;\n delay: Float32Array;\n life: Float32Array;\n vx: Float32Array;\n vy: Float32Array;\n phase: Float32Array;\n color: Uint32Array;\n frame: number;\n}\n\n/**\n * A dead body evaporating: it breaks into its own pixels, which let go top rows first, rise, scatter a little and\n * give out, while the body itself is hidden by the caller. Every particle is one whole world pixel in a colour read\n * off the body's last frame, painted at 12 fps like every other effect; it pales up its own ramp as it rises and\n * steps its alpha down instead of melting, so it stays pixel art the whole way. A cloud is one `PixelLayer` (a pooled\n * texture) rather than a Graphics of up to 1,400 rects.\n */\nexport class Evaporation {\n private readonly clouds: Cloud[] = [];\n private live = 0;\n\n constructor(\n private readonly scene: Stage,\n private readonly track: (object: WorldObject) => void,\n ) {}\n\n play(d: DeathPixels, big: boolean, now: number): void {\n const s = engineSettings.getState();\n const thin = s.reducedFx || s.batterySaver;\n const want = Math.min(d.pixels.length, big ? (thin ? THIN.big : PER_BIG) : thin ? THIN.body : PER_BODY);\n const n = Math.min(want, Math.max(MIN_PER_BODY, (thin ? THIN.budget : BUDGET) - this.live));\n if (n <= 0) return;\n const size = Math.max(1, Math.round(Math.abs(d.scaleY)));\n const g = addPixelLayer(this.scene);\n this.track(g);\n const c: Cloud = {\n g, x: Math.round(d.x), y: Math.round(d.y), born: now, end: now, size, n, frame: -1,\n x0: new Float32Array(n), y0: new Float32Array(n), delay: new Float32Array(n), life: new Float32Array(n),\n vx: new Float32Array(n), vy: new Float32Array(n), phase: new Float32Array(n), color: new Uint32Array(n),\n };\n let top = 0;\n let bottom = -Infinity;\n let halfW = 1;\n const px = d.pixels;\n for (let k = 0; k < px.length; k++) {\n top = Math.min(top, px.dy[k]!);\n bottom = Math.max(bottom, px.dy[k]!);\n halfW = Math.max(halfW, Math.abs(px.dx[k]! * d.scaleX));\n }\n const height = Math.max(1, bottom - top);\n const spread = big ? 520 : 260;\n const lifeMs = big ? 900 : 620;\n // a random subset when the body has more pixels than its share; every pixel otherwise\n const keep = n / px.length;\n let i = 0;\n for (let k = 0; k < px.length && i < n; k++) {\n if (keep < 1 && Math.random() > keep && px.length - k > n - i) continue;\n const pdy = px.dy[k]!;\n const x = px.dx[k]! * d.scaleX;\n const y = pdy * Math.abs(d.scaleY);\n const fromTop = (pdy - top) / height;\n c.x0[i] = x;\n c.y0[i] = y;\n // the top lets go first and the feet last, so the body dissolves upwards\n c.delay[i] = fromTop * spread * 0.6 + Math.random() * spread * 0.4;\n c.life[i] = lifeMs * (0.6 + Math.random() * 0.6);\n c.vx[i] = (x / halfW) * 5 + (Math.random() - 0.5) * 10;\n c.vy[i] = -(10 + Math.random() * 16) * (big ? 1.3 : 1);\n c.phase[i] = Math.random() * Math.PI * 2;\n c.color[i] = px.color[k]!;\n c.end = Math.max(c.end, now + c.delay[i]! + c.life[i]!);\n i++;\n }\n c.n = i;\n this.live += c.n;\n this.clouds.push(c);\n this.draw(c, now);\n }\n\n update(now: number): void {\n for (let k = this.clouds.length - 1; k >= 0; k--) {\n const c = this.clouds[k]!;\n if (!c.g.active || now >= c.end) {\n if (c.g.active) c.g.destroy();\n this.live -= c.n;\n this.clouds.splice(k, 1);\n continue;\n }\n this.draw(c, now);\n }\n }\n\n private draw(c: Cloud, now: number): void {\n const frame = Math.floor((now - c.born) / FRAME_MS);\n if (frame === c.frame) return;\n c.frame = frame;\n const ms = frame * FRAME_MS;\n const g = c.g;\n g.clear();\n g.setPosition(c.x, c.y).setDepth(c.y + 2);\n const s = c.size;\n for (let i = 0; i < c.n; i++) {\n const t = ms - c.delay[i]!;\n let x = c.x0[i]!;\n let y = c.y0[i]!;\n let color = c.color[i]!;\n let alpha = 1;\n if (t > 0) {\n const k = t / c.life[i]!;\n if (k >= 1) continue;\n const sec = t / 1000;\n // rises faster as it goes, and sways as it rises\n x += c.vx[i]! * sec + Math.sin(c.phase[i]! + sec * 9) * k * 1.5;\n y += c.vy[i]! * sec - 22 * sec * sec;\n const r = ramp(color);\n color = k < 0.3 ? color : k < 0.6 ? r.light : r.hi;\n alpha = k < 0.55 ? 1 : k < 0.8 ? 0.66 : 0.33;\n }\n g.rect(Math.round(x), Math.round(y), s, s, color, alpha);\n }\n g.present();\n }\n}\n"],"names":["FRAME_MS","BUDGET","PER_BODY","PER_BIG","MIN_PER_BODY","THIN","Evaporation","scene","track","d","big","now","s","engineSettings","thin","want","n","size","g","addPixelLayer","c","top","bottom","halfW","px","k","height","spread","lifeMs","keep","i","pdy","x","y","fromTop","frame","ms","t","color","alpha","sec","r","ramp"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"NightShade.js","sources":["../../src/pixel/NightShade.ts"],"sourcesContent":["import type { Stage } from '../core/Stage';\nimport { tintNight, type DayClock } from '../ambient/DayClock';\nimport { Darkness } from './Darkness';\n\nexport interface NightShadeOptions {\n /** The day the night follows. */\n clock: DayClock;\n /** This place's own night colour, pulled in as the night deepens (`tintNight`); none keeps the clock's. */\n tint?: number;\n /** How far the tint takes over at full night (default 0.6). */\n tintShare?: number;\n /** A lamp's hole in the night against the glow it is drawn with (default 1.6: the light reaches past the glow). */\n lampScale?: number;\n /** The radius (world px) of the light a body carries at full night; 0 for none (default 64). */\n carriedLight?: number;\n /** The night share below which nothing is carried (default 0.55): at dusk a hole round the feet reads as a spotlight. */\n carriedFrom?: number;\n}\n\n/**\n * The outdoor night: a `DayClock`'s grade laid over the world as a dithered `Darkness`, deepening at dusk and lifting\n * at dawn, with every lamp, window and fire burning a hole in it — the same erase trick an underground is lit by,\n * only with a sheet whose colour and depth follow the clock. A place under no sky (a cave) builds a `Darkness` of its\n * own instead; at noon the sheet has no alpha and costs nothing.\n */\nexport class NightShade {\n private readonly dark: Darkness;\n private readonly lampScale: number;\n private readonly carried: number;\n private readonly carriedFrom: number;\n\n constructor(\n stage: Stage,\n private readonly options: NightShadeOptions,\n ) {\n this.dark = new Darkness(stage, 0x000000, 0);\n this.dark.setShade(0, 0);\n this.lampScale = options.lampScale ?? 1.6;\n this.carried = options.carriedLight ?? 64;\n this.carriedFrom = Math.min(0.99, options.carriedFrom ?? 0.55);\n }\n\n /** A light the place glows with, `glowSize` the diameter of the glow drawn for it: by night it is what is seen by. */\n addLight(x: number, y: number, glowSize: number, color?: number, flicker = 0): void {\n this.dark.addLamp(x, y, (glowSize / 2) * this.lampScale, flicker, color);\n }\n\n /** Lamps colour the dark less (a \"fewer ambient lights\" setting); their holes stay as wide. */\n setLampsDimmed(dim: boolean): void {\n this.dark.setLampsDimmed(dim);\n }\n\n /** `now` the frame's ms, `wallMs` the shared wall time the clock reads (`serverNow()`), `px`/`py` the player. */\n update(now: number, wallMs: number, px: number, py: number): void {\n const day = this.options.clock.at(wallMs);\n this.dark.setShade(tintNight(day, this.options.tint, this.options.tintShare), day.alpha);\n const from = this.carriedFrom;\n this.dark.update(now, px, py, Math.max(0, (day.night - from) / (1 - from)) * this.carried);\n }\n\n destroy(): void {\n this.dark.destroy();\n }\n}\n"],"names":["NightShade","stage","options","Darkness","x","y","glowSize","color","flicker","dim","now","wallMs","px","py","day","tintNight","from"],"mappings":";;AAyBO,MAAMA,EAAW;AAAA,EAMtB,YACEC,GACiBC,GACjB;AADiB,SAAA,UAAAA,GAEjB,KAAK,OAAO,IAAIC,EAASF,GAAO,GAAU,CAAC,GAC3C,KAAK,KAAK,SAAS,GAAG,CAAC,GACvB,KAAK,YAAYC,EAAQ,aAAa,KACtC,KAAK,UAAUA,EAAQ,gBAAgB,IACvC,KAAK,cAAc,KAAK,IAAI,MAAMA,EAAQ,eAAe,IAAI;AAAA,EAC/D;AAAA,EAPmB;AAAA,EAPF;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAcjB,SAASE,GAAWC,GAAWC,GAAkBC,GAAgBC,IAAU,GAAS;AAClF,SAAK,KAAK,QAAQJ,GAAGC,GAAIC,IAAW,IAAK,KAAK,WAAWE,GAASD,CAAK;AAAA,EACzE;AAAA;AAAA,EAGA,eAAeE,GAAoB;AACjC,SAAK,KAAK,eAAeA,CAAG;AAAA,EAC9B;AAAA;AAAA,EAGA,OAAOC,GAAaC,GAAgBC,GAAYC,GAAkB;AAChE,UAAMC,IAAM,KAAK,QAAQ,MAAM,GAAGH,CAAM;AACxC,SAAK,KAAK,SAASI,EAAUD,GAAK,KAAK,QAAQ,MAAM,KAAK,QAAQ,SAAS,GAAGA,EAAI,KAAK;AACvF,UAAME,IAAO,KAAK;AAClB,SAAK,KAAK,OAAON,GAAKE,GAAIC,GAAI,KAAK,IAAI,IAAIC,EAAI,QAAQE,MAAS,IAAIA,EAAK,IAAI,KAAK,OAAO;AAAA,EAC3F;AAAA,EAEA,UAAgB;AACd,SAAK,KAAK,QAAA;AAAA,EACZ;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PixelLayer.js","sources":["../../src/pixel/PixelLayer.ts"],"sourcesContent":["import { BufferImageSource, Sprite, Texture } from 'pixi.js';\nimport { Graphics } from '../core/Graphics';\nimport { patchPixi } from '../core/pixiPatches';\nimport type { Stage } from '../core/Stage';\n\npatchPixi();\n\n/** Larger than this a frame is laid as rects after all: a texture that size is no saving. */\nconst MAX_SIDE = 1024;\n/**\n * The sides a buffer is made in, each about a third to a half over the last: a layer that swells frame by frame\n * crosses a handful of them, not one every 32 px, and a spent buffer fits the next layer of its class (`POOL`).\n */\nconst SIDES = [16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024];\n/** Spent buffers kept per size class, and in all, for the next layers to take instead of making a texture. */\nconst POOL_PER_CLASS = 24;\nconst POOL_MAX_BYTES = 24 * 1024 * 1024;\n/** A layer whose frames have stayed this many presents well inside a quarter of its buffer trades it for a smaller one. */\nconst SHRINK_AFTER = 60;\n\n/** One buffer, the texture over it, and its size: what a layer holds and the pool keeps. */\ninterface Surface {\n pixels: Uint8Array;\n words: Uint32Array;\n source: BufferImageSource;\n texture: Texture;\n capW: number;\n capH: number;\n}\n\nconst POOL = new Map<number, Surface[]>();\nlet pooledBytes = 0;\n/** Textures made since boot (`PixelLayer.stats`): the pool is working when this stops climbing. */\nlet made = 0;\n\nconst sideFor = (n: number): number => {\n for (const s of SIDES) if (s >= n) return s;\n return Math.ceil(n / 256) * 256;\n};\nconst classOf = (w: number, h: number) => w * 4096 + h;\n\nfunction takeSurface(capW: number, capH: number): Surface {\n // the exact class, else the smallest pooled one that holds it within twice its area: a 48×24 frame takes a spent\n // 48×32 rather than making a texture of its own\n let list = POOL.get(classOf(capW, capH));\n if (!list?.length) {\n let best = Infinity;\n for (const [k, l] of POOL) {\n if (!l.length) continue;\n const w = Math.floor(k / 4096);\n const h = k % 4096;\n const area = w * h;\n if (w >= capW && h >= capH && area <= capW * capH * 2 && area < best) {\n best = area;\n list = l;\n }\n }\n }\n const hit = list?.pop();\n if (hit) {\n pooledBytes -= hit.pixels.byteLength;\n return hit;\n }\n made++;\n const pixels = new Uint8Array(capW * capH * 4);\n const source = new BufferImageSource({\n resource: pixels,\n width: capW,\n height: capH,\n format: 'rgba8unorm',\n alphaMode: 'premultiplied-alpha',\n scaleMode: 'nearest',\n });\n return { pixels, words: new Uint32Array(pixels.buffer), source, texture: new Texture({ source }), capW, capH };\n}\n\nfunction giveSurface(s: Surface): void {\n const key = classOf(s.capW, s.capH);\n let list = POOL.get(key);\n if (!list) POOL.set(key, (list = []));\n if (list.length >= POOL_PER_CLASS || pooledBytes + s.pixels.byteLength > POOL_MAX_BYTES) {\n s.texture.destroy(true);\n return;\n }\n list.push(s);\n pooledBytes += s.pixels.byteLength;\n}\n\n/**\n * A `Graphics` for the pixel effects: every `fillRect` of a frame is written into a small RGBA buffer\n * and shown as **one** sprite, instead of becoming a rect Pixi must tessellate, batch and upload. An effect's frame is\n * hundreds to thousands of one-pixel runs (a poison cloud, a status mark round every body in a pack), and as rects they\n * were most of a fight's CPU on a phone (profiled). The pixels are the same: whole\n * world pixels, a translucent fill blended over what was painted before it in the same frame, as a Graphics did.\n *\n * Paint between `clear()` and `present()`; anything other than `fillStyle`/`fillRect` still draws through the\n * Graphics underneath, so a painter using another shape keeps working. Solid pixels may also be **staged**\n * (`stage`, from `pixelTone`'s batch) and laid in one go by `commit`, which is what a batch's flush does.\n *\n * Buffers and their textures are pooled by size class (a texture per effect ran to ~114 a second in a busy fight,\n * and each one stayed in Pixi's batch tables), so a destroyed layer hands its texture\n * to the next one; `PixelLayer.drainPool` lets them all go when the map changes.\n */\n/** A new `PixelLayer` on the stage's world layer. */\nexport function addPixelLayer(stage: Stage): PixelLayer {\n return stage.add.existing(new PixelLayer());\n}\n\nexport class PixelLayer extends Graphics {\n /** This frame's rects: x, y, w, h, colour, alpha — six numbers each. */\n private readonly rects: number[] = [];\n /** Staged solid runs not yet committed: x, y, w, colour — four numbers each; later ones win. */\n private readonly staged: number[] = [];\n private ink = 0xffffff;\n private inkAlpha = 1;\n private readonly sprite = new Sprite(Texture.EMPTY);\n private surface: Surface | null = null;\n /** Where the buffer's first pixel sits in the layer, as of the last `present`. */\n private originX = 0;\n private originY = 0;\n /** The last frame's size, and how many presents in a row it stayed well inside a quarter of the buffer. */\n private lastW = 0;\n private lastH = 0;\n private small = 0;\n\n constructor() {\n super();\n this.sprite.visible = false;\n this.addChild(this.sprite);\n }\n\n /** Textures made, spent buffers waiting in the pool and their bytes (the leak tools read it). */\n static stats(): { made: number; pooled: number; pooledBytes: number } {\n let pooled = 0;\n for (const l of POOL.values()) pooled += l.length;\n return { made, pooled, pooledBytes };\n }\n\n /** Lets every pooled buffer go (the world changed map): the next map's effects start a pool of their own. */\n static drainPool(): void {\n for (const list of POOL.values()) for (const s of list) s.texture.destroy(true);\n POOL.clear();\n pooledBytes = 0;\n }\n\n override fillStyle(color: number, alpha = 1): this {\n this.ink = color;\n this.inkAlpha = alpha;\n return super.fillStyle(color, alpha);\n }\n\n override fillRect(x: number, y: number, w: number, h: number): this {\n const x0 = Math.round(x);\n const y0 = Math.round(y);\n const rw = Math.round(x + w) - x0;\n const rh = Math.round(y + h) - y0;\n if (rw > 0 && rh > 0 && this.inkAlpha > 0) this.rects.push(x0, y0, rw, rh, this.ink, this.inkAlpha);\n return this;\n }\n\n /** `fillStyle` + `fillRect` on whole pixels in one call, leaving the fill style alone (a hot loop's way in). */\n rect(x: number, y: number, w: number, h: number, color: number, alpha = 1): void {\n // a coordinate off to Infinity or NaN would make the frame's box endless (`present`), so it is dropped here\n if (w > 0 && h > 0 && alpha > 0 && Number.isFinite(x) && Number.isFinite(y) && Number.isFinite(w) && Number.isFinite(h)) this.rects.push(x, y, w, h, color, alpha);\n }\n\n /**\n * A solid run of `w` whole pixels at (x, y) in `color`, held until `commit` lays every staged run in the order given\n * (the later winning where they cross), after whatever was drawn before the commit. What `pixelTone`'s batch writes\n * instead of filing each pixel in a map and sorting it into rects.\n */\n stage(x: number, y: number, w: number, color: number): void {\n if (Number.isFinite(x) && Number.isFinite(y) && Number.isFinite(w)) this.staged.push(x, y, w, color);\n }\n\n /** Lays the staged runs, as opaque rects, after everything drawn so far. */\n commit(): void {\n const s = this.staged;\n const n = s.length;\n if (n === 0) return;\n const r = this.rects;\n for (let i = 0; i < n; i += 4) r.push(s[i]!, s[i + 1]!, s[i + 2]!, 1, s[i + 3]!, 1);\n s.length = 0;\n }\n\n override clear(): this {\n this.rects.length = 0;\n this.staged.length = 0;\n return super.clear();\n }\n\n /** How far the last frame reached from the layer's origin, in world pixels (0 when nothing showed). */\n reach(): number {\n if (!this.sprite.visible) return 0;\n return Math.max(Math.abs(this.originX), Math.abs(this.originY), Math.abs(this.originX + this.lastW), Math.abs(this.originY + this.lastH));\n }\n\n /** Writes this frame's rects into the texture and shows it; call once the frame is painted. */\n present(): void {\n this.commit();\n const r = this.rects;\n if (r.length === 0) {\n this.sprite.visible = false;\n return;\n }\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n for (let i = 0; i < r.length; i += 6) {\n if (r[i]! < minX) minX = r[i]!;\n if (r[i + 1]! < minY) minY = r[i + 1]!;\n if (r[i]! + r[i + 2]! > maxX) maxX = r[i]! + r[i + 2]!;\n if (r[i + 1]! + r[i + 3]! > maxY) maxY = r[i + 1]! + r[i + 3]!;\n }\n const w = maxX - minX;\n const h = maxY - minY;\n if (w > MAX_SIDE || h > MAX_SIDE) {\n this.sprite.visible = false;\n // the Graphics' own fillRect, never chained off `super.fillStyle(…)`: that returns `this`, so `.fillRect` was this\n // class's, which pushed every rect back onto `r` while it was walked — the loop never ended and grew the array to\n // a gigabyte (a body evaporating far across the screen froze PCs for seconds and killed mobile web views)\n const n = r.length;\n for (let i = 0; i < n; i += 6) {\n super.fillStyle(r[i + 4]!, r[i + 5]!);\n super.fillRect(r[i]!, r[i + 1]!, r[i + 2]!, r[i + 3]!);\n }\n return;\n }\n const surface = this.fit(w, h);\n const { words, pixels, capW: cap } = surface;\n // the whole buffer: the sprite always shows all of it at one texel a world pixel. Cropping the texture's frame to\n // this frame's size instead stretched it to the old size (a Pixi sprite is sized by `orig`), and an effect that\n // shrank — a falling column — came down as blocks blown up several times over\n words.fill(0);\n for (let i = 0; i < r.length; i += 6) {\n const x0 = r[i]! - minX;\n const y0 = r[i + 1]! - minY;\n const rw = r[i + 2]!;\n const rh = r[i + 3]!;\n const c = r[i + 4]!;\n const a = r[i + 5]!;\n const cr = (c >> 16) & 255;\n const cg = (c >> 8) & 255;\n const cb = c & 255;\n if (a >= 1) {\n // little-endian RGBA bytes, opaque: no blend\n const word = (0xff000000 | (cb << 16) | (cg << 8) | cr) >>> 0;\n if (rw === 1 && rh === 1) {\n words[y0 * cap + x0] = word;\n continue;\n }\n for (let y = y0; y < y0 + rh; y++) {\n const row = y * cap + x0;\n if (rw < 8) for (let p = row, end = row + rw; p < end; p++) words[p] = word;\n else words.fill(word, row, row + rw);\n }\n continue;\n }\n // premultiplied source-over, what a translucent Graphics fill did over the pixels under it\n const keep = 1 - a;\n const pr = cr * a;\n const pg = cg * a;\n const pb = cb * a;\n const pa = 255 * a;\n for (let y = y0; y < y0 + rh; y++) {\n for (let p = (y * cap + x0) * 4, end = p + rw * 4; p < end; p += 4) {\n pixels[p] = pr + pixels[p]! * keep;\n pixels[p + 1] = pg + pixels[p + 1]! * keep;\n pixels[p + 2] = pb + pixels[p + 2]! * keep;\n pixels[p + 3] = pa + pixels[p + 3]! * keep;\n }\n }\n }\n surface.source.update();\n this.sprite.texture = surface.texture;\n this.sprite.position.set(minX, minY);\n this.sprite.visible = true;\n this.originX = minX;\n this.originY = minY;\n this.lastW = w;\n this.lastH = h;\n }\n\n /**\n * Shows `other`'s last frame instead of painting one: its texture, placed where it placed it. A frame shared by\n * several bodies (the same aura on two bodies) is painted once and shown by each of their layers.\n */\n mirror(other: PixelLayer): void {\n const s = other.surface;\n if (!s || !other.sprite.visible) {\n this.hideMirror();\n return;\n }\n this.sprite.texture = s.texture;\n this.sprite.position.set(other.originX, other.originY);\n this.sprite.visible = true;\n this.originX = other.originX;\n this.originY = other.originY;\n this.lastW = other.lastW;\n this.lastH = other.lastH;\n }\n\n /** Shows nothing and lets go of the texture a `mirror` showed. */\n hideMirror(): void {\n this.sprite.visible = false;\n this.sprite.texture = this.surface?.texture ?? Texture.EMPTY;\n }\n\n /** The premultiplied [r, g, b, a] last presented at layer pixel (x, y); zeros where nothing was (tests read it). */\n pixelAt(x: number, y: number): [number, number, number, number] {\n const s = this.surface;\n const capW = s?.capW ?? 0;\n const capH = s?.capH ?? 0;\n // the sprite must show the buffer one texel to a world pixel, or every pixel read here lands somewhere else\n if (this.sprite.width !== capW || this.sprite.height !== capH) throw new Error('PixelLayer sprite is scaled');\n const px = x - this.originX;\n const py = y - this.originY;\n if (!s || !this.sprite.visible || px < 0 || py < 0 || px >= capW || py >= capH) return [0, 0, 0, 0];\n const p = (py * capW + px) * 4;\n return [s.pixels[p]!, s.pixels[p + 1]!, s.pixels[p + 2]!, s.pixels[p + 3]!];\n }\n\n /** The buffer's size (tests read it). */\n capacity(): { w: number; h: number } {\n return { w: this.surface?.capW ?? 0, h: this.surface?.capH ?? 0 };\n }\n\n /**\n * A buffer at least `w` × `h`: grown a size class at a time and kept while it fits, traded back to the pool for a\n * smaller one only once the frames have stayed well inside it for `SHRINK_AFTER` presents (a long-lived layer — an\n * aura, a status mark — that once swelled would otherwise hold and upload its largest buffer for good).\n */\n private fit(w: number, h: number): Surface {\n const s = this.surface;\n if (s && w <= s.capW && h <= s.capH) {\n const roomy = s.capW * s.capH >= 128 * 128 && w * 4 <= s.capW && h * 4 <= s.capH;\n this.small = roomy ? this.small + 1 : 0;\n if (this.small < SHRINK_AFTER) return s;\n }\n this.small = 0;\n // growing keeps the larger side it had, so a frame that swells one way and then the other is not traded each time\n const grow = !!s && (w > s.capW || h > s.capH);\n const capW = sideFor(Math.max(1, w, grow ? s!.capW : 0));\n const capH = sideFor(Math.max(1, h, grow ? s!.capH : 0));\n this.dropSurface();\n const next = takeSurface(capW, capH);\n this.surface = next;\n this.sprite.texture = next.texture;\n return next;\n }\n\n private dropSurface(): void {\n this.sprite.texture = Texture.EMPTY;\n if (this.surface) giveSurface(this.surface);\n this.surface = null;\n }\n\n override destroy(): void {\n if (this.destroyed) return;\n this.dropSurface();\n super.destroy();\n }\n}\n"],"names":["patchPixi","MAX_SIDE","SIDES","POOL_PER_CLASS","POOL_MAX_BYTES","SHRINK_AFTER","POOL","pooledBytes","made","sideFor","s","classOf","w","h","takeSurface","capW","capH","list","best","k","l","area","hit","pixels","source","BufferImageSource","Texture","giveSurface","key","addPixelLayer","stage","PixelLayer","Graphics","Sprite","pooled","color","alpha","x","y","x0","y0","rw","rh","n","r","i","minX","minY","maxX","maxY","surface","words","cap","c","a","cr","cg","cb","word","row","p","end","keep","pr","pg","pb","pa","other","px","py","roomy","grow","next"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SwayProp.js","sources":["../../src/pixel/SwayProp.ts"],"sourcesContent":["import { Rectangle, Sprite, Texture, type TextureSource } from 'pixi.js';\nimport type { Image } from '../core/Image';\nimport { buildRig, gustAt, swayOffset, type Rect, type SwayDef, type SwayRig } from './swayRig';\n\n/** Sub-textures, one per (frame, rect), shared by every prop cut from the same source. */\nconst SLICES = new WeakMap<TextureSource, Map<string, Texture>>();\n\nfunction slice(frame: Texture, r: Rect): Texture {\n let cache = SLICES.get(frame.source);\n if (!cache) SLICES.set(frame.source, (cache = new Map()));\n const f = frame.frame;\n const k = `${f.x + r.x},${f.y + r.y},${r.w},${r.h}`;\n let t = cache.get(k);\n if (!t) cache.set(k, (t = new Texture({ source: frame.source, frame: new Rectangle(f.x + r.x, f.y + r.y, r.w, r.h) })));\n return t;\n}\n\n/**\n * The opaque width of each row of a frame, read once off its canvas (a `crown` finds its trunk in them); undefined\n * when the source is not a 2D canvas. The atlas pages are canvases kept on the CPU, so this costs no GPU read-back.\n */\nexport function opaqueRowWidths(frame: Texture): number[] | undefined {\n const canvas = frame.source.resource as HTMLCanvasElement | undefined;\n const ctx = canvas?.getContext?.('2d');\n if (!ctx) return undefined;\n const { x, y, width: w, height: h } = frame.frame;\n const data = ctx.getImageData(x, y, w, h).data;\n const out: number[] = [];\n for (let row = 0; row < h; row++) {\n let l = -1;\n let r = -1;\n for (let col = 0; col < w; col++)\n if (data[(row * w + col) * 4 + 3]! > 0) {\n if (l < 0) l = col;\n r = col;\n }\n out.push(l < 0 ? 0 : r - l + 1);\n }\n return out;\n}\n\nexport interface SwayPropOptions {\n /** Which `tufts` list to read (default 0). */\n variant?: number;\n /** This prop's place in the cycle, 0..1 (`tileHash` of its tile keeps it from one visit to the next). */\n phase?: number;\n /** Its pace against the def's `speed` (default 1): a little apart per prop, so a wood never sways in step. */\n pace?: number;\n /** Row widths for a `crown` (default: read off the frame with `opaqueRowWidths`). */\n rowWidths?: readonly number[];\n /** A rig already built for this frame (props of one frame share one). */\n rig?: SwayRig;\n}\n\nexport interface SwayWind {\n /** 0 still … 1 a full wind. */\n wind: number;\n /** A gust's period (s) and wavelength across the world (px); see `gustAt`. */\n gustSeconds: number;\n gustPx: number;\n /** The least reach while any wind blows (`swayOffset`). */\n minReach?: number;\n}\n\n/**\n * One sprite swaying in the wind, in whole world pixels. The placed image becomes the rig's first (largest) still\n * piece — so whatever culls, fades or sorts it keeps working — and the other pieces and the strips are sprites beside\n * it in the same parent, at its depth, following its visibility and alpha. A strip's sprite is moved only when its\n * whole-pixel offset changes.\n */\nexport class SwayProp {\n private readonly parts: Sprite[] = [];\n private readonly strips: { sprite: Sprite; homeX: number; weight: number; lag: number; offset: number }[] = [];\n private readonly phase: number;\n private readonly speed: number;\n private shown: boolean;\n private alpha: number;\n /** False when the frame is too small or the rig has nothing to move: `update` then does nothing. */\n readonly active: boolean;\n\n constructor(\n private readonly img: Image,\n private readonly def: SwayDef,\n options: SwayPropOptions = {},\n ) {\n this.phase = options.phase ?? 0;\n this.speed = def.speed * (options.pace ?? 1);\n this.shown = img.visible;\n this.alpha = img.alpha;\n const frame = img.texture;\n const w = frame.frame.width;\n const h = frame.frame.height;\n const parent = img.parent;\n const rig =\n options.rig ?? (w >= 2 && h >= 2 ? buildRig(def, w, h, def.mode === 'crown' ? (options.rowWidths ?? opaqueRowWidths(frame)) : undefined, options.variant ?? 0) : null);\n this.active = !!rig && !!parent && rig.pieces.length > 0 && rig.strips.length > 0;\n if (!this.active || !rig || !parent) return;\n const ax = img.anchor.x * w;\n const ay = img.anchor.y * h;\n const left = Math.round(img.x - ax);\n const top = Math.round(img.y - ay);\n const [first, ...rest] = rig.pieces;\n img.texture = slice(frame, first!);\n img.anchor.set((ax - first!.x) / first!.w, (ay - first!.y) / first!.h);\n const add = (r: Rect): Sprite => {\n const s = new Sprite(slice(frame, r));\n s.position.set(left + r.x, top + r.y);\n s.zIndex = img.zIndex;\n s.visible = img.visible;\n s.alpha = img.alpha;\n parent.addChild(s);\n this.parts.push(s);\n return s;\n };\n for (const r of rest) add(r);\n for (const st of rig.strips) this.strips.push({ sprite: add(st.rect), homeX: left + st.rect.x, weight: st.weight, lag: st.lag, offset: 0 });\n }\n\n /** `tSec` seconds of any steady clock; `enabled` false stands everything still (an ambient-effects setting). */\n update(tSec: number, air: SwayWind, enabled = true): void {\n if (!this.active) return;\n const shown = this.img.visible;\n if (shown !== this.shown) {\n this.shown = shown;\n for (const p of this.parts) p.visible = shown;\n }\n if (!shown) return;\n if (this.img.alpha !== this.alpha) {\n this.alpha = this.img.alpha;\n for (const p of this.parts) p.alpha = this.alpha;\n }\n const still = !enabled || air.wind <= 0;\n const gust = still ? 0 : gustAt(tSec, this.img.x, air.gustSeconds, air.gustPx);\n for (const st of this.strips) {\n const off = still ? 0 : swayOffset(tSec, this.speed, this.phase, st.lag, this.def.amp, air.wind, gust, st.weight, air.minReach ?? 0);\n if (off === st.offset) continue;\n st.offset = off;\n st.sprite.x = st.homeX + off;\n }\n }\n\n destroy(): void {\n for (const p of this.parts) p.destroy();\n this.parts.length = 0;\n this.strips.length = 0;\n }\n}\n"],"names":["SLICES","slice","frame","r","cache","f","k","t","Texture","Rectangle","opaqueRowWidths","ctx","x","y","w","h","data","out","row","l","col","SwayProp","img","def","options","parent","rig","buildRig","ax","ay","left","top","first","rest","add","s","Sprite","st","tSec","air","enabled","shown","p","still","gust","gustAt","off","swayOffset"],"mappings":";;AAKA,MAAMA,wBAAa,QAAA;AAEnB,SAASC,EAAMC,GAAgBC,GAAkB;AAC/C,MAAIC,IAAQJ,EAAO,IAAIE,EAAM,MAAM;AACnC,EAAKE,KAAOJ,EAAO,IAAIE,EAAM,QAASE,IAAQ,oBAAI,KAAM;AACxD,QAAMC,IAAIH,EAAM,OACVI,IAAI,GAAGD,EAAE,IAAIF,EAAE,CAAC,IAAIE,EAAE,IAAIF,EAAE,CAAC,IAAIA,EAAE,CAAC,IAAIA,EAAE,CAAC;AACjD,MAAII,IAAIH,EAAM,IAAIE,CAAC;AACnB,SAAKC,KAAGH,EAAM,IAAIE,GAAIC,IAAI,IAAIC,EAAQ,EAAE,QAAQN,EAAM,QAAQ,OAAO,IAAIO,EAAUJ,EAAE,IAAIF,EAAE,GAAGE,EAAE,IAAIF,EAAE,GAAGA,EAAE,GAAGA,EAAE,CAAC,EAAA,CAAG,CAAE,GAC/GI;AACT;AAMO,SAASG,EAAgBR,GAAsC;AAEpE,QAAMS,IADST,EAAM,OAAO,UACR,aAAa,IAAI;AACrC,MAAI,CAACS,EAAK;AACV,QAAM,EAAE,GAAAC,GAAG,GAAAC,GAAG,OAAOC,GAAG,QAAQC,MAAMb,EAAM,OACtCc,IAAOL,EAAI,aAAaC,GAAGC,GAAGC,GAAGC,CAAC,EAAE,MACpCE,IAAgB,CAAA;AACtB,WAASC,IAAM,GAAGA,IAAMH,GAAGG,KAAO;AAChC,QAAIC,IAAI,IACJhB,IAAI;AACR,aAASiB,IAAM,GAAGA,IAAMN,GAAGM;AACzB,MAAIJ,GAAME,IAAMJ,IAAIM,KAAO,IAAI,CAAC,IAAK,MAC/BD,IAAI,MAAGA,IAAIC,IACfjB,IAAIiB;AAER,IAAAH,EAAI,KAAKE,IAAI,IAAI,IAAIhB,IAAIgB,IAAI,CAAC;AAAA,EAChC;AACA,SAAOF;AACT;AA+BO,MAAMI,EAAS;AAAA,EAUpB,YACmBC,GACAC,GACjBC,IAA2B,CAAA,GAC3B;AAHiB,SAAA,MAAAF,GACA,KAAA,MAAAC,GAGjB,KAAK,QAAQC,EAAQ,SAAS,GAC9B,KAAK,QAAQD,EAAI,SAASC,EAAQ,QAAQ,IAC1C,KAAK,QAAQF,EAAI,SACjB,KAAK,QAAQA,EAAI;AACjB,UAAMpB,IAAQoB,EAAI,SACZR,IAAIZ,EAAM,MAAM,OAChBa,IAAIb,EAAM,MAAM,QAChBuB,IAASH,EAAI,QACbI,IACJF,EAAQ,QAAQV,KAAK,KAAKC,KAAK,IAAIY,EAASJ,GAAKT,GAAGC,GAAGQ,EAAI,SAAS,UAAWC,EAAQ,aAAad,EAAgBR,CAAK,IAAK,QAAWsB,EAAQ,WAAW,CAAC,IAAI;AAEnK,QADA,KAAK,SAAS,CAAC,CAACE,KAAO,CAAC,CAACD,KAAUC,EAAI,OAAO,SAAS,KAAKA,EAAI,OAAO,SAAS,GAC5E,CAAC,KAAK,UAAU,CAACA,KAAO,CAACD,EAAQ;AACrC,UAAMG,IAAKN,EAAI,OAAO,IAAIR,GACpBe,IAAKP,EAAI,OAAO,IAAIP,GACpBe,IAAO,KAAK,MAAMR,EAAI,IAAIM,CAAE,GAC5BG,IAAM,KAAK,MAAMT,EAAI,IAAIO,CAAE,GAC3B,CAACG,GAAO,GAAGC,CAAI,IAAIP,EAAI;AAC7B,IAAAJ,EAAI,UAAUrB,EAAMC,GAAO8B,CAAM,GACjCV,EAAI,OAAO,KAAKM,IAAKI,EAAO,KAAKA,EAAO,IAAIH,IAAKG,EAAO,KAAKA,EAAO,CAAC;AACrE,UAAME,IAAM,CAAC/B,MAAoB;AAC/B,YAAMgC,IAAI,IAAIC,EAAOnC,EAAMC,GAAOC,CAAC,CAAC;AACpC,aAAAgC,EAAE,SAAS,IAAIL,IAAO3B,EAAE,GAAG4B,IAAM5B,EAAE,CAAC,GACpCgC,EAAE,SAASb,EAAI,QACfa,EAAE,UAAUb,EAAI,SAChBa,EAAE,QAAQb,EAAI,OACdG,EAAO,SAASU,CAAC,GACjB,KAAK,MAAM,KAAKA,CAAC,GACVA;AAAA,IACT;AACA,eAAWhC,KAAK8B,EAAM,CAAAC,EAAI/B,CAAC;AAC3B,eAAWkC,KAAMX,EAAI,OAAQ,MAAK,OAAO,KAAK,EAAE,QAAQQ,EAAIG,EAAG,IAAI,GAAG,OAAOP,IAAOO,EAAG,KAAK,GAAG,QAAQA,EAAG,QAAQ,KAAKA,EAAG,KAAK,QAAQ,EAAA,CAAG;AAAA,EAC5I;AAAA,EAnCmB;AAAA,EACA;AAAA,EAXF,QAAkB,CAAA;AAAA,EAClB,SAA2F,CAAA;AAAA,EAC3F;AAAA,EACA;AAAA,EACT;AAAA,EACA;AAAA;AAAA,EAEC;AAAA;AAAA,EAyCT,OAAOC,GAAcC,GAAeC,IAAU,IAAY;AACxD,QAAI,CAAC,KAAK,OAAQ;AAClB,UAAMC,IAAQ,KAAK,IAAI;AACvB,QAAIA,MAAU,KAAK,OAAO;AACxB,WAAK,QAAQA;AACb,iBAAWC,KAAK,KAAK,MAAO,CAAAA,EAAE,UAAUD;AAAA,IAC1C;AACA,QAAI,CAACA,EAAO;AACZ,QAAI,KAAK,IAAI,UAAU,KAAK,OAAO;AACjC,WAAK,QAAQ,KAAK,IAAI;AACtB,iBAAWC,KAAK,KAAK,MAAO,CAAAA,EAAE,QAAQ,KAAK;AAAA,IAC7C;AACA,UAAMC,IAAQ,CAACH,KAAWD,EAAI,QAAQ,GAChCK,IAAOD,IAAQ,IAAIE,EAAOP,GAAM,KAAK,IAAI,GAAGC,EAAI,aAAaA,EAAI,MAAM;AAC7E,eAAWF,KAAM,KAAK,QAAQ;AAC5B,YAAMS,IAAMH,IAAQ,IAAII,EAAWT,GAAM,KAAK,OAAO,KAAK,OAAOD,EAAG,KAAK,KAAK,IAAI,KAAKE,EAAI,MAAMK,GAAMP,EAAG,QAAQE,EAAI,YAAY,CAAC;AACnI,MAAIO,MAAQT,EAAG,WACfA,EAAG,SAASS,GACZT,EAAG,OAAO,IAAIA,EAAG,QAAQS;AAAA,IAC3B;AAAA,EACF;AAAA,EAEA,UAAgB;AACd,eAAWJ,KAAK,KAAK,MAAO,CAAAA,EAAE,QAAA;AAC9B,SAAK,MAAM,SAAS,GACpB,KAAK,OAAO,SAAS;AAAA,EACvB;AACF;"}
|
package/dist/pixel/lights.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lights.js","sources":["../../src/pixel/lights.ts"],"sourcesContent":["/*\n * Short-lived light: what a fight throws into the dark — a fireball or a bolt in flight, a blast,\n * a blow landing. The map's own darkness (`Darkness`: the underground, a cave, the outdoor night) cuts each\n * one out of its sheet like a lamp, so a spell lights the corridor it flies down. Looks only, client-only; one list\n * for the whole client, since only one map is ever drawn. `engineSettings.combatLights` turns them off, and so does\n * `engineSettings.batterySaver`: the list still keeps time, but nothing reads it, so the dark stays as the map's lamps leave it.\n */\n\nimport { engineSettings } from '../settings';\n\nexport interface TransientLight {\n x: number;\n y: number;\n /** Radius at its brightest, world px. */\n r: number;\n born: number;\n /** When it is gone; a carried light's is pushed to `CARRIED_MAX_MS` and brought in by its `end()`. */\n until: number;\n /** A light riding a moving thing reads its place every frame. */\n follow?: () => { x: number; y: number };\n /** A flash shrinks as it goes out; a carried light keeps its size. */\n fades: boolean;\n /** The colour it lays on what it lights: a fireball's orange, a spell's violet, a blade's white. */\n color: number;\n}\n\n/** A light nobody named a colour for: the pale gold of a spark. */\nconst SPARK = 0xfff0c8;\n\nconst LIGHTS: TransientLight[] = [];\n/** More than this at once adds nothing a player can see, and each is a sprite in the night's redraw. */\nconst MAX_LIGHTS = 48;\n\n/** A flash at (x, y) in `color`: `r` px at once, shrinking to nothing over `ms`. */\nexport function flashLight(x: number, y: number, r: number, ms: number, color = SPARK, now = performance.now()): void {\n if (LIGHTS.length >= MAX_LIGHTS) LIGHTS.shift();\n LIGHTS.push({ x, y, r, born: now, until: now + ms, fades: true, color });\n}\n\n/** The longest a carried light burns when nothing ends it: no shot flies, nor any spell is held, that long. */\nconst CARRIED_MAX_MS = 20_000;\n\n/** A light that rides `at` until `end()` is called (a shot in flight), and never past `CARRIED_MAX_MS`. */\nexport function carriedLight(at: () => { x: number; y: number }, r: number, color = SPARK, now = performance.now()): { end: () => void } {\n const p = at();\n const light: TransientLight = { x: p.x, y: p.y, r, born: now, until: now + CARRIED_MAX_MS, follow: at, fades: false, color };\n if (LIGHTS.length >= MAX_LIGHTS) LIGHTS.shift();\n LIGHTS.push(light);\n return { end: () => (light.until = 0) };\n}\n\nconst NONE: readonly TransientLight[] = [];\n\n/** The lights burning at `now`, each with its radius then; spent ones are dropped. None while combat lights are off. */\nexport function liveLights(now: number): readonly TransientLight[] {\n for (let i = LIGHTS.length - 1; i >= 0; i--) if (LIGHTS[i]!.until <= now) LIGHTS.splice(i, 1);\n const settings = engineSettings.getState();\n // the battery saver puts the transient lights out too: each one redraws the night\n if (settings.combatLights === false || settings.batterySaver) return NONE;\n for (const l of LIGHTS) {\n if (!l.follow) continue;\n // a light whose rider has gone is put out, never left to throw in every frame's night\n let p: { x: number; y: number };\n try {\n p = l.follow();\n } catch {\n l.until = 0;\n l.follow = undefined;\n continue;\n }\n l.x = p.x;\n l.y = p.y;\n }\n return LIGHTS;\n}\n\n/** The radius `l` burns at `now`: a flash eases out from its full size, a carried light holds it. */\nexport function lightRadius(l: TransientLight, now: number): number {\n if (!l.fades) return l.r;\n const t = Math.min(1, (now - l.born) / Math.max(1, l.until - l.born));\n return l.r * Math.sqrt(1 - t);\n}\n\n/** A new map: nothing of the last one's fight is left burning. */\nexport function clearLights(): void {\n LIGHTS.length = 0;\n}\n"],"names":["SPARK","LIGHTS","MAX_LIGHTS","flashLight","x","y","r","ms","color","now","CARRIED_MAX_MS","carriedLight","at","p","light","NONE","liveLights","i","settings","engineSettings","l","lightRadius","clearLights"],"mappings":";AA2BA,MAAMA,IAAQ,UAERC,IAA2B,CAAA,GAE3BC,IAAa;AAGZ,SAASC,EAAWC,GAAWC,GAAWC,GAAWC,GAAYC,IAAQR,GAAOS,IAAM,YAAY,OAAa;AACpH,EAAIR,EAAO,UAAUC,KAAYD,EAAO,MAAA,GACxCA,EAAO,KAAK,EAAE,GAAAG,GAAG,GAAAC,GAAG,GAAAC,GAAG,MAAMG,GAAK,OAAOA,IAAMF,GAAI,OAAO,IAAM,OAAAC,GAAO;AACzE;AAGA,MAAME,IAAiB;AAGhB,SAASC,EAAaC,GAAoCN,GAAWE,IAAQR,GAAOS,IAAM,YAAY,OAA4B;AACvI,QAAMI,IAAID,EAAA,GACJE,IAAwB,EAAE,GAAGD,EAAE,GAAG,GAAGA,EAAE,GAAG,GAAAP,GAAG,MAAMG,GAAK,OAAOA,IAAMC,GAAgB,QAAQE,GAAI,OAAO,IAAO,OAAAJ,EAAA;AACrH,SAAIP,EAAO,UAAUC,KAAYD,EAAO,MAAA,GACxCA,EAAO,KAAKa,CAAK,GACV,EAAE,KAAK,MAAOA,EAAM,QAAQ,EAAA;AACrC;AAEA,MAAMC,IAAkC,CAAA;AAGjC,SAASC,EAAWP,GAAwC;AACjE,WAASQ,IAAIhB,EAAO,SAAS,GAAGgB,KAAK,GAAGA,IAAK,CAAIhB,EAAOgB,CAAC,EAAG,SAASR,KAAKR,EAAO,OAAOgB,GAAG,CAAC;AAC5F,QAAMC,IAAWC,EAAe,SAAA;AAEhC,MAAID,EAAS,iBAAiB,MAASA,EAAS,aAAc,QAAOH;AACrE,aAAWK,KAAKnB,GAAQ;AACtB,QAAI,CAACmB,EAAE,OAAQ;AAEf,QAAIP;AACJ,QAAI;AACF,MAAAA,IAAIO,EAAE,OAAA;AAAA,IACR,QAAQ;AACN,MAAAA,EAAE,QAAQ,GACVA,EAAE,SAAS;AACX;AAAA,IACF;AACA,IAAAA,EAAE,IAAIP,EAAE,GACRO,EAAE,IAAIP,EAAE;AAAA,EACV;AACA,SAAOZ;AACT;AAGO,SAASoB,EAAYD,GAAmBX,GAAqB;AAClE,MAAI,CAACW,EAAE,MAAO,QAAOA,EAAE;AACvB,QAAM,IAAI,KAAK,IAAI,IAAIX,IAAMW,EAAE,QAAQ,KAAK,IAAI,GAAGA,EAAE,QAAQA,EAAE,IAAI,CAAC;AACpE,SAAOA,EAAE,IAAI,KAAK,KAAK,IAAI,CAAC;AAC9B;AAGO,SAASE,IAAoB;AAClC,EAAArB,EAAO,SAAS;AAClB;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"nineSlice.js","sources":["../../src/pixel/nineSlice.ts"],"sourcesContent":["import { CLEAR, PixelGrid, channels, hex, type Color } from './pixel';\n\n/**\n * Pixel-art 9-slice frames for a UI (windows, buttons, fields, wells), built from one drawn corner so the edge is\n * whole pixels instead of CSS gradients and blurred shadows. The corner is ASCII, one letter a role; the other three\n * corners are its mirrors, and on the far side (right and bottom) each role takes its **twin** — a raised frame's lit\n * rim (H) becomes its dark rim (L), a sunk field's shadowed lip (D) its lit one (d) — so light comes from the up-left\n * without drawing four corners. A hard shadow (the frame's own silhouette, shifted) can lie under it.\n *\n * ```ts\n * const frame = nineSlice({ corner: FRAME_CORNERS.panel, palette: { K: '#0b1018', H: '#c9a869', L: '#4a3a24',\n * M: '#8b6e45', G: '#121820', S: '#3c434b', s: '#161b22', F: '#1d242e', R: '#ffe2a0' }, shadow: [2, 2] });\n * frame.grid.toCanvas(); // a canvas for a Pixi NineSliceSprite, with frame.insets\n * frameSvg(frame.grid, 2); // or an SVG for a CSS border-image, sliced at insets × 2\n * ```\n */\n\n/** Ready corners in the roles the default twins know: K ink, H/L lit/dark rim, M/m rim, G/g gap, S/s bevel, F fill, D/d lip, R rivet. */\nexport const FRAME_CORNERS = {\n /** A window: ink, a two-step brass rim, a gap, a lit bevel, a rivet in the corner. */\n panel: ['..KKKKKK', '.KHHHHHH', 'KHHMMMMM', 'KHMKGGGG', 'KHMGRSSS', 'KHMGSFFF', 'KHMGSFFF', 'KHMGSFFF'],\n /** A raised button: a two-step bevel, notched. Give it shadow [0, 1]; pressed, offset [0, 1] with the bevel's colours swapped. */\n button: ['.KKKK', 'KHHHH', 'KHMMM', 'KHMFF', 'KHMFF'],\n /** A small raised button. */\n smallButton: ['.KKK', 'KHHH', 'KHMM', 'KHMF'],\n /** A text field: a hole in the stone, its lip in shadow top-left (D, G) and lit bottom-right (d, g). */\n field: ['.KKKK', 'KDDDD', 'KDGGG', 'KDGFF', 'KDGFF'],\n /** A sunk well (slots, bars' tracks). */\n well: ['.KK', 'KDD', 'KDF'],\n /** A list row: a flat slab lit on the top and left. */\n row: ['.KK', 'KSS', 'KSF'],\n /** A thin frame: one pixel of rim (toasts, a cast bar). */\n thin: ['.KK', 'KHH', 'KHF'],\n} as const satisfies Record<string, readonly string[]>;\n\n/** The role each letter takes on the far side; a palette without the twin keeps the letter. */\nexport const FRAME_TWINS: Readonly<Record<string, string>> = { H: 'L', S: 's', M: 'm', D: 'd', G: 'g' };\n\nexport interface NineSliceSpec {\n /** The top-left corner, square, one letter a role; '.' is nothing. */\n corner: readonly string[];\n /** A colour per letter: a packed `Color` or a CSS hex (`#rgb`, `#rrggbb`, `#rrggbbaa` for see-through fills). */\n palette: Readonly<Record<string, Color | string>>;\n /** Near-side letter → far-side letter (default `FRAME_TWINS`). */\n twins?: Readonly<Record<string, string>>;\n /** The hard shadow's offset in art pixels (default none). */\n shadow?: readonly [number, number];\n /** The shadow's colour (default palette `Z`, else black at 55%). */\n shadowColor?: Color | string;\n /** Moves the frame inside its canvas (a pressed button sits one pixel lower where its shadow was). */\n offset?: readonly [number, number];\n /** Room right and below for the shadow or the offset (default: the shadow). */\n pad?: readonly [number, number];\n /** Rows painted over the frame, the frame's full size ('.' leaves it): a tick in a checkbox. */\n overlay?: readonly string[];\n}\n\n/** How far in from each side the corners reach: the frame's slice lines, in art pixels. */\nexport interface SliceInsets {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\nexport interface NineSlice {\n grid: PixelGrid;\n insets: SliceInsets;\n}\n\nconst toColor = (c: Color | string): Color => (typeof c === 'string' ? hex(c) : c);\n\n/** The frame's letter at (x, y) of the n×n square mirrored out of `corner`, '.' outside it. */\nfunction roleAt(corner: readonly string[], palette: NineSliceSpec['palette'], twins: Readonly<Record<string, string>>, x: number, y: number): string {\n const c = corner.length;\n const n = c * 2 + 1;\n const right = x > c;\n const bottom = y > c;\n const cx = right ? n - 1 - x : x;\n const cy = bottom ? n - 1 - y : y;\n const r = corner[Math.min(cy, c - 1)]![Math.min(cx, c - 1)] ?? '.';\n // which edge the cell belongs to in the corner's own coordinates: above the diagonal the top (or bottom) edge\n const onHorizontal = cx >= cy;\n const far = (right && !onHorizontal) || (bottom && onHorizontal) || (right && bottom);\n const twin = twins[r];\n return far && twin && palette[twin] !== undefined ? twin : r;\n}\n\n/** Builds the frame: a `(2c + 1 + pad)` square-ish grid and its slice insets. */\nexport function nineSlice(spec: NineSliceSpec): NineSlice {\n const { corner, palette } = spec;\n const c = corner.length;\n if (!c || corner.some((row) => row.length !== c)) throw new Error('nineSlice: the corner must be square');\n const twins = spec.twins ?? FRAME_TWINS;\n const n = c * 2 + 1;\n const [sx, sy] = spec.shadow ?? [0, 0];\n const [ox, oy] = spec.offset ?? [0, 0];\n const [px, py] = spec.pad ?? [sx, sy];\n const grid = new PixelGrid(n + px, n + py);\n const shade = spec.shadowColor ?? palette.Z ?? 0x8c000000;\n if (sx || sy) {\n const z = toColor(shade);\n for (let y = 0; y < n; y++) for (let x = 0; x < n; x++) if (roleAt(corner, palette, twins, x, y) !== '.') grid.px(x + sx, y + sy, z);\n }\n for (let y = 0; y < n; y++)\n for (let x = 0; x < n; x++) {\n const over = spec.overlay?.[y]?.[x];\n const r = over && over !== '.' ? over : roleAt(corner, palette, twins, x, y);\n const col = palette[r];\n if (r !== '.' && col !== undefined) grid.px(x + ox, y + oy, toColor(col));\n }\n return { grid, insets: { top: c, left: c, right: c + px, bottom: c + py } };\n}\n\n/**\n * Any pixel grid as an SVG of `scale`-sized squares (`shape-rendering=\"crispEdges\"`), runs of one colour on a row\n * merged into one rect: what a CSS `border-image` takes, sliced at the insets × `scale`.\n */\nexport function frameSvg(grid: PixelGrid, scale = 2): string {\n const rects: string[] = [];\n for (let y = 0; y < grid.h; y++) {\n let x = 0;\n while (x < grid.w) {\n const c = grid.get(x, y);\n let end = x + 1;\n while (end < grid.w && grid.get(end, y) === c) end++;\n if (c !== CLEAR && c >>> 24 > 0) {\n const [r, g, b, a] = channels(c);\n const fill = `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;\n const op = a < 255 ? ` fill-opacity=\"${+(a / 255).toFixed(3)}\"` : '';\n rects.push(`<rect x=\"${x * scale}\" y=\"${y * scale}\" width=\"${(end - x) * scale}\" height=\"${scale}\" fill=\"${fill}\"${op}/>`);\n }\n x = end;\n }\n }\n const w = grid.w * scale;\n const h = grid.h * scale;\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${w}\" height=\"${h}\" viewBox=\"0 0 ${w} ${h}\" shape-rendering=\"crispEdges\">${rects.join('')}</svg>\\n`;\n}\n"],"names":["FRAME_CORNERS","FRAME_TWINS","toColor","c","hex","roleAt","corner","palette","twins","x","y","n","right","bottom","cx","cy","r","onHorizontal","far","twin","nineSlice","spec","row","sx","sy","ox","oy","px","py","grid","PixelGrid","shade","z","over","col","frameSvg","scale","rects","end","CLEAR","g","b","a","channels","fill","op","w","h"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"paintLayer.js","sources":["../../src/pixel/paintLayer.ts"],"sourcesContent":["import type { Graphics } from '../core/Graphics';\nimport type { Stage } from '../core/Stage';\nimport { inView } from '../fx/viewCull';\nimport { addPixelLayer, type PixelLayer } from './PixelLayer';\nimport type { Point } from './pixelPaint';\nimport { beginBatch, endBatch, setCoverage } from './pixelTone';\n\nexport interface PaintLayerOptions {\n /** Where the layer stands (world px), read every frame: a caster, a projectile, a fixed point. */\n at: () => Point;\n /** How long it plays (ms); its last `fadeMs` dither away instead of popping out. */\n duration: number;\n /** Paints one frame `ms` into the effect, in pixels relative to the anchor. */\n paint: (g: Graphics, ms: number) => void;\n /**\n * Its draw order from the anchor's y (`y => y + 45` puts it in front of a body standing there, a negative offset\n * behind it), or a fixed number (an effect lying on the ground under every body). Default `y + 45`.\n */\n depth?: number | ((y: number) => number);\n /** Returning false removes the layer early (its caster gone, its trap sprung). */\n alive?: () => boolean;\n /** Frames a second it is redrawn at (default 12: hand-animated, like the sprites). */\n fps?: number;\n /** Paint only every that many frames (an effect thinned out for performance). Default 1. */\n every?: number;\n /** The longest fade at the end (ms); it is never more than 30% of the effect. Default 260. */\n fadeMs?: number;\n /** Told of the layer as it is made (a game that clears its transient effects on a map change keeps it). */\n onCreate?: (g: PixelLayer) => void;\n}\n\n/** How far outside the screen a layer's anchor may stand and still be painted, beyond what it has reached so far. */\nconst CULL_MARGIN = 64;\n/**\n * The reach a layer is culled by is never less than this: an effect that grows (a ring, a blast) paints a small first\n * frame, and culled by that one it would never paint again as it swept onto the screen.\n */\nconst CULL_REACH_FLOOR = 192;\n\n/**\n * One effect as its own `PixelLayer`, redrawn at `fps` and shown as a single texture: it follows its anchor to the\n * screen pixel while the art inside stays on the world grid, thins out pixel by pixel over its last frames (ordered\n * dither, `setCoverage`), is neither painted nor uploaded while its anchor is off the screen by more than it has\n * reached, and is destroyed when it ends, when `alive` says so, or with the stage's tweens.\n */\nexport function paintLayer(stage: Stage, o: PaintLayerOptions): PixelLayer {\n const { at, duration, paint, alive } = o;\n const depth = o.depth ?? ((y: number) => y + 45);\n const frameMs = 1000 / (o.fps ?? 12);\n const every = o.every ?? 1;\n const fadeMs = Math.min(o.fadeMs ?? 260, duration * 0.3);\n const g = addPixelLayer(stage);\n o.onCreate?.(g);\n const clock = { elapsed: 0 };\n let previous = -1;\n let reached = CULL_REACH_FLOOR;\n const cam = stage.cameras.main;\n const update = () => {\n if (alive && !alive()) {\n g.destroy();\n return;\n }\n const p = at();\n g.setPosition(cam.snap(p.x), cam.snap(p.y)).setDepth(typeof depth === 'number' ? depth : depth(p.y));\n let frame = Math.floor(clock.elapsed / frameMs);\n if (every > 1) frame -= frame % every;\n if (frame === previous) return;\n if (previous >= 0 && !inView(cam, p.x, p.y, CULL_MARGIN + Math.max(reached, g.reach()))) return;\n previous = frame;\n g.clear();\n const ms = Math.min(duration, frame * frameMs);\n setCoverage(fadeMs > 0 && ms > duration - fadeMs ? Math.max(0, (duration - ms) / fadeMs) * 0.85 + 0.15 : 1);\n beginBatch(g);\n try {\n paint(g, ms);\n } finally {\n endBatch();\n setCoverage(1);\n }\n g.present();\n reached = Math.max(reached, g.reach());\n };\n update();\n if (!g.active) return g;\n const tween = stage.tweens.add({ targets: clock, elapsed: duration, duration, onUpdate: update, onComplete: () => g.destroy() });\n g.once('destroyed', () => tween.remove());\n return g;\n}\n"],"names":["CULL_MARGIN","CULL_REACH_FLOOR","paintLayer","stage","o","at","duration","paint","alive","depth","y","frameMs","every","fadeMs","g","addPixelLayer","clock","previous","reached","cam","update","p","frame","inView","ms","setCoverage","beginBatch","endBatch","tween"],"mappings":";;;AAgCA,MAAMA,IAAc,IAKdC,IAAmB;AAQlB,SAASC,EAAWC,GAAcC,GAAkC;AACzE,QAAM,EAAE,IAAAC,GAAI,UAAAC,GAAU,OAAAC,GAAO,OAAAC,MAAUJ,GACjCK,IAAQL,EAAE,UAAU,CAACM,MAAcA,IAAI,KACvCC,IAAU,OAAQP,EAAE,OAAO,KAC3BQ,IAAQR,EAAE,SAAS,GACnBS,IAAS,KAAK,IAAIT,EAAE,UAAU,KAAKE,IAAW,GAAG,GACjDQ,IAAIC,EAAcZ,CAAK;AAC7B,EAAAC,EAAE,WAAWU,CAAC;AACd,QAAME,IAAQ,EAAE,SAAS,EAAA;AACzB,MAAIC,IAAW,IACXC,IAAUjB;AACd,QAAMkB,IAAMhB,EAAM,QAAQ,MACpBiB,IAAS,MAAM;AACnB,QAAIZ,KAAS,CAACA,KAAS;AACrB,MAAAM,EAAE,QAAA;AACF;AAAA,IACF;AACA,UAAMO,IAAIhB,EAAA;AACV,IAAAS,EAAE,YAAYK,EAAI,KAAKE,EAAE,CAAC,GAAGF,EAAI,KAAKE,EAAE,CAAC,CAAC,EAAE,SAAS,OAAOZ,KAAU,WAAWA,IAAQA,EAAMY,EAAE,CAAC,CAAC;AACnG,QAAIC,IAAQ,KAAK,MAAMN,EAAM,UAAUL,CAAO;AAG9C,QAFIC,IAAQ,MAAGU,KAASA,IAAQV,IAC5BU,MAAUL,KACVA,KAAY,KAAK,CAACM,EAAOJ,GAAKE,EAAE,GAAGA,EAAE,GAAGrB,IAAc,KAAK,IAAIkB,GAASJ,EAAE,MAAA,CAAO,CAAC,EAAG;AACzF,IAAAG,IAAWK,GACXR,EAAE,MAAA;AACF,UAAMU,IAAK,KAAK,IAAIlB,GAAUgB,IAAQX,CAAO;AAC7C,IAAAc,EAAYZ,IAAS,KAAKW,IAAKlB,IAAWO,IAAS,KAAK,IAAI,IAAIP,IAAWkB,KAAMX,CAAM,IAAI,OAAO,OAAO,CAAC,GAC1Ga,EAAWZ,CAAC;AACZ,QAAI;AACF,MAAAP,EAAMO,GAAGU,CAAE;AAAA,IACb,UAAA;AACE,MAAAG,EAAA,GACAF,EAAY,CAAC;AAAA,IACf;AACA,IAAAX,EAAE,QAAA,GACFI,IAAU,KAAK,IAAIA,GAASJ,EAAE,OAAO;AAAA,EACvC;AAEA,MADAM,EAAA,GACI,CAACN,EAAE,OAAQ,QAAOA;AACtB,QAAMc,IAAQzB,EAAM,OAAO,IAAI,EAAE,SAASa,GAAO,SAASV,GAAU,UAAAA,GAAU,UAAUc,GAAQ,YAAY,MAAMN,EAAE,QAAA,GAAW;AAC/H,SAAAA,EAAE,KAAK,aAAa,MAAMc,EAAM,QAAQ,GACjCd;AACT;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"palette.js","sources":["../../src/pixel/palette.ts"],"sourcesContent":["import { hex } from './pixel';\n\n/**\n * The default palette (`PALETTE` from `1681-engine/pixel`): warm fantasy tones, readable on green ground. It is a\n * starting point, not a rule: a game brings its own colours, and the helpers that use one of these (an outline, a\n * shadow) take the colour as a parameter.\n */\nexport const P = {\n outline: hex('#1a1220'),\n outlineSoft: hex('#2e2233'),\n black: hex('#0e0b12'),\n white: hex('#f4f0e8'),\n offWhite: hex('#e2dccf'),\n\n skin: hex('#f2c59a'),\n skinShade: hex('#d99d74'),\n skinTan: hex('#cf9468'),\n skinTanShade: hex('#a86c48'),\n blush: hex('#e88c80'),\n\n hairBlack: hex('#2a2230'),\n hairBrown: hex('#5b3a29'),\n hairAuburn: hex('#7a3b2a'),\n hairWhite: hex('#e8e4dc'),\n hairGrey: hex('#9a96a0'),\n\n red: hex('#c8373a'),\n redDark: hex('#8e2331'),\n crimson: hex('#a3243b'),\n orange: hex('#e07a36'),\n gold: hex('#f2c84b'),\n goldDark: hex('#b88a2a'),\n yellow: hex('#f5e06a'),\n green: hex('#5aa04a'),\n greenDark: hex('#2f6b3a'),\n jade: hex('#4fbf8f'),\n jadeDark: hex('#2d8466'),\n teal: hex('#3aa6a0'),\n sky: hex('#7ec4f0'),\n azure: hex('#4a8ad4'),\n blue: hex('#3560b0'),\n navy: hex('#27335e'),\n indigo: hex('#3b2f6e'),\n purple: hex('#7a4bb0'),\n pink: hex('#e89aaa'),\n brown: hex('#7a5234'),\n brownDark: hex('#4e3222'),\n brownLight: hex('#a6784a'),\n tan: hex('#c9a26e'),\n cream: hex('#efe2c0'),\n grey: hex('#8a8690'),\n greyDark: hex('#57535e'),\n greyLight: hex('#bdb9c2'),\n steel: hex('#c8d2dc'),\n steelDark: hex('#7f8a98'),\n iron: hex('#9aa0a8'),\n wood: hex('#9a6a3c'),\n woodDark: hex('#6a4426'),\n woodLight: hex('#bd8c56'),\n straw: hex('#d8b060'),\n strawDark: hex('#a8823e'),\n} as const;\n"],"names":["P","hex"],"mappings":";AAOO,MAAMA,IAAI;AAAA,EACf,SAASC,EAAI,SAAS;AAAA,EACtB,aAAaA,EAAI,SAAS;AAAA,EAC1B,OAAOA,EAAI,SAAS;AAAA,EACpB,OAAOA,EAAI,SAAS;AAAA,EACpB,UAAUA,EAAI,SAAS;AAAA,EAEvB,MAAMA,EAAI,SAAS;AAAA,EACnB,WAAWA,EAAI,SAAS;AAAA,EACxB,SAASA,EAAI,SAAS;AAAA,EACtB,cAAcA,EAAI,SAAS;AAAA,EAC3B,OAAOA,EAAI,SAAS;AAAA,EAEpB,WAAWA,EAAI,SAAS;AAAA,EACxB,WAAWA,EAAI,SAAS;AAAA,EACxB,YAAYA,EAAI,SAAS;AAAA,EACzB,WAAWA,EAAI,SAAS;AAAA,EACxB,UAAUA,EAAI,SAAS;AAAA,EAEvB,KAAKA,EAAI,SAAS;AAAA,EAClB,SAASA,EAAI,SAAS;AAAA,EACtB,SAASA,EAAI,SAAS;AAAA,EACtB,QAAQA,EAAI,SAAS;AAAA,EACrB,MAAMA,EAAI,SAAS;AAAA,EACnB,UAAUA,EAAI,SAAS;AAAA,EACvB,QAAQA,EAAI,SAAS;AAAA,EACrB,OAAOA,EAAI,SAAS;AAAA,EACpB,WAAWA,EAAI,SAAS;AAAA,EACxB,MAAMA,EAAI,SAAS;AAAA,EACnB,UAAUA,EAAI,SAAS;AAAA,EACvB,MAAMA,EAAI,SAAS;AAAA,EACnB,KAAKA,EAAI,SAAS;AAAA,EAClB,OAAOA,EAAI,SAAS;AAAA,EACpB,MAAMA,EAAI,SAAS;AAAA,EACnB,MAAMA,EAAI,SAAS;AAAA,EACnB,QAAQA,EAAI,SAAS;AAAA,EACrB,QAAQA,EAAI,SAAS;AAAA,EACrB,MAAMA,EAAI,SAAS;AAAA,EACnB,OAAOA,EAAI,SAAS;AAAA,EACpB,WAAWA,EAAI,SAAS;AAAA,EACxB,YAAYA,EAAI,SAAS;AAAA,EACzB,KAAKA,EAAI,SAAS;AAAA,EAClB,OAAOA,EAAI,SAAS;AAAA,EACpB,MAAMA,EAAI,SAAS;AAAA,EACnB,UAAUA,EAAI,SAAS;AAAA,EACvB,WAAWA,EAAI,SAAS;AAAA,EACxB,OAAOA,EAAI,SAAS;AAAA,EACpB,WAAWA,EAAI,SAAS;AAAA,EACxB,MAAMA,EAAI,SAAS;AAAA,EACnB,MAAMA,EAAI,SAAS;AAAA,EACnB,UAAUA,EAAI,SAAS;AAAA,EACvB,WAAWA,EAAI,SAAS;AAAA,EACxB,OAAOA,EAAI,SAAS;AAAA,EACpB,WAAWA,EAAI,SAAS;AAC1B;"}
|
package/dist/pixel/pixel.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pixel.js","sources":["../../src/pixel/pixel.ts"],"sourcesContent":["/*\n * Minimal pixel-art canvas. All game art is painted procedurally with these primitives\n * (no external/copyrighted assets). Colors are packed little-endian RGBA (ImageData layout).\n */\n\nexport type Color = number;\n\nexport const CLEAR: Color = 0;\n\nexport function rgba(r: number, g: number, b: number, a = 255): Color {\n const R = Math.max(0, Math.min(255, Math.round(r)));\n const G = Math.max(0, Math.min(255, Math.round(g)));\n const B = Math.max(0, Math.min(255, Math.round(b)));\n const A = Math.max(0, Math.min(255, Math.round(a)));\n return ((A << 24) | (B << 16) | (G << 8) | R) >>> 0;\n}\n\nexport function hex(s: string): Color {\n let h = s.startsWith('#') ? s.slice(1) : s;\n if (h.length === 3) h = [...h].map((c) => c + c).join('');\n const r = parseInt(h.slice(0, 2), 16);\n const g = parseInt(h.slice(2, 4), 16);\n const b = parseInt(h.slice(4, 6), 16);\n const a = h.length >= 8 ? parseInt(h.slice(6, 8), 16) : 255;\n return rgba(r, g, b, a);\n}\n\nexport function channels(c: Color): [number, number, number, number] {\n return [c & 255, (c >>> 8) & 255, (c >>> 16) & 255, (c >>> 24) & 255];\n}\n\n/** f < 1 darkens, f > 1 lightens toward white. */\nexport function shade(c: Color, f: number): Color {\n const [r, g, b, a] = channels(c);\n if (f >= 1) {\n const t = Math.min(1, f - 1);\n return rgba(r + (255 - r) * t, g + (255 - g) * t, b + (255 - b) * t, a);\n }\n return rgba(r * f, g * f, b * f, a);\n}\n\nexport function mix(c1: Color, c2: Color, t: number): Color {\n const a = channels(c1);\n const b = channels(c2);\n return rgba(a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t, a[3] + (b[3] - a[3]) * t);\n}\n\nexport function withAlpha(c: Color, alpha: number): Color {\n const [r, g, b] = channels(c);\n return rgba(r, g, b, alpha);\n}\n\nexport function toCss(c: Color): string {\n const [r, g, b, a] = channels(c);\n return `rgba(${r},${g},${b},${(a / 255).toFixed(3)})`;\n}\n\nexport function hash2(x: number, y: number, seed = 0): number {\n let h = (x * 374761393 + y * 668265263 + seed * 2147483647) | 0;\n h = Math.imul(h ^ (h >>> 13), 1274126177);\n return ((h ^ (h >>> 16)) >>> 0) / 4294967296;\n}\n\nexport function mulberry32(seed: number): () => number {\n let a = seed >>> 0;\n return () => {\n a = (a + 0x6d2b79f5) >>> 0;\n let t = a;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\nexport function valueNoise(x: number, y: number, scale: number, seed: number): number {\n const fx = x / scale;\n const fy = y / scale;\n const x0 = Math.floor(fx);\n const y0 = Math.floor(fy);\n const tx = fx - x0;\n const ty = fy - y0;\n const sx = tx * tx * (3 - 2 * tx);\n const sy = ty * ty * (3 - 2 * ty);\n const a = hash2(x0, y0, seed);\n const b = hash2(x0 + 1, y0, seed);\n const c = hash2(x0, y0 + 1, seed);\n const d = hash2(x0 + 1, y0 + 1, seed);\n return a + (b - a) * sx + (c - a) * sy + (a - b - c + d) * sx * sy;\n}\n\n\n\n\nexport class PixelGrid {\n readonly data: Uint32Array;\n\n constructor(\n readonly w: number,\n readonly h: number,\n ) {\n this.data = new Uint32Array(w * h);\n }\n\n inBounds(x: number, y: number): boolean {\n return x >= 0 && y >= 0 && x < this.w && y < this.h;\n }\n\n px(x: number, y: number, c: Color): void {\n x = Math.floor(x);\n y = Math.floor(y);\n if (x >= 0 && y >= 0 && x < this.w && y < this.h) this.data[y * this.w + x] = c;\n }\n\n get(x: number, y: number): Color {\n if (x < 0 || y < 0 || x >= this.w || y >= this.h) return CLEAR;\n return this.data[y * this.w + x] ?? CLEAR;\n }\n\n alphaAt(x: number, y: number): number {\n return this.get(x, y) >>> 24;\n }\n\n rect(x: number, y: number, w: number, h: number, c: Color): void {\n for (let yy = Math.floor(y); yy < y + h; yy++) for (let xx = Math.floor(x); xx < x + w; xx++) this.px(xx, yy, c);\n }\n\n hline(x0: number, x1: number, y: number, c: Color): void {\n for (let x = Math.min(x0, x1); x <= Math.max(x0, x1); x++) this.px(x, y, c);\n }\n\n vline(x: number, y0: number, y1: number, c: Color): void {\n for (let y = Math.min(y0, y1); y <= Math.max(y0, y1); y++) this.px(x, y, c);\n }\n\n /** Filled ellipse; a pixel is inside when its center lies inside the ellipse. */\n ellipse(cx: number, cy: number, rx: number, ry: number, c: Color): void {\n if (rx <= 0 || ry <= 0) return;\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 const dx = (x + 0.5 - cx) / rx;\n const dy = (y + 0.5 - cy) / ry;\n if (dx * dx + dy * dy <= 1) this.px(x, y, c);\n }\n }\n\n circle(cx: number, cy: number, r: number, c: Color): void {\n this.ellipse(cx, cy, r, r, c);\n }\n\n ring(cx: number, cy: number, r: number, thickness: number, c: Color): void {\n for (let y = Math.floor(cy - r - 1); y <= Math.ceil(cy + r + 1); y++)\n for (let x = Math.floor(cx - r - 1); x <= Math.ceil(cx + r + 1); x++) {\n const d = Math.hypot(x + 0.5 - cx, y + 0.5 - cy);\n if (d <= r && d >= r - thickness) this.px(x, y, c);\n }\n }\n\n line(x0: number, y0: number, x1: number, y1: number, c: Color, thickness = 1): void {\n x0 = Math.round(x0);\n y0 = Math.round(y0);\n x1 = Math.round(x1);\n y1 = Math.round(y1);\n const dx = Math.abs(x1 - x0);\n const dy = -Math.abs(y1 - y0);\n const sx = x0 < x1 ? 1 : -1;\n const sy = y0 < y1 ? 1 : -1;\n let err = dx + dy;\n for (;;) {\n if (thickness <= 1) this.px(x0, y0, c);\n else this.rect(x0 - Math.floor((thickness - 1) / 2), y0 - Math.floor((thickness - 1) / 2), thickness, thickness, c);\n if (x0 === x1 && y0 === y1) break;\n const e2 = 2 * err;\n if (e2 >= dy) {\n err += dy;\n x0 += sx;\n }\n if (e2 <= dx) {\n err += dx;\n y0 += sy;\n }\n }\n }\n\n /** Filled polygon (even-odd scanline on pixel centers). */\n poly(points: [number, number][], c: Color): void {\n if (points.length < 3) return;\n const minY = Math.floor(Math.min(...points.map((p) => p[1])));\n const maxY = Math.ceil(Math.max(...points.map((p) => p[1])));\n for (let y = minY; y <= maxY; y++) {\n const sy = y + 0.5;\n const xs: number[] = [];\n for (let i = 0; i < points.length; i++) {\n const a = points[i]!;\n const b = points[(i + 1) % points.length]!;\n if ((a[1] <= sy && b[1] > sy) || (b[1] <= sy && a[1] > sy)) {\n xs.push(a[0] + ((sy - a[1]) / (b[1] - a[1])) * (b[0] - a[0]));\n }\n }\n xs.sort((p, q) => p - q);\n for (let i = 0; i + 1 < xs.length; i += 2) {\n for (let x = Math.ceil(xs[i]! - 0.5); x <= Math.floor(xs[i + 1]! - 0.5); x++) this.px(x, y, c);\n }\n }\n }\n\n /**\n * Adds `width` pixels of outline around all opaque pixels, one ring at a time — the same result as calling it\n * `width` times, but each ring only visits the neighbours of the ring before it instead of the whole grid again.\n * (A pixel next to ring `k` is always filled by ring `k+1`, so nothing further back can still be waiting.)\n */\n outline(c: Color, diagonal = false, width = 1): void {\n const w = this.w;\n const h = this.h;\n const solid = new Uint8Array(this.data.length);\n let x0 = w, y0 = h, x1 = -1, y1 = -1;\n for (let y = 0; y < h; y++)\n for (let x = 0; x < w; x++) {\n const i = y * w + x;\n if ((this.data[i]! >>> 24) === 0) continue;\n solid[i] = 1;\n if (x < x0) x0 = x;\n if (x > x1) x1 = x;\n if (y < y0) y0 = y;\n y1 = y;\n }\n if (x1 < 0) return;\n const has = (x: number, y: number, i: number) => x >= 0 && y >= 0 && x < w && y < h && solid[i] === 1;\n // The first ring is one sweep of the box the body actually fills, so its interior never reaches a frontier list.\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 let hit = has(x - 1, y, i - 1) || has(x + 1, y, i + 1) || has(x, y - 1, i - w) || has(x, y + 1, i + w);\n if (!hit && diagonal)\n hit = has(x - 1, y - 1, i - w - 1) || has(x + 1, y - 1, i - w + 1) || has(x - 1, y + 1, i + w - 1) || has(x + 1, y + 1, i + w + 1);\n if (hit) {\n this.data[i] = c;\n frontier.push(i);\n }\n }\n for (const i of frontier) solid[i] = 1;\n // Later rings only visit the neighbours of the ring before them.\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 grow = (nx: number, ny: number, k: number) => {\n if (nx < 0 || ny < 0 || nx >= w || ny >= h || solid[k]) return;\n solid[k] = 1;\n this.data[k] = c;\n next.push(k);\n };\n grow(x - 1, y, i - 1);\n grow(x + 1, y, i + 1);\n grow(x, y - 1, i - w);\n grow(x, y + 1, i + w);\n if (diagonal) {\n grow(x - 1, y - 1, i - w - 1);\n grow(x + 1, y - 1, i - w + 1);\n grow(x - 1, y + 1, i + w - 1);\n grow(x + 1, y + 1, i + w + 1);\n }\n }\n frontier = next;\n }\n }\n\n /** Replaces every pixel whose color matches `from`. */\n recolor(from: Color, to: Color): void {\n for (let i = 0; i < this.data.length; i++) if (this.data[i] === from) this.data[i] = to;\n }\n\n /** Multiplies brightness of opaque pixels matching `pred`. */\n tint(f: number, pred: (x: number, y: number) => boolean): void {\n for (let y = 0; y < this.h; y++)\n for (let x = 0; x < this.w; x++) {\n const i = y * this.w + x;\n const c = this.data[i] ?? 0;\n if (c >>> 24 && pred(x, y)) this.data[i] = shade(c, f);\n }\n }\n\n blit(src: PixelGrid, dx: number, dy: number, flipX = false): void {\n for (let y = 0; y < src.h; y++)\n for (let x = 0; x < src.w; x++) {\n const c = src.data[y * src.w + x] ?? 0;\n if (c >>> 24 === 0) continue;\n this.px(dx + (flipX ? src.w - 1 - x : x), dy + y, c);\n }\n }\n\n /** Alpha-blends a color onto an existing pixel. */\n blend(x: number, y: number, c: Color): void {\n if (!this.inBounds(x, y)) return;\n const i = y * this.w + x;\n const dst = this.data[i] ?? 0;\n const [r, g, b, a] = channels(c);\n const t = a / 255;\n const d = channels(dst);\n this.data[i] = rgba(d[0] + (r - d[0]) * t, d[1] + (g - d[1]) * t, d[2] + (b - d[2]) * t, Math.max(d[3], a));\n }\n\n /** Reads a canvas back into a grid (a PNG layer of the paper doll joining the painted ones). */\n static from(canvas: HTMLCanvasElement): PixelGrid {\n const g = new PixelGrid(canvas.width, canvas.height);\n const img = canvas.getContext('2d')!.getImageData(0, 0, canvas.width, canvas.height);\n g.data.set(new Uint32Array(img.data.buffer, img.data.byteOffset, canvas.width * canvas.height));\n return g;\n }\n\n /** The same picture on a grid `factor` times as fine, each pixel becoming a square block. */\n upscaled(factor: number): PixelGrid {\n if (factor === 1) return this;\n const g = new PixelGrid(this.w * factor, this.h * factor);\n for (let y = 0; y < this.h; y++)\n for (let x = 0; x < this.w; x++) {\n const c = this.data[y * this.w + x] ?? 0;\n if (c >>> 24 === 0) continue;\n g.rect(x * factor, y * factor, factor, factor, c);\n }\n return g;\n }\n\n /** Same size and same pixels. */\n equals(other: PixelGrid): boolean {\n if (other.w !== this.w || other.h !== this.h) return false;\n for (let i = 0; i < this.data.length; i++) if (this.data[i] !== other.data[i]) return false;\n return true;\n }\n\n /** Cheap hash of the pixels, for grouping identical frames before comparing them. */\n key(): number {\n let h = 2166136261 >>> 0;\n for (let i = 0; i < this.data.length; i++) {\n h ^= this.data[i]!;\n h = Math.imul(h, 16777619) >>> 0;\n }\n return h >>> 0;\n }\n\n clone(): PixelGrid {\n const g = new PixelGrid(this.w, this.h);\n g.data.set(this.data);\n return g;\n }\n\n toCanvas(): HTMLCanvasElement {\n const canvas = document.createElement('canvas');\n canvas.width = this.w;\n canvas.height = this.h;\n const ctx = canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas not supported');\n const bytes = new Uint8ClampedArray(this.w * this.h * 4);\n bytes.set(new Uint8Array(this.data.buffer, this.data.byteOffset, this.data.byteLength));\n const img = new ImageData(bytes, this.w, this.h);\n ctx.putImageData(img, 0, 0);\n return canvas;\n }\n}\n\n/**\n * A grid that stands for one window of a larger picture: it is drawn on in the larger picture's coordinates and\n * keeps only what falls inside `[x0, x0 + w) × [y0, y0 + h)`. A ground painter can paint a crop of a map into one, so the\n * login screen does not paint the whole of two maps to show 17 tiles of each. Every drawing method goes through\n * `px`/`get`/`inBounds`/`blend`; `outline`, `tint` and the whole-grid helpers work in window coordinates.\n */\nexport class PixelWindow extends PixelGrid {\n constructor(\n readonly x0: number,\n readonly y0: number,\n w: number,\n h: number,\n ) {\n super(w, h);\n }\n\n override inBounds(x: number, y: number): boolean {\n return super.inBounds(x - this.x0, y - this.y0);\n }\n\n override px(x: number, y: number, c: Color): void {\n super.px(Math.floor(x) - this.x0, Math.floor(y) - this.y0, c);\n }\n\n override get(x: number, y: number): Color {\n return super.get(x - this.x0, y - this.y0);\n }\n\n override blend(x: number, y: number, c: Color): void {\n // not through super.blend: it asks `this.inBounds`, which would move the point a second time\n const lx = x - this.x0;\n const ly = y - this.y0;\n if (!super.inBounds(lx, ly)) return;\n const i = ly * this.w + lx;\n const dst = this.data[i] ?? 0;\n const [r, g, b, a] = channels(c);\n const t = a / 255;\n const d = channels(dst);\n this.data[i] = rgba(d[0] + (r - d[0]) * t, d[1] + (g - d[1]) * t, d[2] + (b - d[2]) * t, Math.max(d[3], a));\n }\n}\n\n/**\n * Packs same-sized frames into rows no wider than `maxWidth`. A browser refuses a canvas past about 16k pixels\n * on a side and hands back a blank one, and a player sheet of six hundred frames at twice the detail is wider\n * than that, so it wraps. Readers work the row out from the canvas width.\n */\nexport function makeSheet(frames: PixelGrid[], maxWidth = 16384): HTMLCanvasElement {\n const first = frames[0];\n if (!first) throw new Error('makeSheet: no frames');\n const cols = Math.max(1, Math.min(frames.length, Math.floor(maxWidth / first.w)));\n const rows = Math.ceil(frames.length / cols);\n const sheet = new PixelGrid(first.w * cols, first.h * rows);\n frames.forEach((f, i) => sheet.blit(f, (i % cols) * first.w, Math.floor(i / cols) * first.h));\n return sheet.toCanvas();\n}\n\n/** Scales a canvas by an integer factor with nearest-neighbour filtering (for UI icons). */\nexport function upscale(src: HTMLCanvasElement, factor: number): HTMLCanvasElement {\n const c = document.createElement('canvas');\n c.width = src.width * factor;\n c.height = src.height * factor;\n const ctx = c.getContext('2d');\n if (!ctx) throw new Error('2D canvas not supported');\n ctx.imageSmoothingEnabled = false;\n ctx.drawImage(src, 0, 0, c.width, c.height);\n return c;\n}\n"],"names":["CLEAR","rgba","g","b","a","R","G","B","hex","s","h","c","r","channels","shade","f","t","mix","c1","c2","withAlpha","alpha","toCss","hash2","x","y","seed","mulberry32","valueNoise","scale","fx","fy","x0","y0","tx","ty","sx","sy","d","PixelGrid","w","yy","xx","x1","y1","cx","cy","rx","ry","dx","dy","thickness","err","e2","points","minY","p","maxY","xs","i","q","diagonal","width","solid","has","frontier","hit","step","next","grow","nx","ny","k","from","to","pred","src","flipX","dst","canvas","img","factor","other","ctx","bytes","PixelWindow","lx","ly","makeSheet","frames","maxWidth","first","cols","rows","sheet","upscale"],"mappings":"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"}
|