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":"Timeline.js","sources":["../../src/core/Timeline.ts"],"sourcesContent":["import { TimelineBuilder } from './TimelineBuilder';\nimport type { NestedPlay } from './TimelineChild';\nimport type { Steppable, Tweener, TweenTarget } from './Tweener';\n\nexport interface TimelineOptions {\n /**\n * The manager that steps it: `stage.tweens` makes it follow the stage's `timeScale` and stop with the stage. Leave\n * it out to step the timeline yourself with `update(dt)` (a fixed step, a test, a timeline played by another).\n */\n tweens?: Tweener;\n /** Built but not playing until `play()` / `resume()`. */\n paused?: boolean;\n /** Extra plays after the first (-1 = forever). */\n repeat?: number;\n /** Every second play runs backwards. */\n yoyo?: boolean;\n /** Ms of stillness between plays. */\n repeatDelay?: number;\n /** Its own speed factor, on top of its manager's. */\n timeScale?: number;\n onUpdate?: (timeline: Timeline) => void;\n onRepeat?: (timeline: Timeline) => void;\n /** When the playhead reaches the end it is playing toward: the end, or the start when reversed. */\n onComplete?: (timeline: Timeline) => void;\n}\n\n/**\n * Tweens and calls laid out in time and played as one: in sequence by default, overlapping by position (`'<'`,\n * `'-=100'`, a label), side by side in `parallel` groups, nested (`add`). It can repeat, yoyo, run at its own speed,\n * pause, seek and play backwards, and `finished` resolves when it completes.\n *\n * Built in one go, then it plays: a timeline stepped while empty has nothing left to do and completes, so build it in\n * the frame it is made (or make it `paused` and `play()` it later). Children are kept sorted by start, which is the\n * order they are played in going forward (and the reverse going back), so a later tween on the same property wins.\n */\nexport class Timeline extends TimelineBuilder implements Steppable, NestedPlay {\n timeScale: number;\n readonly repeat: number;\n readonly yoyo: boolean;\n readonly repeatDelay: number;\n /** A timeline holds no targets of its own for `Tweener.killTweensOf`: stop it with `kill()`. */\n readonly targets: readonly object[] = [];\n private readonly options: TimelineOptions;\n private time = 0;\n private paused: boolean;\n private reversed = false;\n private done = false;\n private killed = false;\n /** In its manager's list: set again by anything that brings a finished timeline back to life. */\n private listed = false;\n /** The next render starts the run: a call standing at its first point fires. */\n private fresh = true;\n private nestedIn: Timeline | null = null;\n private resolveFinished: () => void = () => undefined;\n private finishedPromise: Promise<void>;\n private settled = false;\n /** Scratch for `locate`, so moving the playhead allocates nothing. */\n private locCycle = 0;\n private locLocal = 0;\n\n constructor(options: TimelineOptions = {}) {\n super();\n this.options = options;\n this.repeat = options.repeat ?? 0;\n this.yoyo = options.yoyo ?? false;\n this.repeatDelay = Math.max(0, options.repeatDelay ?? 0);\n this.timeScale = options.timeScale ?? 1;\n this.paused = options.paused ?? false;\n this.finishedPromise = this.newPromise();\n this.relist();\n }\n\n protected adopt(child: TimelineBuilder): NestedPlay {\n if (!(child instanceof Timeline) || child === this || child.nestedIn) throw new Error('Timeline.add: a timeline is played by one parent at most, never itself');\n if (!Number.isFinite(child.totalDuration)) throw new Error('Timeline.add: a timeline that repeats forever cannot be nested');\n child.nestedIn = this;\n return child;\n }\n\n // ── playing ───────────────────────────────────────────────────────────────────────────────────────────────────\n\n /** Every play and the delays between them, ms; Infinity when it repeats forever. */\n get totalDuration(): number {\n if (this.repeat === -1) return Infinity;\n return this.duration * (this.repeat + 1) + this.repeatDelay * this.repeat;\n }\n\n /** Where the playhead stands, ms from the start of the first play. */\n get currentTime(): number {\n return this.time;\n }\n\n /** 0–1 over the whole run (over the current play when it repeats forever). */\n get progress(): number {\n const total = this.totalDuration;\n if (Number.isFinite(total)) return total > 0 ? this.time / total : 1;\n const d = this.duration;\n if (d <= 0) return 1;\n this.locate(this.time, total);\n return this.locLocal / d;\n }\n\n get isPaused(): boolean {\n return this.paused;\n }\n\n get isReversed(): boolean {\n return this.reversed;\n }\n\n /** Still has somewhere to go: not completed, not killed. */\n get isActive(): boolean {\n return !this.done && !this.killed;\n }\n\n /** Resolves when the timeline completes (or is killed, so nothing awaits it forever). A restart makes a new one. */\n get finished(): Promise<void> {\n return this.finishedPromise;\n }\n\n /** Advances by `dt` ms (× `timeScale`): how a timeline with no manager is played. */\n update(dt: number): void {\n this.tick(dt);\n }\n\n /** @internal The manager's step: false once there is nothing left to play. */\n step(dt: number): boolean {\n // dropped by the manager if anything below throws: only a clean return keeps it listed\n this.listed = false;\n const alive = this.tick(dt);\n this.listed = alive;\n return alive;\n }\n\n pause(): this {\n this.paused = true;\n return this;\n }\n\n /** Carries on the way it was going. */\n resume(): this {\n this.paused = false;\n this.wake();\n return this;\n }\n\n /** Plays forward from where it stands. */\n play(): this {\n this.reversed = false;\n return this.resume();\n }\n\n /** Turns round and plays toward the start (or forward again, if it was reversed). */\n reverse(): this {\n this.reversed = !this.reversed;\n return this.resume();\n }\n\n /**\n * Jumps to `to` (ms or a label) and shows it there. Calls on the way are skipped unless `fireCalls`; the playing\n * state is kept, so a paused timeline stays paused.\n */\n seek(to: number | string, fireCalls = false): this {\n const total = this.totalDuration;\n const at = typeof to === 'number' ? to : this.labels.get(to);\n if (at === undefined) throw new Error(`Timeline.seek: no label \"${to}\"`);\n const target = Math.min(total, Math.max(0, at));\n this.render(this.time, target, fireCalls, this.fresh && fireCalls);\n this.time = target;\n this.fresh = false;\n this.wake();\n return this;\n }\n\n /** Back to the start, playing forward, with a new `finished` if the old one was settled. */\n restart(): this {\n this.render(this.time, 0, false, false);\n this.time = 0;\n this.reversed = false;\n this.paused = false;\n this.fresh = true;\n if (this.settled) this.finishedPromise = this.newPromise();\n this.wake();\n return this;\n }\n\n /** Stops for good where it stands (nothing is put back) and settles `finished`. */\n kill(): void {\n this.killed = true;\n this.settle();\n }\n\n /** @internal The manager's name for `kill` (`Tweener.clear`, a stage destroyed). */\n remove(): void {\n this.kill();\n }\n\n /** @internal Played by a parent: its own time and calls, as one span. */\n playNested(from: number, to: number, fire: boolean, includeStart: boolean): void {\n this.render(from, to, fire, includeStart);\n this.time = to;\n this.options.onUpdate?.(this);\n if (to >= this.totalDuration && from < to) this.options.onComplete?.(this);\n }\n\n // ── inside ────────────────────────────────────────────────────────────────────────────────────────────────────\n\n private tick(dt: number): boolean {\n if (this.killed || this.nestedIn) return false;\n if (this.done) return false;\n if (this.paused) return true;\n const total = this.totalDuration;\n const from = this.time;\n const delta = dt * this.timeScale;\n let to = this.reversed ? from - delta : from + delta;\n if (to > total) to = total;\n if (to < 0) to = 0;\n if (to !== from || this.fresh) {\n this.render(from, to, true, this.fresh);\n this.time = to;\n this.fresh = false;\n this.options.onUpdate?.(this);\n }\n if (this.killed) return false;\n if (this.reversed ? to <= 0 : to >= total) {\n this.done = true;\n this.options.onComplete?.(this);\n this.settle();\n return false;\n }\n return true;\n }\n\n /** Moves the playhead from `from` to `to` (total time), play by play. */\n private render(from: number, to: number, fire: boolean, includeStart: boolean): void {\n const d = this.duration;\n const total = this.totalDuration;\n const period = d + this.repeatDelay;\n if (this.repeat === 0 || period <= 0) {\n this.playCycle(0, Math.min(d, Math.max(0, from)), Math.min(d, Math.max(0, to)), fire, includeStart);\n return;\n }\n this.locate(from, total);\n const ca = this.locCycle;\n const la = this.locLocal;\n this.locate(to, total);\n const cb = this.locCycle;\n const lb = this.locLocal;\n if (ca === cb) {\n this.playCycle(ca, la, lb, fire, includeStart);\n return;\n }\n // a yoyo's turn has just played its end point, so the next play leaves it out; a plain repeat starts afresh\n const restart = !this.yoyo;\n if (cb > ca) {\n this.playCycle(ca, la, d, fire, includeStart);\n // a frame that crosses many plays (a very short timeline) shows only the last two\n for (let c = Math.max(ca + 1, cb - 1); c <= cb; c++) {\n if (this.nextPlay()) return;\n this.playCycle(c, 0, c === cb ? lb : d, fire, restart);\n }\n } else {\n this.playCycle(ca, la, 0, fire, includeStart);\n for (let c = Math.min(ca - 1, cb + 1); c >= cb; c--) {\n if (this.nextPlay()) return;\n this.playCycle(c, d, c === cb ? lb : 0, fire, restart);\n }\n }\n }\n\n /** A play boundary: `onRepeat`, and a forever timeline whose every target is gone ends here. Answers \"stop\". */\n private nextPlay(): boolean {\n if (this.repeat === -1 && this.orphaned()) {\n this.kill();\n return true;\n }\n this.options.onRepeat?.(this);\n return this.killed;\n }\n\n private playCycle(cycle: number, a: number, b: number, fire: boolean, includeStart: boolean): void {\n if (this.yoyo && cycle % 2 === 1) {\n const d = this.duration;\n a = d - a;\n b = d - b;\n }\n const list = this.children;\n if (b >= a) for (let i = 0; i < list.length; i++) list[i]!.play(a, b, fire, includeStart);\n else for (let i = list.length - 1; i >= 0; i--) list[i]!.play(a, b, fire, includeStart);\n }\n\n /** Total time → play and time within it, into `locCycle` / `locLocal`. */\n private locate(t: number, total: number): void {\n const d = this.duration;\n if (t >= total) {\n this.locCycle = this.repeat;\n this.locLocal = d;\n return;\n }\n const period = d + this.repeatDelay;\n const c = Math.floor(t / period);\n this.locCycle = c;\n this.locLocal = Math.min(d, t - c * period);\n }\n\n /**\n * Every tween's every target is a destroyed display object. Checked only when a forever timeline turns round, as\n * `Tween` drops a forever tween whose sprites were freed: otherwise it would hold them for good.\n */\n private orphaned(): boolean {\n let any = false;\n for (const c of this.children) {\n if (c.kind !== 'tween') continue;\n for (const t of c.targets) {\n if ((t as TweenTarget).destroyed !== true) return false;\n any = true;\n }\n }\n return any;\n }\n\n /** Brought back to life (reversed, sought, restarted, resumed): its manager steps it again. */\n private wake(): void {\n if (this.killed) return;\n // the next step finds out whether there is anywhere left to go, and completes it if not\n this.done = false;\n this.relist();\n }\n\n private relist(): void {\n const tweens = this.options.tweens;\n if (!tweens || this.listed || this.killed || this.nestedIn) return;\n this.listed = true;\n tweens.run(this);\n }\n\n private settle(): void {\n if (this.settled) return;\n this.settled = true;\n this.resolveFinished();\n }\n\n private newPromise(): Promise<void> {\n this.settled = false;\n return new Promise<void>((resolve) => {\n this.resolveFinished = resolve;\n });\n }\n}\n"],"names":["Timeline","TimelineBuilder","options","child","total","d","dt","alive","to","fireCalls","at","target","from","fire","includeStart","delta","period","ca","la","cb","lb","restart","c","cycle","a","b","list","i","any","t","tweens","resolve"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TimelineBuilder.js","sources":["../../src/core/TimelineBuilder.ts"],"sourcesContent":["import { easeByName, type EaseFn } from './eases';\nimport { placeAt, type PlaceContext, type TimelinePosition } from './timelinePlace';\nimport { TimelineChild, type NestedPlay } from './TimelineChild';\nimport type { TweenTarget } from './Tweener';\n\n/** A number to tween to, or `{ from, to }` to start from a given value instead of the target's own. */\nexport type TimelineProps = Record<string, number | { from?: number; to: number }>;\n\nexport interface TimelineTweenOptions {\n /** Ms (default 1000, as `Tweener`'s). */\n duration?: number;\n /** A name `easeByName` knows ('Quad.Out' when left out, as `Tweener`'s) or an ease function. */\n ease?: string | EaseFn;\n at?: TimelinePosition;\n}\n\n/**\n * The building half of `Timeline`: where each tween, call, gap and nested timeline goes. Kept apart from the playing\n * half so each stays readable; nothing here runs per frame.\n */\nexport abstract class TimelineBuilder {\n /** Sorted by start; equal starts keep the order they were added in. */\n protected readonly children: TimelineChild[] = [];\n protected readonly labels = new Map<string, number>();\n /** The longest child's end, kept as children are added rather than summed every frame. */\n private length = 0;\n private lastStart = 0;\n private lastEnd = 0;\n private group: { start: number; end: number } | null = null;\n\n /** Checks `child` may be played by this timeline and takes it over (it stops being stepped alone). */\n protected abstract adopt(child: TimelineBuilder): NestedPlay;\n\n /** One play, ms (the longest child's end). */\n get duration(): number {\n return this.length;\n }\n\n /** Tweens the numeric `props` of `targets` (one object or several), after what came before unless `at` says. */\n to(targets: object | readonly object[], props: TimelineProps, options: TimelineTweenOptions = {}): this {\n const list = (Array.isArray(targets) ? [...targets] : [targets]) as TweenTarget[];\n const names: string[] = [];\n const ends: number[] = [];\n const froms: (number | undefined)[] = [];\n for (const [name, v] of Object.entries(props)) {\n names.push(name);\n if (typeof v === 'number') {\n ends.push(v);\n froms.push(undefined);\n } else {\n ends.push(v.to);\n froms.push(v.from);\n }\n }\n const ease = typeof options.ease === 'function' ? options.ease : easeByName(options.ease);\n const duration = Math.max(0, options.duration ?? 1000);\n const start = this.place(options.at);\n return this.insert(new TimelineChild('tween', start, duration, list, names, ends, froms, ease));\n }\n\n /** Calls `fn` when the playhead passes `at` (default: the end so far), going either way. */\n call(fn: () => void, at?: TimelinePosition): this {\n return this.insert(new TimelineChild('call', this.place(at), 0, [], [], [], [], null, fn));\n }\n\n /** `ms` of nothing: the next child starts that much later, and a wait at the end makes the timeline longer. */\n wait(ms: number): this {\n return this.insert(new TimelineChild('gap', this.place(undefined), Math.max(0, ms)));\n }\n\n /** Names a point (default: the end so far), for positions and `seek`. */\n label(name: string, at?: TimelinePosition): this {\n this.labels.set(name, this.place(at));\n return this;\n }\n\n /** Where a label stands, if it is set. */\n labelTime(name: string): number | undefined {\n return this.labels.get(name);\n }\n\n /**\n * Everything `build` adds starts together, at `at` (default: the end so far); a position inside is measured from\n * the group's start. What follows the group starts at its longest child's end.\n */\n parallel(build: (timeline: this) => void, at?: TimelinePosition): this {\n const outer = this.group;\n const start = this.place(at);\n const group = { start, end: start };\n this.group = group;\n try {\n build(this);\n } finally {\n this.group = outer;\n if (outer) outer.end = Math.max(outer.end, group.end);\n this.lastStart = start;\n this.lastEnd = group.end;\n }\n return this;\n }\n\n /** Plays another timeline (built, not repeating forever) as one child of this one; it is no longer stepped alone. */\n add(child: TimelineBuilder, at?: TimelinePosition): this {\n const nested = this.adopt(child);\n const start = this.place(at);\n return this.insert(new TimelineChild('nested', start, nested.totalDuration, [], [], [], [], null, null, nested));\n }\n\n private place(at: TimelinePosition | undefined): number {\n const end = this.length;\n const ctx: PlaceContext = { fallback: this.group ? this.group.start : end, end, lastStart: this.lastStart, lastEnd: this.lastEnd, labels: this.labels };\n return placeAt(at, ctx);\n }\n\n private insert(child: TimelineChild): this {\n let i = this.children.length;\n while (i > 0 && this.children[i - 1]!.start > child.start) i--;\n this.children.splice(i, 0, child);\n this.length = Math.max(this.length, child.end);\n this.lastStart = child.start;\n this.lastEnd = child.end;\n if (this.group) this.group.end = Math.max(this.group.end, child.end);\n return this;\n }\n}\n"],"names":["TimelineBuilder","targets","props","options","list","names","ends","froms","name","v","ease","easeByName","duration","start","TimelineChild","fn","at","ms","build","outer","group","child","nested","end","ctx","placeAt","i"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TimelineChild.js","sources":["../../src/core/TimelineChild.ts"],"sourcesContent":["import type { EaseFn } from './eases';\nimport type { TweenTarget } from './Tweener';\nimport { readProp, writeProp } from './tweenProps';\n\n/** What a nested timeline offers the timeline playing it. */\nexport interface NestedPlay {\n readonly totalDuration: number;\n /** Plays from `from` to `to` (its own time, ms); `includeStart` fires a call standing exactly at `from`. */\n playNested(from: number, to: number, fire: boolean, includeStart: boolean): void;\n}\n\nexport type ChildKind = 'tween' | 'call' | 'gap' | 'nested';\n\n/**\n * One thing on a timeline: a tween, a call, an empty gap (`wait`) or a nested timeline, at `start` for `duration` ms.\n * A tween reads its start values the first time the playhead reaches it — not when it is added — so a tween that\n * follows another on the same property starts where that one ended, and keeps those values for seeking and reversing.\n */\nexport class TimelineChild {\n /** Every target × prop start value, captured once (`inited`). */\n private readonly froms: number[] = [];\n private inited = false;\n /** Progress (tween) or own time (nested) last written; NaN before the first, so the first always writes. */\n private last = Number.NaN;\n\n constructor(\n readonly kind: ChildKind,\n readonly start: number,\n readonly duration: number,\n readonly targets: readonly TweenTarget[] = [],\n private readonly props: readonly string[] = [],\n private readonly ends: readonly number[] = [],\n /** A start value given as `{ from, to }`, per prop; undefined reads the target's own. */\n private readonly givenFrom: readonly (number | undefined)[] = [],\n private readonly ease: EaseFn | null = null,\n private readonly fn: (() => void) | null = null,\n private readonly nested: NestedPlay | null = null,\n ) {}\n\n get end(): number {\n return this.start + this.duration;\n }\n\n /**\n * The playhead moved from `a` to `b` (the parent's own time, either way). Calls fire when `fire` is set and their\n * time lies in the span with its start left out and its end kept — so a call met at the end of one frame does not\n * fire again at the start of the next — unless `includeStart` keeps the start too (a run or a repeat beginning).\n */\n play(a: number, b: number, fire: boolean, includeStart: boolean): void {\n switch (this.kind) {\n case 'tween':\n this.playTween(b);\n break;\n case 'call':\n if (fire && this.fn && crossed(a, b, this.start, includeStart)) this.fn();\n break;\n case 'nested':\n this.playNested(a, b, fire, includeStart);\n break;\n case 'gap':\n break;\n }\n }\n\n private playTween(b: number): void {\n if (!this.inited) {\n if (b < this.start) return;\n this.capture();\n }\n let p: number;\n if (this.duration > 0) p = Math.min(1, Math.max(0, (b - this.start) / this.duration));\n else p = b >= this.start ? 1 : 0;\n if (p === this.last) return;\n this.last = p;\n const k = this.ease ? this.ease(p) : p;\n const props = this.props;\n const np = props.length;\n const targets = this.targets;\n for (let t = 0; t < targets.length; t++) {\n const target = targets[t]!;\n // a destroyed display object is skipped, as Tweener does: its owner freed it, nothing is left to move\n if (target.destroyed === true) continue;\n for (let i = 0; i < np; i++) {\n const from = this.froms[t * np + i]!;\n writeProp(target, props[i]!, from + (this.ends[i]! - from) * k);\n }\n }\n }\n\n private capture(): void {\n this.inited = true;\n const props = this.props;\n for (const target of this.targets) {\n for (let i = 0; i < props.length; i++) this.froms.push(this.givenFrom[i] ?? readProp(target, props[i]!));\n }\n }\n\n private playNested(a: number, b: number, fire: boolean, includeStart: boolean): void {\n const sub = this.nested;\n if (!sub) return;\n if (!this.inited) {\n if (b < this.start) return;\n this.inited = true;\n }\n const total = sub.totalDuration;\n const la = Math.min(total, Math.max(0, a - this.start));\n const lb = Math.min(total, Math.max(0, b - this.start));\n // the nested timeline's own start point is \"crossed\" when the parent came from outside its span\n const forward = b >= a;\n const subInclude = forward ? a < this.start || (a === this.start && includeStart) : a > this.end || (a === this.end && includeStart);\n if (la === lb && lb === this.last && !subInclude) return;\n this.last = lb;\n sub.playNested(la, lb, fire, subInclude);\n }\n}\n\n/**\n * Whether a call at `at` lies in the span the playhead swept from `a` to `b`: the end kept, the start only when\n * `includeStart` says so.\n */\nexport function crossed(a: number, b: number, at: number, includeStart: boolean): boolean {\n if (at === a) return includeStart;\n return b >= a ? at > a && at <= b : at < a && at >= b;\n}\n"],"names":["TimelineChild","kind","start","duration","targets","props","ends","givenFrom","ease","fn","nested","a","b","fire","includeStart","crossed","p","k","np","t","target","i","from","writeProp","readProp","sub","total","la","lb","subInclude","at"],"mappings":"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"}
|
package/dist/core/Tweener.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Tweener.js","sources":["../../src/core/Tweener.ts"],"sourcesContent":["import { easeByName } from './eases';\nimport { Timeline, type TimelineOptions } from './Timeline';\nimport { readProp as read, writeProp as write } from './tweenProps';\n\nexport type TweenTarget = Record<string, unknown> & { destroyed?: boolean };\n\n/** Tween config in the shape the renderer has always used: numeric props at the top level or under `props`. */\nexport interface TweenConfig {\n targets: object | object[];\n duration?: number;\n delay?: number;\n /** Ms to stay at the end values before a yoyo plays back. */\n hold?: number;\n yoyo?: boolean;\n /** Extra plays after the first (-1 = forever). */\n repeat?: number;\n ease?: string;\n props?: Record<string, number>;\n onStart?: () => void;\n onUpdate?: () => void;\n onComplete?: () => void;\n [prop: string]: unknown;\n}\n\nconst RESERVED = new Set([\n 'targets', 'duration', 'delay', 'hold', 'yoyo', 'repeat', 'ease', 'props', 'onStart', 'onUpdate', 'onComplete',\n]);\n\ninterface Track {\n target: TweenTarget;\n prop: string;\n from: number;\n to: number;\n}\n\n/** One running tween; `remove()` stops it without calling onComplete. */\nexport class Tween {\n private tracks: Track[] | null = null;\n private elapsed = 0;\n private plays = 0;\n removed = false;\n readonly targets: TweenTarget[];\n\n constructor(\n private readonly cfg: TweenConfig,\n private readonly ends: Record<string, number>,\n ) {\n const t = cfg.targets;\n this.targets = (Array.isArray(t) ? t : [t]) as TweenTarget[];\n }\n\n /**\n * Every target is a destroyed display object: nothing is left to move. A forever tween (`repeat: -1`) on a sprite\n * its owner freed used to stay in the list for good, holding the sprite, and the list grew with every map loaded.\n * A plain object (a clock a callback reads) has no `destroyed` and is never taken for one. Only a forever tween is\n * dropped this way: a finite one still runs out and calls its `onComplete`, which may clean up more than the sprite.\n */\n private orphaned(): boolean {\n if (this.cfg.repeat !== -1) return false;\n const ts = this.targets;\n if (ts.length === 0) return false;\n for (const t of ts) if (t.destroyed !== true) return false;\n return true;\n }\n\n remove(): void {\n this.removed = true;\n }\n\n stop(): void {\n this.removed = true;\n }\n\n /** Advances by `dt` ms; returns false once the tween is finished. */\n step(dt: number): boolean {\n if (this.removed) return false;\n if (this.orphaned()) {\n this.removed = true;\n return false;\n }\n const cfg = this.cfg;\n const delay = cfg.delay ?? 0;\n this.elapsed += dt;\n if (this.elapsed < delay) return true;\n if (!this.tracks) {\n this.tracks = [];\n for (const target of this.targets) {\n for (const [prop, to] of Object.entries(this.ends)) this.tracks.push({ target, prop, from: read(target, prop), to });\n }\n cfg.onStart?.();\n }\n const duration = Math.max(1, cfg.duration ?? 1000);\n const hold = cfg.yoyo ? cfg.hold ?? 0 : 0;\n const cycle = cfg.yoyo ? duration * 2 + hold : duration;\n let local = this.elapsed - delay - this.plays * cycle;\n const repeat = cfg.repeat ?? 0;\n let finished = false;\n if (local >= cycle) {\n if (repeat === -1 || this.plays < repeat) {\n this.plays++;\n local -= cycle;\n } else {\n local = cycle;\n finished = true;\n }\n }\n let p: number;\n if (!cfg.yoyo) p = local / duration;\n else if (local <= duration) p = local / duration;\n else if (local <= duration + hold) p = 1;\n else p = 1 - (local - duration - hold) / duration;\n const k = easeByName(cfg.ease)(Math.min(1, Math.max(0, p)));\n for (const t of this.tracks) if (!t.target.destroyed) write(t.target, t.prop, t.from + (t.to - t.from) * k);\n cfg.onUpdate?.();\n if (finished) {\n this.removed = true;\n cfg.onComplete?.();\n return false;\n }\n return true;\n }\n}\n\n/**\n * Anything the manager can step each frame: a `Tween`, or a `Timeline` it drives. `step` answers false once finished\n * (the manager then drops it); `remove` stops it for good; `targets` is what `killTweensOf` matches against.\n */\nexport interface Steppable {\n step(dt: number): boolean;\n remove(): void;\n readonly targets: readonly object[];\n}\n\n/** Frame-driven tween manager (the renderer's replacement for Phaser's TweenManager). */\nexport class Tweener {\n /** Global speed factor; hit-stop slows every tween at once. */\n timeScale = 1;\n private tweens: Steppable[] = [];\n\n add(cfg: TweenConfig): Tween {\n const ends: Record<string, number> = { ...(cfg.props ?? {}) };\n const starts: Record<string, number> = {};\n for (const [k, v] of Object.entries(cfg)) {\n if (RESERVED.has(k)) continue;\n if (typeof v === 'number') ends[k] = v;\n else if (v && typeof v === 'object' && 'to' in v) {\n const range = v as { from?: number; to: number };\n ends[k] = range.to;\n if (range.from !== undefined) starts[k] = range.from;\n }\n }\n // `{ from, to }` jumps to `from` right away, before any delay\n const targets = (Array.isArray(cfg.targets) ? cfg.targets : [cfg.targets]) as Record<string, unknown>[];\n for (const t of targets) for (const [k, v] of Object.entries(starts)) t[k] = v;\n const tween = new Tween(cfg, ends);\n this.tweens.push(tween);\n return tween;\n }\n\n /**\n * Steps `item` every frame from the next one on, at this manager's `timeScale`, until its `step` answers false or\n * the manager is cleared (a stage's manager is cleared when the stage is destroyed).\n */\n run<T extends Steppable>(item: T): T {\n this.tweens.push(item);\n return item;\n }\n\n /** A `Timeline` driven by this manager: it follows `timeScale` and stops with `clear`. */\n timeline(options: Omit<TimelineOptions, 'tweens'> = {}): Timeline {\n return new Timeline({ ...options, tweens: this });\n }\n\n killTweensOf(target: object | object[]): void {\n const set = new Set(Array.isArray(target) ? target : [target]);\n for (const t of this.tweens) if (t.targets.some((x) => set.has(x))) t.remove();\n }\n\n update(dt: number): void {\n const scaled = dt * this.timeScale;\n // tweens started from a callback run from the next frame on, like Phaser's\n // compacted in place: the ones added meanwhile sit past `n` and keep their order after the survivors\n const list = this.tweens;\n const n = list.length;\n let kept = 0;\n for (let i = 0; i < n; i++) {\n const t = list[i]!;\n if (stepSafely(t, scaled)) list[kept++] = t;\n }\n // a callback that cleared the manager dropped this list, and every tween on it was removed\n if (this.tweens !== list) return;\n for (let i = n; i < list.length; i++) list[kept++] = list[i]!;\n list.length = kept;\n }\n\n clear(): void {\n for (const t of this.tweens) t.remove();\n this.tweens = [];\n }\n}\n\n/** A tween whose step or callback throws is dropped, so it neither throws every frame nor leaves the list half compacted. */\nfunction stepSafely(t: { step(dt: number): boolean }, dt: number): boolean {\n try {\n return t.step(dt);\n } catch (err) {\n console.error('[tween] dropped after an error', err);\n return false;\n }\n}\n"],"names":["RESERVED","Tween","cfg","ends","t","ts","dt","delay","target","prop","to","read","duration","hold","cycle","local","repeat","finished","p","k","easeByName","write","Tweener","starts","v","range","targets","tween","item","options","Timeline","set","x","scaled","list","kept","i","stepSafely","err"],"mappings":"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"}
|
package/dist/core/atlas.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"atlas.js","sources":["../../src/core/atlas.ts"],"sourcesContent":["import type { TextureRegistry } from './TextureRegistry';\nimport { ATLAS_PAGE_FULL, planAtlas } from './atlasPlan';\n\ninterface AtlasItem {\n name: string;\n canvas: HTMLCanvasElement;\n frameWidth?: number;\n frameHeight?: number;\n}\n\n/**\n * Packs many small procedurally painted canvases into as few textures as the GPU allows, so Pixi can batch every\n * sprite of the world into very few draw calls. One page on every desktop (up to 8192 square); a device whose GPU\n * takes less gets pages of `limit` (`atlasPageLimit`, `planAtlas`). Every frame is registered under the one `key`\n * whichever page holds it — the pages after the first are textures of their own (`key~1`, `key~2`…) whose frames\n * the first page's texture adopts — so the world keeps asking for `atlas` and a frame name, and never knows.\n * Frame names: `name` for single images, `name#i` for sheet frames.\n */\nexport class AtlasBuilder {\n private items: AtlasItem[] = [];\n\n add(name: string, canvas: HTMLCanvasElement): void {\n this.items.push({ name, canvas });\n }\n\n addSheet(name: string, canvas: HTMLCanvasElement, frameWidth: number, frameHeight: number): void {\n this.items.push({ name, canvas, frameWidth, frameHeight });\n }\n\n build(textures: TextureRegistry, key: string, limit = ATLAS_PAGE_FULL): void {\n const items = this.items;\n const plan = planAtlas(items.map((it) => ({ w: it.canvas.width, h: it.canvas.height })), limit);\n const pages = plan.pages.map((p, i) => {\n const canvas = document.createElement('canvas');\n canvas.width = p.width;\n canvas.height = p.height;\n // kept on the CPU (`willReadFrequently`): the atlas is drawn once, then read many times — the sway rigs' trunks,\n // a death's evaporating pixels, a hit's white silhouette — and on a GPU canvas the first read pulled the whole\n // page back, 300 ms on entering a new map (profiled)\n return { canvas, ctx: canvas.getContext('2d', { willReadFrequently: true })!, key: i === 0 ? key : `${key}~${i}` };\n });\n items.forEach((it, i) => {\n const at = plan.places[i]!;\n pages[at.page]!.ctx.drawImage(it.canvas, at.x, at.y);\n });\n const first = textures.addCanvas(key, pages[0]!.canvas);\n const texOf = pages.map((p, i) => (i === 0 ? first : textures.addCanvas(p.key, p.canvas)));\n items.forEach((it, i) => {\n const at = plan.places[i]!;\n const page = texOf[at.page]!;\n // a frame on a later page is also registered on the first, so `atlas` + its name finds it\n const add = (name: string, x: number, y: number, w: number, h: number) => {\n page.add(name, 0, x, y, w, h);\n if (at.page > 0) first.adopt(name, page);\n };\n if (it.frameWidth && it.frameHeight) {\n // a sheet may wrap into rows (`makeSheet`'s `maxWidth`, a very large creature's); frames are read row by row\n const cols = Math.floor(it.canvas.width / it.frameWidth);\n const rows = Math.max(1, Math.floor(it.canvas.height / it.frameHeight));\n for (let f = 0; f < cols * rows; f++)\n add(`${it.name}#${f}`, at.x + (f % cols) * it.frameWidth, at.y + Math.floor(f / cols) * it.frameHeight, it.frameWidth, it.frameHeight);\n } else {\n add(it.name, at.x, at.y, it.canvas.width, it.canvas.height);\n }\n });\n const sizes = plan.pages.map((p) => `${p.width}x${p.height}`).join(' + ');\n console.info(`[atlas] ${key}: ${items.length} images packed into ${sizes}${plan.pages.length > 1 ? ` (pages of ${limit})` : ''}`);\n }\n}\n"],"names":["AtlasBuilder","name","canvas","frameWidth","frameHeight","textures","key","limit","ATLAS_PAGE_FULL","items","plan","planAtlas","it","pages","p","i","at","first","texOf","page","add","x","y","w","h","cols","rows","f","sizes"],"mappings":";AAkBO,MAAMA,EAAa;AAAA,EAChB,QAAqB,CAAA;AAAA,EAE7B,IAAIC,GAAcC,GAAiC;AACjD,SAAK,MAAM,KAAK,EAAE,MAAAD,GAAM,QAAAC,GAAQ;AAAA,EAClC;AAAA,EAEA,SAASD,GAAcC,GAA2BC,GAAoBC,GAA2B;AAC/F,SAAK,MAAM,KAAK,EAAE,MAAAH,GAAM,QAAAC,GAAQ,YAAAC,GAAY,aAAAC,GAAa;AAAA,EAC3D;AAAA,EAEA,MAAMC,GAA2BC,GAAaC,IAAQC,GAAuB;AAC3E,UAAMC,IAAQ,KAAK,OACbC,IAAOC,EAAUF,EAAM,IAAI,CAACG,OAAQ,EAAE,GAAGA,EAAG,OAAO,OAAO,GAAGA,EAAG,OAAO,OAAA,EAAS,GAAGL,CAAK,GACxFM,IAAQH,EAAK,MAAM,IAAI,CAACI,GAAGC,MAAM;AACrC,YAAMb,IAAS,SAAS,cAAc,QAAQ;AAC9C,aAAAA,EAAO,QAAQY,EAAE,OACjBZ,EAAO,SAASY,EAAE,QAIX,EAAE,QAAAZ,GAAQ,KAAKA,EAAO,WAAW,MAAM,EAAE,oBAAoB,GAAA,CAAM,GAAI,KAAKa,MAAM,IAAIT,IAAM,GAAGA,CAAG,IAAIS,CAAC,GAAA;AAAA,IAChH,CAAC;AACD,IAAAN,EAAM,QAAQ,CAACG,GAAIG,MAAM;AACvB,YAAMC,IAAKN,EAAK,OAAOK,CAAC;AACxB,MAAAF,EAAMG,EAAG,IAAI,EAAG,IAAI,UAAUJ,EAAG,QAAQI,EAAG,GAAGA,EAAG,CAAC;AAAA,IACrD,CAAC;AACD,UAAMC,IAAQZ,EAAS,UAAUC,GAAKO,EAAM,CAAC,EAAG,MAAM,GAChDK,IAAQL,EAAM,IAAI,CAACC,GAAGC,MAAOA,MAAM,IAAIE,IAAQZ,EAAS,UAAUS,EAAE,KAAKA,EAAE,MAAM,CAAE;AACzF,IAAAL,EAAM,QAAQ,CAACG,GAAIG,MAAM;AACvB,YAAMC,IAAKN,EAAK,OAAOK,CAAC,GAClBI,IAAOD,EAAMF,EAAG,IAAI,GAEpBI,IAAM,CAACnB,GAAcoB,GAAWC,GAAWC,GAAWC,MAAc;AACxE,QAAAL,EAAK,IAAIlB,GAAM,GAAGoB,GAAGC,GAAGC,GAAGC,CAAC,GACxBR,EAAG,OAAO,KAAGC,EAAM,MAAMhB,GAAMkB,CAAI;AAAA,MACzC;AACA,UAAIP,EAAG,cAAcA,EAAG,aAAa;AAEnC,cAAMa,IAAO,KAAK,MAAMb,EAAG,OAAO,QAAQA,EAAG,UAAU,GACjDc,IAAO,KAAK,IAAI,GAAG,KAAK,MAAMd,EAAG,OAAO,SAASA,EAAG,WAAW,CAAC;AACtE,iBAASe,IAAI,GAAGA,IAAIF,IAAOC,GAAMC;AAC/B,UAAAP,EAAI,GAAGR,EAAG,IAAI,IAAIe,CAAC,IAAIX,EAAG,IAAKW,IAAIF,IAAQb,EAAG,YAAYI,EAAG,IAAI,KAAK,MAAMW,IAAIF,CAAI,IAAIb,EAAG,aAAaA,EAAG,YAAYA,EAAG,WAAW;AAAA,MACzI;AACE,QAAAQ,EAAIR,EAAG,MAAMI,EAAG,GAAGA,EAAG,GAAGJ,EAAG,OAAO,OAAOA,EAAG,OAAO,MAAM;AAAA,IAE9D,CAAC;AACD,UAAMgB,IAAQlB,EAAK,MAAM,IAAI,CAACI,MAAM,GAAGA,EAAE,KAAK,IAAIA,EAAE,MAAM,EAAE,EAAE,KAAK,KAAK;AACxE,YAAQ,KAAK,WAAWR,CAAG,KAAKG,EAAM,MAAM,uBAAuBmB,CAAK,GAAGlB,EAAK,MAAM,SAAS,IAAI,cAAcH,CAAK,MAAM,EAAE,EAAE;AAAA,EAClI;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"atlasPlan.js","sources":["../../src/core/atlasPlan.ts"],"sourcesContent":["/*\n * Where every image goes in the world atlas, kept pure (no canvas) so the packing decision is unit-tested. The\n * builder (`atlas.ts`) paints what this plans.\n */\n\nexport const ATLAS_PAD = 2;\nconst MIN_ATLAS_WIDTH = 1024;\n/** The page side every desktop GPU takes, and the one the atlas was built to before pages existed. */\nexport const ATLAS_PAGE_FULL = 8192;\n/** The page side on a GPU that reports less than `ATLAS_PAGE_FULL` (older iPhones and iPads: 4096). */\nconst ATLAS_PAGE_SMALL = 4096;\n\n/**\n * How big an atlas page may be on this device: 8192 wherever the GPU takes it (every desktop — unchanged), else\n * split at 4096, or at the largest power of two the GPU does take if that is smaller still. Unknown: 8192, as before.\n */\nexport function atlasPageLimit(maxTextureSize: number | null): number {\n if (maxTextureSize === null || maxTextureSize >= ATLAS_PAGE_FULL) return ATLAS_PAGE_FULL;\n return Math.min(ATLAS_PAGE_SMALL, 2 ** Math.floor(Math.log2(Math.max(1, maxTextureSize))));\n}\n\nexport interface AtlasSize {\n w: number;\n h: number;\n}\n\nexport interface AtlasPage {\n width: number;\n height: number;\n}\n\nexport interface AtlasPlan {\n pages: AtlasPage[];\n /** One per input, in input order: the page and the top-left corner. */\n places: { page: number; x: number; y: number }[];\n}\n\n/**\n * Shelf-packs the images, tallest first, into pages no larger than `limit` on either side. The page width is the\n * power of two that fits the widest image and the square root of the total area (at least 1024), capped at `limit`;\n * a shelf that would run past `limit` starts a new page. With everything fitting under `limit` this is exactly the\n * single page the atlas always was. An image larger than `limit` itself is given a page of its own (the GPU will\n * refuse it, but nothing else is lost with it).\n */\nexport function planAtlas(sizes: readonly AtlasSize[], limit: number): AtlasPlan {\n const order = sizes.map((_, i) => i).sort((a, b) => sizes[b]!.h - sizes[a]!.h);\n const area = sizes.reduce((sum, s) => sum + (s.w + ATLAS_PAD) * (s.h + ATLAS_PAD), 0);\n const widest = Math.max(1, ...sizes.map((s) => s.w + ATLAS_PAD));\n const width = Math.min(limit, Math.max(MIN_ATLAS_WIDTH, 2 ** Math.ceil(Math.log2(Math.max(widest, Math.sqrt(area))))));\n const places: AtlasPlan['places'] = new Array(sizes.length);\n const pages: AtlasPage[] = [];\n let page = 0;\n let x = 0;\n let y = 0;\n let shelf = 0;\n let pageWidth = width;\n const close = () => pages.push({ width: pageWidth, height: y + shelf });\n for (const i of order) {\n const w = sizes[i]!.w + ATLAS_PAD;\n const h = sizes[i]!.h + ATLAS_PAD;\n if (x > 0 && x + w > pageWidth) {\n x = 0;\n y += shelf;\n shelf = 0;\n }\n if (y > 0 && y + h > limit) {\n close();\n page++;\n x = 0;\n y = 0;\n shelf = 0;\n pageWidth = width;\n }\n // an image wider than the page (only past `limit`) widens its own page rather than overlapping the edge\n if (w > pageWidth) pageWidth = w;\n places[i] = { page, x, y };\n x += w;\n shelf = Math.max(shelf, h);\n }\n if (sizes.length > 0) close();\n return { pages, places };\n}\n"],"names":["ATLAS_PAD","ATLAS_PAGE_FULL","atlasPageLimit","maxTextureSize","planAtlas","sizes","limit","order","_","i","b","area","sum","s","widest","width","places","pages","page","x","y","shelf","pageWidth","close","w","h"],"mappings":"AAKO,MAAMA,IAAY;AAGlB,MAAMC,IAAkB;AAQxB,SAASC,EAAeC,GAAuC;AACpE,SAAIA,MAAmB,QAAQA,KAAkB,OAAwB,OAClE,KAAK,IAAI,MAAkB,KAAK,KAAK,MAAM,KAAK,KAAK,KAAK,IAAI,GAAGA,CAAc,CAAC,CAAC,CAAC;AAC3F;AAyBO,SAASC,EAAUC,GAA6BC,GAA0B;AAC/E,QAAMC,IAAQF,EAAM,IAAI,CAACG,GAAGC,MAAMA,CAAC,EAAE,KAAK,CAAC,GAAGC,MAAML,EAAMK,CAAC,EAAG,IAAIL,EAAM,CAAC,EAAG,CAAC,GACvEM,IAAON,EAAM,OAAO,CAACO,GAAKC,MAAMD,KAAOC,EAAE,IAAI,MAAcA,EAAE,IAAI,IAAY,CAAC,GAC9EC,IAAS,KAAK,IAAI,GAAG,GAAGT,EAAM,IAAI,CAACQ,MAAMA,EAAE,IAAI,CAAS,CAAC,GACzDE,IAAQ,KAAK,IAAIT,GAAO,KAAK,IAAI,MAAiB,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,IAAIQ,GAAQ,KAAK,KAAKH,CAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAC/GK,IAA8B,IAAI,MAAMX,EAAM,MAAM,GACpDY,IAAqB,CAAA;AAC3B,MAAIC,IAAO,GACPC,IAAI,GACJC,IAAI,GACJC,IAAQ,GACRC,IAAYP;AAChB,QAAMQ,IAAQ,MAAMN,EAAM,KAAK,EAAE,OAAOK,GAAW,QAAQF,IAAIC,GAAO;AACtE,aAAWZ,KAAKF,GAAO;AACrB,UAAMiB,IAAInB,EAAMI,CAAC,EAAG,IAAI,GAClBgB,IAAIpB,EAAMI,CAAC,EAAG,IAAI;AACxB,IAAIU,IAAI,KAAKA,IAAIK,IAAIF,MACnBH,IAAI,GACJC,KAAKC,GACLA,IAAQ,IAEND,IAAI,KAAKA,IAAIK,IAAInB,MACnBiB,EAAA,GACAL,KACAC,IAAI,GACJC,IAAI,GACJC,IAAQ,GACRC,IAAYP,IAGVS,IAAIF,MAAWA,IAAYE,IAC/BR,EAAOP,CAAC,IAAI,EAAE,MAAAS,GAAM,GAAAC,GAAG,GAAAC,EAAA,GACvBD,KAAKK,GACLH,IAAQ,KAAK,IAAIA,GAAOI,CAAC;AAAA,EAC3B;AACA,SAAIpB,EAAM,SAAS,KAAGkB,EAAA,GACf,EAAE,OAAAN,GAAO,QAAAD,EAAA;AAClB;"}
|
package/dist/core/eases.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"eases.js","sources":["../../src/core/eases.ts"],"sourcesContent":["export type EaseFn = (t: number) => number;\n\nconst linear: EaseFn = (t) => t;\n\n/** Easing functions by the names tween configs use ('Cubic.Out', 'Back.Out', ...). */\nconst EASES: Record<string, EaseFn> = {\n Linear: linear,\n 'Quad.In': (t) => t * t,\n 'Quad.Out': (t) => t * (2 - t),\n 'Cubic.Out': (t) => 1 - Math.pow(1 - t, 3),\n 'Sine.In': (t) => 1 - Math.cos((t * Math.PI) / 2),\n 'Sine.InOut': (t) => -(Math.cos(Math.PI * t) - 1) / 2,\n 'Back.Out': (t) => {\n const s = 1.70158;\n const u = t - 1;\n return u * u * ((s + 1) * u + s) + 1;\n },\n};\n\nexport function easeByName(name: string | undefined): EaseFn {\n if (!name) return EASES['Quad.Out']!; // Phaser's default tween ease is Power1 = Quad.Out\n const fn = EASES[name];\n if (!fn) console.warn(`[tween] unknown ease ${name}`);\n return fn ?? linear;\n}\n"],"names":["linear","t","EASES","easeByName","name","fn"],"mappings":"AAEA,MAAMA,IAAiB,CAACC,MAAMA,GAGxBC,IAAgC;AAAA,EACpC,QAAQF;AAAA,EACR,WAAW,CAACC,MAAMA,IAAIA;AAAA,EACtB,YAAY,CAACA,MAAMA,KAAK,IAAIA;AAAA,EAC5B,aAAa,CAACA,MAAM,IAAI,KAAK,IAAI,IAAIA,GAAG,CAAC;AAAA,EACzC,WAAW,CAACA,MAAM,IAAI,KAAK,IAAKA,IAAI,KAAK,KAAM,CAAC;AAAA,EAChD,cAAc,CAACA,MAAM,EAAE,KAAK,IAAI,KAAK,KAAKA,CAAC,IAAI,KAAK;AAAA,EACpD,YAAY,CAACA,MAAM;AAEjB,UAAM,IAAIA,IAAI;AACd,WAAO,IAAI,KAAM,UAAS,IAAI,WAAK;AAAA,EACrC;AACF;AAEO,SAASE,EAAWC,GAAkC;AAC3D,MAAI,CAACA,EAAM,QAAOF,EAAM,UAAU;AAClC,QAAMG,IAAKH,EAAME,CAAI;AACrB,SAAKC,KAAI,QAAQ,KAAK,wBAAwBD,CAAI,EAAE,GAC7CC,KAAML;AACf;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gameObject.js","sources":["../../src/core/gameObject.ts"],"sourcesContent":["import { ColorMatrixFilter, type BLEND_MODES, type Container } from 'pixi.js';\nimport type { Stage } from './Stage';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Ctor<T> = new (...args: any[]) => T;\n\n/**\n * One solid-fill filter list per colour, shared by every object filled with it (a hit flash, a silhouette): a filter\n * made and destroyed on every hit of every body was garbage and shader state a blow. Kept for good —\n * a handful of colours are ever used.\n */\nconst FILLS = new Map<number, ColorMatrixFilter[]>();\n\nfunction fillFilters(color: number): ColorMatrixFilter[] {\n let list = FILLS.get(color);\n if (list) return list;\n const f = new ColorMatrixFilter();\n f.antialias = 'off';\n const r = ((color >> 16) & 0xff) / 255;\n const g = ((color >> 8) & 0xff) / 255;\n const b = (color & 0xff) / 255;\n f.matrix = [0, 0, 0, 0, r, 0, 0, 0, 0, g, 0, 0, 0, 0, b, 0, 0, 0, 1, 0];\n FILLS.set(color, (list = [f]));\n return list;\n}\n\n/** What `GameObject` adds to a Pixi display object. */\nexport interface GameObjectMembers {\n scene: Stage;\n /** False once destroyed. */\n readonly active: boolean;\n /** The draw order in the world layer (`zIndex`): bigger is in front. */\n depth: number;\n /** Scale without the flip: stays positive on a mirrored sprite. */\n scaleX: number;\n scaleY: number;\n flipX: boolean;\n flipY: boolean;\n setScale(x: number, y?: number): this;\n setFlipX(v: boolean): this;\n setFlipY(v: boolean): this;\n setFlip(x: boolean, y: boolean): this;\n setDepth(d: number): this;\n setAlpha(a?: number): this;\n setVisible(v: boolean): this;\n setPosition(x?: number, y?: number): this;\n setRotation(r?: number): this;\n setAngle(a?: number): this;\n setBlendMode(mode: BLEND_MODES): this;\n setTint(color?: number): this;\n /** Paints every opaque pixel in one solid colour (hit flash, silhouettes). */\n setTintFill(color: number): this;\n clearTint(): this;\n /** 0 pins the object to the screen instead of the world. */\n setScrollFactor(f: number): this;\n destroy(): void;\n}\n\n/**\n * The chainable setters the world code was written against (setDepth, setAlpha, setTintFill...), added onto a Pixi\n * display object. Scale is kept apart from flipping, so `scaleX` stays positive on a mirrored sprite as it did\n * before, and a tween on scale cannot undo a flip.\n */\nexport function GameObject<TBase extends Ctor<Container>>(Base: TBase): TBase & Ctor<GameObjectMembers> {\n return class extends Base {\n scene!: Stage;\n private sx = 1;\n private sy = 1;\n private fx = false;\n private fy = false;\n /** The colour this object is filled with (`setTintFill`), or null. */\n private solidFillColor: number | null = null;\n\n /** False once destroyed. */\n get active(): boolean {\n return !this.destroyed;\n }\n\n get depth(): number {\n return this.zIndex;\n }\n\n set depth(d: number) {\n this.zIndex = d;\n }\n\n get scaleX(): number {\n return this.sx;\n }\n\n set scaleX(v: number) {\n this.sx = v;\n this.applyScale();\n }\n\n get scaleY(): number {\n return this.sy;\n }\n\n set scaleY(v: number) {\n this.sy = v;\n this.applyScale();\n }\n\n get flipX(): boolean {\n return this.fx;\n }\n\n set flipX(v: boolean) {\n this.fx = v;\n this.applyScale();\n }\n\n get flipY(): boolean {\n return this.fy;\n }\n\n set flipY(v: boolean) {\n this.fy = v;\n this.applyScale();\n }\n\n private applyScale(): void {\n if (!this.destroyed) this.scale.set(this.sx * (this.fx ? -1 : 1), this.sy * (this.fy ? -1 : 1));\n }\n\n setScale(x: number, y = x): this {\n this.sx = x;\n this.sy = y;\n this.applyScale();\n return this;\n }\n\n setFlipX(v: boolean): this {\n this.flipX = v;\n return this;\n }\n\n setFlipY(v: boolean): this {\n this.flipY = v;\n return this;\n }\n\n setFlip(x: boolean, y: boolean): this {\n this.fx = x;\n this.fy = y;\n this.applyScale();\n return this;\n }\n\n setDepth(d: number): this {\n this.zIndex = d;\n return this;\n }\n\n setAlpha(a = 1): this {\n this.alpha = a;\n return this;\n }\n\n setVisible(v: boolean): this {\n this.visible = v;\n return this;\n }\n\n setPosition(x = 0, y = x): this {\n this.position.set(x, y);\n return this;\n }\n\n setRotation(r = 0): this {\n this.rotation = r;\n return this;\n }\n\n setAngle(a = 0): this {\n this.angle = a;\n return this;\n }\n\n setBlendMode(mode: BLEND_MODES): this {\n this.blendMode = mode;\n return this;\n }\n\n setTint(color = 0xffffff): this {\n this.clearFill();\n this.tint = color;\n return this;\n }\n\n /** Paints every opaque pixel in one solid colour (hit flash, silhouettes). */\n setTintFill(color: number): this {\n this.tint = 0xffffff;\n if (this.solidFillColor === color) return this;\n this.solidFillColor = color;\n this.filters = fillFilters(color);\n return this;\n }\n\n clearTint(): this {\n this.clearFill();\n this.tint = 0xffffff;\n return this;\n }\n\n private clearFill(): void {\n if (this.solidFillColor === null) return;\n this.filters = null;\n this.solidFillColor = null;\n }\n\n /** 0 pins the object to the screen instead of the world (full-screen flashes). */\n setScrollFactor(f: number): this {\n if (f === 0) this.scene.screen.addChild(this);\n return this;\n }\n\n override destroy(): void {\n if (this.destroyed) return;\n // the fill filters are shared (`FILLS`): let go of them, never destroy them\n if (this.solidFillColor !== null) this.filters = null;\n this.solidFillColor = null;\n // `context`: a Pixi Graphics destroyed with options keeps its GraphicsContext — and that context's GPU buffers —\n // until Pixi's own GC sweeps it a minute or more later. Every effect layer is a Graphics, so a crowded fight left\n // hundreds a second on the GPU, and a phone that ran out dropped the WebGL context (the world rebuilt, seconds\n // frozen, the socket timed out).\n super.destroy({ children: true, context: true });\n }\n };\n}\n"],"names":["FILLS","fillFilters","color","list","f","ColorMatrixFilter","r","g","b","GameObject","Base","d","v","x","y","a","mode"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pixiPatches.js","sources":["../../src/core/pixiPatches.ts"],"sourcesContent":["import { BatchTextureArray } from 'pixi.js';\n\nlet patched = false;\n\n/**\n * Pixi 8's `BatchTextureArray.clear` sets `ids[source.uid] = null` instead of deleting the key, and its batches are\n * pooled, so every pooled batch keeps one key for every texture source ever drawn through it — about 70% of what a\n * fight's heap grew by in an hour (Pixi 8.21.0). The replacement deletes the key, which reads the\n * same to Pixi (`checkAndUpdateTexture` treats null and a missing key alike). Applied once, and only while `clear`\n * still has the shape it was written against, so a Pixi that fixed or changed it is left alone.\n */\nexport function patchPixi(): boolean {\n if (patched) return true;\n const proto = BatchTextureArray.prototype as unknown as { clear: () => void };\n const probe = new BatchTextureArray() as unknown as { ids: unknown; textures: unknown; count: unknown };\n const shaped = typeof proto.clear === 'function' && typeof probe.ids === 'object' && Array.isArray(probe.textures) && typeof probe.count === 'number';\n if (!shaped || !/ids\\[[^\\]]+\\]\\s*=\\s*null/.test(String(proto.clear))) return false;\n proto.clear = function clear(this: { ids: Record<number, number | null>; textures: ({ uid: number } | null)[]; count: number }): void {\n const ids = this.ids;\n const textures = this.textures;\n for (let i = 0; i < this.count; i++) {\n const t = textures[i];\n textures[i] = null;\n if (t) delete ids[t.uid];\n }\n this.count = 0;\n };\n patched = true;\n return true;\n}\n"],"names":["patched","patchPixi","proto","BatchTextureArray","probe","ids","textures","i","t"],"mappings":";AAEA,IAAIA,IAAU;AASP,SAASC,IAAqB;AACnC,MAAID,EAAS,QAAO;AACpB,QAAME,IAAQC,EAAkB,WAC1BC,IAAQ,IAAID,EAAA;AAElB,SAAI,EADW,OAAOD,EAAM,SAAU,cAAc,OAAOE,EAAM,OAAQ,YAAY,MAAM,QAAQA,EAAM,QAAQ,KAAK,OAAOA,EAAM,SAAU,aAC9H,CAAC,2BAA2B,KAAK,OAAOF,EAAM,KAAK,CAAC,IAAU,MAC7EA,EAAM,QAAQ,WAAwH;AACpI,UAAMG,IAAM,KAAK,KACXC,IAAW,KAAK;AACtB,aAASC,IAAI,GAAGA,IAAI,KAAK,OAAOA,KAAK;AACnC,YAAMC,IAAIF,EAASC,CAAC;AACpB,MAAAD,EAASC,CAAC,IAAI,MACVC,KAAG,OAAOH,EAAIG,EAAE,GAAG;AAAA,IACzB;AACA,SAAK,QAAQ;AAAA,EACf,GACAR,IAAU,IACH;AACT;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"timelinePlace.js","sources":["../../src/core/timelinePlace.ts"],"sourcesContent":["/**\n * Where a child goes on a timeline: a time in ms from its start, or a string —\n * `'<'` the start of the child added last, `'>'` its end, `'+=200'` / `'-=100'` 200 ms after / 100 ms before the\n * default place (the timeline's end, or a `parallel` group's start), `'intro'` a label, and any of `'<'`, `'>'` or a\n * label followed by `+=n` / `-=n`. A string of digits is a time.\n */\nexport type TimelinePosition = number | string;\n\n/** What a position is measured against; `labels` is written to when a position names a label not yet set. */\nexport interface PlaceContext {\n /** Where a child goes when no position is given: the timeline's end, or the open `parallel` group's start. */\n fallback: number;\n /** The end of the timeline as built so far: where a label first named in a position is set. */\n end: number;\n lastStart: number;\n lastEnd: number;\n labels: Map<string, number>;\n}\n\nconst SHAPE = /^(.*?)(?:([+-])=(\\d+(?:\\.\\d+)?))?$/;\nconst DIGITS = /^\\d+(?:\\.\\d+)?$/;\n\n/**\n * The start time (ms, never below 0) `at` names. A label not set yet is set at the timeline's end, the way GSAP does,\n * so `to(a, …, { at: 'hit' })` both places the child and names the spot for the children after it.\n */\nexport function placeAt(at: TimelinePosition | undefined, ctx: PlaceContext): number {\n if (at === undefined) return ctx.fallback;\n if (typeof at === 'number') return Math.max(0, at);\n const text = at.trim();\n if (DIGITS.test(text)) return Number(text);\n const m = SHAPE.exec(text);\n // the pattern matches any string (both parts may be empty), so this is only for the type checker\n if (!m) return ctx.fallback;\n const anchor = m[1] ?? '';\n let base: number;\n if (anchor === '') base = ctx.fallback;\n else if (anchor === '<') base = ctx.lastStart;\n else if (anchor === '>') base = ctx.lastEnd;\n else {\n const label = ctx.labels.get(anchor);\n if (label === undefined) {\n ctx.labels.set(anchor, ctx.end);\n base = ctx.end;\n } else base = label;\n }\n const amount = m[3] === undefined ? 0 : Number(m[3]) * (m[2] === '-' ? -1 : 1);\n return Math.max(0, base + amount);\n}\n"],"names":["SHAPE","DIGITS","placeAt","at","ctx","text","m","anchor","base","label","amount"],"mappings":"AAmBA,MAAMA,IAAQ,sCACRC,IAAS;AAMR,SAASC,EAAQC,GAAkCC,GAA2B;AACnF,MAAID,MAAO,OAAW,QAAOC,EAAI;AACjC,MAAI,OAAOD,KAAO,iBAAiB,KAAK,IAAI,GAAGA,CAAE;AACjD,QAAME,IAAOF,EAAG,KAAA;AAChB,MAAIF,EAAO,KAAKI,CAAI,EAAG,QAAO,OAAOA,CAAI;AACzC,QAAMC,IAAIN,EAAM,KAAKK,CAAI;AAEzB,MAAI,CAACC,EAAG,QAAOF,EAAI;AACnB,QAAMG,IAASD,EAAE,CAAC,KAAK;AACvB,MAAIE;AACJ,MAAID,MAAW,GAAI,CAAAC,IAAOJ,EAAI;AAAA,WACrBG,MAAW,IAAK,CAAAC,IAAOJ,EAAI;AAAA,WAC3BG,MAAW,IAAK,CAAAC,IAAOJ,EAAI;AAAA,OAC/B;AACH,UAAMK,IAAQL,EAAI,OAAO,IAAIG,CAAM;AACnC,IAAIE,MAAU,UACZL,EAAI,OAAO,IAAIG,GAAQH,EAAI,GAAG,GAC9BI,IAAOJ,EAAI,OACNI,IAAOC;AAAA,EAChB;AACA,QAAMC,IAASJ,EAAE,CAAC,MAAM,SAAY,IAAI,OAAOA,EAAE,CAAC,CAAC,KAAKA,EAAE,CAAC,MAAM,MAAM,KAAK;AAC5E,SAAO,KAAK,IAAI,GAAGE,IAAOE,CAAM;AAClC;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tweenProps.js","sources":["../../src/core/tweenProps.ts"],"sourcesContent":["import type { TweenTarget } from './Tweener';\n\n/**\n * How a tween reads and writes one property, shared by `Tweener` and `Timeline` so both treat `scale` the same way:\n * one number written to `scaleX` and `scaleY`, the shape every display object here exposes.\n */\nexport function readProp(target: TweenTarget, prop: string): number {\n const v = prop === 'scale' ? target['scaleX'] : target[prop];\n return typeof v === 'number' ? v : 0;\n}\n\nexport function writeProp(target: TweenTarget, prop: string, value: number): void {\n if (prop === 'scale') {\n target['scaleX'] = value;\n target['scaleY'] = value;\n } else target[prop] = value;\n}\n"],"names":["readProp","target","prop","v","writeProp","value"],"mappings":"AAMO,SAASA,EAASC,GAAqBC,GAAsB;AAClE,QAAMC,IAAID,MAAS,UAAUD,EAAO,SAAYA,EAAOC,CAAI;AAC3D,SAAO,OAAOC,KAAM,WAAWA,IAAI;AACrC;AAEO,SAASC,EAAUH,GAAqBC,GAAcG,GAAqB;AAChF,EAAIH,MAAS,WACXD,EAAO,SAAYI,GACnBJ,EAAO,SAAYI,KACdJ,EAAOC,CAAI,IAAIG;AACxB;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DebugOverlay.js","sources":["../../src/debug/DebugOverlay.ts"],"sourcesContent":["import { Container, Graphics, Sprite, Text, Texture } from 'pixi.js';\nimport type { Renderer } from 'pixi.js';\nimport type { ComponentType, World } from '../ecs/World';\nimport type { Engine } from '../engine/Engine';\nimport { DrawCallCounter } from './DrawCallCounter';\nimport { ErrorLog } from './ErrorLog';\nimport { FrameStats } from './FrameStats';\nimport { inspectWorld } from './inspectWorld';\n\n/** Draws one box in world coordinates (a hurtbox, a wall, a trigger), in `color` (default green). */\nexport type BoxDrawer = (x: number, y: number, width: number, height: number, color?: number) => void;\n\n/** Draws one line in world coordinates (a turned collider's edge, a ray), in `color` (default green). */\nexport type LineDrawer = (x0: number, y0: number, x1: number, y1: number, color?: number) => void;\n\nexport interface DebugOverlayOptions {\n /**\n * `KeyboardEvent.code` that shows and hides it (default 'Backquote'). The key's own browser action is prevented\n * (F3 would open the find bar, F12 the developer tools).\n */\n key?: string;\n /** The world to count and inspect (default the current scene's). */\n world?: () => World | undefined;\n /** Component types the inspector lists; none, and it lists every entity with all it holds. */\n components?: readonly ComponentType<unknown>[];\n /** Entities listed at most (default 12). */\n inspectLimit?: number;\n /** Called every shown frame to draw collision boxes (and lines: turned shapes, rays). */\n colliders?: (draw: BoxDrawer, line: LineDrawer) => void;\n /** Errors kept (default 8). */\n errors?: number;\n /** Where the key is listened for (default `window`). */\n target?: Window | HTMLElement;\n}\n\nconst GRAPH_W = 180;\nconst GRAPH_H = 40;\n/** Text is rebuilt this often: a Pixi `Text` re-rasterises on every change, which would cost more than it measures. */\nconst TEXT_MS = 250;\n\n/**\n * A developer overlay toggled by a key: FPS with a frame-time graph, draw calls and textures, the entity count, a\n * list of entities and their components, collision boxes, and the last errors the frame guard caught.\n *\n * Hidden, it costs nothing per frame: nothing is drawn or listened to but the key and the engine's `error` event\n * (so faults from before it was opened are still listed). Shown, it rides the engine's `frame` event.\n */\nexport class DebugOverlay {\n readonly errors: ErrorLog;\n private readonly stats = new FrameStats(GRAPH_W / 2);\n private readonly panel = new Container();\n private readonly back = new Sprite(Texture.WHITE);\n private readonly graph = new Graphics();\n private readonly text = new Text({ text: '', style: { fontFamily: 'monospace', fontSize: 11, fill: 0xe8e8e8, lineHeight: 13 } });\n private readonly boxes = new Graphics();\n private readonly boxLayer = new Container();\n private readonly draws: DrawCallCounter;\n private readonly cleanup: (() => void)[] = [];\n private offFrame: (() => void) | null = null;\n private lastText = -Infinity;\n private drawCalls = 0;\n private shown = false;\n private readonly drawBox: BoxDrawer = (x, y, w, h, color = 0x40ff60) => {\n this.boxes.rect(x, y, w, h).stroke({ width: 1, color, alpha: 0.9, pixelLine: true });\n };\n\n private readonly drawLine: LineDrawer = (x0, y0, x1, y1, color = 0x40ff60) => {\n this.boxes.moveTo(x0, y0).lineTo(x1, y1).stroke({ width: 1, color, alpha: 0.9, pixelLine: true });\n };\n\n constructor(\n private readonly engine: Engine,\n private readonly options: DebugOverlayOptions = {},\n ) {\n this.errors = new ErrorLog(options.errors ?? 8);\n this.draws = new DrawCallCounter(engine.app.renderer);\n this.back.tint = 0x000000;\n this.back.alpha = 0.7;\n this.graph.position.set(6, 6);\n this.text.position.set(6, GRAPH_H + 12);\n this.panel.addChild(this.back, this.graph, this.text);\n this.boxLayer.addChild(this.boxes);\n this.cleanup.push(engine.events.on('error', ({ where, error }) => this.errors.add(where, error)));\n const target = options.target ?? window;\n const key = options.key ?? 'Backquote';\n const onKey = (e: Event) => {\n const k = e as KeyboardEvent;\n if (k.code !== key) return;\n // the overlay's key, not the browser's: F3 opened find-in-page over the game\n k.preventDefault();\n if (!k.repeat) this.toggle();\n };\n target.addEventListener('keydown', onKey);\n this.cleanup.push(() => target.removeEventListener('keydown', onKey));\n }\n\n get visible(): boolean {\n return this.shown;\n }\n\n toggle(): void {\n if (this.shown) this.hide();\n else this.show();\n }\n\n show(): void {\n if (this.shown) return;\n this.shown = true;\n const stage = this.engine.stage;\n stage.screen.addChild(this.boxLayer, this.panel);\n stage.cameras.main.follow(this.boxLayer);\n this.draws.install();\n this.draws.take();\n this.stats.clear();\n this.lastText = -Infinity;\n this.offFrame = this.engine.events.on('frame', ({ now, dt }) => this.frame(now, dt));\n }\n\n hide(): void {\n if (!this.shown) return;\n this.shown = false;\n this.offFrame?.();\n this.offFrame = null;\n this.draws.uninstall();\n this.engine.stage.cameras.main.unfollow(this.boxLayer);\n this.panel.removeFromParent();\n this.boxLayer.removeFromParent();\n this.boxes.clear();\n }\n\n destroy(): void {\n this.hide();\n for (const fn of this.cleanup.splice(0)) fn();\n this.panel.destroy({ children: true });\n this.boxLayer.destroy({ children: true });\n }\n\n private frame(now: number, dt: number): void {\n this.stats.push(dt * 1000);\n // counted while the previous frame rendered: the 'frame' event comes before this frame's render\n this.drawCalls = this.draws.take();\n this.paintGraph();\n this.boxes.clear();\n if (this.options.colliders) this.options.colliders(this.drawBox, this.drawLine);\n if (now - this.lastText >= TEXT_MS) {\n this.lastText = now;\n this.paintText();\n }\n }\n\n private paintGraph(): void {\n const g = this.graph.clear();\n g.rect(0, 0, GRAPH_W, GRAPH_H).fill({ color: 0x202020 });\n // 16.7 ms (60 fps) and 33.3 ms (30 fps) guides; the graph tops out at 50 ms\n for (const ms of [1000 / 60, 1000 / 30]) g.rect(0, GRAPH_H - (ms / 50) * GRAPH_H, GRAPH_W, 1).fill({ color: 0x505050 });\n const n = this.stats.count;\n for (let i = 0; i < n; i++) {\n const ms = this.stats.at(i);\n const h = Math.max(1, Math.min(GRAPH_H, Math.round((ms / 50) * GRAPH_H)));\n const color = ms > 1000 / 30 ? 0xff5050 : ms > 1000 / 55 ? 0xffc040 : 0x60e070;\n g.rect(i * 2, GRAPH_H - h, 2, h).fill({ color });\n }\n }\n\n private paintText(): void {\n const renderer = this.engine.app.renderer;\n const world = this.options.world ? this.options.world() : this.engine.scene?.world;\n const lines: string[] = [\n `${this.stats.fps.toFixed(0)} fps avg ${this.stats.average.toFixed(1)} ms max ${this.stats.max.toFixed(1)} ms`,\n `draw calls ${this.draws.supported ? this.drawCalls : 'n/a'} textures ${textureCount(renderer)}`,\n `entities ${world ? world.size : '-'} scene ${this.engine.scene?.constructor.name ?? '-'}`,\n ];\n if (world) {\n for (const row of inspectWorld(world, this.options.components, this.options.inspectLimit ?? 12)) lines.push(`#${row.entity} ${row.components.join(' | ')}`);\n }\n if (this.errors.entries.length) {\n lines.push('errors:');\n for (const e of this.errors.entries) lines.push(`${e.count > 1 ? `${e.count}x ` : ''}${e.where}: ${e.message}`.slice(0, 120));\n }\n this.text.text = lines.join('\\n');\n this.back.width = Math.max(GRAPH_W + 12, Math.ceil(this.text.width) + 12);\n this.back.height = Math.ceil(this.text.y + this.text.height + 6);\n }\n}\n\n/** Textures the renderer holds on the GPU; the WebGL and WebGPU systems expose it, the canvas fallback does not. */\nfunction textureCount(renderer: Renderer): number | string {\n const managed = (renderer.texture as { managedTextures?: readonly unknown[] }).managedTextures;\n return managed ? managed.length : 'n/a';\n}\n"],"names":["GRAPH_W","GRAPH_H","TEXT_MS","DebugOverlay","engine","options","ErrorLog","DrawCallCounter","where","error","target","key","onKey","e","k","FrameStats","Container","Sprite","Texture","Graphics","Text","x","y","w","h","color","x0","y0","x1","y1","stage","now","dt","fn","g","ms","n","i","renderer","world","lines","textureCount","row","inspectWorld","managed"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DrawCallCounter.js","sources":["../../src/debug/DrawCallCounter.ts"],"sourcesContent":["import type { Renderer } from 'pixi.js';\n\nconst DRAW_CALLS = ['drawElements', 'drawArrays', 'drawElementsInstanced', 'drawArraysInstanced'] as const;\n\n/**\n * Counts WebGL draw calls by wrapping the context's draw functions while it is installed, since Pixi 8 keeps no\n * count of its own. Installed only while the debug overlay is open, so a closed overlay costs nothing; on WebGPU\n * (no `gl`) it counts nothing and says so.\n */\nexport class DrawCallCounter {\n /** Draw calls since the last `take`. */\n private calls = 0;\n private restore: (() => void) | null = null;\n\n constructor(private readonly renderer: Renderer) {}\n\n /** Whether this renderer can be counted (WebGL). */\n get supported(): boolean {\n return glOf(this.renderer) !== null;\n }\n\n install(): void {\n const gl = glOf(this.renderer);\n if (!gl || this.restore) return;\n const target = gl as unknown as Record<string, unknown>;\n const saved: [string, unknown][] = [];\n for (const name of DRAW_CALLS) {\n const fn = target[name];\n if (typeof fn !== 'function') continue;\n saved.push([name, fn]);\n target[name] = (...args: unknown[]) => {\n this.calls++;\n return (fn as (...a: unknown[]) => unknown).apply(gl, args);\n };\n }\n this.restore = () => {\n for (const [name, fn] of saved) target[name] = fn;\n };\n }\n\n uninstall(): void {\n this.restore?.();\n this.restore = null;\n }\n\n /** Draw calls since the last call, and starts counting again. */\n take(): number {\n const n = this.calls;\n this.calls = 0;\n return n;\n }\n}\n\nfunction glOf(renderer: Renderer): WebGL2RenderingContext | WebGLRenderingContext | null {\n const gl = (renderer as unknown as { gl?: WebGL2RenderingContext | WebGLRenderingContext }).gl;\n return gl ?? null;\n}\n"],"names":["DRAW_CALLS","DrawCallCounter","renderer","glOf","gl","target","saved","name","fn","args","n"],"mappings":"AAEA,MAAMA,IAAa,CAAC,gBAAgB,cAAc,yBAAyB,qBAAqB;AAOzF,MAAMC,EAAgB;AAAA,EAK3B,YAA6BC,GAAoB;AAApB,SAAA,WAAAA;AAAA,EAAqB;AAAA,EAArB;AAAA;AAAA,EAHrB,QAAQ;AAAA,EACR,UAA+B;AAAA;AAAA,EAKvC,IAAI,YAAqB;AACvB,WAAOC,EAAK,KAAK,QAAQ,MAAM;AAAA,EACjC;AAAA,EAEA,UAAgB;AACd,UAAMC,IAAKD,EAAK,KAAK,QAAQ;AAC7B,QAAI,CAACC,KAAM,KAAK,QAAS;AACzB,UAAMC,IAASD,GACTE,IAA6B,CAAA;AACnC,eAAWC,KAAQP,GAAY;AAC7B,YAAMQ,IAAKH,EAAOE,CAAI;AACtB,MAAI,OAAOC,KAAO,eAClBF,EAAM,KAAK,CAACC,GAAMC,CAAE,CAAC,GACrBH,EAAOE,CAAI,IAAI,IAAIE,OACjB,KAAK,SACGD,EAAoC,MAAMJ,GAAIK,CAAI;AAAA,IAE9D;AACA,SAAK,UAAU,MAAM;AACnB,iBAAW,CAACF,GAAMC,CAAE,KAAKF,EAAO,CAAAD,EAAOE,CAAI,IAAIC;AAAA,IACjD;AAAA,EACF;AAAA,EAEA,YAAkB;AAChB,SAAK,UAAA,GACL,KAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAGA,OAAe;AACb,UAAME,IAAI,KAAK;AACf,gBAAK,QAAQ,GACNA;AAAA,EACT;AACF;AAEA,SAASP,EAAKD,GAA2E;AAEvF,SADYA,EAAgF,MAC/E;AACf;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorLog.js","sources":["../../src/debug/ErrorLog.ts"],"sourcesContent":["/** One fault the frame guard (or a system) caught. */\nexport interface LoggedError {\n where: string;\n message: string;\n /** How many times in a row the same fault came in. */\n count: number;\n at: number;\n}\n\n/**\n * The last `limit` distinct faults, newest last. A fault repeating every frame is one entry with a count, not\n * sixty lines a second pushing everything else out.\n */\nexport class ErrorLog {\n readonly entries: LoggedError[] = [];\n\n constructor(readonly limit = 10) {}\n\n add(where: string, error: unknown, at = Date.now()): void {\n const message = error instanceof Error ? error.message : String(error);\n const last = this.entries[this.entries.length - 1];\n if (last && last.where === where && last.message === message) {\n last.count++;\n last.at = at;\n return;\n }\n this.entries.push({ where, message, count: 1, at });\n if (this.entries.length > this.limit) this.entries.shift();\n }\n\n clear(): void {\n this.entries.length = 0;\n }\n}\n"],"names":["ErrorLog","limit","where","error","at","message","last"],"mappings":"AAaO,MAAMA,EAAS;AAAA,EAGpB,YAAqBC,IAAQ,IAAI;AAAZ,SAAA,QAAAA;AAAA,EAAa;AAAA,EAAb;AAAA,EAFZ,UAAyB,CAAA;AAAA,EAIlC,IAAIC,GAAeC,GAAgBC,IAAK,KAAK,OAAa;AACxD,UAAMC,IAAUF,aAAiB,QAAQA,EAAM,UAAU,OAAOA,CAAK,GAC/DG,IAAO,KAAK,QAAQ,KAAK,QAAQ,SAAS,CAAC;AACjD,QAAIA,KAAQA,EAAK,UAAUJ,KAASI,EAAK,YAAYD,GAAS;AAC5D,MAAAC,EAAK,SACLA,EAAK,KAAKF;AACV;AAAA,IACF;AACA,SAAK,QAAQ,KAAK,EAAE,OAAAF,GAAO,SAAAG,GAAS,OAAO,GAAG,IAAAD,GAAI,GAC9C,KAAK,QAAQ,SAAS,KAAK,SAAO,KAAK,QAAQ,MAAA;AAAA,EACrD;AAAA,EAEA,QAAc;AACZ,SAAK,QAAQ,SAAS;AAAA,EACxB;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FrameStats.js","sources":["../../src/debug/FrameStats.ts"],"sourcesContent":["/**\n * The last `size` frame times in a ring of numbers: pure, allocation-free, read by the debug overlay's graph and\n * its FPS line. Averages are over the ring, so one long frame shows in `max` without wrecking the FPS shown.\n */\nexport class FrameStats {\n readonly times: Float32Array;\n private head = 0;\n private filled = 0;\n\n constructor(readonly size = 120) {\n this.times = new Float32Array(size);\n }\n\n /** Records one frame's length in ms. */\n push(ms: number): void {\n this.times[this.head] = ms;\n this.head = (this.head + 1) % this.size;\n if (this.filled < this.size) this.filled++;\n }\n\n get count(): number {\n return this.filled;\n }\n\n /** The `i`-th frame, oldest first. */\n at(i: number): number {\n return this.times[(this.head - this.filled + i + this.size) % this.size]!;\n }\n\n get average(): number {\n if (this.filled === 0) return 0;\n let sum = 0;\n for (let i = 0; i < this.filled; i++) sum += this.times[i]!;\n return sum / this.filled;\n }\n\n get max(): number {\n let m = 0;\n for (let i = 0; i < this.filled; i++) if (this.times[i]! > m) m = this.times[i]!;\n return m;\n }\n\n get fps(): number {\n const avg = this.average;\n return avg > 0 ? 1000 / avg : 0;\n }\n\n clear(): void {\n this.head = 0;\n this.filled = 0;\n }\n}\n"],"names":["FrameStats","size","ms","i","sum","m","avg"],"mappings":"AAIO,MAAMA,EAAW;AAAA,EAKtB,YAAqBC,IAAO,KAAK;AAAZ,SAAA,OAAAA,GACnB,KAAK,QAAQ,IAAI,aAAaA,CAAI;AAAA,EACpC;AAAA,EAFqB;AAAA,EAJZ;AAAA,EACD,OAAO;AAAA,EACP,SAAS;AAAA;AAAA,EAOjB,KAAKC,GAAkB;AACrB,SAAK,MAAM,KAAK,IAAI,IAAIA,GACxB,KAAK,QAAQ,KAAK,OAAO,KAAK,KAAK,MAC/B,KAAK,SAAS,KAAK,QAAM,KAAK;AAAA,EACpC;AAAA,EAEA,IAAI,QAAgB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,GAAGC,GAAmB;AACpB,WAAO,KAAK,OAAO,KAAK,OAAO,KAAK,SAASA,IAAI,KAAK,QAAQ,KAAK,IAAI;AAAA,EACzE;AAAA,EAEA,IAAI,UAAkB;AACpB,QAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,QAAIC,IAAM;AACV,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,IAAK,CAAAA,KAAO,KAAK,MAAM,CAAC;AACzD,WAAOA,IAAM,KAAK;AAAA,EACpB;AAAA,EAEA,IAAI,MAAc;AAChB,QAAIC,IAAI;AACR,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,IAAK,CAAI,KAAK,MAAM,CAAC,IAAKA,MAAGA,IAAI,KAAK,MAAM,CAAC;AAC9E,WAAOA;AAAA,EACT;AAAA,EAEA,IAAI,MAAc;AAChB,UAAMC,IAAM,KAAK;AACjB,WAAOA,IAAM,IAAI,MAAOA,IAAM;AAAA,EAChC;AAAA,EAEA,QAAc;AACZ,SAAK,OAAO,GACZ,KAAK,SAAS;AAAA,EAChB;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"inspectWorld.js","sources":["../../src/debug/inspectWorld.ts"],"sourcesContent":["import type { ComponentType, Entity, World } from '../ecs/World';\n\n/** One entity as the inspector lists it. */\nexport interface EntityRow {\n entity: Entity;\n /** `Name` or `Name {…}` per component it holds, of those asked about. */\n components: string[];\n}\n\n/**\n * Lists up to `limit` entities with a short preview of each component's value: those holding any of `components`\n * when the game names some (only those shown), else every live entity with all it holds (`World.entities`,\n * `componentsOf`). For the debug overlay: it allocates, and runs a few times a second only while the overlay is open.\n */\nexport function inspectWorld(world: World, components?: readonly ComponentType<unknown>[], limit = 20, previewLength = 40): EntityRow[] {\n if (!components?.length) {\n return world\n .entities()\n .slice(0, limit)\n .map((e) => ({ entity: e, components: world.componentsOf(e).map((type) => describe(type, world.get(e, type), previewLength)) }));\n }\n const rows = new Map<Entity, EntityRow>();\n for (const type of components) {\n world.each(type, (e, value) => {\n let row = rows.get(e);\n if (!row) {\n if (rows.size >= limit) return;\n row = { entity: e, components: [] };\n rows.set(e, row);\n }\n row.components.push(describe(type, value, previewLength));\n });\n }\n return [...rows.values()].sort((a, b) => a.entity - b.entity);\n}\n\nfunction describe(type: ComponentType<unknown>, value: unknown, max: number): string {\n // a field component's stored value is only its slot: the numbers live in the world's columns\n if ('fields' in type) return type.name;\n let text: string;\n try {\n text = typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value);\n } catch {\n text = '[unprintable]';\n }\n if (text === undefined) return type.name;\n return `${type.name} ${text.length > max ? text.slice(0, max - 1) + '…' : text}`;\n}\n"],"names":["inspectWorld","world","components","limit","previewLength","type","describe","rows","e","value","row","a","b","max","text"],"mappings":"AAcO,SAASA,EAAaC,GAAcC,GAAgDC,IAAQ,IAAIC,IAAgB,IAAiB;AACtI,MAAI,CAACF,GAAY;AACf,WAAOD,EACJ,SAAA,EACA,MAAM,GAAGE,CAAK,EACd,IAAI,CAAC,OAAO,EAAE,QAAQ,GAAG,YAAYF,EAAM,aAAa,CAAC,EAAE,IAAI,CAACI,MAASC,EAASD,GAAMJ,EAAM,IAAI,GAAGI,CAAI,GAAGD,CAAa,CAAC,IAAI;AAEnI,QAAMG,wBAAW,IAAA;AACjB,aAAWF,KAAQH;AACjB,IAAAD,EAAM,KAAKI,GAAM,CAACG,GAAGC,MAAU;AAC7B,UAAIC,IAAMH,EAAK,IAAIC,CAAC;AACpB,UAAI,CAACE,GAAK;AACR,YAAIH,EAAK,QAAQJ,EAAO;AACxB,QAAAO,IAAM,EAAE,QAAQF,GAAG,YAAY,CAAA,EAAC,GAChCD,EAAK,IAAIC,GAAGE,CAAG;AAAA,MACjB;AACA,MAAAA,EAAI,WAAW,KAAKJ,EAASD,GAAMI,GAAOL,CAAa,CAAC;AAAA,IAC1D,CAAC;AAEH,SAAO,CAAC,GAAGG,EAAK,OAAA,CAAQ,EAAE,KAAK,CAACI,GAAGC,MAAMD,EAAE,SAASC,EAAE,MAAM;AAC9D;AAEA,SAASN,EAASD,GAA8BI,GAAgBI,GAAqB;AAEnF,MAAI,YAAYR,EAAM,QAAOA,EAAK;AAClC,MAAIS;AACJ,MAAI;AACF,IAAAA,IAAO,OAAOL,KAAU,YAAYA,MAAU,OAAO,KAAK,UAAUA,CAAK,IAAI,OAAOA,CAAK;AAAA,EAC3F,QAAQ;AACN,IAAAK,IAAO;AAAA,EACT;AACA,SAAIA,MAAS,SAAkBT,EAAK,OAC7B,GAAGA,EAAK,IAAI,IAAIS,EAAK,SAASD,IAAMC,EAAK,MAAM,GAAGD,IAAM,CAAC,IAAI,MAAMC,CAAI;AAChF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorDeduper.js","sources":["../../src/diagnostics/ErrorDeduper.ts"],"sourcesContent":["/**\n * Lets through each distinct error once, and only so many in all: a loop throwing every frame is one report, not\n * sixty a second. Errors are told apart by the first `keyLength` characters of their message.\n */\nexport class ErrorDeduper {\n private readonly seen = new Set<string>();\n\n constructor(\n private readonly max = 10,\n private readonly keyLength = 120,\n ) {}\n\n /** The key to report under, or null when this error was already let through or the allowance is spent. */\n admit(message: string): string | null {\n const key = message.slice(0, this.keyLength);\n if (this.seen.has(key) || this.seen.size >= this.max) return null;\n this.seen.add(key);\n return key;\n }\n\n /** Forgets what was let through (a new session). */\n reset(): void {\n this.seen.clear();\n }\n}\n\n/** The first `lines` lines of a stack, at most `max` characters. */\nexport function trimStack(stack: string | undefined, lines = 4, max = 600): string {\n return (stack ?? '').split('\\n').slice(0, lines).join('\\n').slice(0, max);\n}\n"],"names":["ErrorDeduper","max","keyLength","message","key","trimStack","stack","lines"],"mappings":"AAIO,MAAMA,EAAa;AAAA,EAGxB,YACmBC,IAAM,IACNC,IAAY,KAC7B;AAFiB,SAAA,MAAAD,GACA,KAAA,YAAAC;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EAJF,2BAAW,IAAA;AAAA;AAAA,EAQ5B,MAAMC,GAAgC;AACpC,UAAMC,IAAMD,EAAQ,MAAM,GAAG,KAAK,SAAS;AAC3C,WAAI,KAAK,KAAK,IAAIC,CAAG,KAAK,KAAK,KAAK,QAAQ,KAAK,MAAY,QAC7D,KAAK,KAAK,IAAIA,CAAG,GACVA;AAAA,EACT;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,KAAK,MAAA;AAAA,EACZ;AACF;AAGO,SAASC,EAAUC,GAA2BC,IAAQ,GAAGN,IAAM,KAAa;AACjF,UAAQK,KAAS,IAAI,MAAM;AAAA,CAAI,EAAE,MAAM,GAAGC,CAAK,EAAE,KAAK;AAAA,CAAI,EAAE,MAAM,GAAGN,CAAG;AAC1E;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FrameSampler.js","sources":["../../src/diagnostics/FrameSampler.ts"],"sourcesContent":["import { type Ticker, UPDATE_PRIORITY } from 'pixi.js';\nimport { looksThrottled, summarizeFrames, type FrameSummary } from './frameStats';\n\nexport interface FrameSamplerOptions {\n /** How long one sample spans, ms (default a minute). */\n windowMs?: number;\n /** A frame longer than this is a hitch, ms (default 50). */\n hitchMs?: number;\n /** Fewer frames than this in a window is no sample (default 30). */\n minFrames?: number;\n /** A window whose median frame is over this came from a throttled background window and is dropped (default 500). */\n throttledMedianMs?: number;\n /** Frames are not counted while this says so (default: the page is hidden). */\n paused?: () => boolean;\n now?: () => number;\n}\n\n/**\n * Measures how a game really runs on a player's device: each frame costs one number pushed on an array, and every\n * `windowMs` the window is summed into a `FrameSummary` handed to `onSample` — FPS, the 1% low, the 95th-percentile\n * frame, hitches. The engine sends nothing anywhere: what the game does with a sample (a readout, a report to its\n * server) is the callback's.\n */\nexport class FrameSampler {\n private frames: number[] = [];\n private since: number;\n private readonly opts: Required<Omit<FrameSamplerOptions, 'paused' | 'now'>>;\n private readonly paused: () => boolean;\n private readonly now: () => number;\n\n constructor(private readonly onSample: (summary: FrameSummary) => void, options: FrameSamplerOptions = {}) {\n this.opts = {\n windowMs: options.windowMs ?? 60_000,\n hitchMs: options.hitchMs ?? 50,\n minFrames: options.minFrames ?? 30,\n throttledMedianMs: options.throttledMedianMs ?? 500,\n };\n this.paused = options.paused ?? (() => typeof document !== 'undefined' && document.hidden);\n this.now = options.now ?? (() => performance.now());\n this.since = this.now();\n }\n\n /** One frame of `deltaMs`; sums the window once it is full. */\n push(deltaMs: number): void {\n if (this.paused()) return;\n this.frames.push(deltaMs);\n const now = this.now();\n const span = now - this.since;\n if (span < this.opts.windowMs) return;\n const sample = this.frames;\n this.frames = [];\n this.since = now;\n if (sample.length < this.opts.minFrames) return;\n const summary = summarizeFrames(sample, span, this.opts.hitchMs);\n if (looksThrottled(summary, this.opts.throttledMedianMs)) return;\n this.onSample(summary);\n }\n\n /** Starts a fresh window, dropping what was counted (after a scene change, say). */\n reset(): void {\n this.frames = [];\n this.since = this.now();\n }\n\n /** Feeds the sampler from a Pixi ticker, after the render; returns the detach. */\n attach(ticker: Ticker): () => void {\n const tick = () => this.push(ticker.deltaMS);\n ticker.add(tick, undefined, UPDATE_PRIORITY.UTILITY);\n return () => ticker.remove(tick);\n }\n}\n"],"names":["FrameSampler","onSample","options","deltaMs","now","span","sample","summary","summarizeFrames","looksThrottled","ticker","tick","UPDATE_PRIORITY"],"mappings":";;AAuBO,MAAMA,EAAa;AAAA,EAOxB,YAA6BC,GAA2CC,IAA+B,IAAI;AAA9E,SAAA,WAAAD,GAC3B,KAAK,OAAO;AAAA,MACV,UAAUC,EAAQ,YAAY;AAAA,MAC9B,SAASA,EAAQ,WAAW;AAAA,MAC5B,WAAWA,EAAQ,aAAa;AAAA,MAChC,mBAAmBA,EAAQ,qBAAqB;AAAA,IAAA,GAElD,KAAK,SAASA,EAAQ,WAAW,MAAM,OAAO,WAAa,OAAe,SAAS,SACnF,KAAK,MAAMA,EAAQ,QAAQ,MAAM,YAAY,QAC7C,KAAK,QAAQ,KAAK,IAAA;AAAA,EACpB;AAAA,EAV6B;AAAA,EANrB,SAAmB,CAAA;AAAA,EACnB;AAAA,EACS;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAejB,KAAKC,GAAuB;AAC1B,QAAI,KAAK,SAAU;AACnB,SAAK,OAAO,KAAKA,CAAO;AACxB,UAAMC,IAAM,KAAK,IAAA,GACXC,IAAOD,IAAM,KAAK;AACxB,QAAIC,IAAO,KAAK,KAAK,SAAU;AAC/B,UAAMC,IAAS,KAAK;AAGpB,QAFA,KAAK,SAAS,CAAA,GACd,KAAK,QAAQF,GACTE,EAAO,SAAS,KAAK,KAAK,UAAW;AACzC,UAAMC,IAAUC,EAAgBF,GAAQD,GAAM,KAAK,KAAK,OAAO;AAC/D,IAAII,EAAeF,GAAS,KAAK,KAAK,iBAAiB,KACvD,KAAK,SAASA,CAAO;AAAA,EACvB;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,SAAS,CAAA,GACd,KAAK,QAAQ,KAAK,IAAA;AAAA,EACpB;AAAA;AAAA,EAGA,OAAOG,GAA4B;AACjC,UAAMC,IAAO,MAAM,KAAK,KAAKD,EAAO,OAAO;AAC3C,WAAAA,EAAO,IAAIC,GAAM,QAAWC,EAAgB,OAAO,GAC5C,MAAMF,EAAO,OAAOC,CAAI;AAAA,EACjC;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bootWatch.js","sources":["../../src/diagnostics/bootWatch.ts"],"sourcesContent":["/** What `bootWatch` reports when a start hangs. */\nexport interface BootStall {\n /** The step the start stood on. */\n step: string;\n /** Every step reached, in order, with the ms since the watch began when each was reached. */\n steps: { name: string; atMs: number }[];\n /** `stuck: <step>`, a line short enough for a player's screenshot. */\n detail: string;\n}\n\nexport interface BootWatch {\n /** Moves the watch on to a named step. */\n step(name: string): void;\n /** The start finished: the watch stops. */\n done(): void;\n}\n\n/**\n * Says when a start has not finished `ms` after it began, naming the step it stood on. A world that never got built\n * otherwise leaves the player looking at a loading line forever while the rest of the page goes on under it; named\n * steps turn \"it hangs\" into \"it hangs loading textures\".\n */\nexport function bootWatch(ms: number, onStuck: (stall: BootStall) => void, now: () => number = () => performance.now()): BootWatch {\n const began = now();\n const steps = [{ name: 'init', atMs: 0 }];\n let finished = false;\n const timer = setTimeout(() => {\n if (finished) return;\n const step = steps[steps.length - 1]!.name;\n onStuck({ step, steps: steps.slice(), detail: `stuck: ${step}` });\n }, ms);\n return {\n step(name: string) {\n if (!finished) steps.push({ name, atMs: Math.round(now() - began) });\n },\n done() {\n finished = true;\n clearTimeout(timer);\n },\n };\n}\n"],"names":["bootWatch","ms","onStuck","now","began","steps","finished","timer","step","name"],"mappings":"AAsBO,SAASA,EAAUC,GAAYC,GAAqCC,IAAoB,MAAM,YAAY,OAAkB;AACjI,QAAMC,IAAQD,EAAA,GACRE,IAAQ,CAAC,EAAE,MAAM,QAAQ,MAAM,GAAG;AACxC,MAAIC,IAAW;AACf,QAAMC,IAAQ,WAAW,MAAM;AAC7B,QAAID,EAAU;AACd,UAAME,IAAOH,EAAMA,EAAM,SAAS,CAAC,EAAG;AACtC,IAAAH,EAAQ,EAAE,MAAAM,GAAM,OAAOH,EAAM,SAAS,QAAQ,UAAUG,CAAI,GAAA,CAAI;AAAA,EAClE,GAAGP,CAAE;AACL,SAAO;AAAA,IACL,KAAKQ,GAAc;AACjB,MAAKH,KAAUD,EAAM,KAAK,EAAE,MAAAI,GAAM,MAAM,KAAK,MAAMN,MAAQC,CAAK,GAAG;AAAA,IACrE;AAAA,IACA,OAAO;AACL,MAAAE,IAAW,IACX,aAAaC,CAAK;AAAA,IACpB;AAAA,EAAA;AAEJ;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"captureErrors.js","sources":["../../src/diagnostics/captureErrors.ts"],"sourcesContent":["import { Stage } from '../core/Stage';\nimport { ErrorDeduper, trimStack } from './ErrorDeduper';\n\n/** One error, as handed to `captureErrors`' callback. */\nexport interface CapturedError {\n message: string;\n /** The first lines of its stack, or ''. */\n stack: string;\n /** `uncaught`, `rejection`, or `frame` for one a `Stage` step caught so the frame could go on. */\n source: 'uncaught' | 'rejection' | 'frame';\n}\n\nexport interface CaptureErrorsOptions {\n /** Distinct errors reported in all (default 10). */\n max?: number;\n /** Also report what a `Stage` frame caught (`Stage.onCaught`; default true): there a fault shows only as a stuck view. */\n frames?: boolean;\n /** Shared with the game's own reports (`admit` before sending), so one error is not told twice. */\n deduper?: ErrorDeduper;\n}\n\n/**\n * Hands every error nothing else caught — `error` and `unhandledrejection` on the window, and what a frame caught —\n * to `onError`, each distinct one once. The engine sends nothing itself; reporting it is the game's. Returns the\n * uninstall, which puts back whatever held `Stage.onCaught` before.\n */\nexport function captureErrors(onError: (e: CapturedError) => void, options: CaptureErrorsOptions = {}): () => void {\n const dedupe = options.deduper ?? new ErrorDeduper(options.max ?? 10);\n const report = (message: string, stack: string | undefined, source: CapturedError['source']) => {\n const key = dedupe.admit(message);\n if (key !== null) onError({ message: key, stack: trimStack(stack), source });\n };\n const onWindowError = (e: ErrorEvent) => report(e.message || String(e.error), (e.error as { stack?: string } | undefined)?.stack, 'uncaught');\n const onRejection = (e: PromiseRejectionEvent) => {\n const r = e.reason as { message?: string; stack?: string } | undefined;\n report(r?.message ?? String(e.reason), r?.stack, 'rejection');\n };\n const onCaught = (where: string, err: unknown) => {\n const e = err as { message?: string; stack?: string } | undefined;\n report(`${where}: ${e?.message ?? String(err)}`, e?.stack, 'frame');\n };\n const win = typeof window !== 'undefined' ? window : undefined;\n win?.addEventListener('error', onWindowError);\n win?.addEventListener('unhandledrejection', onRejection);\n const previous = Stage.onCaught;\n if (options.frames !== false) {\n Stage.onCaught = (where, err) => {\n previous?.(where, err);\n onCaught(where, err);\n };\n }\n const installed = Stage.onCaught;\n return () => {\n win?.removeEventListener('error', onWindowError);\n win?.removeEventListener('unhandledrejection', onRejection);\n if (options.frames !== false && Stage.onCaught === installed) Stage.onCaught = previous;\n };\n}\n"],"names":["captureErrors","onError","options","dedupe","ErrorDeduper","report","message","stack","source","key","trimStack","onWindowError","onRejection","r","onCaught","where","err","e","win","previous","Stage","installed"],"mappings":";;AA0BO,SAASA,EAAcC,GAAqCC,IAAgC,IAAgB;AACjH,QAAMC,IAASD,EAAQ,WAAW,IAAIE,EAAaF,EAAQ,OAAO,EAAE,GAC9DG,IAAS,CAACC,GAAiBC,GAA2BC,MAAoC;AAC9F,UAAMC,IAAMN,EAAO,MAAMG,CAAO;AAChC,IAAIG,MAAQ,QAAMR,EAAQ,EAAE,SAASQ,GAAK,OAAOC,EAAUH,CAAK,GAAG,QAAAC,EAAA,CAAQ;AAAA,EAC7E,GACMG,IAAgB,CAAC,MAAkBN,EAAO,EAAE,WAAW,OAAO,EAAE,KAAK,GAAI,EAAE,OAA0C,OAAO,UAAU,GACtIO,IAAc,CAAC,MAA6B;AAChD,UAAMC,IAAI,EAAE;AACZ,IAAAR,EAAOQ,GAAG,WAAW,OAAO,EAAE,MAAM,GAAGA,GAAG,OAAO,WAAW;AAAA,EAC9D,GACMC,IAAW,CAACC,GAAeC,MAAiB;AAChD,UAAMC,IAAID;AACV,IAAAX,EAAO,GAAGU,CAAK,KAAKE,GAAG,WAAW,OAAOD,CAAG,CAAC,IAAIC,GAAG,OAAO,OAAO;AAAA,EACpE,GACMC,IAAM,OAAO,SAAW,MAAc,SAAS;AACrD,EAAAA,GAAK,iBAAiB,SAASP,CAAa,GAC5CO,GAAK,iBAAiB,sBAAsBN,CAAW;AACvD,QAAMO,IAAWC,EAAM;AACvB,EAAIlB,EAAQ,WAAW,OACrBkB,EAAM,WAAW,CAACL,GAAOC,MAAQ;AAC/B,IAAAG,IAAWJ,GAAOC,CAAG,GACrBF,EAASC,GAAOC,CAAG;AAAA,EACrB;AAEF,QAAMK,IAAYD,EAAM;AACxB,SAAO,MAAM;AACX,IAAAF,GAAK,oBAAoB,SAASP,CAAa,GAC/CO,GAAK,oBAAoB,sBAAsBN,CAAW,GACtDV,EAAQ,WAAW,MAASkB,EAAM,aAAaC,QAAiB,WAAWF;AAAA,EACjF;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"contextLoss.js","sources":["../../src/diagnostics/contextLoss.ts"],"sourcesContent":["/*\n * A WebGL context can be taken away at any time: a phone's GPU resetting, a driver crash, an Android WebView taking\n * the GPU back from an app in the background. Pixi does not rebuild a lost world by itself, so the game must — and a\n * GPU that keeps dropping it will not be saved by another try, so after a few losses in a short while it should stop\n * and say so instead of rebuilding forever.\n */\n\nexport interface ContextLossPolicy {\n /** Losses tolerated inside `windowMs` before giving up (default 3). */\n maxLosses?: number;\n /** How far back losses are counted, ms (default two minutes). */\n windowMs?: number;\n}\n\nexport interface ContextLossVerdict {\n /** The losses still inside the window, this one included (unless the page was hidden). */\n losses: number[];\n /** Stop rebuilding: the GPU keeps failing. */\n giveUp: boolean;\n /** How long to wait before rebuilding (`rebuildDelayMs`). */\n delayMs: number;\n}\n\n/**\n * One more context loss at `now`, given the earlier ones. A loss while the page is `hidden` is the system taking the\n * GPU back, not the GPU failing, so it is not counted against it.\n */\nexport function noteContextLoss(earlier: readonly number[], now: number, hidden = false, policy: ContextLossPolicy = {}): ContextLossVerdict {\n const { maxLosses = 3, windowMs = 120_000 } = policy;\n const kept = earlier.filter((t) => now - t < windowMs);\n const losses = hidden ? kept : kept.concat(now);\n return { losses, giveUp: losses.length > maxLosses, delayMs: rebuildDelayMs(losses.length) };\n}\n\n/**\n * How long to wait before rebuilding after a loss, with `losses` counted in the window: a GPU that has just reset is\n * given longer each time it drops the context again (a rebuild at once was dropped at once on a phone).\n */\nexport function rebuildDelayMs(losses: number): number {\n return losses <= 1 ? 250 : losses === 2 ? 2_000 : 5_000;\n}\n\n/**\n * Calls `onLost` with a verdict each time `canvas` (the renderer's) loses its WebGL context; the game tears its world\n * down and rebuilds it after `delayMs`, or shows an error when `giveUp`. The event's default is prevented, which is\n * what lets the browser restore the context at all. Returns the uninstall.\n */\nexport function watchContextLoss(\n canvas: EventTarget,\n onLost: (verdict: ContextLossVerdict) => void,\n options: ContextLossPolicy & { onRestored?: () => void; now?: () => number; hidden?: () => boolean } = {},\n): () => void {\n const now = options.now ?? (() => performance.now());\n const hidden = options.hidden ?? (() => typeof document !== 'undefined' && document.hidden);\n let losses: number[] = [];\n const lost = (e: Event) => {\n e.preventDefault();\n const verdict = noteContextLoss(losses, now(), hidden(), options);\n losses = verdict.losses;\n onLost(verdict);\n };\n const restored = () => options.onRestored?.();\n canvas.addEventListener('webglcontextlost', lost);\n canvas.addEventListener('webglcontextrestored', restored);\n return () => {\n canvas.removeEventListener('webglcontextlost', lost);\n canvas.removeEventListener('webglcontextrestored', restored);\n };\n}\n"],"names":["noteContextLoss","earlier","now","hidden","policy","maxLosses","windowMs","kept","t","losses","rebuildDelayMs","watchContextLoss","canvas","onLost","options","lost","e","verdict","restored"],"mappings":"AA2BO,SAASA,EAAgBC,GAA4BC,GAAaC,IAAS,IAAOC,IAA4B,IAAwB;AAC3I,QAAM,EAAE,WAAAC,IAAY,GAAG,UAAAC,IAAW,SAAYF,GACxCG,IAAON,EAAQ,OAAO,CAACO,MAAMN,IAAMM,IAAIF,CAAQ,GAC/CG,IAASN,IAASI,IAAOA,EAAK,OAAOL,CAAG;AAC9C,SAAO,EAAE,QAAAO,GAAQ,QAAQA,EAAO,SAASJ,GAAW,SAASK,EAAeD,EAAO,MAAM,EAAA;AAC3F;AAMO,SAASC,EAAeD,GAAwB;AACrD,SAAOA,KAAU,IAAI,MAAMA,MAAW,IAAI,MAAQ;AACpD;AAOO,SAASE,EACdC,GACAC,GACAC,IAAuG,CAAA,GAC3F;AACZ,QAAMZ,IAAMY,EAAQ,QAAQ,MAAM,YAAY,IAAA,IACxCX,IAASW,EAAQ,WAAW,MAAM,OAAO,WAAa,OAAe,SAAS;AACpF,MAAIL,IAAmB,CAAA;AACvB,QAAMM,IAAO,CAACC,MAAa;AACzB,IAAAA,EAAE,eAAA;AACF,UAAMC,IAAUjB,EAAgBS,GAAQP,KAAOC,EAAA,GAAUW,CAAO;AAChE,IAAAL,IAASQ,EAAQ,QACjBJ,EAAOI,CAAO;AAAA,EAChB,GACMC,IAAW,MAAMJ,EAAQ,aAAA;AAC/B,SAAAF,EAAO,iBAAiB,oBAAoBG,CAAI,GAChDH,EAAO,iBAAiB,wBAAwBM,CAAQ,GACjD,MAAM;AACX,IAAAN,EAAO,oBAAoB,oBAAoBG,CAAI,GACnDH,EAAO,oBAAoB,wBAAwBM,CAAQ;AAAA,EAC7D;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"frameStats.js","sources":["../../src/diagnostics/frameStats.ts"],"sourcesContent":["/** A summary of the frames of one sampling window. */\nexport interface FrameSummary {\n /** Frames rendered. */\n frames: number;\n /** Frames a second over the window. */\n fps: number;\n /** The \"1% low\": the frame rate of the slowest hundredth of frames, which is what stutter feels like. */\n fpsLow: number;\n /** The 95th-percentile frame time, ms. */\n frameP95: number;\n /** The median frame time, ms. */\n frameMedian: number;\n /** The longest frame, ms. */\n worstMs: number;\n /** Frames longer than the hitch threshold: the ones a player feels. */\n hitches: number;\n}\n\n/** The value at fraction `p` (0..1) of an ascending list, by nearest rank; 0 for an empty list. */\nexport function percentile(sorted: readonly number[], p: number): number {\n if (sorted.length === 0) return 0;\n return sorted[Math.min(sorted.length - 1, Math.max(0, Math.floor(sorted.length * p)))]!;\n}\n\n/** Frames longer than `thresholdMs`. */\nexport function countHitches(frameMs: readonly number[], thresholdMs: number): number {\n let n = 0;\n for (const d of frameMs) if (d > thresholdMs) n++;\n return n;\n}\n\nconst round1 = (n: number) => Math.round(n * 10) / 10;\n\n/** The summary of frame times (ms) rendered over `spanMs`; a frame over `hitchMs` (default 50) is a hitch. */\nexport function summarizeFrames(frameMs: readonly number[], spanMs: number, hitchMs = 50): FrameSummary {\n const sorted = [...frameMs].sort((a, b) => a - b);\n return {\n frames: frameMs.length,\n fps: spanMs > 0 ? round1((frameMs.length * 1000) / spanMs) : 0,\n fpsLow: round1(1000 / Math.max(1, percentile(sorted, 0.99))),\n frameP95: round1(percentile(sorted, 0.95)),\n frameMedian: round1(percentile(sorted, 0.5)),\n worstMs: round1(sorted[sorted.length - 1] ?? 0),\n hitches: countHitches(frameMs, hitchMs),\n };\n}\n\n/**\n * A window the browser throttled: one covered by another window is not `hidden`, but runs at about a frame a second,\n * and a second copy of a game parked behind the first read as its heaviest scene. Nothing really played runs with\n * its median frame half a second long.\n */\nexport function looksThrottled(summary: FrameSummary, medianLimitMs = 500): boolean {\n return summary.frameMedian > medianLimitMs;\n}\n"],"names":["percentile","sorted","p","countHitches","frameMs","thresholdMs","n","d","round1","summarizeFrames","spanMs","hitchMs","a","b","looksThrottled","summary","medianLimitMs"],"mappings":"AAmBO,SAASA,EAAWC,GAA2BC,GAAmB;AACvE,SAAID,EAAO,WAAW,IAAU,IACzBA,EAAO,KAAK,IAAIA,EAAO,SAAS,GAAG,KAAK,IAAI,GAAG,KAAK,MAAMA,EAAO,SAASC,CAAC,CAAC,CAAC,CAAC;AACvF;AAGO,SAASC,EAAaC,GAA4BC,GAA6B;AACpF,MAAIC,IAAI;AACR,aAAWC,KAAKH,EAAS,CAAIG,IAAIF,KAAaC;AAC9C,SAAOA;AACT;AAEA,MAAME,IAAS,CAACF,MAAc,KAAK,MAAMA,IAAI,EAAE,IAAI;AAG5C,SAASG,EAAgBL,GAA4BM,GAAgBC,IAAU,IAAkB;AACtG,QAAMV,IAAS,CAAC,GAAGG,CAAO,EAAE,KAAK,CAACQ,GAAGC,MAAMD,IAAIC,CAAC;AAChD,SAAO;AAAA,IACL,QAAQT,EAAQ;AAAA,IAChB,KAAKM,IAAS,IAAIF,EAAQJ,EAAQ,SAAS,MAAQM,CAAM,IAAI;AAAA,IAC7D,QAAQF,EAAO,MAAO,KAAK,IAAI,GAAGR,EAAWC,GAAQ,IAAI,CAAC,CAAC;AAAA,IAC3D,UAAUO,EAAOR,EAAWC,GAAQ,IAAI,CAAC;AAAA,IACzC,aAAaO,EAAOR,EAAWC,GAAQ,GAAG,CAAC;AAAA,IAC3C,SAASO,EAAOP,EAAOA,EAAO,SAAS,CAAC,KAAK,CAAC;AAAA,IAC9C,SAASE,EAAaC,GAASO,CAAO;AAAA,EAAA;AAE1C;AAOO,SAASG,EAAeC,GAAuBC,IAAgB,KAAc;AAClF,SAAOD,EAAQ,cAAcC;AAC/B;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gpuInfo.js","sources":["../../src/diagnostics/gpuInfo.ts"],"sourcesContent":["import type { Renderer } from 'pixi.js';\n\nfunction glOf(renderer: Renderer): WebGLRenderingContext | undefined {\n return (renderer as { gl?: WebGLRenderingContext }).gl;\n}\n\n/**\n * The largest texture side the GPU takes (WebGL's MAX_TEXTURE_SIZE), or null when the renderer is not WebGL. Many\n * phones stop at 4096, so an atlas planned for 8192 must split its pages there (`atlasPageLimit`).\n */\nexport function maxTextureSize(renderer: Renderer): number | null {\n const gl = glOf(renderer);\n if (!gl) return null;\n const n = Number(gl.getParameter(gl.MAX_TEXTURE_SIZE));\n return Number.isFinite(n) && n > 0 ? n : null;\n}\n\n/** The GPU's own name where the browser gives it (WEBGL_debug_renderer_info), else null; for telling devices apart. */\nexport function gpuName(renderer: Renderer, max = 80): string | null {\n try {\n const gl = glOf(renderer);\n const ext = gl?.getExtension('WEBGL_debug_renderer_info');\n return gl && ext ? String(gl.getParameter(ext.UNMASKED_RENDERER_WEBGL)).slice(0, max) : null;\n } catch {\n return null;\n }\n}\n"],"names":["glOf","renderer","maxTextureSize","gl","n","gpuName","max","ext"],"mappings":"AAEA,SAASA,EAAKC,GAAuD;AACnE,SAAQA,EAA4C;AACtD;AAMO,SAASC,EAAeD,GAAmC;AAChE,QAAME,IAAKH,EAAKC,CAAQ;AACxB,MAAI,CAACE,EAAI,QAAO;AAChB,QAAMC,IAAI,OAAOD,EAAG,aAAaA,EAAG,gBAAgB,CAAC;AACrD,SAAO,OAAO,SAASC,CAAC,KAAKA,IAAI,IAAIA,IAAI;AAC3C;AAGO,SAASC,EAAQJ,GAAoBK,IAAM,IAAmB;AACnE,MAAI;AACF,UAAMH,IAAKH,EAAKC,CAAQ,GAClBM,IAAMJ,GAAI,aAAa,2BAA2B;AACxD,WAAOA,KAAMI,IAAM,OAAOJ,EAAG,aAAaI,EAAI,uBAAuB,CAAC,EAAE,MAAM,GAAGD,CAAG,IAAI;AAAA,EAC1F,QAAQ;AACN,WAAO;AAAA,EACT;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"renderGuard.js","sources":["../../src/diagnostics/renderGuard.ts"],"sourcesContent":["import { type Application, UPDATE_PRIORITY } from 'pixi.js';\n\n/** An error as one short line, for a player's screenshot or a report. */\nexport function describeError(err: unknown, max = 160): string {\n const text = err instanceof Error ? `${err.name}: ${err.message}` : String(err);\n return text.length > max ? `${text.slice(0, max)}…` : text;\n}\n\n/**\n * Replaces Pixi's render on the ticker with one that survives a throw and reports a renderer that keeps failing.\n * A ticker listener that throws never asks for the next frame, and `Stage`'s per-step guard covers the game's steps,\n * not the renderer's: a render that threw on its first frame froze the canvas in silence while the page went on.\n * A single bad frame is let go; `failures` in a row are reported once. Returns the uninstall, which puts Pixi's own\n * render back.\n */\nexport function guardRender(app: Application, onFail: (detail: string, err: unknown) => void, failures = 30): () => void {\n app.ticker.remove(app.render, app);\n let inARow = 0;\n let told = false;\n const render = () => {\n try {\n app.render();\n inARow = 0;\n } catch (err) {\n inARow++;\n if (inARow === 1) console.error('[render] frame failed', err);\n if (inARow >= failures && !told) {\n told = true;\n onFail(`render: ${describeError(err)}`, err);\n }\n }\n };\n app.ticker.add(render, undefined, UPDATE_PRIORITY.LOW);\n return () => {\n app.ticker.remove(render);\n app.ticker.add(app.render, app, UPDATE_PRIORITY.LOW);\n };\n}\n"],"names":["describeError","err","max","text","guardRender","app","onFail","failures","inARow","told","render","UPDATE_PRIORITY"],"mappings":";AAGO,SAASA,EAAcC,GAAcC,IAAM,KAAa;AAC7D,QAAMC,IAAOF,aAAe,QAAQ,GAAGA,EAAI,IAAI,KAAKA,EAAI,OAAO,KAAK,OAAOA,CAAG;AAC9E,SAAOE,EAAK,SAASD,IAAM,GAAGC,EAAK,MAAM,GAAGD,CAAG,CAAC,MAAMC;AACxD;AASO,SAASC,EAAYC,GAAkBC,GAAgDC,IAAW,IAAgB;AACvH,EAAAF,EAAI,OAAO,OAAOA,EAAI,QAAQA,CAAG;AACjC,MAAIG,IAAS,GACTC,IAAO;AACX,QAAMC,IAAS,MAAM;AACnB,QAAI;AACF,MAAAL,EAAI,OAAA,GACJG,IAAS;AAAA,IACX,SAASP,GAAK;AACZ,MAAAO,KACIA,MAAW,KAAG,QAAQ,MAAM,yBAAyBP,CAAG,GACxDO,KAAUD,KAAY,CAACE,MACzBA,IAAO,IACPH,EAAO,WAAWN,EAAcC,CAAG,CAAC,IAAIA,CAAG;AAAA,IAE/C;AAAA,EACF;AACA,SAAAI,EAAI,OAAO,IAAIK,GAAQ,QAAWC,EAAgB,GAAG,GAC9C,MAAM;AACX,IAAAN,EAAI,OAAO,OAAOK,CAAM,GACxBL,EAAI,OAAO,IAAIA,EAAI,QAAQA,GAAKM,EAAgB,GAAG;AAAA,EACrD;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SparseSet.js","sources":["../../src/ecs/SparseSet.ts"],"sourcesContent":["/**\n * One component type's storage: a sparse set. `sparse[index]` is where the entity's value sits in the dense arrays\n * (or -1), and `entities`/`values` are packed with no holes, so walking a component is a walk over two flat arrays\n * and finding one is two array reads. Removing swaps the last entry into the hole.\n *\n * Indexed by the entity's slot index (`entityIndex`), not the whole id: the generation is checked by the world.\n */\nexport class SparseSet<T> {\n private sparse: Int32Array = new Int32Array(64).fill(-1);\n /** Packed entity ids, in the same order as `values`. */\n readonly entities: number[] = [];\n readonly values: T[] = [];\n /** Bumped whenever an entity joins or leaves the set (not when a value changes): query caches key on it. */\n version = 0;\n\n get size(): number {\n return this.entities.length;\n }\n\n has(index: number): boolean {\n return index < this.sparse.length && this.sparse[index]! >= 0;\n }\n\n /** The value at slot `index`, or undefined. */\n get(index: number): T | undefined {\n if (index >= this.sparse.length) return undefined;\n const d = this.sparse[index]!;\n return d >= 0 ? this.values[d] : undefined;\n }\n\n set(index: number, entity: number, value: T): void {\n this.grow(index);\n const d = this.sparse[index]!;\n if (d >= 0) {\n this.values[d] = value;\n this.entities[d] = entity;\n return;\n }\n this.sparse[index] = this.entities.length;\n this.version++;\n this.entities.push(entity);\n this.values.push(value);\n }\n\n /** Removes slot `index`; true if it was there. */\n delete(index: number, indexOf: (entity: number) => number): boolean {\n if (index >= this.sparse.length) return false;\n const d = this.sparse[index]!;\n if (d < 0) return false;\n const last = this.entities.length - 1;\n if (d !== last) {\n const moved = this.entities[last]!;\n this.entities[d] = moved;\n this.values[d] = this.values[last]!;\n this.sparse[indexOf(moved)] = d;\n }\n this.entities.pop();\n this.values.pop();\n this.sparse[index] = -1;\n this.version++;\n return true;\n }\n\n clear(): void {\n this.version++;\n this.sparse.fill(-1);\n this.entities.length = 0;\n this.values.length = 0;\n }\n\n private grow(index: number): void {\n if (index < this.sparse.length) return;\n let n = this.sparse.length;\n while (n <= index) n *= 2;\n const next = new Int32Array(n).fill(-1);\n next.set(this.sparse);\n this.sparse = next;\n }\n}\n"],"names":["SparseSet","index","d","entity","value","indexOf","last","moved","n","next"],"mappings":"AAOO,MAAMA,EAAa;AAAA,EAChB,SAAqB,IAAI,WAAW,EAAE,EAAE,KAAK,EAAE;AAAA;AAAA,EAE9C,WAAqB,CAAA;AAAA,EACrB,SAAc,CAAA;AAAA;AAAA,EAEvB,UAAU;AAAA,EAEV,IAAI,OAAe;AACjB,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA,EAEA,IAAIC,GAAwB;AAC1B,WAAOA,IAAQ,KAAK,OAAO,UAAU,KAAK,OAAOA,CAAK,KAAM;AAAA,EAC9D;AAAA;AAAA,EAGA,IAAIA,GAA8B;AAChC,QAAIA,KAAS,KAAK,OAAO,OAAQ;AACjC,UAAMC,IAAI,KAAK,OAAOD,CAAK;AAC3B,WAAOC,KAAK,IAAI,KAAK,OAAOA,CAAC,IAAI;AAAA,EACnC;AAAA,EAEA,IAAID,GAAeE,GAAgBC,GAAgB;AACjD,SAAK,KAAKH,CAAK;AACf,UAAMC,IAAI,KAAK,OAAOD,CAAK;AAC3B,QAAIC,KAAK,GAAG;AACV,WAAK,OAAOA,CAAC,IAAIE,GACjB,KAAK,SAASF,CAAC,IAAIC;AACnB;AAAA,IACF;AACA,SAAK,OAAOF,CAAK,IAAI,KAAK,SAAS,QACnC,KAAK,WACL,KAAK,SAAS,KAAKE,CAAM,GACzB,KAAK,OAAO,KAAKC,CAAK;AAAA,EACxB;AAAA;AAAA,EAGA,OAAOH,GAAeI,GAA8C;AAClE,QAAIJ,KAAS,KAAK,OAAO,OAAQ,QAAO;AACxC,UAAMC,IAAI,KAAK,OAAOD,CAAK;AAC3B,QAAIC,IAAI,EAAG,QAAO;AAClB,UAAMI,IAAO,KAAK,SAAS,SAAS;AACpC,QAAIJ,MAAMI,GAAM;AACd,YAAMC,IAAQ,KAAK,SAASD,CAAI;AAChC,WAAK,SAASJ,CAAC,IAAIK,GACnB,KAAK,OAAOL,CAAC,IAAI,KAAK,OAAOI,CAAI,GACjC,KAAK,OAAOD,EAAQE,CAAK,CAAC,IAAIL;AAAA,IAChC;AACA,gBAAK,SAAS,IAAA,GACd,KAAK,OAAO,IAAA,GACZ,KAAK,OAAOD,CAAK,IAAI,IACrB,KAAK,WACE;AAAA,EACT;AAAA,EAEA,QAAc;AACZ,SAAK,WACL,KAAK,OAAO,KAAK,EAAE,GACnB,KAAK,SAAS,SAAS,GACvB,KAAK,OAAO,SAAS;AAAA,EACvB;AAAA,EAEQ,KAAKA,GAAqB;AAChC,QAAIA,IAAQ,KAAK,OAAO,OAAQ;AAChC,QAAIO,IAAI,KAAK,OAAO;AACpB,WAAOA,KAAKP,IAAO,CAAAO,KAAK;AACxB,UAAMC,IAAO,IAAI,WAAWD,CAAC,EAAE,KAAK,EAAE;AACtC,IAAAC,EAAK,IAAI,KAAK,MAAM,GACpB,KAAK,SAASA;AAAA,EAChB;AACF;"}
|
package/dist/ecs/World.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"World.js","sources":["../../src/ecs/World.ts"],"sourcesContent":["import { SparseSet } from './SparseSet';\nimport { growColumns, isFields, type Columns, type FieldSpec, type FieldValues, type FieldsType } from './fields';\n\n/**\n * An entity–component–system store. Entities are numbers; components are data kept in one sparse set per component\n * type; systems are functions run in the order they were added, each in a phase:\n *\n * - `fixed` runs once per simulation step (`FixedLoop`), for movement and rules;\n * - `frame` runs once per rendered frame, for drawing and anything purely visual.\n *\n * Storage is a sparse set per component (packed arrays, O(1) lookups), the layout fast ECS libraries use. For hot\n * loops prefer `each` (a callback, no allocation per entity) over `query` (an iterator yielding a fresh row).\n *\n * Entity ids are recycled: an id carries its slot index and a generation, so an id kept after its entity was\n * destroyed never matches the entity that later reuses the slot.\n *\n * Changing structure while walking: adding components is always safe (new entries are not visited this walk).\n * Removing a component or destroying an entity while a `query`, an `each` or a system run is in progress is deferred\n * until it ends, so nothing is skipped or visited twice.\n */\n\nexport type Entity = number;\n\n/** A component type: a typed key. Make one per kind of data with `defineComponent`. */\nexport interface ComponentType<T> {\n readonly id: number;\n readonly name: string;\n /** Phantom field carrying `T`; never set. */\n readonly __type?: T;\n}\n\nlet nextComponentId = 0;\n/** Every component type made, by id: what a debugger names a store by. */\nconst TYPES: ComponentType<unknown>[] = [];\n\nexport function defineComponent<T>(name: string): ComponentType<T> {\n const type = { id: nextComponentId++, name };\n TYPES[type.id] = type;\n return type;\n}\n\n/** A component of numeric fields kept in typed-array columns (`fields.ts`): the fast form for data every body has. */\nexport function defineFields<const S extends FieldSpec>(name: string, fields: S): FieldsType<S> {\n const type = { id: nextComponentId++, name, fields };\n TYPES[type.id] = type;\n return type;\n}\n\nexport type SystemPhase = 'fixed' | 'frame';\n\nexport interface SystemContext {\n world: World;\n /** Seconds this run covers: one step in `fixed`, the frame's length in `frame`. */\n dt: number;\n /** `performance.now()` of the frame. */\n now: number;\n /** In `frame`: the share of a step since the last fixed step (0–1), for interpolation. 0 in `fixed`. */\n alpha: number;\n}\n\nexport interface System {\n readonly name: string;\n readonly phase: SystemPhase;\n run(ctx: SystemContext): void;\n /** Called when the system is taken out of its world (or the world is cleared). */\n dispose?(): void;\n}\n\n/** A system from a function. */\nexport function system(name: string, phase: SystemPhase, run: (ctx: SystemContext) => void): System {\n return { name, phase, run };\n}\n\nexport type ComponentValues<T extends readonly ComponentType<unknown>[]> = { [K in keyof T]: T[K] extends ComponentType<infer V> ? V : never };\n\n/** Slot index in the low 22 bits (4 million live entities), generation above. */\nconst INDEX_BITS = 22;\nconst INDEX_MASK = (1 << INDEX_BITS) - 1;\nconst MAX_GENERATION = 2 ** 31;\n\n/** The slot an entity id sits in. */\nexport const entityIndex = (e: Entity): number => e & INDEX_MASK;\n\ntype Pending = { kind: 'destroy'; e: Entity } | { kind: 'remove'; e: Entity; type: ComponentType<unknown> };\n\nexport class World {\n /** Current generation per slot; an id is alive when its generation matches and the slot is in use. */\n private generations: number[] = [];\n private readonly inUse: boolean[] = [];\n private readonly free: number[] = [];\n private live = 0;\n private readonly stores: (SparseSet<unknown> | undefined)[] = [];\n /** Typed-array columns of the field components, by component id. */\n private readonly cols: (Record<string, ArrayLike<number>> | undefined)[] = [];\n private readonly systems: System[] = [];\n private readonly pending: Pending[] = [];\n /** `slots` results by the component ids asked for, rebuilt only when one of those sets changed membership. */\n private readonly slotCache = new Map<string, { versions: number[]; slots: Int32Array; view: Int32Array }>();\n /** Walks (`query`, `each`, system runs) in progress: structure changes wait until it is 0. */\n private walking = 0;\n\n /** A new entity with no components. */\n create(): Entity {\n let index: number;\n if (this.free.length) {\n index = this.free.pop()!;\n } else {\n index = this.generations.length;\n if (index > INDEX_MASK) throw new Error('World: too many live entities');\n this.generations.push(0);\n this.inUse.push(false);\n }\n this.inUse[index] = true;\n this.live++;\n return this.generations[index]! * (INDEX_MASK + 1) + index;\n }\n\n /** A new entity with these components. */\n spawn(...components: [ComponentType<unknown>, unknown][]): Entity {\n const e = this.create();\n for (const [type, value] of components) this.add(e, type, value);\n return e;\n }\n\n isAlive(e: Entity): boolean {\n const index = entityIndex(e);\n return this.inUse[index] === true && this.generations[index] === Math.floor(e / (INDEX_MASK + 1));\n }\n\n /** Every live entity, for tools (a debugger, a save); allocates, so not for a per-frame path. */\n entities(): Entity[] {\n const out: Entity[] = [];\n for (let i = 0; i < this.inUse.length; i++) if (this.inUse[i]) out.push(this.generations[i]! * (INDEX_MASK + 1) + i);\n return out;\n }\n\n /** The component types `e` holds, for tools; allocates. */\n componentsOf(e: Entity): ComponentType<unknown>[] {\n if (!this.isAlive(e)) return [];\n const index = entityIndex(e);\n const out: ComponentType<unknown>[] = [];\n this.stores.forEach((store, id) => {\n if (store?.has(index)) out.push(TYPES[id]!);\n });\n return out;\n }\n\n /** Live entities. */\n get size(): number {\n return this.live;\n }\n\n /** Removes an entity and every component it had (deferred while a walk is in progress). */\n destroy(e: Entity): void {\n if (!this.isAlive(e)) return;\n if (this.walking > 0) {\n this.pending.push({ kind: 'destroy', e });\n return;\n }\n const index = entityIndex(e);\n for (const store of this.stores) store?.delete(index, entityIndex);\n this.inUse[index] = false;\n this.generations[index] = (this.generations[index]! + 1) % MAX_GENERATION;\n this.free.push(index);\n this.live--;\n }\n\n /** Gives an entity a field component; fields left out start at 0. Returns the slot index into its columns. */\n add<S extends FieldSpec>(e: Entity, type: FieldsType<S>, values?: FieldValues<S>): number;\n add<T>(e: Entity, type: ComponentType<T>, value: T): T;\n add(e: Entity, type: ComponentType<unknown>, value?: unknown): unknown {\n if (!this.isAlive(e)) throw new Error(`World.add: entity ${e} is not alive`);\n const index = entityIndex(e);\n if (isFields(type)) {\n const cols = this.columnsOf(type, index + 1) as Record<string, { [i: number]: number }>;\n const values = (value ?? {}) as Record<string, number>;\n for (const name of Object.keys(type.fields)) cols[name]![index] = values[name] ?? 0;\n this.store(type).set(index, e, index);\n return index;\n }\n this.store(type).set(index, e, value);\n return value;\n }\n\n /**\n * The typed-array columns of a field component, indexed by slot. Fetch them per system run: adding entities may\n * grow them into new arrays.\n */\n columns<S extends FieldSpec>(type: FieldsType<S>): Columns<S> {\n return this.columnsOf(type, this.generations.length) as unknown as Columns<S>;\n }\n\n get<T>(e: Entity, type: ComponentType<T>): T | undefined {\n if (!this.isAlive(e)) return undefined;\n return this.stores[type.id]?.get(entityIndex(e)) as T | undefined;\n }\n\n /** The component, or an error naming what is missing: for code that knows the entity has it. */\n require<T>(e: Entity, type: ComponentType<T>): T {\n const store = this.stores[type.id];\n if (!this.isAlive(e) || !store?.has(entityIndex(e))) throw new Error(`World.require: entity ${e} has no ${type.name}`);\n return store.get(entityIndex(e)) as T;\n }\n\n has(e: Entity, type: ComponentType<unknown>): boolean {\n return this.isAlive(e) && (this.stores[type.id]?.has(entityIndex(e)) ?? false);\n }\n\n /** Takes one component off an entity (deferred while a walk is in progress). */\n remove(e: Entity, type: ComponentType<unknown>): void {\n if (!this.isAlive(e)) return;\n if (this.walking > 0) {\n this.pending.push({ kind: 'remove', e, type });\n return;\n }\n this.stores[type.id]?.delete(entityIndex(e), entityIndex);\n }\n\n /** How many entities hold `type`. */\n count(type: ComponentType<unknown>): number {\n return this.stores[type.id]?.size ?? 0;\n }\n\n /**\n * Calls `fn(entity, ...components)` for every live entity holding all of `types`. Allocates nothing per entity:\n * the form for systems that walk thousands of bodies every step.\n */\n each<const T extends readonly ComponentType<unknown>[]>(...args: [...types: T, fn: (e: Entity, ...values: ComponentValues<T>) => void]): void {\n const fn = args[args.length - 1] as (e: Entity, ...values: unknown[]) => void;\n const types = args.slice(0, -1) as unknown as T;\n const sets = this.setsFor(types);\n if (!sets) return;\n const lead = smallest(sets);\n const n = lead.entities.length;\n this.walking++;\n try {\n if (sets.length === 1) {\n for (let i = n - 1; i >= 0; i--) fn(lead.entities[i]!, lead.values[i]);\n } else if (sets.length === 2) {\n const [a, b] = sets as [SparseSet<unknown>, SparseSet<unknown>];\n for (let i = n - 1; i >= 0; i--) {\n const e = lead.entities[i]!;\n const index = entityIndex(e);\n if (!a.has(index) || !b.has(index)) continue;\n fn(e, a.get(index), b.get(index));\n }\n } else {\n const row: unknown[] = new Array(sets.length);\n for (let i = n - 1; i >= 0; i--) {\n const e = lead.entities[i]!;\n const index = entityIndex(e);\n let all = true;\n for (let s = 0; s < sets.length; s++) {\n const set = sets[s]!;\n if (!set.has(index)) {\n all = false;\n break;\n }\n row[s] = set.get(index);\n }\n if (all) fn(e, ...row);\n }\n }\n } finally {\n this.endWalk();\n }\n }\n\n /**\n * The slot index of every entity holding all of `types`, as a typed array: the fastest walk there is, made for\n * field components (`defineFields`), whose columns are indexed by slot.\n *\n * ```ts\n * const p = world.columns(Position), v = world.columns(Velocity);\n * const slots = world.slots(Position, Velocity);\n * for (let k = 0; k < slots.length; k++) { const i = slots[k]!; p.x[i]! += v.x[i]! * dt; }\n * ```\n *\n * The result is cached and rebuilt only when an entity joins or leaves one of those components, so asking every\n * step is cheap. It is a view the world owns: read it, don't keep it past the step, and fetch it again after\n * adding or removing components.\n */\n slots(...types: ComponentType<unknown>[]): Int32Array {\n const key = types.map((t) => t.id).join(',');\n const sets = this.setsFor(types);\n if (!sets) return EMPTY_SLOTS;\n let cached = this.slotCache.get(key);\n if (cached && cached.versions.every((v, k) => v === sets[k]!.version)) return cached.view;\n const lead = smallest(sets);\n const out = cached && cached.slots.length >= lead.size ? cached.slots : new Int32Array(Math.max(16, lead.size));\n let n = 0;\n for (let d = 0; d < lead.entities.length; d++) {\n const index = entityIndex(lead.entities[d]!);\n let all = true;\n for (const set of sets) {\n if (set !== lead && !set.has(index)) {\n all = false;\n break;\n }\n }\n if (all) out[n++] = index;\n }\n cached = { versions: sets.map((x) => x.version), slots: out, view: out.subarray(0, n) };\n this.slotCache.set(key, cached);\n return cached.view;\n }\n\n /**\n * Every live entity holding all of `types`, with those components in the same order. Yields a fresh row per\n * entity: convenient, but `each` is the form for hot loops.\n */\n *query<const T extends readonly ComponentType<unknown>[]>(...types: T): IterableIterator<[Entity, ...ComponentValues<T>]> {\n const sets = this.setsFor(types);\n if (!sets) return;\n const lead = smallest(sets);\n this.walking++;\n try {\n for (let i = lead.entities.length - 1; i >= 0; i--) {\n const e = lead.entities[i]!;\n const index = entityIndex(e);\n const row: unknown[] = [e];\n let all = true;\n for (const set of sets) {\n if (!set.has(index)) {\n all = false;\n break;\n }\n row.push(set.get(index));\n }\n if (all) yield row as [Entity, ...ComponentValues<T>];\n }\n } finally {\n this.endWalk();\n }\n }\n\n /** The first entity matching the query, or undefined: for singletons (the player, the camera target). */\n first<const T extends readonly ComponentType<unknown>[]>(...types: T): [Entity, ...ComponentValues<T>] | undefined {\n for (const row of this.query(...types)) return row;\n return undefined;\n }\n\n addSystem(s: System): this {\n if (this.systems.some((x) => x.name === s.name)) throw new Error(`World.addSystem: a system named \"${s.name}\" is already added`);\n this.systems.push(s);\n return this;\n }\n\n removeSystem(name: string): void {\n const i = this.systems.findIndex((s) => s.name === name);\n if (i < 0) return;\n const [s] = this.systems.splice(i, 1);\n s!.dispose?.();\n }\n\n /** Runs every system of `phase` in the order added. A system that throws is reported and the rest still run. */\n run(phase: SystemPhase, ctx: Omit<SystemContext, 'world'>, onError?: (system: System, err: unknown) => void): void {\n const full: SystemContext = { dt: ctx.dt, now: ctx.now, alpha: ctx.alpha, world: this };\n this.walking++;\n try {\n for (const s of this.systems) {\n if (s.phase !== phase) continue;\n try {\n s.run(full);\n } catch (err) {\n if (onError) onError(s, err);\n else throw err;\n }\n }\n } finally {\n this.endWalk();\n }\n }\n\n /** Every entity, component and system gone. */\n clear(): void {\n for (const s of this.systems.splice(0)) s.dispose?.();\n for (const store of this.stores) store?.clear();\n this.generations = this.generations.map((g) => (g + 1) % MAX_GENERATION);\n this.inUse.fill(false);\n this.free.length = 0;\n for (let i = this.generations.length - 1; i >= 0; i--) this.free.push(i);\n this.live = 0;\n this.pending.length = 0;\n this.slotCache.clear();\n }\n\n private columnsOf(type: FieldsType<FieldSpec>, slots: number): Record<string, ArrayLike<number>> {\n let cols = this.cols[type.id];\n const have = cols ? (Object.values(cols)[0]?.length ?? 0) : 0;\n if (!cols || have < slots) {\n let n = Math.max(64, have);\n while (n < slots) n *= 2;\n cols = growColumns(type.fields, n, cols);\n this.cols[type.id] = cols;\n }\n return cols;\n }\n\n private store<T>(type: ComponentType<T>): SparseSet<unknown> {\n let s = this.stores[type.id];\n if (!s) {\n s = new SparseSet<unknown>();\n this.stores[type.id] = s;\n }\n return s;\n }\n\n /** The stores for `types`, or null when one has never been used (so nothing can match). */\n private setsFor(types: readonly ComponentType<unknown>[]): SparseSet<unknown>[] | null {\n if (types.length === 0) return null;\n const sets: SparseSet<unknown>[] = [];\n for (const t of types) {\n const s = this.stores[t.id];\n if (!s || s.size === 0) return null;\n sets.push(s);\n }\n return sets;\n }\n\n private endWalk(): void {\n this.walking--;\n if (this.walking > 0 || this.pending.length === 0) return;\n const pending = this.pending.splice(0);\n for (const p of pending) {\n if (p.kind === 'destroy') this.destroy(p.e);\n else this.remove(p.e, p.type);\n }\n }\n}\n\nconst EMPTY_SLOTS = new Int32Array(0);\n\nfunction smallest(sets: SparseSet<unknown>[]): SparseSet<unknown> {\n let lead = sets[0]!;\n for (const s of sets) if (s.size < lead.size) lead = s;\n return lead;\n}\n"],"names":["nextComponentId","TYPES","defineComponent","name","type","defineFields","fields","system","phase","run","INDEX_BITS","INDEX_MASK","MAX_GENERATION","entityIndex","e","World","index","components","value","out","i","store","id","isFields","cols","values","args","fn","types","sets","lead","smallest","n","a","b","row","all","s","set","key","t","EMPTY_SLOTS","cached","v","k","d","x","ctx","onError","full","err","g","slots","have","growColumns","SparseSet","pending","p"],"mappings":"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"}
|
package/dist/ecs/fields.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fields.js","sources":["../../src/ecs/fields.ts"],"sourcesContent":["import type { ComponentType } from './World';\n\n/**\n * Numeric components stored as columns of typed arrays instead of one object per entity: `Position.x` and\n * `Position.y` are two `Float32Array`s indexed by the entity's slot. A system walking thousands of bodies then reads\n * memory in order and makes no garbage, which is several times faster than objects at scale (see `bench/`).\n *\n * ```ts\n * const Position = defineFields('Position', { x: 'f32', y: 'f32' });\n * world.add(e, Position, { x: 10, y: 20 });\n * const p = world.columns(Position); // fetch per run: columns grow (and are replaced) as entities are added\n * world.each(Position, (e, i) => { p.x[i] += 1; });\n * ```\n *\n * In `each`/`query` a field component's value is the entity's slot index `i`, the index into every column.\n */\nexport type FieldKind = 'f32' | 'f64' | 'i8' | 'i16' | 'i32' | 'u8' | 'u16' | 'u32';\n\nexport type FieldSpec = Record<string, FieldKind>;\n\nexport type ArrayOf<K extends FieldKind> = K extends 'f32'\n ? Float32Array\n : K extends 'f64'\n ? Float64Array\n : K extends 'i8'\n ? Int8Array\n : K extends 'i16'\n ? Int16Array\n : K extends 'i32'\n ? Int32Array\n : K extends 'u8'\n ? Uint8Array\n : K extends 'u16'\n ? Uint16Array\n : Uint32Array;\n\n/** One typed array per field, indexed by slot. */\nexport type Columns<S extends FieldSpec> = { [K in keyof S]: ArrayOf<S[K]> };\n\n/** What `add` takes for a field component: any subset of its fields (the rest start at 0). */\nexport type FieldValues<S extends FieldSpec> = { [K in keyof S]?: number };\n\n/** A component of numeric fields. Its value in `each`/`query` is the slot index. */\nexport interface FieldsType<S extends FieldSpec> extends ComponentType<number> {\n readonly fields: S;\n}\n\nconst CTORS: Record<FieldKind, new (n: number) => ArrayLike<number> & { set(a: ArrayLike<number>): void }> = {\n f32: Float32Array,\n f64: Float64Array,\n i8: Int8Array,\n i16: Int16Array,\n i32: Int32Array,\n u8: Uint8Array,\n u16: Uint16Array,\n u32: Uint32Array,\n};\n\nexport function isFields(type: ComponentType<unknown>): type is FieldsType<FieldSpec> {\n return (type as { fields?: unknown }).fields !== undefined;\n}\n\n/** Fresh columns of at least `n` slots, keeping what `old` held. */\nexport function growColumns(spec: FieldSpec, n: number, old?: Record<string, ArrayLike<number>>): Record<string, ArrayLike<number>> {\n const out: Record<string, ArrayLike<number>> = {};\n for (const [name, kind] of Object.entries(spec)) {\n const col = new CTORS[kind](n);\n const prev = old?.[name];\n if (prev) col.set(prev);\n out[name] = col;\n }\n return out;\n}\n"],"names":["CTORS","isFields","type","growColumns","spec","n","old","out","name","kind","col","prev"],"mappings":"AA+CA,MAAMA,IAAuG;AAAA,EAC3G,KAAK;AAAA,EACL,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,KAAK;AACP;AAEO,SAASC,EAASC,GAA6D;AACpF,SAAQA,EAA8B,WAAW;AACnD;AAGO,SAASC,EAAYC,GAAiBC,GAAWC,GAA4E;AAClI,QAAMC,IAAyC,CAAA;AAC/C,aAAW,CAACC,GAAMC,CAAI,KAAK,OAAO,QAAQL,CAAI,GAAG;AAC/C,UAAMM,IAAM,IAAIV,EAAMS,CAAI,EAAEJ,CAAC,GACvBM,IAAOL,IAAME,CAAI;AACvB,IAAIG,KAAMD,EAAI,IAAIC,CAAI,GACtBJ,EAAIC,CAAI,IAAIE;AAAA,EACd;AACA,SAAOH;AACT;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Engine.js","sources":["../../src/engine/Engine.ts"],"sourcesContent":["import { Application, TextureSource } from 'pixi.js';\nimport { Stage } from '../core/Stage';\nimport { TextureRegistry } from '../core/TextureRegistry';\nimport { patchPixi } from '../core/pixiPatches';\nimport { EventBus } from '../events/EventBus';\nimport { FixedLoop } from './FixedLoop';\nimport { SceneManager } from './SceneManager';\nimport type { Scene } from './Scene';\n\nexport interface EngineOptions {\n /** Where the canvas goes. */\n parent: HTMLElement;\n /** A fixed canvas size in CSS pixels. Leave both out to fill `parent` and follow its size. */\n width?: number;\n height?: number;\n /** CSS colour behind the world (default black). */\n background?: string;\n /** World pixels are drawn this many screen pixels wide (default 1). Whole numbers keep pixel art crisp. */\n zoom?: number;\n /** Simulation steps per second (default 60). */\n hz?: number;\n /** Textures shared by the stage; a fresh registry by default. */\n textures?: TextureRegistry;\n /**\n * Pixel art: nearest-neighbour textures, no antialiasing, one backing pixel per CSS pixel, and a camera that lands\n * on whole pixels (default false: smooth textures drawn at the screen's own density, for any other art style).\n */\n pixelPerfect?: boolean;\n /** Backing pixels per CSS pixel when not `pixelPerfect` (default `devicePixelRatio`, at most 2). */\n resolution?: number;\n /**\n * Renderer preference (default 'webgl'). WebGL 2 runs everywhere the engine is tested. 'webgpu' is experimental:\n * Pixi falls back to WebGL where WebGPU is missing, but the engine's own paths (pooled `PixelLayer` buffers, the\n * `erase` blend `Darkness` cuts lights with, the fill filters) have only been profiled on WebGL.\n */\n preference?: 'webgl' | 'webgpu';\n}\n\nexport interface EngineEvents {\n /** Something a guarded step or a system threw; the frame carried on. */\n error: { where: string; error: unknown };\n /** After every rendered frame's update, before Pixi renders. */\n frame: { now: number; dt: number };\n}\n\n/** The stage the engine drives: its per-frame update is the engine's tick. */\nclass EngineStage extends Stage {\n constructor(\n app: Application,\n textures: TextureRegistry,\n private readonly tick: (now: number, delta: number) => void,\n ) {\n super(app, textures);\n }\n\n override update(now: number, delta: number): void {\n this.tick(now, delta);\n }\n}\n\n/**\n * The top of the engine: a Pixi application, the `Stage` (world and screen layers, camera, tweens, timers,\n * pointer), a fixed-step simulation loop and a stack of scenes.\n *\n * ```ts\n * const engine = await Engine.create({ parent: document.body, zoom: 3 });\n * await engine.scenes.replace(new MyLevel());\n * ```\n */\nexport class Engine {\n readonly stage: Stage;\n readonly loop: FixedLoop;\n readonly scenes: SceneManager;\n readonly events = new EventBus<EngineEvents>();\n private running = true;\n private destroyed = false;\n private readonly onTick = (t: { deltaMS: number }) => this.stage.frame(t.deltaMS);\n\n private constructor(\n readonly app: Application,\n textures: TextureRegistry,\n options: EngineOptions,\n ) {\n this.loop = new FixedLoop(options.hz === undefined ? {} : { hz: options.hz });\n this.stage = new EngineStage(app, textures, (now, delta) => this.tick(now, delta));\n const cam = this.stage.cameras.main;\n cam.setZoom(options.zoom ?? 1);\n cam.defaultZoom = cam.zoom;\n cam.roundPixels = options.pixelPerfect === true;\n cam.resolution = app.renderer.resolution;\n this.scenes = new SceneManager(this);\n Stage.onCaught = (where, error) => this.events.emit('error', { where, error });\n app.ticker.add(this.onTick);\n }\n\n static async create(options: EngineOptions): Promise<Engine> {\n const pixel = options.pixelPerfect === true;\n TextureSource.defaultOptions.scaleMode = pixel ? 'nearest' : 'linear';\n const resolution = pixel ? 1 : (options.resolution ?? Math.min(2, typeof devicePixelRatio === 'number' ? devicePixelRatio : 1));\n patchPixi();\n const app = new Application();\n const fill = options.width === undefined && options.height === undefined;\n await app.init({\n ...(fill ? { resizeTo: options.parent } : { width: options.width ?? 800, height: options.height ?? 600 }),\n background: options.background ?? '#000000',\n antialias: !pixel,\n resolution,\n autoDensity: true,\n preference: options.preference ?? 'webgl',\n });\n options.parent.appendChild(app.canvas);\n return new Engine(app, options.textures ?? new TextureRegistry(), options);\n }\n\n /** The scene that updates now, if any. */\n get scene(): Scene | undefined {\n return this.scenes.current;\n }\n\n /** Stops simulating and drawing; the canvas keeps its last frame. */\n pause(): void {\n if (!this.running) return;\n this.running = false;\n this.app.ticker.stop();\n }\n\n resume(): void {\n if (this.running || this.destroyed) return;\n this.running = true;\n this.loop.reset();\n this.app.ticker.start();\n }\n\n get paused(): boolean {\n return !this.running;\n }\n\n /** Reports a fault to `events.error` (and the console when nobody listens). */\n reportError(where: string, error: unknown): void {\n if (this.events.listenerCount('error') === 0) console.error(`[engine] ${where}`, error);\n this.events.emit('error', { where, error });\n }\n\n /** Leaves every scene, removes the canvas and frees the renderer. */\n async destroy(): Promise<void> {\n if (this.destroyed) return;\n this.destroyed = true;\n await this.scenes.clear();\n this.app.ticker.remove(this.onTick);\n if (Stage.onCaught) Stage.onCaught = null;\n this.stage.destroy();\n this.events.clear();\n this.app.destroy({ removeView: true }, { children: true });\n }\n\n private tick(now: number, delta: number): void {\n const scene = this.scenes.current;\n if (scene) {\n const report = (s: { name: string }, err: unknown) => this.reportError(`system ${s.name}`, err);\n const step = this.loop.stepSeconds;\n const alpha = this.loop.advance(delta, () => {\n scene.world.run('fixed', { dt: step, now, alpha: 0 }, report);\n scene.fixedUpdate(step);\n });\n const dt = delta / 1000;\n scene.world.run('frame', { dt, now, alpha }, report);\n scene.update(dt, alpha);\n }\n this.events.emit('frame', { now, dt: delta / 1000 });\n }\n}\n"],"names":["EngineStage","Stage","app","textures","tick","now","delta","Engine","options","FixedLoop","cam","SceneManager","where","error","EventBus","t","pixel","TextureSource","resolution","patchPixi","Application","fill","TextureRegistry","scene","report","s","err","step","alpha","dt"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FixedLoop.js","sources":["../../src/engine/FixedLoop.ts"],"sourcesContent":["/**\n * A fixed-timestep accumulator: frames arrive whenever the display wants them, the simulation advances in equal\n * steps. Movement and anything a server replays must run here, so every machine takes the same steps whatever its\n * frame rate.\n *\n * A frame that comes very late (a tab in the background, a debugger) would owe hundreds of steps; at most\n * `maxStepsPerFrame` are run and the rest of the debt is dropped, so the game slows down instead of freezing while it\n * catches up (the \"spiral of death\").\n */\nexport interface FixedLoopOptions {\n /** Steps per second (default 60). */\n hz?: number;\n /** The most steps one frame may run before the debt is dropped (default 8). */\n maxStepsPerFrame?: number;\n}\n\nexport class FixedLoop {\n readonly stepMs: number;\n readonly stepSeconds: number;\n private readonly maxSteps: number;\n private carry = 0;\n /** Steps taken since the loop started. */\n tick = 0;\n\n constructor(options: FixedLoopOptions = {}) {\n const hz = options.hz ?? 60;\n if (!(hz > 0)) throw new Error(`FixedLoop: hz must be positive, got ${hz}`);\n this.stepMs = 1000 / hz;\n this.stepSeconds = 1 / hz;\n this.maxSteps = Math.max(1, options.maxStepsPerFrame ?? 8);\n }\n\n /**\n * Feeds one frame of `deltaMs` and runs `step` as many times as it owes. Returns the share of a step left over\n * (0–1), for interpolating what is drawn between the last two steps.\n */\n advance(deltaMs: number, step: (tick: number) => void): number {\n if (deltaMs > 0 && Number.isFinite(deltaMs)) this.carry += deltaMs;\n let ran = 0;\n while (this.carry >= this.stepMs) {\n if (ran === this.maxSteps) {\n this.carry %= this.stepMs;\n break;\n }\n this.carry -= this.stepMs;\n ran++;\n step(this.tick++);\n }\n return this.carry / this.stepMs;\n }\n\n /** Forgets any debt, e.g. after a scene change or a long load. */\n reset(): void {\n this.carry = 0;\n }\n}\n"],"names":["FixedLoop","options","hz","deltaMs","step","ran"],"mappings":"AAgBO,MAAMA,EAAU;AAAA,EACZ;AAAA,EACA;AAAA,EACQ;AAAA,EACT,QAAQ;AAAA;AAAA,EAEhB,OAAO;AAAA,EAEP,YAAYC,IAA4B,IAAI;AAC1C,UAAMC,IAAKD,EAAQ,MAAM;AACzB,QAAI,EAAEC,IAAK,GAAI,OAAM,IAAI,MAAM,uCAAuCA,CAAE,EAAE;AAC1E,SAAK,SAAS,MAAOA,GACrB,KAAK,cAAc,IAAIA,GACvB,KAAK,WAAW,KAAK,IAAI,GAAGD,EAAQ,oBAAoB,CAAC;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAQE,GAAiBC,GAAsC;AAC7D,IAAID,IAAU,KAAK,OAAO,SAASA,CAAO,WAAQ,SAASA;AAC3D,QAAIE,IAAM;AACV,WAAO,KAAK,SAAS,KAAK,UAAQ;AAChC,UAAIA,MAAQ,KAAK,UAAU;AACzB,aAAK,SAAS,KAAK;AACnB;AAAA,MACF;AACA,WAAK,SAAS,KAAK,QACnBA,KACAD,EAAK,KAAK,MAAM;AAAA,IAClB;AACA,WAAO,KAAK,QAAQ,KAAK;AAAA,EAC3B;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,QAAQ;AAAA,EACf;AACF;"}
|
package/dist/engine/Scene.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Scene.js","sources":["../../src/engine/Scene.ts"],"sourcesContent":["import { Container } from 'pixi.js';\nimport { World } from '../ecs/World';\nimport type { Engine } from './Engine';\n\n/** Input a scene can be told to forget its pending presses on (an `Actions`). */\nexport interface FlushableInput {\n flush(): void;\n}\n\n/** A timer `Scene.delay` started; `remove` cancels it. */\nexport interface SceneTimer {\n remove(): void;\n}\n\n/**\n * One screen of the game: a title, a level, a pause menu. A scene owns a `World` of entities and systems and a\n * `root` container in the engine's world layer; both are cleared when it leaves, so nothing it made outlives it.\n *\n * Override the hooks you need. `fixedUpdate` runs once per simulation step, `update` once per rendered frame, both\n * after the world's own systems of that phase.\n *\n * Two things a scene should hand the engine so they end with it: timers (`delay`, not `stage.time.delayedCall`, whose\n * timers belong to the stage and fire after the scene has gone) and its input (`useInput`, flushed when a scene pushed\n * over this one pops, so presses made in a pause menu are not read again by the level under it).\n *\n * Names taken: `world`, `root` and `hud` are the scene's own fields, so a subclass cannot declare a field of those\n * names for something else (a HUD class of the game's is best kept as `display`, `overlay`, …).\n */\nexport abstract class Scene {\n readonly world = new World();\n /** Everything the scene draws in the world goes under this (the camera moves it). */\n readonly root = new Container({ sortableChildren: true });\n /** Everything the scene draws on the screen (HUD) goes under this. */\n readonly hud = new Container();\n private bound: Engine | null = null;\n private readonly timers = new Set<SceneTimer>();\n private readonly inputs: FlushableInput[] = [];\n\n /** The engine running this scene; only valid between `enter` and `exit`. */\n get engine(): Engine {\n if (!this.bound) throw new Error(`${this.constructor.name}: not running in an engine`);\n return this.bound;\n }\n\n /** Called once when the scene becomes active. May be async: the scene starts updating when it resolves. */\n onEnter(): void | Promise<void> {}\n /** Called when the scene is removed. The world and containers are cleared after it returns. */\n onExit(): void {}\n /** Called when another scene is pushed over this one. */\n onPause(): void {}\n /** Called when the scene over this one is popped. */\n onResume(): void {}\n /** Once per simulation step, `dt` in seconds. */\n fixedUpdate(_dt: number): void {}\n /** Once per rendered frame, `dt` in seconds; `alpha` is the share of a step since the last one (0–1). */\n update(_dt: number, _alpha: number): void {}\n\n /**\n * Calls `fn` after `ms` on the stage's clock, unless the scene has left by then: its timers are cancelled when it\n * exits. Only valid while the scene is running (between `enter` and `exit`).\n */\n delay(ms: number, fn: () => void): SceneTimer {\n const handle = this.engine.stage.time.delayedCall(ms, () => {\n this.timers.delete(timer);\n fn();\n });\n const timer: SceneTimer = {\n remove: () => {\n this.timers.delete(timer);\n handle.remove();\n },\n };\n this.timers.add(timer);\n return timer;\n }\n\n /**\n * Registers input the scene reads (an `Actions`) and returns it, so a field can be written in one line:\n * `private readonly input = this.useInput(new Actions(keyboard, bindings));`. When a scene pushed over this one\n * pops, every registered input is flushed before `onResume`, so a key pressed in the menu is not a press here.\n */\n useInput<T extends FlushableInput>(input: T): T {\n this.inputs.push(input);\n return input;\n }\n\n /** @internal Called by the scene manager before `onResume`. */\n flushInput(): void {\n for (const i of this.inputs) i.flush();\n }\n\n /** @internal Engine-side wiring; games call `engine.scenes`. */\n attach(engine: Engine): void {\n this.bound = engine;\n engine.stage.world.addChild(this.root);\n engine.stage.screen.addChild(this.hud);\n }\n\n /** @internal Clears what the scene made but keeps its containers, so the same scene may be entered again. */\n detach(): void {\n for (const t of [...this.timers]) t.remove();\n this.world.clear();\n for (const c of [this.root, this.hud]) {\n for (const child of c.removeChildren()) child.destroy({ children: true });\n c.removeFromParent();\n }\n this.bound = null;\n }\n}\n"],"names":["Scene","World","Container","_dt","_alpha","ms","fn","handle","timer","input","i","engine","c","child"],"mappings":";;AA4BO,MAAeA,EAAM;AAAA,EACjB,QAAQ,IAAIC,EAAA;AAAA;AAAA,EAEZ,OAAO,IAAIC,EAAU,EAAE,kBAAkB,IAAM;AAAA;AAAA,EAE/C,MAAM,IAAIA,EAAA;AAAA,EACX,QAAuB;AAAA,EACd,6BAAa,IAAA;AAAA,EACb,SAA2B,CAAA;AAAA;AAAA,EAG5C,IAAI,SAAiB;AACnB,QAAI,CAAC,KAAK,MAAO,OAAM,IAAI,MAAM,GAAG,KAAK,YAAY,IAAI,4BAA4B;AACrF,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,UAAgC;AAAA,EAAC;AAAA;AAAA,EAEjC,SAAe;AAAA,EAAC;AAAA;AAAA,EAEhB,UAAgB;AAAA,EAAC;AAAA;AAAA,EAEjB,WAAiB;AAAA,EAAC;AAAA;AAAA,EAElB,YAAYC,GAAmB;AAAA,EAAC;AAAA;AAAA,EAEhC,OAAOA,GAAaC,GAAsB;AAAA,EAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAM3C,MAAMC,GAAYC,GAA4B;AAC5C,UAAMC,IAAS,KAAK,OAAO,MAAM,KAAK,YAAYF,GAAI,MAAM;AAC1D,WAAK,OAAO,OAAOG,CAAK,GACxBF,EAAA;AAAA,IACF,CAAC,GACKE,IAAoB;AAAA,MACxB,QAAQ,MAAM;AACZ,aAAK,OAAO,OAAOA,CAAK,GACxBD,EAAO,OAAA;AAAA,MACT;AAAA,IAAA;AAEF,gBAAK,OAAO,IAAIC,CAAK,GACdA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAmCC,GAAa;AAC9C,gBAAK,OAAO,KAAKA,CAAK,GACfA;AAAA,EACT;AAAA;AAAA,EAGA,aAAmB;AACjB,eAAWC,KAAK,KAAK,OAAQ,CAAAA,EAAE,MAAA;AAAA,EACjC;AAAA;AAAA,EAGA,OAAOC,GAAsB;AAC3B,SAAK,QAAQA,GACbA,EAAO,MAAM,MAAM,SAAS,KAAK,IAAI,GACrCA,EAAO,MAAM,OAAO,SAAS,KAAK,GAAG;AAAA,EACvC;AAAA;AAAA,EAGA,SAAe;AACb,eAAW,KAAK,CAAC,GAAG,KAAK,MAAM,KAAK,OAAA;AACpC,SAAK,MAAM,MAAA;AACX,eAAWC,KAAK,CAAC,KAAK,MAAM,KAAK,GAAG,GAAG;AACrC,iBAAWC,KAASD,EAAE,eAAA,KAAwB,QAAQ,EAAE,UAAU,IAAM;AACxE,MAAAA,EAAE,iBAAA;AAAA,IACJ;AACA,SAAK,QAAQ;AAAA,EACf;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SceneManager.js","sources":["../../src/engine/SceneManager.ts"],"sourcesContent":["import type { Engine } from './Engine';\nimport type { Scene } from './Scene';\n\n/**\n * A stack of scenes. Only the top one updates; the ones under it are paused but still drawn (a pause menu over the\n * level). Changes are queued and applied one at a time, so a scene that replaces itself from inside its own update,\n * or an async `onEnter` still loading, never leaves two scenes half entered.\n *\n * The camera is the engine's, not a scene's: `replace` resets it (`Camera.reset`: no target, no bounds, the default\n * zoom) after the old scenes leave and before the new one enters, so a level's follow and bounds never hold over a\n * title screen. `push` and `pop` leave it as it is: the scene under a pause menu keeps its view. `pop` flushes the\n * resumed scene's input (`Scene.useInput`) before its `onResume`.\n */\nexport class SceneManager {\n private readonly stack: Scene[] = [];\n private queue: Promise<void> = Promise.resolve();\n /** True while an async `onEnter` is running: nothing updates until it is done. */\n private entering = false;\n\n constructor(private readonly engine: Engine) {}\n\n /** The scene that updates, if any. */\n get current(): Scene | undefined {\n return this.entering ? undefined : this.stack[this.stack.length - 1];\n }\n\n /** Every scene on the stack, bottom first. */\n get all(): readonly Scene[] {\n return this.stack;\n }\n\n /** Removes every scene and enters `scene`. */\n replace(scene: Scene): Promise<void> {\n return this.enqueue(async () => {\n while (this.stack.length) this.exitTop();\n this.engine.stage.cameras.main.reset();\n await this.enter(scene);\n });\n }\n\n /** Pauses the current scene and enters `scene` over it. */\n push(scene: Scene): Promise<void> {\n return this.enqueue(async () => {\n this.stack[this.stack.length - 1]?.onPause();\n await this.enter(scene);\n });\n }\n\n /** Leaves the current scene and resumes the one under it. */\n pop(): Promise<void> {\n return this.enqueue(async () => {\n if (!this.stack.length) return;\n this.exitTop();\n const under = this.stack[this.stack.length - 1];\n if (!under) return;\n under.flushInput();\n under.onResume();\n });\n }\n\n /** Leaves every scene (the engine is shutting down). */\n clear(): Promise<void> {\n return this.enqueue(async () => {\n while (this.stack.length) this.exitTop();\n });\n }\n\n private async enter(scene: Scene): Promise<void> {\n if (this.stack.includes(scene)) throw new Error(`${scene.constructor.name} is already on the scene stack`);\n scene.attach(this.engine);\n this.stack.push(scene);\n this.entering = true;\n try {\n await scene.onEnter();\n } finally {\n this.entering = false;\n }\n this.engine.loop.reset();\n }\n\n private exitTop(): void {\n const scene = this.stack.pop();\n if (!scene) return;\n try {\n scene.onExit();\n } finally {\n scene.detach();\n }\n }\n\n private enqueue(job: () => Promise<void>): Promise<void> {\n const run = this.queue.then(job);\n // a failed change must not wedge every change after it\n this.queue = run.catch((err) => this.engine.reportError('scene', err));\n return run;\n }\n}\n"],"names":["SceneManager","engine","scene","under","job","run","err"],"mappings":"AAaO,MAAMA,EAAa;AAAA,EAMxB,YAA6BC,GAAgB;AAAhB,SAAA,SAAAA;AAAA,EAAiB;AAAA,EAAjB;AAAA,EALZ,QAAiB,CAAA;AAAA,EAC1B,QAAuB,QAAQ,QAAA;AAAA;AAAA,EAE/B,WAAW;AAAA;AAAA,EAKnB,IAAI,UAA6B;AAC/B,WAAO,KAAK,WAAW,SAAY,KAAK,MAAM,KAAK,MAAM,SAAS,CAAC;AAAA,EACrE;AAAA;AAAA,EAGA,IAAI,MAAwB;AAC1B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,QAAQC,GAA6B;AACnC,WAAO,KAAK,QAAQ,YAAY;AAC9B,aAAO,KAAK,MAAM,SAAQ,MAAK,QAAA;AAC/B,WAAK,OAAO,MAAM,QAAQ,KAAK,MAAA,GAC/B,MAAM,KAAK,MAAMA,CAAK;AAAA,IACxB,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,KAAKA,GAA6B;AAChC,WAAO,KAAK,QAAQ,YAAY;AAC9B,WAAK,MAAM,KAAK,MAAM,SAAS,CAAC,GAAG,QAAA,GACnC,MAAM,KAAK,MAAMA,CAAK;AAAA,IACxB,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAqB;AACnB,WAAO,KAAK,QAAQ,YAAY;AAC9B,UAAI,CAAC,KAAK,MAAM,OAAQ;AACxB,WAAK,QAAA;AACL,YAAMC,IAAQ,KAAK,MAAM,KAAK,MAAM,SAAS,CAAC;AAC9C,MAAKA,MACLA,EAAM,WAAA,GACNA,EAAM,SAAA;AAAA,IACR,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,QAAuB;AACrB,WAAO,KAAK,QAAQ,YAAY;AAC9B,aAAO,KAAK,MAAM,SAAQ,MAAK,QAAA;AAAA,IACjC,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,MAAMD,GAA6B;AAC/C,QAAI,KAAK,MAAM,SAASA,CAAK,EAAG,OAAM,IAAI,MAAM,GAAGA,EAAM,YAAY,IAAI,gCAAgC;AACzG,IAAAA,EAAM,OAAO,KAAK,MAAM,GACxB,KAAK,MAAM,KAAKA,CAAK,GACrB,KAAK,WAAW;AAChB,QAAI;AACF,YAAMA,EAAM,QAAA;AAAA,IACd,UAAA;AACE,WAAK,WAAW;AAAA,IAClB;AACA,SAAK,OAAO,KAAK,MAAA;AAAA,EACnB;AAAA,EAEQ,UAAgB;AACtB,UAAMA,IAAQ,KAAK,MAAM,IAAA;AACzB,QAAKA;AACL,UAAI;AACF,QAAAA,EAAM,OAAA;AAAA,MACR,UAAA;AACE,QAAAA,EAAM,OAAA;AAAA,MACR;AAAA,EACF;AAAA,EAEQ,QAAQE,GAAyC;AACvD,UAAMC,IAAM,KAAK,MAAM,KAAKD,CAAG;AAE/B,gBAAK,QAAQC,EAAI,MAAM,CAACC,MAAQ,KAAK,OAAO,YAAY,SAASA,CAAG,CAAC,GAC9DD;AAAA,EACT;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"EventBus.js","sources":["../../src/events/EventBus.ts"],"sourcesContent":["/**\n * Typed observer: listeners subscribe per event name and run in subscription order.\n * `Events` maps each event name to its payload, e.g. `EventBus<{ enemyKilled: { id: number } }>`.\n *\n * A listener that throws does not stop the ones after it; the error is handed to `onError` (the console by default).\n */\nexport class EventBus<Events extends object> {\n private readonly listeners = new Map<keyof Events, ((payload: never) => void)[]>();\n\n constructor(private readonly onError: (type: keyof Events, err: unknown) => void = (type, err) => console.error(`[events] ${String(type)} listener failed`, err)) {}\n\n /** Subscribes to `type`; returns the unsubscribe function. */\n on<K extends keyof Events>(type: K, fn: (payload: Events[K]) => void): () => void {\n this.listeners.set(type, [...(this.listeners.get(type) ?? []), fn]);\n return () => this.off(type, fn);\n }\n\n /** Subscribes for the next `type` only. */\n once<K extends keyof Events>(type: K, fn: (payload: Events[K]) => void): () => void {\n const off = this.on(type, (payload) => {\n off();\n fn(payload);\n });\n return off;\n }\n\n off<K extends keyof Events>(type: K, fn: (payload: Events[K]) => void): void {\n const list = this.listeners.get(type);\n if (!list) return;\n const left = list.filter((f) => f !== fn);\n if (left.length) this.listeners.set(type, left);\n else this.listeners.delete(type);\n }\n\n emit<K extends keyof Events>(type: K, payload: Events[K]): void {\n // the list is replaced, never mutated, on every (un)subscribe: walking this one is safe while listeners change it\n for (const fn of this.listeners.get(type) ?? []) {\n try {\n (fn as (payload: Events[K]) => void)(payload);\n } catch (err) {\n this.onError(type, err);\n }\n }\n }\n\n listenerCount(type: keyof Events): number {\n return this.listeners.get(type)?.length ?? 0;\n }\n\n clear(): void {\n this.listeners.clear();\n }\n}\n"],"names":["EventBus","onError","type","err","fn","off","payload","list","left","f"],"mappings":"AAMO,MAAMA,EAAgC;AAAA,EAG3C,YAA6BC,IAAsD,CAACC,GAAMC,MAAQ,QAAQ,MAAM,YAAY,OAAOD,CAAI,CAAC,oBAAoBC,CAAG,GAAG;AAArI,SAAA,UAAAF;AAAA,EAAsI;AAAA,EAAtI;AAAA,EAFZ,gCAAgB,IAAA;AAAA;AAAA,EAKjC,GAA2BC,GAASE,GAA8C;AAChF,gBAAK,UAAU,IAAIF,GAAM,CAAC,GAAI,KAAK,UAAU,IAAIA,CAAI,KAAK,CAAA,GAAKE,CAAE,CAAC,GAC3D,MAAM,KAAK,IAAIF,GAAME,CAAE;AAAA,EAChC;AAAA;AAAA,EAGA,KAA6BF,GAASE,GAA8C;AAClF,UAAMC,IAAM,KAAK,GAAGH,GAAM,CAACI,MAAY;AACrC,MAAAD,EAAA,GACAD,EAAGE,CAAO;AAAA,IACZ,CAAC;AACD,WAAOD;AAAA,EACT;AAAA,EAEA,IAA4BH,GAASE,GAAwC;AAC3E,UAAMG,IAAO,KAAK,UAAU,IAAIL,CAAI;AACpC,QAAI,CAACK,EAAM;AACX,UAAMC,IAAOD,EAAK,OAAO,CAACE,MAAMA,MAAML,CAAE;AACxC,IAAII,EAAK,SAAQ,KAAK,UAAU,IAAIN,GAAMM,CAAI,IACzC,KAAK,UAAU,OAAON,CAAI;AAAA,EACjC;AAAA,EAEA,KAA6BA,GAASI,GAA0B;AAE9D,eAAWF,KAAM,KAAK,UAAU,IAAIF,CAAI,KAAK;AAC3C,UAAI;AACD,QAAAE,EAAoCE,CAAO;AAAA,MAC9C,SAASH,GAAK;AACZ,aAAK,QAAQD,GAAMC,CAAG;AAAA,MACxB;AAAA,EAEJ;AAAA,EAEA,cAAcD,GAA4B;AACxC,WAAO,KAAK,UAAU,IAAIA,CAAI,GAAG,UAAU;AAAA,EAC7C;AAAA,EAEA,QAAc;AACZ,SAAK,UAAU,MAAA;AAAA,EACjB;AACF;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FloatingText.js","sources":["../../src/fx/FloatingText.ts"],"sourcesContent":["import { type BitmapLabel, installLabelFont } from '../core/BitmapLabel';\nimport type { Container } from 'pixi.js';\nimport type { Stage } from '../core/Stage';\n\n/** One look of floating text: a size and a fill colour. Each look is its own baked font. */\nexport interface FloatingTextStyle {\n size: number;\n color: string;\n}\n\nexport interface FloatingTextOptions {\n /** A font already loaded (`document.fonts.load`), or the glyphs bake in the fallback. */\n fontFamily: string;\n /** Outline colour (default near-black). */\n stroke?: string;\n /** Outline width for a size (default a fifth of it, at least 2). */\n strokeWidth?: (size: number) => number;\n /** Rasterising resolution (default 4, crisp under a zoomed camera). */\n resolution?: number;\n /** Spent labels of one style kept for reuse; the rest are destroyed (default 24). */\n keepPerStyle?: number;\n /** Letters beyond ASCII the labels print (see `installLabelFont`), baked with each style's font. */\n glyphs?: string;\n /** Depth given to every label taken (default: left as made). */\n depth?: number;\n /**\n * Where new labels are added (default the stage's world layer). Give a scene's `root` and the labels live and die\n * with the scene: on the world layer they outlive it, and a label still floating when the scene changes stays.\n */\n parent?: Container;\n}\n\n/** How `float` moves a label: pops from `pop`× to 1, holds, then rises `rise` px while fading out. */\nexport interface FloatOptions {\n rise?: number;\n /** Sideways drift over the fade, px (pass a random one for a crowd of numbers that do not stack). */\n driftX?: number;\n pop?: number;\n popMs?: number;\n holdMs?: number;\n fadeMs?: number;\n}\n\nconst keyOf = (s: FloatingTextStyle) => `${s.size}|${s.color}`;\n\n/**\n * Floating text — damage numbers, pickups, \"+1\" — drawn from bitmap fonts and reused rather than created and\n * destroyed each time: a crowded fight throws dozens a second, and as canvas text each one repainted a canvas at\n * resolution 4 and uploaded it. A label keeps its font, so spent ones are pooled per style and only their string\n * changes. Fonts are baked ahead (`warm`): a style first met mid-fight baked its glyph pages on that frame.\n */\nexport class FloatingText {\n private readonly pools = new Map<string, BitmapLabel[]>();\n private readonly stroke: string;\n private readonly strokeWidth: (size: number) => number;\n private readonly resolution: number;\n private readonly keep: number;\n\n constructor(\n private readonly stage: Stage,\n private readonly options: FloatingTextOptions,\n ) {\n this.stroke = options.stroke ?? '#140e18';\n this.strokeWidth = options.strokeWidth ?? ((size) => Math.max(2, Math.round(size / 5)));\n this.resolution = options.resolution ?? 4;\n this.keep = options.keepPerStyle ?? 24;\n }\n\n /**\n * Bakes the fonts of `styles`, one a task starting `delayMs` from now, so no frame pays for them all at once — ten\n * at once froze a world's first frame half a second (each font measures and kerns every glyph). Returns a cancel.\n */\n warm(styles: readonly FloatingTextStyle[], delayMs = 1000, gapMs = 50): () => void {\n const todo = [...styles];\n let timer: ReturnType<typeof setTimeout> | undefined;\n const next = () => {\n const s = todo.shift();\n if (!s) return;\n this.install(s);\n timer = setTimeout(next, gapMs);\n };\n timer = setTimeout(next, delayMs);\n return () => clearTimeout(timer);\n }\n\n /** Bakes one style's font now. */\n install(style: FloatingTextStyle): void {\n installLabelFont(this.options.fontFamily, style.size, style.color, this.stroke, this.strokeWidth(style.size), this.resolution, this.options.glyphs);\n }\n\n /** A label ready to animate: shown, opaque, unscaled, anchored at its bottom centre on (x, y). */\n take(x: number, y: number, text: string, style: FloatingTextStyle): BitmapLabel {\n const pool = this.pools.get(keyOf(style));\n let t = pool?.pop();\n while (t && t.destroyed) t = pool?.pop();\n if (!t) {\n t = this.stage.add\n .label(x, y, text, this.options.fontFamily, style.size, style.color, this.stroke, this.strokeWidth(style.size), this.resolution, this.options.glyphs)\n .setOrigin(0.5, 1);\n // made on the world layer by the factory; moved under the game's own container when it named one\n if (this.options.parent) this.options.parent.addChild(t);\n } else {\n t.setText(text);\n t.position.set(x, y);\n t.alpha = 1;\n t.setScale(1);\n t.visible = true;\n }\n if (this.options.depth !== undefined) t.setDepth(this.options.depth);\n return t;\n }\n\n /** Hands a finished label back for reuse. */\n release(t: BitmapLabel, style: FloatingTextStyle): void {\n if (t.destroyed) return;\n const key = keyOf(style);\n let pool = this.pools.get(key);\n if (!pool) this.pools.set(key, (pool = []));\n if (pool.length >= this.keep) {\n t.destroy();\n return;\n }\n t.visible = false;\n pool.push(t);\n }\n\n /** A label that pops, rises, fades and goes back to the pool by itself, on the stage's own tweens. */\n float(x: number, y: number, text: string, style: FloatingTextStyle, options: FloatOptions = {}): BitmapLabel {\n const { rise = 16, driftX = 0, pop = 1.4, popMs = 120, holdMs = 350, fadeMs = 500 } = options;\n const t = this.take(x, y, text, style);\n const tweens = this.stage.tweens;\n if (pop !== 1) {\n t.setScale(pop);\n tweens.add({ targets: t, scale: 1, duration: popMs, ease: 'Back.Out' });\n }\n tweens.add({\n targets: t,\n x: x + driftX,\n y: y - rise,\n alpha: 0,\n delay: holdMs,\n duration: fadeMs,\n ease: 'Sine.In',\n onComplete: () => this.release(t, style),\n });\n return t;\n }\n\n /** Destroys every pooled label (a scene ending); labels still out are the scene's to free. */\n clear(): void {\n for (const pool of this.pools.values()) for (const t of pool) if (!t.destroyed) t.destroy();\n this.pools.clear();\n }\n}\n"],"names":["keyOf","s","FloatingText","stage","options","size","styles","delayMs","gapMs","todo","timer","next","style","installLabelFont","x","y","text","pool","t","key","rise","driftX","pop","popMs","holdMs","fadeMs","tweens"],"mappings":"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"}
|