rn-gamekit 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +68 -0
- package/README.md +95 -14
- package/lib/module/assets/defineAssets.js +28 -3
- package/lib/module/assets/defineAssets.js.map +1 -1
- package/lib/module/assets.js +11 -0
- package/lib/module/assets.js.map +1 -0
- package/lib/module/audio/createGameAudio.js +784 -0
- package/lib/module/audio/createGameAudio.js.map +1 -0
- package/lib/module/audio/errors.js +12 -0
- package/lib/module/audio/errors.js.map +1 -0
- package/lib/module/audio/index.js +5 -0
- package/lib/module/audio/index.js.map +1 -0
- package/lib/module/audio/resolver.js +37 -0
- package/lib/module/audio/resolver.js.map +1 -0
- package/lib/module/audio/types.js +4 -0
- package/lib/module/audio/types.js.map +1 -0
- package/lib/module/audio.js +9 -0
- package/lib/module/audio.js.map +1 -0
- package/lib/module/camera2d/bounds.js +59 -0
- package/lib/module/camera2d/bounds.js.map +1 -0
- package/lib/module/camera2d/extents.js +25 -0
- package/lib/module/camera2d/extents.js.map +1 -0
- package/lib/module/camera2d/follow.js +138 -0
- package/lib/module/camera2d/follow.js.map +1 -0
- package/lib/module/camera2d/index.js +20 -0
- package/lib/module/camera2d/index.js.map +1 -0
- package/lib/module/camera2d/interpolation.js +98 -0
- package/lib/module/camera2d/interpolation.js.map +1 -0
- package/lib/module/camera2d/shake.js +85 -0
- package/lib/module/camera2d/shake.js.map +1 -0
- package/lib/module/camera2d/transform.js +151 -0
- package/lib/module/camera2d/transform.js.map +1 -0
- package/lib/module/camera2d/types.js +4 -0
- package/lib/module/camera2d/types.js.map +1 -0
- package/lib/module/camera2d/validation.js +98 -0
- package/lib/module/camera2d/validation.js.map +1 -0
- package/lib/module/camera2d/visibility.js +138 -0
- package/lib/module/camera2d/visibility.js.map +1 -0
- package/lib/module/camera2d.js +9 -0
- package/lib/module/camera2d.js.map +1 -0
- package/lib/module/collision2d/colliders.js +266 -0
- package/lib/module/collision2d/colliders.js.map +1 -0
- package/lib/module/collision2d/debug.js +105 -0
- package/lib/module/collision2d/debug.js.map +1 -0
- package/lib/module/collision2d/filters.js +40 -0
- package/lib/module/collision2d/filters.js.map +1 -0
- package/lib/module/collision2d/index.js +12 -0
- package/lib/module/collision2d/index.js.map +1 -0
- package/lib/module/collision2d/intersections.js +67 -0
- package/lib/module/collision2d/intersections.js.map +1 -0
- package/lib/module/collision2d/manifolds.js +218 -0
- package/lib/module/collision2d/manifolds.js.map +1 -0
- package/lib/module/collision2d/segments.js +187 -0
- package/lib/module/collision2d/segments.js.map +1 -0
- package/lib/module/collision2d/spatialHash.js +167 -0
- package/lib/module/collision2d/spatialHash.js.map +1 -0
- package/lib/module/collision2d/sweeps.js +255 -0
- package/lib/module/collision2d/sweeps.js.map +1 -0
- package/lib/module/collision2d.js +9 -0
- package/lib/module/collision2d.js.map +1 -0
- package/lib/module/core/input/buttonPad.js +214 -0
- package/lib/module/core/input/buttonPad.js.map +1 -0
- package/lib/module/core/input/createInputBuffer.js +66 -14
- package/lib/module/core/input/createInputBuffer.js.map +1 -1
- package/lib/module/core/session/createGameSession.js +454 -50
- package/lib/module/core/session/createGameSession.js.map +1 -1
- package/lib/module/core/session/deepFreeze.js +92 -2
- package/lib/module/core/session/deepFreeze.js.map +1 -1
- package/lib/module/core/session/types.js +12 -0
- package/lib/module/core/session/types.js.map +1 -1
- package/lib/module/definition/defineGame.js +23 -0
- package/lib/module/definition/defineGame.js.map +1 -1
- package/lib/module/events/defineGameEvents.js +46 -0
- package/lib/module/events/defineGameEvents.js.map +1 -0
- package/lib/module/events/errors.js +16 -0
- package/lib/module/events/errors.js.map +1 -0
- package/lib/module/events/index.js +7 -0
- package/lib/module/events/index.js.map +1 -0
- package/lib/module/events/payload.js +219 -0
- package/lib/module/events/payload.js.map +1 -0
- package/lib/module/events/seed.js +33 -0
- package/lib/module/events/seed.js.map +1 -0
- package/lib/module/events/types.js +4 -0
- package/lib/module/events/types.js.map +1 -0
- package/lib/module/events.js +13 -0
- package/lib/module/events.js.map +1 -0
- package/lib/module/geometry/helpers.js +140 -0
- package/lib/module/geometry/helpers.js.map +1 -0
- package/lib/module/geometry/index.js +5 -0
- package/lib/module/geometry/index.js.map +1 -0
- package/lib/module/geometry/validation.js +104 -0
- package/lib/module/geometry/validation.js.map +1 -0
- package/lib/module/geometry.js +9 -0
- package/lib/module/geometry.js.map +1 -0
- package/lib/module/haptics/createGameHaptics.js +214 -0
- package/lib/module/haptics/createGameHaptics.js.map +1 -0
- package/lib/module/haptics/errors.js +12 -0
- package/lib/module/haptics/errors.js.map +1 -0
- package/lib/module/haptics/index.js +5 -0
- package/lib/module/haptics/index.js.map +1 -0
- package/lib/module/haptics/resolver.js +37 -0
- package/lib/module/haptics/resolver.js.map +1 -0
- package/lib/module/haptics/types.js +4 -0
- package/lib/module/haptics/types.js.map +1 -0
- package/lib/module/haptics.js +9 -0
- package/lib/module/haptics.js.map +1 -0
- package/lib/module/index.js +8 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/particles/createParticleSystem.js +481 -0
- package/lib/module/particles/createParticleSystem.js.map +1 -0
- package/lib/module/particles/defineParticleEffect.js +178 -0
- package/lib/module/particles/defineParticleEffect.js.map +1 -0
- package/lib/module/particles/errors.js +9 -0
- package/lib/module/particles/errors.js.map +1 -0
- package/lib/module/particles/index.js +8 -0
- package/lib/module/particles/index.js.map +1 -0
- package/lib/module/particles/sampling.js +121 -0
- package/lib/module/particles/sampling.js.map +1 -0
- package/lib/module/particles/types.js +4 -0
- package/lib/module/particles/types.js.map +1 -0
- package/lib/module/particles.js +4 -0
- package/lib/module/particles.js.map +1 -0
- package/lib/module/react/GameButtonPad.js +344 -0
- package/lib/module/react/GameButtonPad.js.map +1 -0
- package/lib/module/react/GamePointerInput.js +74 -16
- package/lib/module/react/GamePointerInput.js.map +1 -1
- package/lib/module/react/GameView.js +139 -60
- package/lib/module/react/GameView.js.map +1 -1
- package/lib/module/react/assets/createGameAssetStore.js +145 -95
- package/lib/module/react/assets/createGameAssetStore.js.map +1 -1
- package/lib/module/react/assets/useGameAssets.js +60 -35
- package/lib/module/react/assets/useGameAssets.js.map +1 -1
- package/lib/module/react/bindAppLifecycle.js +46 -17
- package/lib/module/react/bindAppLifecycle.js.map +1 -1
- package/lib/module/react/bindGameSession.js +13 -2
- package/lib/module/react/bindGameSession.js.map +1 -1
- package/lib/module/react/camera2d/GameLayer2D.js +50 -0
- package/lib/module/react/camera2d/GameLayer2D.js.map +1 -0
- package/lib/module/react/camera2d/defineGameCamera2D.js +30 -0
- package/lib/module/react/camera2d/defineGameCamera2D.js.map +1 -0
- package/lib/module/react/camera2d/usePresentedCameraBinding.js +177 -0
- package/lib/module/react/camera2d/usePresentedCameraBinding.js.map +1 -0
- package/lib/module/react/cleanupList.js +38 -0
- package/lib/module/react/cleanupList.js.map +1 -0
- package/lib/module/react/lifecycleSource.js +49 -0
- package/lib/module/react/lifecycleSource.js.map +1 -0
- package/lib/module/react/particles/ParticleView.js +308 -0
- package/lib/module/react/particles/ParticleView.js.map +1 -0
- package/lib/module/react/particles/culling.js +115 -0
- package/lib/module/react/particles/culling.js.map +1 -0
- package/lib/module/react/particles/index.js +7 -0
- package/lib/module/react/particles/index.js.map +1 -0
- package/lib/module/react/particles/spriteXForm.js +45 -0
- package/lib/module/react/particles/spriteXForm.js.map +1 -0
- package/lib/module/react/particles/useParticlePresentation.js +204 -0
- package/lib/module/react/particles/useParticlePresentation.js.map +1 -0
- package/lib/module/react/pointerBinding.js +55 -17
- package/lib/module/react/pointerBinding.js.map +1 -1
- package/lib/module/react/pointerCamera.js +28 -0
- package/lib/module/react/pointerCamera.js.map +1 -0
- package/lib/module/react/pointerCoalescer.js +16 -9
- package/lib/module/react/pointerCoalescer.js.map +1 -1
- package/lib/module/react/sprites/GameSprite.js +5 -17
- package/lib/module/react/sprites/GameSprite.js.map +1 -1
- package/lib/module/react/sprites/GameWorld2D.js +112 -9
- package/lib/module/react/sprites/GameWorld2D.js.map +1 -1
- package/lib/module/react/sprites/Sprite.js +48 -63
- package/lib/module/react/sprites/Sprite.js.map +1 -1
- package/lib/module/react/sprites/SpriteBatch.js +53 -28
- package/lib/module/react/sprites/SpriteBatch.js.map +1 -1
- package/lib/module/react/sprites/batchVisibility.js +58 -0
- package/lib/module/react/sprites/batchVisibility.js.map +1 -0
- package/lib/module/react/sprites/spriteSelection.js +103 -0
- package/lib/module/react/sprites/spriteSelection.js.map +1 -0
- package/lib/module/react/sprites/spriteTransform.js +46 -25
- package/lib/module/react/sprites/spriteTransform.js.map +1 -1
- package/lib/module/react/tilemap/TileMapLayer2D.js +217 -0
- package/lib/module/react/tilemap/TileMapLayer2D.js.map +1 -0
- package/lib/module/react/tilemap/tilePresentation.js +374 -0
- package/lib/module/react/tilemap/tilePresentation.js.map +1 -0
- package/lib/module/react/useGameSession.js +70 -0
- package/lib/module/react/useGameSession.js.map +1 -0
- package/lib/module/react/useGameSessionStatus.js +46 -0
- package/lib/module/react/useGameSessionStatus.js.map +1 -0
- package/lib/module/react/viewportBinding.js +5 -1
- package/lib/module/react/viewportBinding.js.map +1 -1
- package/lib/module/react.js +12 -1
- package/lib/module/react.js.map +1 -1
- package/lib/module/scene/defineScene.js +17 -0
- package/lib/module/scene/defineScene.js.map +1 -1
- package/lib/module/sprites/sampleSpriteClip.js +66 -6
- package/lib/module/sprites/sampleSpriteClip.js.map +1 -1
- package/lib/module/sprites/spriteAnimationState.js +10 -20
- package/lib/module/sprites/spriteAnimationState.js.map +1 -1
- package/lib/module/sprites.js +11 -0
- package/lib/module/sprites.js.map +1 -0
- package/lib/module/storage/adapters/asyncStorage.js +57 -0
- package/lib/module/storage/adapters/asyncStorage.js.map +1 -0
- package/lib/module/storage/adapters/memory.js +50 -0
- package/lib/module/storage/adapters/memory.js.map +1 -0
- package/lib/module/storage/errors.js +34 -0
- package/lib/module/storage/errors.js.map +1 -0
- package/lib/module/storage/index.js +11 -0
- package/lib/module/storage/index.js.map +1 -0
- package/lib/module/storage/schema.js +151 -0
- package/lib/module/storage/schema.js.map +1 -0
- package/lib/module/storage/serialization.js +249 -0
- package/lib/module/storage/serialization.js.map +1 -0
- package/lib/module/storage/store.js +387 -0
- package/lib/module/storage/store.js.map +1 -0
- package/lib/module/storage/types.js +16 -0
- package/lib/module/storage/types.js.map +1 -0
- package/lib/module/storage/validation.js +111 -0
- package/lib/module/storage/validation.js.map +1 -0
- package/lib/module/storage.js +4 -0
- package/lib/module/storage.js.map +1 -0
- package/lib/module/testing.js +4 -0
- package/lib/module/testing.js.map +1 -1
- package/lib/module/tilemap/chunkStats.js +27 -0
- package/lib/module/tilemap/chunkStats.js.map +1 -0
- package/lib/module/tilemap/definitions.js +308 -0
- package/lib/module/tilemap/definitions.js.map +1 -0
- package/lib/module/tilemap/errors.js +13 -0
- package/lib/module/tilemap/errors.js.map +1 -0
- package/lib/module/tilemap/index.js +10 -0
- package/lib/module/tilemap/index.js.map +1 -0
- package/lib/module/tilemap/movement.js +379 -0
- package/lib/module/tilemap/movement.js.map +1 -0
- package/lib/module/tilemap/queries.js +114 -0
- package/lib/module/tilemap/queries.js.map +1 -0
- package/lib/module/tilemap/tiledAdapter.js +141 -0
- package/lib/module/tilemap/tiledAdapter.js.map +1 -0
- package/lib/module/tilemap/types.js +4 -0
- package/lib/module/tilemap/types.js.map +1 -0
- package/lib/module/tilemap.js +4 -0
- package/lib/module/tilemap.js.map +1 -0
- package/lib/typescript/src/assets/defineAssets.d.ts +1 -1
- package/lib/typescript/src/assets/defineAssets.d.ts.map +1 -1
- package/lib/typescript/src/assets.d.ts +11 -0
- package/lib/typescript/src/assets.d.ts.map +1 -0
- package/lib/typescript/src/audio/createGameAudio.d.ts +5 -0
- package/lib/typescript/src/audio/createGameAudio.d.ts.map +1 -0
- package/lib/typescript/src/audio/errors.d.ts +6 -0
- package/lib/typescript/src/audio/errors.d.ts.map +1 -0
- package/lib/typescript/src/audio/index.d.ts +4 -0
- package/lib/typescript/src/audio/index.d.ts.map +1 -0
- package/lib/typescript/src/audio/resolver.d.ts +44 -0
- package/lib/typescript/src/audio/resolver.d.ts.map +1 -0
- package/lib/typescript/src/audio/types.d.ts +42 -0
- package/lib/typescript/src/audio/types.d.ts.map +1 -0
- package/lib/typescript/src/audio.d.ts +8 -0
- package/lib/typescript/src/audio.d.ts.map +1 -0
- package/lib/typescript/src/camera2d/bounds.d.ts +25 -0
- package/lib/typescript/src/camera2d/bounds.d.ts.map +1 -0
- package/lib/typescript/src/camera2d/extents.d.ts +17 -0
- package/lib/typescript/src/camera2d/extents.d.ts.map +1 -0
- package/lib/typescript/src/camera2d/follow.d.ts +24 -0
- package/lib/typescript/src/camera2d/follow.d.ts.map +1 -0
- package/lib/typescript/src/camera2d/index.d.ts +19 -0
- package/lib/typescript/src/camera2d/index.d.ts.map +1 -0
- package/lib/typescript/src/camera2d/interpolation.d.ts +17 -0
- package/lib/typescript/src/camera2d/interpolation.d.ts.map +1 -0
- package/lib/typescript/src/camera2d/shake.d.ts +20 -0
- package/lib/typescript/src/camera2d/shake.d.ts.map +1 -0
- package/lib/typescript/src/camera2d/transform.d.ts +44 -0
- package/lib/typescript/src/camera2d/transform.d.ts.map +1 -0
- package/lib/typescript/src/camera2d/types.d.ts +74 -0
- package/lib/typescript/src/camera2d/types.d.ts.map +1 -0
- package/lib/typescript/src/camera2d/validation.d.ts +30 -0
- package/lib/typescript/src/camera2d/validation.d.ts.map +1 -0
- package/lib/typescript/src/camera2d/visibility.d.ts +47 -0
- package/lib/typescript/src/camera2d/visibility.d.ts.map +1 -0
- package/lib/typescript/src/camera2d.d.ts +7 -0
- package/lib/typescript/src/camera2d.d.ts.map +1 -0
- package/lib/typescript/src/collision2d/colliders.d.ts +120 -0
- package/lib/typescript/src/collision2d/colliders.d.ts.map +1 -0
- package/lib/typescript/src/collision2d/debug.d.ts +67 -0
- package/lib/typescript/src/collision2d/debug.d.ts.map +1 -0
- package/lib/typescript/src/collision2d/filters.d.ts +28 -0
- package/lib/typescript/src/collision2d/filters.d.ts.map +1 -0
- package/lib/typescript/src/collision2d/index.d.ts +17 -0
- package/lib/typescript/src/collision2d/index.d.ts.map +1 -0
- package/lib/typescript/src/collision2d/intersections.d.ts +24 -0
- package/lib/typescript/src/collision2d/intersections.d.ts.map +1 -0
- package/lib/typescript/src/collision2d/manifolds.d.ts +39 -0
- package/lib/typescript/src/collision2d/manifolds.d.ts.map +1 -0
- package/lib/typescript/src/collision2d/segments.d.ts +24 -0
- package/lib/typescript/src/collision2d/segments.d.ts.map +1 -0
- package/lib/typescript/src/collision2d/spatialHash.d.ts +50 -0
- package/lib/typescript/src/collision2d/spatialHash.d.ts.map +1 -0
- package/lib/typescript/src/collision2d/sweeps.d.ts +60 -0
- package/lib/typescript/src/collision2d/sweeps.d.ts.map +1 -0
- package/lib/typescript/src/collision2d.d.ts +7 -0
- package/lib/typescript/src/collision2d.d.ts.map +1 -0
- package/lib/typescript/src/core/input/buttonPad.d.ts +59 -0
- package/lib/typescript/src/core/input/buttonPad.d.ts.map +1 -0
- package/lib/typescript/src/core/input/createInputBuffer.d.ts +1 -1
- package/lib/typescript/src/core/input/createInputBuffer.d.ts.map +1 -1
- package/lib/typescript/src/core/input/types.d.ts +7 -0
- package/lib/typescript/src/core/input/types.d.ts.map +1 -1
- package/lib/typescript/src/core/session/createGameSession.d.ts +4 -2
- package/lib/typescript/src/core/session/createGameSession.d.ts.map +1 -1
- package/lib/typescript/src/core/session/deepFreeze.d.ts +21 -1
- package/lib/typescript/src/core/session/deepFreeze.d.ts.map +1 -1
- package/lib/typescript/src/core/session/diagnostics.d.ts +2 -0
- package/lib/typescript/src/core/session/diagnostics.d.ts.map +1 -1
- package/lib/typescript/src/core/session/types.d.ts +69 -4
- package/lib/typescript/src/core/session/types.d.ts.map +1 -1
- package/lib/typescript/src/definition/defineGame.d.ts +12 -1
- package/lib/typescript/src/definition/defineGame.d.ts.map +1 -1
- package/lib/typescript/src/definition/types.d.ts +10 -1
- package/lib/typescript/src/definition/types.d.ts.map +1 -1
- package/lib/typescript/src/events/defineGameEvents.d.ts +26 -0
- package/lib/typescript/src/events/defineGameEvents.d.ts.map +1 -0
- package/lib/typescript/src/events/errors.d.ts +12 -0
- package/lib/typescript/src/events/errors.d.ts.map +1 -0
- package/lib/typescript/src/events/index.d.ts +6 -0
- package/lib/typescript/src/events/index.d.ts.map +1 -0
- package/lib/typescript/src/events/payload.d.ts +21 -0
- package/lib/typescript/src/events/payload.d.ts.map +1 -0
- package/lib/typescript/src/events/seed.d.ts +3 -0
- package/lib/typescript/src/events/seed.d.ts.map +1 -0
- package/lib/typescript/src/events/types.d.ts +42 -0
- package/lib/typescript/src/events/types.d.ts.map +1 -0
- package/lib/typescript/src/events.d.ts +12 -0
- package/lib/typescript/src/events.d.ts.map +1 -0
- package/lib/typescript/src/geometry/helpers.d.ts +35 -0
- package/lib/typescript/src/geometry/helpers.d.ts.map +1 -0
- package/lib/typescript/src/geometry/index.d.ts +4 -0
- package/lib/typescript/src/geometry/index.d.ts.map +1 -0
- package/lib/typescript/src/geometry/types.d.ts +49 -0
- package/lib/typescript/src/geometry/types.d.ts.map +1 -1
- package/lib/typescript/src/geometry/validation.d.ts +44 -0
- package/lib/typescript/src/geometry/validation.d.ts.map +1 -0
- package/lib/typescript/src/geometry.d.ts +7 -0
- package/lib/typescript/src/geometry.d.ts.map +1 -0
- package/lib/typescript/src/haptics/createGameHaptics.d.ts +3 -0
- package/lib/typescript/src/haptics/createGameHaptics.d.ts.map +1 -0
- package/lib/typescript/src/haptics/errors.d.ts +6 -0
- package/lib/typescript/src/haptics/errors.d.ts.map +1 -0
- package/lib/typescript/src/haptics/index.d.ts +4 -0
- package/lib/typescript/src/haptics/index.d.ts.map +1 -0
- package/lib/typescript/src/haptics/resolver.d.ts +22 -0
- package/lib/typescript/src/haptics/resolver.d.ts.map +1 -0
- package/lib/typescript/src/haptics/types.d.ts +37 -0
- package/lib/typescript/src/haptics/types.d.ts.map +1 -0
- package/lib/typescript/src/haptics.d.ts +8 -0
- package/lib/typescript/src/haptics.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +10 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/particles/createParticleSystem.d.ts +5 -0
- package/lib/typescript/src/particles/createParticleSystem.d.ts.map +1 -0
- package/lib/typescript/src/particles/defineParticleEffect.d.ts +3 -0
- package/lib/typescript/src/particles/defineParticleEffect.d.ts.map +1 -0
- package/lib/typescript/src/particles/errors.d.ts +5 -0
- package/lib/typescript/src/particles/errors.d.ts.map +1 -0
- package/lib/typescript/src/particles/index.d.ts +6 -0
- package/lib/typescript/src/particles/index.d.ts.map +1 -0
- package/lib/typescript/src/particles/sampling.d.ts +54 -0
- package/lib/typescript/src/particles/sampling.d.ts.map +1 -0
- package/lib/typescript/src/particles/types.d.ts +292 -0
- package/lib/typescript/src/particles/types.d.ts.map +1 -0
- package/lib/typescript/src/particles.d.ts +2 -0
- package/lib/typescript/src/particles.d.ts.map +1 -0
- package/lib/typescript/src/react/GameButtonPad.d.ts +84 -0
- package/lib/typescript/src/react/GameButtonPad.d.ts.map +1 -0
- package/lib/typescript/src/react/GamePointerInput.d.ts.map +1 -1
- package/lib/typescript/src/react/GameView.d.ts +27 -1
- package/lib/typescript/src/react/GameView.d.ts.map +1 -1
- package/lib/typescript/src/react/assets/createGameAssetStore.d.ts.map +1 -1
- package/lib/typescript/src/react/assets/useGameAssets.d.ts.map +1 -1
- package/lib/typescript/src/react/bindAppLifecycle.d.ts +7 -1
- package/lib/typescript/src/react/bindAppLifecycle.d.ts.map +1 -1
- package/lib/typescript/src/react/bindGameSession.d.ts.map +1 -1
- package/lib/typescript/src/react/camera2d/GameLayer2D.d.ts +24 -0
- package/lib/typescript/src/react/camera2d/GameLayer2D.d.ts.map +1 -0
- package/lib/typescript/src/react/camera2d/defineGameCamera2D.d.ts +44 -0
- package/lib/typescript/src/react/camera2d/defineGameCamera2D.d.ts.map +1 -0
- package/lib/typescript/src/react/camera2d/usePresentedCameraBinding.d.ts +22 -0
- package/lib/typescript/src/react/camera2d/usePresentedCameraBinding.d.ts.map +1 -0
- package/lib/typescript/src/react/cleanupList.d.ts +21 -0
- package/lib/typescript/src/react/cleanupList.d.ts.map +1 -0
- package/lib/typescript/src/react/instrumentation.d.ts +33 -3
- package/lib/typescript/src/react/instrumentation.d.ts.map +1 -1
- package/lib/typescript/src/react/lifecycleSource.d.ts +21 -0
- package/lib/typescript/src/react/lifecycleSource.d.ts.map +1 -0
- package/lib/typescript/src/react/particles/ParticleView.d.ts +65 -0
- package/lib/typescript/src/react/particles/ParticleView.d.ts.map +1 -0
- package/lib/typescript/src/react/particles/culling.d.ts +70 -0
- package/lib/typescript/src/react/particles/culling.d.ts.map +1 -0
- package/lib/typescript/src/react/particles/index.d.ts +7 -0
- package/lib/typescript/src/react/particles/index.d.ts.map +1 -0
- package/lib/typescript/src/react/particles/spriteXForm.d.ts +28 -0
- package/lib/typescript/src/react/particles/spriteXForm.d.ts.map +1 -0
- package/lib/typescript/src/react/particles/useParticlePresentation.d.ts +48 -0
- package/lib/typescript/src/react/particles/useParticlePresentation.d.ts.map +1 -0
- package/lib/typescript/src/react/pointerBinding.d.ts +38 -8
- package/lib/typescript/src/react/pointerBinding.d.ts.map +1 -1
- package/lib/typescript/src/react/pointerCamera.d.ts +20 -0
- package/lib/typescript/src/react/pointerCamera.d.ts.map +1 -0
- package/lib/typescript/src/react/pointerCoalescer.d.ts +17 -1
- package/lib/typescript/src/react/pointerCoalescer.d.ts.map +1 -1
- package/lib/typescript/src/react/sprites/GameSprite.d.ts +3 -10
- package/lib/typescript/src/react/sprites/GameSprite.d.ts.map +1 -1
- package/lib/typescript/src/react/sprites/GameWorld2D.d.ts +51 -6
- package/lib/typescript/src/react/sprites/GameWorld2D.d.ts.map +1 -1
- package/lib/typescript/src/react/sprites/Sprite.d.ts.map +1 -1
- package/lib/typescript/src/react/sprites/SpriteBatch.d.ts +22 -1
- package/lib/typescript/src/react/sprites/SpriteBatch.d.ts.map +1 -1
- package/lib/typescript/src/react/sprites/batchVisibility.d.ts +28 -0
- package/lib/typescript/src/react/sprites/batchVisibility.d.ts.map +1 -0
- package/lib/typescript/src/react/sprites/spriteSelection.d.ts +58 -0
- package/lib/typescript/src/react/sprites/spriteSelection.d.ts.map +1 -0
- package/lib/typescript/src/react/sprites/spriteTransform.d.ts +27 -12
- package/lib/typescript/src/react/sprites/spriteTransform.d.ts.map +1 -1
- package/lib/typescript/src/react/tilemap/TileMapLayer2D.d.ts +69 -0
- package/lib/typescript/src/react/tilemap/TileMapLayer2D.d.ts.map +1 -0
- package/lib/typescript/src/react/tilemap/tilePresentation.d.ts +217 -0
- package/lib/typescript/src/react/tilemap/tilePresentation.d.ts.map +1 -0
- package/lib/typescript/src/react/useGameSession.d.ts +48 -0
- package/lib/typescript/src/react/useGameSession.d.ts.map +1 -0
- package/lib/typescript/src/react/useGameSessionStatus.d.ts +23 -0
- package/lib/typescript/src/react/useGameSessionStatus.d.ts.map +1 -0
- package/lib/typescript/src/react/viewportBinding.d.ts +5 -1
- package/lib/typescript/src/react/viewportBinding.d.ts.map +1 -1
- package/lib/typescript/src/react.d.ts +18 -1
- package/lib/typescript/src/react.d.ts.map +1 -1
- package/lib/typescript/src/scene/defineScene.d.ts +22 -2
- package/lib/typescript/src/scene/defineScene.d.ts.map +1 -1
- package/lib/typescript/src/scene/types.d.ts +24 -4
- package/lib/typescript/src/scene/types.d.ts.map +1 -1
- package/lib/typescript/src/sprites/sampleSpriteClip.d.ts +26 -2
- package/lib/typescript/src/sprites/sampleSpriteClip.d.ts.map +1 -1
- package/lib/typescript/src/sprites/spriteAnimationState.d.ts +6 -7
- package/lib/typescript/src/sprites/spriteAnimationState.d.ts.map +1 -1
- package/lib/typescript/src/sprites.d.ts +10 -0
- package/lib/typescript/src/sprites.d.ts.map +1 -0
- package/lib/typescript/src/storage/adapters/asyncStorage.d.ts +18 -0
- package/lib/typescript/src/storage/adapters/asyncStorage.d.ts.map +1 -0
- package/lib/typescript/src/storage/adapters/memory.d.ts +16 -0
- package/lib/typescript/src/storage/adapters/memory.d.ts.map +1 -0
- package/lib/typescript/src/storage/errors.d.ts +37 -0
- package/lib/typescript/src/storage/errors.d.ts.map +1 -0
- package/lib/typescript/src/storage/index.d.ts +11 -0
- package/lib/typescript/src/storage/index.d.ts.map +1 -0
- package/lib/typescript/src/storage/schema.d.ts +22 -0
- package/lib/typescript/src/storage/schema.d.ts.map +1 -0
- package/lib/typescript/src/storage/serialization.d.ts +23 -0
- package/lib/typescript/src/storage/serialization.d.ts.map +1 -0
- package/lib/typescript/src/storage/store.d.ts +3 -0
- package/lib/typescript/src/storage/store.d.ts.map +1 -0
- package/lib/typescript/src/storage/types.d.ts +69 -0
- package/lib/typescript/src/storage/types.d.ts.map +1 -0
- package/lib/typescript/src/storage/validation.d.ts +24 -0
- package/lib/typescript/src/storage/validation.d.ts.map +1 -0
- package/lib/typescript/src/storage.d.ts +2 -0
- package/lib/typescript/src/storage.d.ts.map +1 -0
- package/lib/typescript/src/testing.d.ts +1 -0
- package/lib/typescript/src/testing.d.ts.map +1 -1
- package/lib/typescript/src/tilemap/chunkStats.d.ts +14 -0
- package/lib/typescript/src/tilemap/chunkStats.d.ts.map +1 -0
- package/lib/typescript/src/tilemap/definitions.d.ts +38 -0
- package/lib/typescript/src/tilemap/definitions.d.ts.map +1 -0
- package/lib/typescript/src/tilemap/errors.d.ts +7 -0
- package/lib/typescript/src/tilemap/errors.d.ts.map +1 -0
- package/lib/typescript/src/tilemap/index.d.ts +8 -0
- package/lib/typescript/src/tilemap/index.d.ts.map +1 -0
- package/lib/typescript/src/tilemap/movement.d.ts +52 -0
- package/lib/typescript/src/tilemap/movement.d.ts.map +1 -0
- package/lib/typescript/src/tilemap/queries.d.ts +21 -0
- package/lib/typescript/src/tilemap/queries.d.ts.map +1 -0
- package/lib/typescript/src/tilemap/tiledAdapter.d.ts +32 -0
- package/lib/typescript/src/tilemap/tiledAdapter.d.ts.map +1 -0
- package/lib/typescript/src/tilemap/types.d.ts +127 -0
- package/lib/typescript/src/tilemap/types.d.ts.map +1 -0
- package/lib/typescript/src/tilemap.d.ts +2 -0
- package/lib/typescript/src/tilemap.d.ts.map +1 -0
- package/package.json +94 -4
- package/src/assets/defineAssets.ts +31 -5
- package/src/assets.ts +27 -0
- package/src/audio/createGameAudio.ts +749 -0
- package/src/audio/errors.ts +12 -0
- package/src/audio/index.ts +9 -0
- package/src/audio/resolver.ts +74 -0
- package/src/audio/types.ts +47 -0
- package/src/audio.ts +13 -0
- package/src/camera2d/bounds.ts +69 -0
- package/src/camera2d/extents.ts +25 -0
- package/src/camera2d/follow.ts +165 -0
- package/src/camera2d/index.ts +29 -0
- package/src/camera2d/interpolation.ts +113 -0
- package/src/camera2d/shake.ts +90 -0
- package/src/camera2d/transform.ts +148 -0
- package/src/camera2d/types.ts +73 -0
- package/src/camera2d/validation.ts +126 -0
- package/src/camera2d/visibility.ts +181 -0
- package/src/camera2d.ts +6 -0
- package/src/collision2d/colliders.ts +292 -0
- package/src/collision2d/debug.ts +133 -0
- package/src/collision2d/filters.ts +48 -0
- package/src/collision2d/index.ts +74 -0
- package/src/collision2d/intersections.ts +82 -0
- package/src/collision2d/manifolds.ts +191 -0
- package/src/collision2d/segments.ts +176 -0
- package/src/collision2d/spatialHash.ts +211 -0
- package/src/collision2d/sweeps.ts +269 -0
- package/src/collision2d.ts +6 -0
- package/src/core/input/buttonPad.ts +235 -0
- package/src/core/input/createInputBuffer.ts +95 -14
- package/src/core/input/types.ts +7 -0
- package/src/core/session/createGameSession.ts +480 -53
- package/src/core/session/deepFreeze.ts +104 -3
- package/src/core/session/diagnostics.ts +2 -0
- package/src/core/session/types.ts +77 -4
- package/src/definition/defineGame.ts +61 -3
- package/src/definition/types.ts +10 -0
- package/src/events/defineGameEvents.ts +46 -0
- package/src/events/errors.ts +14 -0
- package/src/events/index.ts +14 -0
- package/src/events/payload.ts +234 -0
- package/src/events/seed.ts +33 -0
- package/src/events/types.ts +53 -0
- package/src/events.ts +20 -0
- package/src/geometry/helpers.ts +136 -0
- package/src/geometry/index.ts +14 -0
- package/src/geometry/types.ts +54 -0
- package/src/geometry/validation.ts +131 -0
- package/src/geometry.ts +6 -0
- package/src/haptics/createGameHaptics.ts +214 -0
- package/src/haptics/errors.ts +12 -0
- package/src/haptics/index.ts +3 -0
- package/src/haptics/resolver.ts +58 -0
- package/src/haptics/types.ts +48 -0
- package/src/haptics.ts +7 -0
- package/src/index.ts +37 -1
- package/src/particles/createParticleSystem.ts +505 -0
- package/src/particles/defineParticleEffect.ts +164 -0
- package/src/particles/errors.ts +6 -0
- package/src/particles/index.ts +5 -0
- package/src/particles/sampling.ts +141 -0
- package/src/particles/types.ts +327 -0
- package/src/particles.ts +1 -0
- package/src/react/GameButtonPad.tsx +436 -0
- package/src/react/GamePointerInput.tsx +73 -8
- package/src/react/GameView.tsx +160 -61
- package/src/react/assets/createGameAssetStore.ts +140 -107
- package/src/react/assets/useGameAssets.ts +63 -31
- package/src/react/bindAppLifecycle.ts +55 -19
- package/src/react/bindGameSession.ts +12 -2
- package/src/react/camera2d/GameLayer2D.tsx +43 -0
- package/src/react/camera2d/defineGameCamera2D.ts +59 -0
- package/src/react/camera2d/usePresentedCameraBinding.ts +185 -0
- package/src/react/cleanupList.ts +48 -0
- package/src/react/instrumentation.ts +31 -3
- package/src/react/lifecycleSource.ts +58 -0
- package/src/react/particles/ParticleView.tsx +358 -0
- package/src/react/particles/culling.ts +145 -0
- package/src/react/particles/index.ts +6 -0
- package/src/react/particles/spriteXForm.ts +72 -0
- package/src/react/particles/useParticlePresentation.ts +261 -0
- package/src/react/pointerBinding.ts +74 -16
- package/src/react/pointerCamera.ts +33 -0
- package/src/react/pointerCoalescer.ts +36 -10
- package/src/react/sprites/GameSprite.tsx +4 -33
- package/src/react/sprites/GameWorld2D.tsx +113 -7
- package/src/react/sprites/Sprite.tsx +51 -72
- package/src/react/sprites/SpriteBatch.tsx +74 -22
- package/src/react/sprites/batchVisibility.ts +76 -0
- package/src/react/sprites/spriteSelection.ts +133 -0
- package/src/react/sprites/spriteTransform.ts +46 -26
- package/src/react/tilemap/TileMapLayer2D.tsx +274 -0
- package/src/react/tilemap/tilePresentation.ts +491 -0
- package/src/react/useGameSession.ts +90 -0
- package/src/react/useGameSessionStatus.ts +53 -0
- package/src/react/viewportBinding.ts +5 -1
- package/src/react.ts +18 -1
- package/src/scene/defineScene.ts +47 -5
- package/src/scene/types.ts +37 -3
- package/src/sprites/sampleSpriteClip.ts +82 -14
- package/src/sprites/spriteAnimationState.ts +29 -25
- package/src/sprites.ts +22 -0
- package/src/storage/adapters/asyncStorage.ts +77 -0
- package/src/storage/adapters/memory.ts +58 -0
- package/src/storage/errors.ts +88 -0
- package/src/storage/index.ts +18 -0
- package/src/storage/schema.ts +147 -0
- package/src/storage/serialization.ts +248 -0
- package/src/storage/store.ts +380 -0
- package/src/storage/types.ts +76 -0
- package/src/storage/validation.ts +102 -0
- package/src/storage.ts +1 -0
- package/src/testing.ts +4 -0
- package/src/tilemap/chunkStats.ts +24 -0
- package/src/tilemap/definitions.ts +327 -0
- package/src/tilemap/errors.ts +11 -0
- package/src/tilemap/index.ts +10 -0
- package/src/tilemap/movement.ts +368 -0
- package/src/tilemap/queries.ts +136 -0
- package/src/tilemap/tiledAdapter.ts +165 -0
- package/src/tilemap/types.ts +119 -0
- package/src/tilemap.ts +1 -0
|
@@ -0,0 +1,784 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { createAudioInstallationError, GameAudioError } from "./errors.js";
|
|
4
|
+
import { loadAudioApi } from "./resolver.js";
|
|
5
|
+
const CATEGORIES = ['master', 'music', 'sfx', 'ui'];
|
|
6
|
+
function isValidVolume(volume) {
|
|
7
|
+
return Number.isFinite(volume) && volume >= 0 && volume <= 1;
|
|
8
|
+
}
|
|
9
|
+
function assertCategory(category) {
|
|
10
|
+
if (!CATEGORIES.includes(category)) {
|
|
11
|
+
throw new GameAudioError(`Unknown audio category "${String(category)}"`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function assertVolume(volume) {
|
|
15
|
+
if (!isValidVolume(volume)) {
|
|
16
|
+
throw new GameAudioError(`Volume must be a finite number in [0, 1], got ${String(volume)}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// GS-AUDIO-04: backend-global interruption observation is reference
|
|
21
|
+
// counted — the first live instance enables it, the last dispose disables
|
|
22
|
+
// it, so two instances can never disable each other's observation.
|
|
23
|
+
let interruptionObserverCount = 0;
|
|
24
|
+
let assetInputLoader = null;
|
|
25
|
+
export function __setAssetInputLoader(fn) {
|
|
26
|
+
assetInputLoader = fn;
|
|
27
|
+
}
|
|
28
|
+
export function __getAssetInputLoader() {
|
|
29
|
+
return assetInputLoader;
|
|
30
|
+
}
|
|
31
|
+
export async function createGameAudio(options) {
|
|
32
|
+
let api;
|
|
33
|
+
try {
|
|
34
|
+
api = await loadAudioApi();
|
|
35
|
+
} catch {
|
|
36
|
+
throw createAudioInstallationError();
|
|
37
|
+
}
|
|
38
|
+
if (!api || !api.AudioContext) {
|
|
39
|
+
throw createAudioInstallationError();
|
|
40
|
+
}
|
|
41
|
+
if (!options || typeof options.sounds !== 'object' || options.sounds === null) {
|
|
42
|
+
throw new GameAudioError('createGameAudio requires { sounds: Record<string, number> }');
|
|
43
|
+
}
|
|
44
|
+
const soundIds = Object.keys(options.sounds);
|
|
45
|
+
if (soundIds.length === 0) {
|
|
46
|
+
throw new GameAudioError('createGameAudio sounds must not be empty');
|
|
47
|
+
}
|
|
48
|
+
for (const id of soundIds) {
|
|
49
|
+
const v = options.sounds[id];
|
|
50
|
+
if (typeof v !== 'number' || !Number.isFinite(v)) {
|
|
51
|
+
throw new GameAudioError(`Sound "${id}" must be a finite asset ID (require result)`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const {
|
|
55
|
+
AudioContext,
|
|
56
|
+
AudioManager
|
|
57
|
+
} = api;
|
|
58
|
+
let context = null;
|
|
59
|
+
let masterGain = null;
|
|
60
|
+
const categoryGains = {
|
|
61
|
+
master: null,
|
|
62
|
+
music: null,
|
|
63
|
+
sfx: null,
|
|
64
|
+
ui: null
|
|
65
|
+
};
|
|
66
|
+
let interruptionSub = null;
|
|
67
|
+
let interruptionEnabled = false;
|
|
68
|
+
let appStateSub = null;
|
|
69
|
+
let idleSuspendTimer = null;
|
|
70
|
+
|
|
71
|
+
// Create context in a transaction so failure cleans up
|
|
72
|
+
try {
|
|
73
|
+
context = new AudioContext();
|
|
74
|
+
} catch (e) {
|
|
75
|
+
throw e;
|
|
76
|
+
}
|
|
77
|
+
const volumes = {
|
|
78
|
+
master: 1,
|
|
79
|
+
music: 1,
|
|
80
|
+
sfx: 1,
|
|
81
|
+
ui: 1
|
|
82
|
+
};
|
|
83
|
+
let muted = false;
|
|
84
|
+
let disposed = false;
|
|
85
|
+
let userPaused = false;
|
|
86
|
+
let sessionPaused = false;
|
|
87
|
+
let appPaused = false;
|
|
88
|
+
let interruptionPaused = false;
|
|
89
|
+
let interruptionRequiresExplicitResume = false;
|
|
90
|
+
const bufferCache = new Map();
|
|
91
|
+
const pendingDecodes = new Map();
|
|
92
|
+
async function getBuffer(id) {
|
|
93
|
+
if (disposed) throw new GameAudioError('GameAudio is disposed');
|
|
94
|
+
if (bufferCache.has(id)) return bufferCache.get(id);
|
|
95
|
+
if (pendingDecodes.has(id)) return pendingDecodes.get(id);
|
|
96
|
+
const assetId = options.sounds[id];
|
|
97
|
+
const promise = (async () => {
|
|
98
|
+
let input;
|
|
99
|
+
if (assetInputLoader) {
|
|
100
|
+
input = await assetInputLoader(assetId);
|
|
101
|
+
} else {
|
|
102
|
+
let assetUri = null;
|
|
103
|
+
let expoAssetFailed = false;
|
|
104
|
+
try {
|
|
105
|
+
const {
|
|
106
|
+
Asset: ExpoAsset
|
|
107
|
+
} = await import('expo-asset');
|
|
108
|
+
const asset = ExpoAsset.fromModule(assetId);
|
|
109
|
+
await asset.downloadAsync();
|
|
110
|
+
assetUri = asset.localUri ?? asset.uri;
|
|
111
|
+
} catch {
|
|
112
|
+
expoAssetFailed = true;
|
|
113
|
+
}
|
|
114
|
+
if (assetUri) input = assetUri;else if (expoAssetFailed) input = assetId;else throw new GameAudioError(`Failed to resolve asset for sound "${String(id)}" — expo-asset returned no URI`);
|
|
115
|
+
}
|
|
116
|
+
let buffer;
|
|
117
|
+
try {
|
|
118
|
+
buffer = await context.decodeAudioData(input);
|
|
119
|
+
} catch (e) {
|
|
120
|
+
throw new GameAudioError(`Failed to decode audio asset "${String(id)}": ${e.message}`);
|
|
121
|
+
}
|
|
122
|
+
bufferCache.set(id, buffer);
|
|
123
|
+
pendingDecodes.delete(id);
|
|
124
|
+
return buffer;
|
|
125
|
+
})();
|
|
126
|
+
pendingDecodes.set(id, promise);
|
|
127
|
+
try {
|
|
128
|
+
return await promise;
|
|
129
|
+
} catch (e) {
|
|
130
|
+
pendingDecodes.delete(id);
|
|
131
|
+
throw e;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function applyGain(category) {
|
|
135
|
+
const gain = categoryGains[category];
|
|
136
|
+
if (!gain) return;
|
|
137
|
+
// F1: write only own volume; composition happens in graph (master * category)
|
|
138
|
+
const value = volumes[category];
|
|
139
|
+
try {
|
|
140
|
+
const param = gain.gain;
|
|
141
|
+
const now = context.currentTime ?? 0;
|
|
142
|
+
if (param.cancelScheduledValues) param.cancelScheduledValues(now);
|
|
143
|
+
if (param.setValueAtTime) param.setValueAtTime(param.value, now);
|
|
144
|
+
if (param.linearRampToValueAtTime) param.linearRampToValueAtTime(value, now + 0.02);else param.value = value;
|
|
145
|
+
} catch {
|
|
146
|
+
try {
|
|
147
|
+
gain.gain.value = value;
|
|
148
|
+
} catch {}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function applyAllGains() {
|
|
152
|
+
for (const c of CATEGORIES) applyGain(c);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Transaction: gain graph plus eager decode, with cleanup on failure
|
|
156
|
+
// (F3, GS-AUDIO-03). Any failure closes the half-built context before
|
|
157
|
+
// rethrowing — init never returns a silent partial object.
|
|
158
|
+
try {
|
|
159
|
+
// Gain setup — master -> destination, categories -> master (F1).
|
|
160
|
+
// GS-AUDIO-03: the gain graph is REQUIRED setup. A failure throws
|
|
161
|
+
// transactionally (the init boundary closes the context and rethrows)
|
|
162
|
+
// instead of returning an apparently usable but silent object.
|
|
163
|
+
const mg = context.createGain?.();
|
|
164
|
+
if (!mg) {
|
|
165
|
+
throw new GameAudioError('createGameAudio requires a working channel gain graph (master createGain failed)');
|
|
166
|
+
}
|
|
167
|
+
masterGain = mg;
|
|
168
|
+
try {
|
|
169
|
+
mg.connect(context.destination);
|
|
170
|
+
} catch (e) {
|
|
171
|
+
throw new GameAudioError(`createGameAudio requires a working channel gain graph (master connect failed: ${e.message})`);
|
|
172
|
+
}
|
|
173
|
+
for (const cat of CATEGORIES) {
|
|
174
|
+
if (cat === 'master') {
|
|
175
|
+
categoryGains[cat] = mg;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
const cg = context.createGain?.();
|
|
179
|
+
if (!cg) {
|
|
180
|
+
throw new GameAudioError(`createGameAudio requires a working channel gain graph (category ${cat} createGain failed)`);
|
|
181
|
+
}
|
|
182
|
+
categoryGains[cat] = cg;
|
|
183
|
+
try {
|
|
184
|
+
cg.connect(mg);
|
|
185
|
+
} catch (e) {
|
|
186
|
+
throw new GameAudioError(`createGameAudio requires a working channel gain graph (category ${cat} connect failed: ${e.message})`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
await Promise.all(soundIds.map(id => getBuffer(id)));
|
|
190
|
+
} catch (e) {
|
|
191
|
+
// Close context exactly once before rethrowing
|
|
192
|
+
try {
|
|
193
|
+
await context.close();
|
|
194
|
+
} catch {}
|
|
195
|
+
pendingDecodes.clear();
|
|
196
|
+
bufferCache.clear();
|
|
197
|
+
throw e;
|
|
198
|
+
}
|
|
199
|
+
let currentMusicId = null;
|
|
200
|
+
let currentMusicNode = null;
|
|
201
|
+
let musicGeneration = 0;
|
|
202
|
+
let activeMusicGeneration = 0;
|
|
203
|
+
let pendingMusic = null;
|
|
204
|
+
const activeVoices = new Set();
|
|
205
|
+
const concurrencyMap = new Map();
|
|
206
|
+
// GS-AUDIO-02: pending reservations are tracked distinctly from live
|
|
207
|
+
// voices. Only pending work takes cancellation tokens (removed when its
|
|
208
|
+
// continuation runs); live voices use normal stop/cleanup and lose all
|
|
209
|
+
// owner references immediately — never a token.
|
|
210
|
+
const pendingReservations = new Set();
|
|
211
|
+
const cancelledReservations = new Set();
|
|
212
|
+
function isEffectivelyPaused() {
|
|
213
|
+
return userPaused || sessionPaused || appPaused || interruptionPaused || muted;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// GS-AUDIO-04: serialized suspend/resume intents. Every pause-affecting
|
|
217
|
+
// call enqueues one link; each link re-reads the CURRENT effective pause
|
|
218
|
+
// state and converges the context to it, so rapid intent changes settle
|
|
219
|
+
// to the latest request instead of racing delayed native promises.
|
|
220
|
+
// Native calls never overlap: at most one suspend/resume is in flight.
|
|
221
|
+
let suspendChain = Promise.resolve();
|
|
222
|
+
async function runSuspendLink() {
|
|
223
|
+
if (disposed || !context) return;
|
|
224
|
+
if (isEffectivelyPaused()) {
|
|
225
|
+
if (idleSuspendTimer) {
|
|
226
|
+
clearTimeout(idleSuspendTimer);
|
|
227
|
+
idleSuspendTimer = null;
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
await context.suspend();
|
|
231
|
+
} catch {}
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
const state = context.state;
|
|
236
|
+
if (state === 'suspended') {
|
|
237
|
+
await context.resume();
|
|
238
|
+
}
|
|
239
|
+
} catch {}
|
|
240
|
+
applyAllGains();
|
|
241
|
+
// If we have deferred music intent and now unpaused, start it (F4)
|
|
242
|
+
if (pendingMusic && !isEffectivelyPaused()) {
|
|
243
|
+
const {
|
|
244
|
+
id,
|
|
245
|
+
generation
|
|
246
|
+
} = pendingMusic;
|
|
247
|
+
pendingMusic = null;
|
|
248
|
+
void startMusicInternal(id, generation);
|
|
249
|
+
}
|
|
250
|
+
scheduleIdleSuspend();
|
|
251
|
+
}
|
|
252
|
+
function requestSuspendUpdate() {
|
|
253
|
+
suspendChain = suspendChain.then(runSuspendLink, runSuspendLink);
|
|
254
|
+
}
|
|
255
|
+
function updateSuspendState() {
|
|
256
|
+
requestSuspendUpdate();
|
|
257
|
+
}
|
|
258
|
+
function scheduleIdleSuspend() {
|
|
259
|
+
if (idleSuspendTimer) clearTimeout(idleSuspendTimer);
|
|
260
|
+
if (disposed || !context || isEffectivelyPaused()) return;
|
|
261
|
+
if (activeVoices.size === 0 && currentMusicNode === null && !pendingMusic) {
|
|
262
|
+
idleSuspendTimer = setTimeout(() => {
|
|
263
|
+
if (!disposed && context && activeVoices.size === 0 && currentMusicNode === null && !pendingMusic && !isEffectivelyPaused()) {
|
|
264
|
+
void context.suspend().catch(() => {});
|
|
265
|
+
}
|
|
266
|
+
}, 1500);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** Drop one live voice: normal stop/cleanup with no token (GS-AUDIO-02). */
|
|
271
|
+
function dropVoice(voice, concurrencyKey) {
|
|
272
|
+
activeVoices.delete(voice);
|
|
273
|
+
if (concurrencyKey !== undefined) {
|
|
274
|
+
const set = concurrencyMap.get(concurrencyKey);
|
|
275
|
+
if (set) {
|
|
276
|
+
set.delete(voice);
|
|
277
|
+
if (set.size === 0) concurrencyMap.delete(concurrencyKey);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
cancelledReservations.delete(voice);
|
|
281
|
+
scheduleIdleSuspend();
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** Drop one pending reservation from every collection that holds it. */
|
|
285
|
+
function dropReservation(reservation, concurrencyKey) {
|
|
286
|
+
pendingReservations.delete(reservation);
|
|
287
|
+
const set = concurrencyMap.get(concurrencyKey);
|
|
288
|
+
if (set) {
|
|
289
|
+
set.delete(reservation);
|
|
290
|
+
if (set.size === 0) concurrencyMap.delete(concurrencyKey);
|
|
291
|
+
}
|
|
292
|
+
cancelledReservations.delete(reservation);
|
|
293
|
+
}
|
|
294
|
+
const ensureNotDisposed = () => {
|
|
295
|
+
if (disposed) throw new GameAudioError('GameAudio is disposed');
|
|
296
|
+
};
|
|
297
|
+
function handleInterruption(event) {
|
|
298
|
+
const e = event;
|
|
299
|
+
if (e.type === 'began') {
|
|
300
|
+
interruptionPaused = true;
|
|
301
|
+
interruptionRequiresExplicitResume = false;
|
|
302
|
+
requestSuspendUpdate();
|
|
303
|
+
} else if (e.type === 'ended') {
|
|
304
|
+
if (e.shouldResume) {
|
|
305
|
+
if (interruptionRequiresExplicitResume) {
|
|
306
|
+
// Denied auto-resume persists until explicit user action (F2)
|
|
307
|
+
requestSuspendUpdate();
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
interruptionPaused = false;
|
|
311
|
+
interruptionRequiresExplicitResume = false;
|
|
312
|
+
updateSuspendState();
|
|
313
|
+
} else {
|
|
314
|
+
// Platform says don't auto-resume: keep paused until explicit user action (F2)
|
|
315
|
+
interruptionPaused = true;
|
|
316
|
+
interruptionRequiresExplicitResume = true;
|
|
317
|
+
requestSuspendUpdate();
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
try {
|
|
322
|
+
// GS-AUDIO-03: interruption observation is explicitly optional. Without
|
|
323
|
+
// an AudioManager the service still plays, but warns once so the gap
|
|
324
|
+
// is visible instead of silently disappearing behind a no-op fallback.
|
|
325
|
+
if (AudioManager) {
|
|
326
|
+
if (interruptionObserverCount === 0) {
|
|
327
|
+
AudioManager.observeAudioInterruptions(true);
|
|
328
|
+
}
|
|
329
|
+
interruptionObserverCount++;
|
|
330
|
+
interruptionEnabled = true;
|
|
331
|
+
interruptionSub = AudioManager.addSystemEventListener('interruption', handleInterruption);
|
|
332
|
+
} else {
|
|
333
|
+
console.warn('[GameAudio] AudioManager unavailable — interruption observation disabled');
|
|
334
|
+
}
|
|
335
|
+
} catch {
|
|
336
|
+
interruptionSub = null;
|
|
337
|
+
}
|
|
338
|
+
try {
|
|
339
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
340
|
+
const RN = require('react-native');
|
|
341
|
+
const AppState = RN?.AppState;
|
|
342
|
+
if (AppState?.addEventListener) {
|
|
343
|
+
const isInactive = s => s === 'inactive' || s === 'background';
|
|
344
|
+
if (isInactive(AppState.currentState)) {
|
|
345
|
+
appPaused = true;
|
|
346
|
+
requestSuspendUpdate();
|
|
347
|
+
}
|
|
348
|
+
appStateSub = AppState.addEventListener('change', next => {
|
|
349
|
+
if (isInactive(next)) {
|
|
350
|
+
appPaused = true;
|
|
351
|
+
requestSuspendUpdate();
|
|
352
|
+
} else if (next === 'active') {
|
|
353
|
+
appPaused = false;
|
|
354
|
+
updateSuspendState();
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
} catch {}
|
|
359
|
+
applyAllGains();
|
|
360
|
+
// Initial idle suspend (F2): resource with no playback should suspend
|
|
361
|
+
scheduleIdleSuspend();
|
|
362
|
+
async function startMusicInternal(id, generation) {
|
|
363
|
+
if (disposed || !context) return;
|
|
364
|
+
if (isEffectivelyPaused()) {
|
|
365
|
+
// Defer until resume
|
|
366
|
+
pendingMusic = {
|
|
367
|
+
id,
|
|
368
|
+
generation
|
|
369
|
+
};
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (generation !== musicGeneration) return;
|
|
373
|
+
try {
|
|
374
|
+
const buffer = await getBuffer(id);
|
|
375
|
+
if (disposed || !context) return;
|
|
376
|
+
if (generation !== musicGeneration) return;
|
|
377
|
+
if (isEffectivelyPaused()) {
|
|
378
|
+
pendingMusic = {
|
|
379
|
+
id,
|
|
380
|
+
generation
|
|
381
|
+
};
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (currentMusicId !== id || activeMusicGeneration !== generation) {
|
|
385
|
+
// If another generation has taken over, don't start
|
|
386
|
+
if (generation !== musicGeneration) return;
|
|
387
|
+
}
|
|
388
|
+
const source = context.createBufferSource();
|
|
389
|
+
source.buffer = buffer;
|
|
390
|
+
source.loop = true;
|
|
391
|
+
const dest = categoryGains['music'] ?? masterGain ?? context.destination;
|
|
392
|
+
source.connect(dest);
|
|
393
|
+
// Stop previous
|
|
394
|
+
if (currentMusicNode) {
|
|
395
|
+
try {
|
|
396
|
+
currentMusicNode.stop();
|
|
397
|
+
} catch {}
|
|
398
|
+
}
|
|
399
|
+
currentMusicNode = source;
|
|
400
|
+
currentMusicId = id;
|
|
401
|
+
activeMusicGeneration = generation;
|
|
402
|
+
try {
|
|
403
|
+
source.start();
|
|
404
|
+
} catch (e) {
|
|
405
|
+
// GS-AUDIO-01: a failed music start must not wedge the music node —
|
|
406
|
+
// clear it so idle suspension and retries proceed, then report.
|
|
407
|
+
try {
|
|
408
|
+
source.disconnect?.();
|
|
409
|
+
} catch {}
|
|
410
|
+
if (currentMusicNode === source) {
|
|
411
|
+
currentMusicNode = null;
|
|
412
|
+
}
|
|
413
|
+
scheduleIdleSuspend();
|
|
414
|
+
throw e;
|
|
415
|
+
}
|
|
416
|
+
if (idleSuspendTimer) {
|
|
417
|
+
clearTimeout(idleSuspendTimer);
|
|
418
|
+
idleSuspendTimer = null;
|
|
419
|
+
}
|
|
420
|
+
const state = context.state;
|
|
421
|
+
if (state === 'suspended' && !isEffectivelyPaused()) await context.resume().catch(() => {});
|
|
422
|
+
} catch (e) {
|
|
423
|
+
if (generation === musicGeneration) {
|
|
424
|
+
console.warn(`[GameAudio] playMusic("${String(id)}") failed:`, e);
|
|
425
|
+
if (currentMusicId === id && activeMusicGeneration === generation) {
|
|
426
|
+
currentMusicId = null;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
let lifecycleDetach = null;
|
|
432
|
+
const setSessionPaused = paused => {
|
|
433
|
+
sessionPaused = paused;
|
|
434
|
+
requestSuspendUpdate();
|
|
435
|
+
};
|
|
436
|
+
const audio = {
|
|
437
|
+
play(id, opts) {
|
|
438
|
+
ensureNotDisposed();
|
|
439
|
+
if (isEffectivelyPaused()) return;
|
|
440
|
+
if (opts?.category !== undefined) assertCategory(opts.category);
|
|
441
|
+
if (opts?.volume !== undefined) assertVolume(opts.volume);
|
|
442
|
+
const category = opts?.category ?? 'sfx';
|
|
443
|
+
const concurrencyKey = opts?.concurrency?.key ?? String(id);
|
|
444
|
+
const limit = opts?.concurrency?.limit;
|
|
445
|
+
const overflow = opts?.concurrency?.overflow ?? 'drop-new';
|
|
446
|
+
let reservation = null;
|
|
447
|
+
if (limit !== undefined) {
|
|
448
|
+
if (!Number.isFinite(limit) || limit < 1 || Math.floor(limit) !== limit) {
|
|
449
|
+
throw new GameAudioError(`Concurrency limit must be a positive integer, got ${String(limit)}`);
|
|
450
|
+
}
|
|
451
|
+
let set = concurrencyMap.get(concurrencyKey);
|
|
452
|
+
if (!set) {
|
|
453
|
+
set = new Set();
|
|
454
|
+
concurrencyMap.set(concurrencyKey, set);
|
|
455
|
+
}
|
|
456
|
+
if (set.size >= limit) {
|
|
457
|
+
if (overflow === 'drop-new') return;
|
|
458
|
+
if (overflow === 'stop-oldest') {
|
|
459
|
+
const oldest = set.values().next().value;
|
|
460
|
+
if (oldest) {
|
|
461
|
+
if (pendingReservations.has(oldest)) {
|
|
462
|
+
cancelledReservations.add(oldest);
|
|
463
|
+
} else {
|
|
464
|
+
try {
|
|
465
|
+
oldest.stop();
|
|
466
|
+
} catch {}
|
|
467
|
+
dropVoice(oldest, concurrencyKey);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
reservation = {};
|
|
473
|
+
set.add(reservation);
|
|
474
|
+
pendingReservations.add(reservation);
|
|
475
|
+
}
|
|
476
|
+
try {
|
|
477
|
+
const state = context.state;
|
|
478
|
+
if (state === 'suspended' && !isEffectivelyPaused()) void context.resume().catch(() => {});
|
|
479
|
+
} catch {}
|
|
480
|
+
if (idleSuspendTimer) {
|
|
481
|
+
clearTimeout(idleSuspendTimer);
|
|
482
|
+
idleSuspendTimer = null;
|
|
483
|
+
}
|
|
484
|
+
void (async () => {
|
|
485
|
+
if (disposed || !context || isEffectivelyPaused()) {
|
|
486
|
+
if (reservation) {
|
|
487
|
+
dropReservation(reservation, concurrencyKey);
|
|
488
|
+
}
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
try {
|
|
492
|
+
const buffer = await getBuffer(id);
|
|
493
|
+
if (disposed || !context || isEffectivelyPaused()) {
|
|
494
|
+
if (reservation) {
|
|
495
|
+
dropReservation(reservation, concurrencyKey);
|
|
496
|
+
}
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (reservation && cancelledReservations.has(reservation)) {
|
|
500
|
+
dropReservation(reservation, concurrencyKey);
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
// GS-AUDIO-01: one idempotent cleanup owns every stage from here.
|
|
504
|
+
// A failure in create/connect/start removes BOTH the reservation
|
|
505
|
+
// and the actual source, detaches handlers, disconnects owned
|
|
506
|
+
// nodes, and reconsiders idle suspension — never half-registered.
|
|
507
|
+
let voiceGain = null;
|
|
508
|
+
let cleaned = false;
|
|
509
|
+
let source = null;
|
|
510
|
+
const releaseVoice = () => {
|
|
511
|
+
if (cleaned) return;
|
|
512
|
+
cleaned = true;
|
|
513
|
+
if (source !== null) {
|
|
514
|
+
dropVoice(source, limit !== undefined ? concurrencyKey : undefined);
|
|
515
|
+
}
|
|
516
|
+
if (reservation && limit !== undefined) {
|
|
517
|
+
dropReservation(reservation, concurrencyKey);
|
|
518
|
+
}
|
|
519
|
+
const anySource = source;
|
|
520
|
+
if (anySource !== null && anySource.onended === cleanup) anySource.onended = null;
|
|
521
|
+
try {
|
|
522
|
+
source?.disconnect?.();
|
|
523
|
+
} catch {}
|
|
524
|
+
try {
|
|
525
|
+
if (voiceGain !== null) {
|
|
526
|
+
voiceGain.disconnect?.();
|
|
527
|
+
}
|
|
528
|
+
} catch {}
|
|
529
|
+
scheduleIdleSuspend();
|
|
530
|
+
};
|
|
531
|
+
const cleanup = () => {
|
|
532
|
+
releaseVoice();
|
|
533
|
+
};
|
|
534
|
+
try {
|
|
535
|
+
source = context.createBufferSource();
|
|
536
|
+
source.buffer = buffer;
|
|
537
|
+
if (opts?.loop) source.loop = true;
|
|
538
|
+
let dest = context.destination;
|
|
539
|
+
const catGain = categoryGains[category];
|
|
540
|
+
if (catGain) dest = catGain;else if (masterGain) dest = masterGain;
|
|
541
|
+
if (opts?.volume !== undefined) {
|
|
542
|
+
try {
|
|
543
|
+
voiceGain = context.createGain();
|
|
544
|
+
if (voiceGain) {
|
|
545
|
+
const v = opts.volume;
|
|
546
|
+
try {
|
|
547
|
+
voiceGain.gain.value = v;
|
|
548
|
+
} catch {}
|
|
549
|
+
voiceGain.connect(dest);
|
|
550
|
+
dest = voiceGain;
|
|
551
|
+
}
|
|
552
|
+
} catch {}
|
|
553
|
+
}
|
|
554
|
+
source.connect(dest);
|
|
555
|
+
activeVoices.add(source);
|
|
556
|
+
if (reservation) {
|
|
557
|
+
pendingReservations.delete(reservation);
|
|
558
|
+
const set = concurrencyMap.get(concurrencyKey);
|
|
559
|
+
if (set) {
|
|
560
|
+
set.delete(reservation);
|
|
561
|
+
set.add(source);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
const anySource = source;
|
|
565
|
+
if (typeof anySource.addEventListener === 'function') anySource.addEventListener('ended', cleanup);else anySource.onended = cleanup;
|
|
566
|
+
source.start();
|
|
567
|
+
if (idleSuspendTimer) {
|
|
568
|
+
clearTimeout(idleSuspendTimer);
|
|
569
|
+
idleSuspendTimer = null;
|
|
570
|
+
}
|
|
571
|
+
} catch (e) {
|
|
572
|
+
releaseVoice();
|
|
573
|
+
console.warn(`[GameAudio] play("${String(id)}") failed:`, e);
|
|
574
|
+
}
|
|
575
|
+
} catch (e) {
|
|
576
|
+
// Pre-voice stages (decode/buffer) failed: no voice exists, but
|
|
577
|
+
// the reservation must still be released exactly once.
|
|
578
|
+
if (reservation) {
|
|
579
|
+
const set = concurrencyMap.get(concurrencyKey);
|
|
580
|
+
if (set) {
|
|
581
|
+
set.delete(reservation);
|
|
582
|
+
if (set.size === 0) concurrencyMap.delete(concurrencyKey);
|
|
583
|
+
}
|
|
584
|
+
cancelledReservations.delete(reservation);
|
|
585
|
+
}
|
|
586
|
+
console.warn(`[GameAudio] play("${String(id)}") failed:`, e);
|
|
587
|
+
}
|
|
588
|
+
})();
|
|
589
|
+
},
|
|
590
|
+
async playMusic(id) {
|
|
591
|
+
ensureNotDisposed();
|
|
592
|
+
musicGeneration++;
|
|
593
|
+
const gen = musicGeneration;
|
|
594
|
+
// Stop previous immediately and mark generation
|
|
595
|
+
if (currentMusicNode) {
|
|
596
|
+
try {
|
|
597
|
+
currentMusicNode.stop();
|
|
598
|
+
} catch {}
|
|
599
|
+
currentMusicNode = null;
|
|
600
|
+
}
|
|
601
|
+
currentMusicId = id;
|
|
602
|
+
// If effectively paused, defer (F4)
|
|
603
|
+
if (isEffectivelyPaused()) {
|
|
604
|
+
pendingMusic = {
|
|
605
|
+
id: id,
|
|
606
|
+
generation: gen
|
|
607
|
+
};
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
await startMusicInternal(id, gen);
|
|
611
|
+
},
|
|
612
|
+
stopMusic() {
|
|
613
|
+
ensureNotDisposed();
|
|
614
|
+
musicGeneration++;
|
|
615
|
+
if (currentMusicNode) {
|
|
616
|
+
try {
|
|
617
|
+
currentMusicNode.stop();
|
|
618
|
+
} catch {}
|
|
619
|
+
currentMusicNode = null;
|
|
620
|
+
}
|
|
621
|
+
currentMusicId = null;
|
|
622
|
+
pendingMusic = null;
|
|
623
|
+
scheduleIdleSuspend();
|
|
624
|
+
},
|
|
625
|
+
pause() {
|
|
626
|
+
ensureNotDisposed();
|
|
627
|
+
if (userPaused) return;
|
|
628
|
+
userPaused = true;
|
|
629
|
+
requestSuspendUpdate();
|
|
630
|
+
// FF1: keep denied-interruption flag intact; only explicit resume() clears it
|
|
631
|
+
},
|
|
632
|
+
resume() {
|
|
633
|
+
ensureNotDisposed();
|
|
634
|
+
if (!userPaused && !interruptionRequiresExplicitResume) return;
|
|
635
|
+
const wasExplicit = interruptionRequiresExplicitResume;
|
|
636
|
+
userPaused = false;
|
|
637
|
+
if (wasExplicit) {
|
|
638
|
+
// Explicit user action clears denied auto-resume (F2)
|
|
639
|
+
interruptionPaused = false;
|
|
640
|
+
interruptionRequiresExplicitResume = false;
|
|
641
|
+
}
|
|
642
|
+
updateSuspendState();
|
|
643
|
+
},
|
|
644
|
+
setVolume(category, volume) {
|
|
645
|
+
ensureNotDisposed();
|
|
646
|
+
assertCategory(category);
|
|
647
|
+
assertVolume(volume);
|
|
648
|
+
volumes[category] = volume;
|
|
649
|
+
applyGain(category);
|
|
650
|
+
// F1: don't re-apply master when changing category, and vice versa — only own gain
|
|
651
|
+
},
|
|
652
|
+
getVolume(category) {
|
|
653
|
+
ensureNotDisposed();
|
|
654
|
+
assertCategory(category);
|
|
655
|
+
return volumes[category];
|
|
656
|
+
},
|
|
657
|
+
setMuted(next) {
|
|
658
|
+
ensureNotDisposed();
|
|
659
|
+
muted = Boolean(next);
|
|
660
|
+
updateSuspendState();
|
|
661
|
+
},
|
|
662
|
+
isMuted() {
|
|
663
|
+
ensureNotDisposed();
|
|
664
|
+
return muted;
|
|
665
|
+
},
|
|
666
|
+
setPaused(next) {
|
|
667
|
+
ensureNotDisposed();
|
|
668
|
+
setSessionPaused(Boolean(next));
|
|
669
|
+
},
|
|
670
|
+
bindLifecycle(source) {
|
|
671
|
+
return bindAudioLifecycle(source);
|
|
672
|
+
},
|
|
673
|
+
/** Compat alias for the pre-T20.7 session channel; use setPaused. */
|
|
674
|
+
_setSessionPaused(p) {
|
|
675
|
+
ensureNotDisposed();
|
|
676
|
+
setSessionPaused(Boolean(p));
|
|
677
|
+
},
|
|
678
|
+
dispose() {
|
|
679
|
+
if (disposed) return;
|
|
680
|
+
disposed = true;
|
|
681
|
+
currentMusicId = null;
|
|
682
|
+
pendingMusic = null;
|
|
683
|
+
if (currentMusicNode) {
|
|
684
|
+
try {
|
|
685
|
+
currentMusicNode.stop();
|
|
686
|
+
} catch {}
|
|
687
|
+
currentMusicNode = null;
|
|
688
|
+
}
|
|
689
|
+
for (const v of activeVoices) {
|
|
690
|
+
try {
|
|
691
|
+
v.stop();
|
|
692
|
+
} catch {}
|
|
693
|
+
}
|
|
694
|
+
activeVoices.clear();
|
|
695
|
+
concurrencyMap.clear();
|
|
696
|
+
cancelledReservations.clear();
|
|
697
|
+
pendingDecodes.clear();
|
|
698
|
+
bufferCache.clear();
|
|
699
|
+
if (idleSuspendTimer) {
|
|
700
|
+
clearTimeout(idleSuspendTimer);
|
|
701
|
+
idleSuspendTimer = null;
|
|
702
|
+
}
|
|
703
|
+
lifecycleDetach?.();
|
|
704
|
+
lifecycleDetach = null;
|
|
705
|
+
if (interruptionSub) {
|
|
706
|
+
try {
|
|
707
|
+
interruptionSub.remove();
|
|
708
|
+
} catch {}
|
|
709
|
+
interruptionSub = null;
|
|
710
|
+
}
|
|
711
|
+
if (interruptionEnabled) {
|
|
712
|
+
interruptionObserverCount = Math.max(0, interruptionObserverCount - 1);
|
|
713
|
+
if (interruptionObserverCount === 0) {
|
|
714
|
+
try {
|
|
715
|
+
AudioManager?.observeAudioInterruptions(false);
|
|
716
|
+
} catch {}
|
|
717
|
+
}
|
|
718
|
+
interruptionEnabled = false;
|
|
719
|
+
}
|
|
720
|
+
if (appStateSub) {
|
|
721
|
+
try {
|
|
722
|
+
appStateSub.remove();
|
|
723
|
+
} catch {}
|
|
724
|
+
appStateSub = null;
|
|
725
|
+
}
|
|
726
|
+
if (context) void context.close().catch(() => {});
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
const bindAudioLifecycle = source => {
|
|
730
|
+
ensureNotDisposed();
|
|
731
|
+
// One active source: a replacement detaches the previous one first.
|
|
732
|
+
lifecycleDetach?.();
|
|
733
|
+
lifecycleDetach = null;
|
|
734
|
+
// Apply the current status immediately, then follow transitions.
|
|
735
|
+
// AppState backgrounding stays independent (separate flag).
|
|
736
|
+
setSessionPaused(source.getStatus() !== 'running');
|
|
737
|
+
const sourceDetach = source.subscribe(status => {
|
|
738
|
+
setSessionPaused(status !== 'running');
|
|
739
|
+
});
|
|
740
|
+
let detached = false;
|
|
741
|
+
const detach = () => {
|
|
742
|
+
if (detached) return;
|
|
743
|
+
detached = true;
|
|
744
|
+
sourceDetach();
|
|
745
|
+
if (lifecycleDetach === detach) lifecycleDetach = null;
|
|
746
|
+
};
|
|
747
|
+
lifecycleDetach = detach;
|
|
748
|
+
return detach;
|
|
749
|
+
};
|
|
750
|
+
audio._isIdleSuspended = () => {
|
|
751
|
+
try {
|
|
752
|
+
return context.state === 'suspended';
|
|
753
|
+
} catch {
|
|
754
|
+
return false;
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
audio._getConcurrencyCount = k => concurrencyMap.get(k)?.size ?? 0;
|
|
758
|
+
// Test-only collection sizes for the ownership suites (GS-AUDIO-01/02).
|
|
759
|
+
audio._getVoiceCounts = () => ({
|
|
760
|
+
activeVoices: activeVoices.size,
|
|
761
|
+
concurrencyKeys: concurrencyMap.size,
|
|
762
|
+
cancelled: cancelledReservations.size
|
|
763
|
+
});
|
|
764
|
+
audio._handleInterruption = handleInterruption;
|
|
765
|
+
audio._getVolumes = () => ({
|
|
766
|
+
...volumes
|
|
767
|
+
});
|
|
768
|
+
audio._getGainTargets = () => {
|
|
769
|
+
const out = {};
|
|
770
|
+
for (const c of CATEGORIES) {
|
|
771
|
+
const g = categoryGains[c];
|
|
772
|
+
if (g) {
|
|
773
|
+
try {
|
|
774
|
+
out[c] = g.gain.value;
|
|
775
|
+
} catch {
|
|
776
|
+
out[c] = volumes[c];
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
return out;
|
|
781
|
+
};
|
|
782
|
+
return audio;
|
|
783
|
+
}
|
|
784
|
+
//# sourceMappingURL=createGameAudio.js.map
|