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.
Files changed (601) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +95 -14
  3. package/lib/module/assets/defineAssets.js +28 -3
  4. package/lib/module/assets/defineAssets.js.map +1 -1
  5. package/lib/module/assets.js +11 -0
  6. package/lib/module/assets.js.map +1 -0
  7. package/lib/module/audio/createGameAudio.js +784 -0
  8. package/lib/module/audio/createGameAudio.js.map +1 -0
  9. package/lib/module/audio/errors.js +12 -0
  10. package/lib/module/audio/errors.js.map +1 -0
  11. package/lib/module/audio/index.js +5 -0
  12. package/lib/module/audio/index.js.map +1 -0
  13. package/lib/module/audio/resolver.js +37 -0
  14. package/lib/module/audio/resolver.js.map +1 -0
  15. package/lib/module/audio/types.js +4 -0
  16. package/lib/module/audio/types.js.map +1 -0
  17. package/lib/module/audio.js +9 -0
  18. package/lib/module/audio.js.map +1 -0
  19. package/lib/module/camera2d/bounds.js +59 -0
  20. package/lib/module/camera2d/bounds.js.map +1 -0
  21. package/lib/module/camera2d/extents.js +25 -0
  22. package/lib/module/camera2d/extents.js.map +1 -0
  23. package/lib/module/camera2d/follow.js +138 -0
  24. package/lib/module/camera2d/follow.js.map +1 -0
  25. package/lib/module/camera2d/index.js +20 -0
  26. package/lib/module/camera2d/index.js.map +1 -0
  27. package/lib/module/camera2d/interpolation.js +98 -0
  28. package/lib/module/camera2d/interpolation.js.map +1 -0
  29. package/lib/module/camera2d/shake.js +85 -0
  30. package/lib/module/camera2d/shake.js.map +1 -0
  31. package/lib/module/camera2d/transform.js +151 -0
  32. package/lib/module/camera2d/transform.js.map +1 -0
  33. package/lib/module/camera2d/types.js +4 -0
  34. package/lib/module/camera2d/types.js.map +1 -0
  35. package/lib/module/camera2d/validation.js +98 -0
  36. package/lib/module/camera2d/validation.js.map +1 -0
  37. package/lib/module/camera2d/visibility.js +138 -0
  38. package/lib/module/camera2d/visibility.js.map +1 -0
  39. package/lib/module/camera2d.js +9 -0
  40. package/lib/module/camera2d.js.map +1 -0
  41. package/lib/module/collision2d/colliders.js +266 -0
  42. package/lib/module/collision2d/colliders.js.map +1 -0
  43. package/lib/module/collision2d/debug.js +105 -0
  44. package/lib/module/collision2d/debug.js.map +1 -0
  45. package/lib/module/collision2d/filters.js +40 -0
  46. package/lib/module/collision2d/filters.js.map +1 -0
  47. package/lib/module/collision2d/index.js +12 -0
  48. package/lib/module/collision2d/index.js.map +1 -0
  49. package/lib/module/collision2d/intersections.js +67 -0
  50. package/lib/module/collision2d/intersections.js.map +1 -0
  51. package/lib/module/collision2d/manifolds.js +218 -0
  52. package/lib/module/collision2d/manifolds.js.map +1 -0
  53. package/lib/module/collision2d/segments.js +187 -0
  54. package/lib/module/collision2d/segments.js.map +1 -0
  55. package/lib/module/collision2d/spatialHash.js +167 -0
  56. package/lib/module/collision2d/spatialHash.js.map +1 -0
  57. package/lib/module/collision2d/sweeps.js +255 -0
  58. package/lib/module/collision2d/sweeps.js.map +1 -0
  59. package/lib/module/collision2d.js +9 -0
  60. package/lib/module/collision2d.js.map +1 -0
  61. package/lib/module/core/input/buttonPad.js +214 -0
  62. package/lib/module/core/input/buttonPad.js.map +1 -0
  63. package/lib/module/core/input/createInputBuffer.js +66 -14
  64. package/lib/module/core/input/createInputBuffer.js.map +1 -1
  65. package/lib/module/core/session/createGameSession.js +454 -50
  66. package/lib/module/core/session/createGameSession.js.map +1 -1
  67. package/lib/module/core/session/deepFreeze.js +92 -2
  68. package/lib/module/core/session/deepFreeze.js.map +1 -1
  69. package/lib/module/core/session/types.js +12 -0
  70. package/lib/module/core/session/types.js.map +1 -1
  71. package/lib/module/definition/defineGame.js +23 -0
  72. package/lib/module/definition/defineGame.js.map +1 -1
  73. package/lib/module/events/defineGameEvents.js +46 -0
  74. package/lib/module/events/defineGameEvents.js.map +1 -0
  75. package/lib/module/events/errors.js +16 -0
  76. package/lib/module/events/errors.js.map +1 -0
  77. package/lib/module/events/index.js +7 -0
  78. package/lib/module/events/index.js.map +1 -0
  79. package/lib/module/events/payload.js +219 -0
  80. package/lib/module/events/payload.js.map +1 -0
  81. package/lib/module/events/seed.js +33 -0
  82. package/lib/module/events/seed.js.map +1 -0
  83. package/lib/module/events/types.js +4 -0
  84. package/lib/module/events/types.js.map +1 -0
  85. package/lib/module/events.js +13 -0
  86. package/lib/module/events.js.map +1 -0
  87. package/lib/module/geometry/helpers.js +140 -0
  88. package/lib/module/geometry/helpers.js.map +1 -0
  89. package/lib/module/geometry/index.js +5 -0
  90. package/lib/module/geometry/index.js.map +1 -0
  91. package/lib/module/geometry/validation.js +104 -0
  92. package/lib/module/geometry/validation.js.map +1 -0
  93. package/lib/module/geometry.js +9 -0
  94. package/lib/module/geometry.js.map +1 -0
  95. package/lib/module/haptics/createGameHaptics.js +214 -0
  96. package/lib/module/haptics/createGameHaptics.js.map +1 -0
  97. package/lib/module/haptics/errors.js +12 -0
  98. package/lib/module/haptics/errors.js.map +1 -0
  99. package/lib/module/haptics/index.js +5 -0
  100. package/lib/module/haptics/index.js.map +1 -0
  101. package/lib/module/haptics/resolver.js +37 -0
  102. package/lib/module/haptics/resolver.js.map +1 -0
  103. package/lib/module/haptics/types.js +4 -0
  104. package/lib/module/haptics/types.js.map +1 -0
  105. package/lib/module/haptics.js +9 -0
  106. package/lib/module/haptics.js.map +1 -0
  107. package/lib/module/index.js +8 -1
  108. package/lib/module/index.js.map +1 -1
  109. package/lib/module/particles/createParticleSystem.js +481 -0
  110. package/lib/module/particles/createParticleSystem.js.map +1 -0
  111. package/lib/module/particles/defineParticleEffect.js +178 -0
  112. package/lib/module/particles/defineParticleEffect.js.map +1 -0
  113. package/lib/module/particles/errors.js +9 -0
  114. package/lib/module/particles/errors.js.map +1 -0
  115. package/lib/module/particles/index.js +8 -0
  116. package/lib/module/particles/index.js.map +1 -0
  117. package/lib/module/particles/sampling.js +121 -0
  118. package/lib/module/particles/sampling.js.map +1 -0
  119. package/lib/module/particles/types.js +4 -0
  120. package/lib/module/particles/types.js.map +1 -0
  121. package/lib/module/particles.js +4 -0
  122. package/lib/module/particles.js.map +1 -0
  123. package/lib/module/react/GameButtonPad.js +344 -0
  124. package/lib/module/react/GameButtonPad.js.map +1 -0
  125. package/lib/module/react/GamePointerInput.js +74 -16
  126. package/lib/module/react/GamePointerInput.js.map +1 -1
  127. package/lib/module/react/GameView.js +139 -60
  128. package/lib/module/react/GameView.js.map +1 -1
  129. package/lib/module/react/assets/createGameAssetStore.js +145 -95
  130. package/lib/module/react/assets/createGameAssetStore.js.map +1 -1
  131. package/lib/module/react/assets/useGameAssets.js +60 -35
  132. package/lib/module/react/assets/useGameAssets.js.map +1 -1
  133. package/lib/module/react/bindAppLifecycle.js +46 -17
  134. package/lib/module/react/bindAppLifecycle.js.map +1 -1
  135. package/lib/module/react/bindGameSession.js +13 -2
  136. package/lib/module/react/bindGameSession.js.map +1 -1
  137. package/lib/module/react/camera2d/GameLayer2D.js +50 -0
  138. package/lib/module/react/camera2d/GameLayer2D.js.map +1 -0
  139. package/lib/module/react/camera2d/defineGameCamera2D.js +30 -0
  140. package/lib/module/react/camera2d/defineGameCamera2D.js.map +1 -0
  141. package/lib/module/react/camera2d/usePresentedCameraBinding.js +177 -0
  142. package/lib/module/react/camera2d/usePresentedCameraBinding.js.map +1 -0
  143. package/lib/module/react/cleanupList.js +38 -0
  144. package/lib/module/react/cleanupList.js.map +1 -0
  145. package/lib/module/react/lifecycleSource.js +49 -0
  146. package/lib/module/react/lifecycleSource.js.map +1 -0
  147. package/lib/module/react/particles/ParticleView.js +308 -0
  148. package/lib/module/react/particles/ParticleView.js.map +1 -0
  149. package/lib/module/react/particles/culling.js +115 -0
  150. package/lib/module/react/particles/culling.js.map +1 -0
  151. package/lib/module/react/particles/index.js +7 -0
  152. package/lib/module/react/particles/index.js.map +1 -0
  153. package/lib/module/react/particles/spriteXForm.js +45 -0
  154. package/lib/module/react/particles/spriteXForm.js.map +1 -0
  155. package/lib/module/react/particles/useParticlePresentation.js +204 -0
  156. package/lib/module/react/particles/useParticlePresentation.js.map +1 -0
  157. package/lib/module/react/pointerBinding.js +55 -17
  158. package/lib/module/react/pointerBinding.js.map +1 -1
  159. package/lib/module/react/pointerCamera.js +28 -0
  160. package/lib/module/react/pointerCamera.js.map +1 -0
  161. package/lib/module/react/pointerCoalescer.js +16 -9
  162. package/lib/module/react/pointerCoalescer.js.map +1 -1
  163. package/lib/module/react/sprites/GameSprite.js +5 -17
  164. package/lib/module/react/sprites/GameSprite.js.map +1 -1
  165. package/lib/module/react/sprites/GameWorld2D.js +112 -9
  166. package/lib/module/react/sprites/GameWorld2D.js.map +1 -1
  167. package/lib/module/react/sprites/Sprite.js +48 -63
  168. package/lib/module/react/sprites/Sprite.js.map +1 -1
  169. package/lib/module/react/sprites/SpriteBatch.js +53 -28
  170. package/lib/module/react/sprites/SpriteBatch.js.map +1 -1
  171. package/lib/module/react/sprites/batchVisibility.js +58 -0
  172. package/lib/module/react/sprites/batchVisibility.js.map +1 -0
  173. package/lib/module/react/sprites/spriteSelection.js +103 -0
  174. package/lib/module/react/sprites/spriteSelection.js.map +1 -0
  175. package/lib/module/react/sprites/spriteTransform.js +46 -25
  176. package/lib/module/react/sprites/spriteTransform.js.map +1 -1
  177. package/lib/module/react/tilemap/TileMapLayer2D.js +217 -0
  178. package/lib/module/react/tilemap/TileMapLayer2D.js.map +1 -0
  179. package/lib/module/react/tilemap/tilePresentation.js +374 -0
  180. package/lib/module/react/tilemap/tilePresentation.js.map +1 -0
  181. package/lib/module/react/useGameSession.js +70 -0
  182. package/lib/module/react/useGameSession.js.map +1 -0
  183. package/lib/module/react/useGameSessionStatus.js +46 -0
  184. package/lib/module/react/useGameSessionStatus.js.map +1 -0
  185. package/lib/module/react/viewportBinding.js +5 -1
  186. package/lib/module/react/viewportBinding.js.map +1 -1
  187. package/lib/module/react.js +12 -1
  188. package/lib/module/react.js.map +1 -1
  189. package/lib/module/scene/defineScene.js +17 -0
  190. package/lib/module/scene/defineScene.js.map +1 -1
  191. package/lib/module/sprites/sampleSpriteClip.js +66 -6
  192. package/lib/module/sprites/sampleSpriteClip.js.map +1 -1
  193. package/lib/module/sprites/spriteAnimationState.js +10 -20
  194. package/lib/module/sprites/spriteAnimationState.js.map +1 -1
  195. package/lib/module/sprites.js +11 -0
  196. package/lib/module/sprites.js.map +1 -0
  197. package/lib/module/storage/adapters/asyncStorage.js +57 -0
  198. package/lib/module/storage/adapters/asyncStorage.js.map +1 -0
  199. package/lib/module/storage/adapters/memory.js +50 -0
  200. package/lib/module/storage/adapters/memory.js.map +1 -0
  201. package/lib/module/storage/errors.js +34 -0
  202. package/lib/module/storage/errors.js.map +1 -0
  203. package/lib/module/storage/index.js +11 -0
  204. package/lib/module/storage/index.js.map +1 -0
  205. package/lib/module/storage/schema.js +151 -0
  206. package/lib/module/storage/schema.js.map +1 -0
  207. package/lib/module/storage/serialization.js +249 -0
  208. package/lib/module/storage/serialization.js.map +1 -0
  209. package/lib/module/storage/store.js +387 -0
  210. package/lib/module/storage/store.js.map +1 -0
  211. package/lib/module/storage/types.js +16 -0
  212. package/lib/module/storage/types.js.map +1 -0
  213. package/lib/module/storage/validation.js +111 -0
  214. package/lib/module/storage/validation.js.map +1 -0
  215. package/lib/module/storage.js +4 -0
  216. package/lib/module/storage.js.map +1 -0
  217. package/lib/module/testing.js +4 -0
  218. package/lib/module/testing.js.map +1 -1
  219. package/lib/module/tilemap/chunkStats.js +27 -0
  220. package/lib/module/tilemap/chunkStats.js.map +1 -0
  221. package/lib/module/tilemap/definitions.js +308 -0
  222. package/lib/module/tilemap/definitions.js.map +1 -0
  223. package/lib/module/tilemap/errors.js +13 -0
  224. package/lib/module/tilemap/errors.js.map +1 -0
  225. package/lib/module/tilemap/index.js +10 -0
  226. package/lib/module/tilemap/index.js.map +1 -0
  227. package/lib/module/tilemap/movement.js +379 -0
  228. package/lib/module/tilemap/movement.js.map +1 -0
  229. package/lib/module/tilemap/queries.js +114 -0
  230. package/lib/module/tilemap/queries.js.map +1 -0
  231. package/lib/module/tilemap/tiledAdapter.js +141 -0
  232. package/lib/module/tilemap/tiledAdapter.js.map +1 -0
  233. package/lib/module/tilemap/types.js +4 -0
  234. package/lib/module/tilemap/types.js.map +1 -0
  235. package/lib/module/tilemap.js +4 -0
  236. package/lib/module/tilemap.js.map +1 -0
  237. package/lib/typescript/src/assets/defineAssets.d.ts +1 -1
  238. package/lib/typescript/src/assets/defineAssets.d.ts.map +1 -1
  239. package/lib/typescript/src/assets.d.ts +11 -0
  240. package/lib/typescript/src/assets.d.ts.map +1 -0
  241. package/lib/typescript/src/audio/createGameAudio.d.ts +5 -0
  242. package/lib/typescript/src/audio/createGameAudio.d.ts.map +1 -0
  243. package/lib/typescript/src/audio/errors.d.ts +6 -0
  244. package/lib/typescript/src/audio/errors.d.ts.map +1 -0
  245. package/lib/typescript/src/audio/index.d.ts +4 -0
  246. package/lib/typescript/src/audio/index.d.ts.map +1 -0
  247. package/lib/typescript/src/audio/resolver.d.ts +44 -0
  248. package/lib/typescript/src/audio/resolver.d.ts.map +1 -0
  249. package/lib/typescript/src/audio/types.d.ts +42 -0
  250. package/lib/typescript/src/audio/types.d.ts.map +1 -0
  251. package/lib/typescript/src/audio.d.ts +8 -0
  252. package/lib/typescript/src/audio.d.ts.map +1 -0
  253. package/lib/typescript/src/camera2d/bounds.d.ts +25 -0
  254. package/lib/typescript/src/camera2d/bounds.d.ts.map +1 -0
  255. package/lib/typescript/src/camera2d/extents.d.ts +17 -0
  256. package/lib/typescript/src/camera2d/extents.d.ts.map +1 -0
  257. package/lib/typescript/src/camera2d/follow.d.ts +24 -0
  258. package/lib/typescript/src/camera2d/follow.d.ts.map +1 -0
  259. package/lib/typescript/src/camera2d/index.d.ts +19 -0
  260. package/lib/typescript/src/camera2d/index.d.ts.map +1 -0
  261. package/lib/typescript/src/camera2d/interpolation.d.ts +17 -0
  262. package/lib/typescript/src/camera2d/interpolation.d.ts.map +1 -0
  263. package/lib/typescript/src/camera2d/shake.d.ts +20 -0
  264. package/lib/typescript/src/camera2d/shake.d.ts.map +1 -0
  265. package/lib/typescript/src/camera2d/transform.d.ts +44 -0
  266. package/lib/typescript/src/camera2d/transform.d.ts.map +1 -0
  267. package/lib/typescript/src/camera2d/types.d.ts +74 -0
  268. package/lib/typescript/src/camera2d/types.d.ts.map +1 -0
  269. package/lib/typescript/src/camera2d/validation.d.ts +30 -0
  270. package/lib/typescript/src/camera2d/validation.d.ts.map +1 -0
  271. package/lib/typescript/src/camera2d/visibility.d.ts +47 -0
  272. package/lib/typescript/src/camera2d/visibility.d.ts.map +1 -0
  273. package/lib/typescript/src/camera2d.d.ts +7 -0
  274. package/lib/typescript/src/camera2d.d.ts.map +1 -0
  275. package/lib/typescript/src/collision2d/colliders.d.ts +120 -0
  276. package/lib/typescript/src/collision2d/colliders.d.ts.map +1 -0
  277. package/lib/typescript/src/collision2d/debug.d.ts +67 -0
  278. package/lib/typescript/src/collision2d/debug.d.ts.map +1 -0
  279. package/lib/typescript/src/collision2d/filters.d.ts +28 -0
  280. package/lib/typescript/src/collision2d/filters.d.ts.map +1 -0
  281. package/lib/typescript/src/collision2d/index.d.ts +17 -0
  282. package/lib/typescript/src/collision2d/index.d.ts.map +1 -0
  283. package/lib/typescript/src/collision2d/intersections.d.ts +24 -0
  284. package/lib/typescript/src/collision2d/intersections.d.ts.map +1 -0
  285. package/lib/typescript/src/collision2d/manifolds.d.ts +39 -0
  286. package/lib/typescript/src/collision2d/manifolds.d.ts.map +1 -0
  287. package/lib/typescript/src/collision2d/segments.d.ts +24 -0
  288. package/lib/typescript/src/collision2d/segments.d.ts.map +1 -0
  289. package/lib/typescript/src/collision2d/spatialHash.d.ts +50 -0
  290. package/lib/typescript/src/collision2d/spatialHash.d.ts.map +1 -0
  291. package/lib/typescript/src/collision2d/sweeps.d.ts +60 -0
  292. package/lib/typescript/src/collision2d/sweeps.d.ts.map +1 -0
  293. package/lib/typescript/src/collision2d.d.ts +7 -0
  294. package/lib/typescript/src/collision2d.d.ts.map +1 -0
  295. package/lib/typescript/src/core/input/buttonPad.d.ts +59 -0
  296. package/lib/typescript/src/core/input/buttonPad.d.ts.map +1 -0
  297. package/lib/typescript/src/core/input/createInputBuffer.d.ts +1 -1
  298. package/lib/typescript/src/core/input/createInputBuffer.d.ts.map +1 -1
  299. package/lib/typescript/src/core/input/types.d.ts +7 -0
  300. package/lib/typescript/src/core/input/types.d.ts.map +1 -1
  301. package/lib/typescript/src/core/session/createGameSession.d.ts +4 -2
  302. package/lib/typescript/src/core/session/createGameSession.d.ts.map +1 -1
  303. package/lib/typescript/src/core/session/deepFreeze.d.ts +21 -1
  304. package/lib/typescript/src/core/session/deepFreeze.d.ts.map +1 -1
  305. package/lib/typescript/src/core/session/diagnostics.d.ts +2 -0
  306. package/lib/typescript/src/core/session/diagnostics.d.ts.map +1 -1
  307. package/lib/typescript/src/core/session/types.d.ts +69 -4
  308. package/lib/typescript/src/core/session/types.d.ts.map +1 -1
  309. package/lib/typescript/src/definition/defineGame.d.ts +12 -1
  310. package/lib/typescript/src/definition/defineGame.d.ts.map +1 -1
  311. package/lib/typescript/src/definition/types.d.ts +10 -1
  312. package/lib/typescript/src/definition/types.d.ts.map +1 -1
  313. package/lib/typescript/src/events/defineGameEvents.d.ts +26 -0
  314. package/lib/typescript/src/events/defineGameEvents.d.ts.map +1 -0
  315. package/lib/typescript/src/events/errors.d.ts +12 -0
  316. package/lib/typescript/src/events/errors.d.ts.map +1 -0
  317. package/lib/typescript/src/events/index.d.ts +6 -0
  318. package/lib/typescript/src/events/index.d.ts.map +1 -0
  319. package/lib/typescript/src/events/payload.d.ts +21 -0
  320. package/lib/typescript/src/events/payload.d.ts.map +1 -0
  321. package/lib/typescript/src/events/seed.d.ts +3 -0
  322. package/lib/typescript/src/events/seed.d.ts.map +1 -0
  323. package/lib/typescript/src/events/types.d.ts +42 -0
  324. package/lib/typescript/src/events/types.d.ts.map +1 -0
  325. package/lib/typescript/src/events.d.ts +12 -0
  326. package/lib/typescript/src/events.d.ts.map +1 -0
  327. package/lib/typescript/src/geometry/helpers.d.ts +35 -0
  328. package/lib/typescript/src/geometry/helpers.d.ts.map +1 -0
  329. package/lib/typescript/src/geometry/index.d.ts +4 -0
  330. package/lib/typescript/src/geometry/index.d.ts.map +1 -0
  331. package/lib/typescript/src/geometry/types.d.ts +49 -0
  332. package/lib/typescript/src/geometry/types.d.ts.map +1 -1
  333. package/lib/typescript/src/geometry/validation.d.ts +44 -0
  334. package/lib/typescript/src/geometry/validation.d.ts.map +1 -0
  335. package/lib/typescript/src/geometry.d.ts +7 -0
  336. package/lib/typescript/src/geometry.d.ts.map +1 -0
  337. package/lib/typescript/src/haptics/createGameHaptics.d.ts +3 -0
  338. package/lib/typescript/src/haptics/createGameHaptics.d.ts.map +1 -0
  339. package/lib/typescript/src/haptics/errors.d.ts +6 -0
  340. package/lib/typescript/src/haptics/errors.d.ts.map +1 -0
  341. package/lib/typescript/src/haptics/index.d.ts +4 -0
  342. package/lib/typescript/src/haptics/index.d.ts.map +1 -0
  343. package/lib/typescript/src/haptics/resolver.d.ts +22 -0
  344. package/lib/typescript/src/haptics/resolver.d.ts.map +1 -0
  345. package/lib/typescript/src/haptics/types.d.ts +37 -0
  346. package/lib/typescript/src/haptics/types.d.ts.map +1 -0
  347. package/lib/typescript/src/haptics.d.ts +8 -0
  348. package/lib/typescript/src/haptics.d.ts.map +1 -0
  349. package/lib/typescript/src/index.d.ts +10 -2
  350. package/lib/typescript/src/index.d.ts.map +1 -1
  351. package/lib/typescript/src/particles/createParticleSystem.d.ts +5 -0
  352. package/lib/typescript/src/particles/createParticleSystem.d.ts.map +1 -0
  353. package/lib/typescript/src/particles/defineParticleEffect.d.ts +3 -0
  354. package/lib/typescript/src/particles/defineParticleEffect.d.ts.map +1 -0
  355. package/lib/typescript/src/particles/errors.d.ts +5 -0
  356. package/lib/typescript/src/particles/errors.d.ts.map +1 -0
  357. package/lib/typescript/src/particles/index.d.ts +6 -0
  358. package/lib/typescript/src/particles/index.d.ts.map +1 -0
  359. package/lib/typescript/src/particles/sampling.d.ts +54 -0
  360. package/lib/typescript/src/particles/sampling.d.ts.map +1 -0
  361. package/lib/typescript/src/particles/types.d.ts +292 -0
  362. package/lib/typescript/src/particles/types.d.ts.map +1 -0
  363. package/lib/typescript/src/particles.d.ts +2 -0
  364. package/lib/typescript/src/particles.d.ts.map +1 -0
  365. package/lib/typescript/src/react/GameButtonPad.d.ts +84 -0
  366. package/lib/typescript/src/react/GameButtonPad.d.ts.map +1 -0
  367. package/lib/typescript/src/react/GamePointerInput.d.ts.map +1 -1
  368. package/lib/typescript/src/react/GameView.d.ts +27 -1
  369. package/lib/typescript/src/react/GameView.d.ts.map +1 -1
  370. package/lib/typescript/src/react/assets/createGameAssetStore.d.ts.map +1 -1
  371. package/lib/typescript/src/react/assets/useGameAssets.d.ts.map +1 -1
  372. package/lib/typescript/src/react/bindAppLifecycle.d.ts +7 -1
  373. package/lib/typescript/src/react/bindAppLifecycle.d.ts.map +1 -1
  374. package/lib/typescript/src/react/bindGameSession.d.ts.map +1 -1
  375. package/lib/typescript/src/react/camera2d/GameLayer2D.d.ts +24 -0
  376. package/lib/typescript/src/react/camera2d/GameLayer2D.d.ts.map +1 -0
  377. package/lib/typescript/src/react/camera2d/defineGameCamera2D.d.ts +44 -0
  378. package/lib/typescript/src/react/camera2d/defineGameCamera2D.d.ts.map +1 -0
  379. package/lib/typescript/src/react/camera2d/usePresentedCameraBinding.d.ts +22 -0
  380. package/lib/typescript/src/react/camera2d/usePresentedCameraBinding.d.ts.map +1 -0
  381. package/lib/typescript/src/react/cleanupList.d.ts +21 -0
  382. package/lib/typescript/src/react/cleanupList.d.ts.map +1 -0
  383. package/lib/typescript/src/react/instrumentation.d.ts +33 -3
  384. package/lib/typescript/src/react/instrumentation.d.ts.map +1 -1
  385. package/lib/typescript/src/react/lifecycleSource.d.ts +21 -0
  386. package/lib/typescript/src/react/lifecycleSource.d.ts.map +1 -0
  387. package/lib/typescript/src/react/particles/ParticleView.d.ts +65 -0
  388. package/lib/typescript/src/react/particles/ParticleView.d.ts.map +1 -0
  389. package/lib/typescript/src/react/particles/culling.d.ts +70 -0
  390. package/lib/typescript/src/react/particles/culling.d.ts.map +1 -0
  391. package/lib/typescript/src/react/particles/index.d.ts +7 -0
  392. package/lib/typescript/src/react/particles/index.d.ts.map +1 -0
  393. package/lib/typescript/src/react/particles/spriteXForm.d.ts +28 -0
  394. package/lib/typescript/src/react/particles/spriteXForm.d.ts.map +1 -0
  395. package/lib/typescript/src/react/particles/useParticlePresentation.d.ts +48 -0
  396. package/lib/typescript/src/react/particles/useParticlePresentation.d.ts.map +1 -0
  397. package/lib/typescript/src/react/pointerBinding.d.ts +38 -8
  398. package/lib/typescript/src/react/pointerBinding.d.ts.map +1 -1
  399. package/lib/typescript/src/react/pointerCamera.d.ts +20 -0
  400. package/lib/typescript/src/react/pointerCamera.d.ts.map +1 -0
  401. package/lib/typescript/src/react/pointerCoalescer.d.ts +17 -1
  402. package/lib/typescript/src/react/pointerCoalescer.d.ts.map +1 -1
  403. package/lib/typescript/src/react/sprites/GameSprite.d.ts +3 -10
  404. package/lib/typescript/src/react/sprites/GameSprite.d.ts.map +1 -1
  405. package/lib/typescript/src/react/sprites/GameWorld2D.d.ts +51 -6
  406. package/lib/typescript/src/react/sprites/GameWorld2D.d.ts.map +1 -1
  407. package/lib/typescript/src/react/sprites/Sprite.d.ts.map +1 -1
  408. package/lib/typescript/src/react/sprites/SpriteBatch.d.ts +22 -1
  409. package/lib/typescript/src/react/sprites/SpriteBatch.d.ts.map +1 -1
  410. package/lib/typescript/src/react/sprites/batchVisibility.d.ts +28 -0
  411. package/lib/typescript/src/react/sprites/batchVisibility.d.ts.map +1 -0
  412. package/lib/typescript/src/react/sprites/spriteSelection.d.ts +58 -0
  413. package/lib/typescript/src/react/sprites/spriteSelection.d.ts.map +1 -0
  414. package/lib/typescript/src/react/sprites/spriteTransform.d.ts +27 -12
  415. package/lib/typescript/src/react/sprites/spriteTransform.d.ts.map +1 -1
  416. package/lib/typescript/src/react/tilemap/TileMapLayer2D.d.ts +69 -0
  417. package/lib/typescript/src/react/tilemap/TileMapLayer2D.d.ts.map +1 -0
  418. package/lib/typescript/src/react/tilemap/tilePresentation.d.ts +217 -0
  419. package/lib/typescript/src/react/tilemap/tilePresentation.d.ts.map +1 -0
  420. package/lib/typescript/src/react/useGameSession.d.ts +48 -0
  421. package/lib/typescript/src/react/useGameSession.d.ts.map +1 -0
  422. package/lib/typescript/src/react/useGameSessionStatus.d.ts +23 -0
  423. package/lib/typescript/src/react/useGameSessionStatus.d.ts.map +1 -0
  424. package/lib/typescript/src/react/viewportBinding.d.ts +5 -1
  425. package/lib/typescript/src/react/viewportBinding.d.ts.map +1 -1
  426. package/lib/typescript/src/react.d.ts +18 -1
  427. package/lib/typescript/src/react.d.ts.map +1 -1
  428. package/lib/typescript/src/scene/defineScene.d.ts +22 -2
  429. package/lib/typescript/src/scene/defineScene.d.ts.map +1 -1
  430. package/lib/typescript/src/scene/types.d.ts +24 -4
  431. package/lib/typescript/src/scene/types.d.ts.map +1 -1
  432. package/lib/typescript/src/sprites/sampleSpriteClip.d.ts +26 -2
  433. package/lib/typescript/src/sprites/sampleSpriteClip.d.ts.map +1 -1
  434. package/lib/typescript/src/sprites/spriteAnimationState.d.ts +6 -7
  435. package/lib/typescript/src/sprites/spriteAnimationState.d.ts.map +1 -1
  436. package/lib/typescript/src/sprites.d.ts +10 -0
  437. package/lib/typescript/src/sprites.d.ts.map +1 -0
  438. package/lib/typescript/src/storage/adapters/asyncStorage.d.ts +18 -0
  439. package/lib/typescript/src/storage/adapters/asyncStorage.d.ts.map +1 -0
  440. package/lib/typescript/src/storage/adapters/memory.d.ts +16 -0
  441. package/lib/typescript/src/storage/adapters/memory.d.ts.map +1 -0
  442. package/lib/typescript/src/storage/errors.d.ts +37 -0
  443. package/lib/typescript/src/storage/errors.d.ts.map +1 -0
  444. package/lib/typescript/src/storage/index.d.ts +11 -0
  445. package/lib/typescript/src/storage/index.d.ts.map +1 -0
  446. package/lib/typescript/src/storage/schema.d.ts +22 -0
  447. package/lib/typescript/src/storage/schema.d.ts.map +1 -0
  448. package/lib/typescript/src/storage/serialization.d.ts +23 -0
  449. package/lib/typescript/src/storage/serialization.d.ts.map +1 -0
  450. package/lib/typescript/src/storage/store.d.ts +3 -0
  451. package/lib/typescript/src/storage/store.d.ts.map +1 -0
  452. package/lib/typescript/src/storage/types.d.ts +69 -0
  453. package/lib/typescript/src/storage/types.d.ts.map +1 -0
  454. package/lib/typescript/src/storage/validation.d.ts +24 -0
  455. package/lib/typescript/src/storage/validation.d.ts.map +1 -0
  456. package/lib/typescript/src/storage.d.ts +2 -0
  457. package/lib/typescript/src/storage.d.ts.map +1 -0
  458. package/lib/typescript/src/testing.d.ts +1 -0
  459. package/lib/typescript/src/testing.d.ts.map +1 -1
  460. package/lib/typescript/src/tilemap/chunkStats.d.ts +14 -0
  461. package/lib/typescript/src/tilemap/chunkStats.d.ts.map +1 -0
  462. package/lib/typescript/src/tilemap/definitions.d.ts +38 -0
  463. package/lib/typescript/src/tilemap/definitions.d.ts.map +1 -0
  464. package/lib/typescript/src/tilemap/errors.d.ts +7 -0
  465. package/lib/typescript/src/tilemap/errors.d.ts.map +1 -0
  466. package/lib/typescript/src/tilemap/index.d.ts +8 -0
  467. package/lib/typescript/src/tilemap/index.d.ts.map +1 -0
  468. package/lib/typescript/src/tilemap/movement.d.ts +52 -0
  469. package/lib/typescript/src/tilemap/movement.d.ts.map +1 -0
  470. package/lib/typescript/src/tilemap/queries.d.ts +21 -0
  471. package/lib/typescript/src/tilemap/queries.d.ts.map +1 -0
  472. package/lib/typescript/src/tilemap/tiledAdapter.d.ts +32 -0
  473. package/lib/typescript/src/tilemap/tiledAdapter.d.ts.map +1 -0
  474. package/lib/typescript/src/tilemap/types.d.ts +127 -0
  475. package/lib/typescript/src/tilemap/types.d.ts.map +1 -0
  476. package/lib/typescript/src/tilemap.d.ts +2 -0
  477. package/lib/typescript/src/tilemap.d.ts.map +1 -0
  478. package/package.json +94 -4
  479. package/src/assets/defineAssets.ts +31 -5
  480. package/src/assets.ts +27 -0
  481. package/src/audio/createGameAudio.ts +749 -0
  482. package/src/audio/errors.ts +12 -0
  483. package/src/audio/index.ts +9 -0
  484. package/src/audio/resolver.ts +74 -0
  485. package/src/audio/types.ts +47 -0
  486. package/src/audio.ts +13 -0
  487. package/src/camera2d/bounds.ts +69 -0
  488. package/src/camera2d/extents.ts +25 -0
  489. package/src/camera2d/follow.ts +165 -0
  490. package/src/camera2d/index.ts +29 -0
  491. package/src/camera2d/interpolation.ts +113 -0
  492. package/src/camera2d/shake.ts +90 -0
  493. package/src/camera2d/transform.ts +148 -0
  494. package/src/camera2d/types.ts +73 -0
  495. package/src/camera2d/validation.ts +126 -0
  496. package/src/camera2d/visibility.ts +181 -0
  497. package/src/camera2d.ts +6 -0
  498. package/src/collision2d/colliders.ts +292 -0
  499. package/src/collision2d/debug.ts +133 -0
  500. package/src/collision2d/filters.ts +48 -0
  501. package/src/collision2d/index.ts +74 -0
  502. package/src/collision2d/intersections.ts +82 -0
  503. package/src/collision2d/manifolds.ts +191 -0
  504. package/src/collision2d/segments.ts +176 -0
  505. package/src/collision2d/spatialHash.ts +211 -0
  506. package/src/collision2d/sweeps.ts +269 -0
  507. package/src/collision2d.ts +6 -0
  508. package/src/core/input/buttonPad.ts +235 -0
  509. package/src/core/input/createInputBuffer.ts +95 -14
  510. package/src/core/input/types.ts +7 -0
  511. package/src/core/session/createGameSession.ts +480 -53
  512. package/src/core/session/deepFreeze.ts +104 -3
  513. package/src/core/session/diagnostics.ts +2 -0
  514. package/src/core/session/types.ts +77 -4
  515. package/src/definition/defineGame.ts +61 -3
  516. package/src/definition/types.ts +10 -0
  517. package/src/events/defineGameEvents.ts +46 -0
  518. package/src/events/errors.ts +14 -0
  519. package/src/events/index.ts +14 -0
  520. package/src/events/payload.ts +234 -0
  521. package/src/events/seed.ts +33 -0
  522. package/src/events/types.ts +53 -0
  523. package/src/events.ts +20 -0
  524. package/src/geometry/helpers.ts +136 -0
  525. package/src/geometry/index.ts +14 -0
  526. package/src/geometry/types.ts +54 -0
  527. package/src/geometry/validation.ts +131 -0
  528. package/src/geometry.ts +6 -0
  529. package/src/haptics/createGameHaptics.ts +214 -0
  530. package/src/haptics/errors.ts +12 -0
  531. package/src/haptics/index.ts +3 -0
  532. package/src/haptics/resolver.ts +58 -0
  533. package/src/haptics/types.ts +48 -0
  534. package/src/haptics.ts +7 -0
  535. package/src/index.ts +37 -1
  536. package/src/particles/createParticleSystem.ts +505 -0
  537. package/src/particles/defineParticleEffect.ts +164 -0
  538. package/src/particles/errors.ts +6 -0
  539. package/src/particles/index.ts +5 -0
  540. package/src/particles/sampling.ts +141 -0
  541. package/src/particles/types.ts +327 -0
  542. package/src/particles.ts +1 -0
  543. package/src/react/GameButtonPad.tsx +436 -0
  544. package/src/react/GamePointerInput.tsx +73 -8
  545. package/src/react/GameView.tsx +160 -61
  546. package/src/react/assets/createGameAssetStore.ts +140 -107
  547. package/src/react/assets/useGameAssets.ts +63 -31
  548. package/src/react/bindAppLifecycle.ts +55 -19
  549. package/src/react/bindGameSession.ts +12 -2
  550. package/src/react/camera2d/GameLayer2D.tsx +43 -0
  551. package/src/react/camera2d/defineGameCamera2D.ts +59 -0
  552. package/src/react/camera2d/usePresentedCameraBinding.ts +185 -0
  553. package/src/react/cleanupList.ts +48 -0
  554. package/src/react/instrumentation.ts +31 -3
  555. package/src/react/lifecycleSource.ts +58 -0
  556. package/src/react/particles/ParticleView.tsx +358 -0
  557. package/src/react/particles/culling.ts +145 -0
  558. package/src/react/particles/index.ts +6 -0
  559. package/src/react/particles/spriteXForm.ts +72 -0
  560. package/src/react/particles/useParticlePresentation.ts +261 -0
  561. package/src/react/pointerBinding.ts +74 -16
  562. package/src/react/pointerCamera.ts +33 -0
  563. package/src/react/pointerCoalescer.ts +36 -10
  564. package/src/react/sprites/GameSprite.tsx +4 -33
  565. package/src/react/sprites/GameWorld2D.tsx +113 -7
  566. package/src/react/sprites/Sprite.tsx +51 -72
  567. package/src/react/sprites/SpriteBatch.tsx +74 -22
  568. package/src/react/sprites/batchVisibility.ts +76 -0
  569. package/src/react/sprites/spriteSelection.ts +133 -0
  570. package/src/react/sprites/spriteTransform.ts +46 -26
  571. package/src/react/tilemap/TileMapLayer2D.tsx +274 -0
  572. package/src/react/tilemap/tilePresentation.ts +491 -0
  573. package/src/react/useGameSession.ts +90 -0
  574. package/src/react/useGameSessionStatus.ts +53 -0
  575. package/src/react/viewportBinding.ts +5 -1
  576. package/src/react.ts +18 -1
  577. package/src/scene/defineScene.ts +47 -5
  578. package/src/scene/types.ts +37 -3
  579. package/src/sprites/sampleSpriteClip.ts +82 -14
  580. package/src/sprites/spriteAnimationState.ts +29 -25
  581. package/src/sprites.ts +22 -0
  582. package/src/storage/adapters/asyncStorage.ts +77 -0
  583. package/src/storage/adapters/memory.ts +58 -0
  584. package/src/storage/errors.ts +88 -0
  585. package/src/storage/index.ts +18 -0
  586. package/src/storage/schema.ts +147 -0
  587. package/src/storage/serialization.ts +248 -0
  588. package/src/storage/store.ts +380 -0
  589. package/src/storage/types.ts +76 -0
  590. package/src/storage/validation.ts +102 -0
  591. package/src/storage.ts +1 -0
  592. package/src/testing.ts +4 -0
  593. package/src/tilemap/chunkStats.ts +24 -0
  594. package/src/tilemap/definitions.ts +327 -0
  595. package/src/tilemap/errors.ts +11 -0
  596. package/src/tilemap/index.ts +10 -0
  597. package/src/tilemap/movement.ts +368 -0
  598. package/src/tilemap/queries.ts +136 -0
  599. package/src/tilemap/tiledAdapter.ts +165 -0
  600. package/src/tilemap/types.ts +119 -0
  601. package/src/tilemap.ts +1 -0
@@ -0,0 +1,436 @@
1
+ /**
2
+ * Multitouch button pad (T20).
3
+ *
4
+ * The React surface over the headless button-pad controller
5
+ * (`core/input/buttonPad`): compose any number of {@link GameButton} zones
6
+ * inside a {@link GameButtonPad}; every active pointer is mapped to the zone
7
+ * it covers and press/release edges flow into the session's declared button
8
+ * actions. Unlike RN `Pressable`, simultaneous fingers on different buttons
9
+ * all register — hold left/right and jump at the same time.
10
+ *
11
+ * Semantics carried over from the reference implementation:
12
+ * - Release edges ALWAYS fire on touch up/cancel, so edge-triggered actions
13
+ * (a jump pulse) re-arm for the next press.
14
+ * - A finger sliding between zones reassigns: release + press.
15
+ * - Unmount releases every held action; no input outlives the pad.
16
+ *
17
+ * GS-INPUT-03: zones measure into pad coordinates (never parent-relative),
18
+ * every mounted zone has an owner identity with idempotent cleanup, and
19
+ * terminal cleanup never touches a disposed session.
20
+ * GS-INPUT-04: one invalidation policy — pause, session replacement, and
21
+ * unmount bump an ownership generation stamped onto every scheduled RN
22
+ * callback; stale callbacks die at ingress, pad ownership clears, and a
23
+ * fresh down is required before moves can acquire again.
24
+ */
25
+ import {
26
+ createContext,
27
+ useCallback,
28
+ useContext,
29
+ useEffect,
30
+ useMemo,
31
+ useRef,
32
+ type ReactNode,
33
+ } from 'react';
34
+ import { findNodeHandle, StyleSheet, View } from 'react-native';
35
+ import { GestureDetector, useManualGesture, type ManualGestureConfig } from 'react-native-gesture-handler';
36
+ import { useSharedValue } from 'react-native-reanimated';
37
+ import { scheduleOnRN } from 'react-native-worklets';
38
+ import type { StyleProp, ViewStyle } from 'react-native';
39
+
40
+ import { createButtonPadController } from '../core/input/buttonPad';
41
+ import type { InputMap } from '../definition/types';
42
+ import type { SceneMap } from '../definition/types';
43
+ import type { GameSession } from '../core/session/types';
44
+
45
+ /** Declared button action names of an input map. */
46
+ export type ButtonActionName<TInput extends InputMap> = {
47
+ [TName in Extract<keyof TInput, string>]: TInput[TName] extends { readonly type: 'button' }
48
+ ? TName
49
+ : never;
50
+ }[Extract<keyof TInput, string>];
51
+
52
+ interface ButtonPadContextValue {
53
+ /**
54
+ * Register/refresh a zone rect with its owner node. Provisional rects
55
+ * come from layout events; pad-space measurement overwrites them.
56
+ */
57
+ readonly setZone: (
58
+ action: string,
59
+ x: number,
60
+ y: number,
61
+ width: number,
62
+ height: number,
63
+ node: unknown,
64
+ ) => void;
65
+ /**
66
+ * Authoritatively measure a zone into pad coordinates. No-op when native
67
+ * nodes are unavailable (the provisional layout registration stands).
68
+ */
69
+ readonly measureZone: (action: string, node: unknown) => void;
70
+ /** Remove a zone (button unmounted or action replaced). */
71
+ readonly removeZone: (action: string, node: unknown) => void;
72
+ }
73
+
74
+ const ButtonPadContext = createContext<ButtonPadContextValue | null>(null);
75
+
76
+ function useButtonPadContext(): ButtonPadContextValue {
77
+ const context = useContext(ButtonPadContext);
78
+ if (context === null) {
79
+ throw new Error('GameButton must be rendered inside a GameButtonPad');
80
+ }
81
+ return context;
82
+ }
83
+
84
+ export interface GameButtonPadProps<TScenes extends SceneMap, TInput extends InputMap> {
85
+ /** The session whose input buffer receives button presses. */
86
+ readonly game: GameSession<TScenes, TInput>;
87
+ /** One or more {@link GameButton} zones (any layout). */
88
+ readonly children?: ReactNode;
89
+ /**
90
+ * Container style layered over the default full-surface overlay. The pad
91
+ * never draws and never blocks touches outside mounted GameButtons.
92
+ */
93
+ readonly style?: StyleProp<ViewStyle>;
94
+ /** Extra hit area around every zone, in dp. Default 0. */
95
+ readonly hitSlop?: number;
96
+ /** Test id for the pad container. */
97
+ readonly testID?: string;
98
+ }
99
+
100
+ /** A host node measurable against the pad container. */
101
+ interface MeasurableNode {
102
+ measureLayout(
103
+ relativeTo: unknown,
104
+ onSuccess: (x: number, y: number, width: number, height: number) => void,
105
+ onFail: () => void,
106
+ ): void;
107
+ }
108
+
109
+ /**
110
+ * Map simultaneous multitouch zones onto declared button actions.
111
+ *
112
+ * The pad is an INVISIBLE FULL-SURFACE OVERLAY: it draws nothing and its
113
+ * empty areas let touches pass through (`pointerEvents="box-none"`), so you
114
+ * can render whatever React children you like underneath and position each
115
+ * {@link GameButton} anywhere on screen — flex rows, corners, one thumb-zone
116
+ * per side — with ordinary styles. Only the measured bounds of mounted
117
+ * {@link GameButton} components capture touches; every other point falls
118
+ * through to the content below.
119
+ */
120
+ export function GameButtonPad<TScenes extends SceneMap, TInput extends InputMap>({
121
+ game,
122
+ children,
123
+ style,
124
+ hitSlop = 0,
125
+ testID,
126
+ }: GameButtonPadProps<TScenes, TInput>) {
127
+ const input = game.input as unknown as {
128
+ press: (action: string) => void;
129
+ release: (action: string) => void;
130
+ };
131
+ const padRef = useRef<View | null>(null);
132
+ // The zone context must stay identical across session replacements:
133
+ // zones belong to mounted buttons, not to sessions, so a game change
134
+ // must never tear down and re-register them (no layout event would
135
+ // restore them). Session/input reads below always use the live values.
136
+ const sessionRef = useRef({ game, input });
137
+ sessionRef.current = { game, input };
138
+
139
+ // Stable across renders: RNGH 3 re-registers gesture callbacks when the
140
+ // config identity changes (same discipline as GamePointerInput).
141
+ const controllerRef = useRef<ReturnType<typeof createButtonPadController> | null>(null);
142
+ if (controllerRef.current === null) {
143
+ controllerRef.current = createButtonPadController({ hitSlop });
144
+ }
145
+ // GS-INPUT-03: owner identity per mounted zone. A second mounted button
146
+ // claiming the same action is a contract violation; re-registration by
147
+ // the owning node (re-layout, re-measure) is idempotent.
148
+ const ownersRef = useRef(new Map<string, unknown>());
149
+ // GS-INPUT-04: ownership generation — bumped on pause, session
150
+ // replacement, and unmount; stamped onto every scheduled RN callback and
151
+ // checked at ingress so stale callbacks die instead of pressing a dead
152
+ // session or a replaced binding.
153
+ const generationRef = useRef(1);
154
+ const generationSV = useSharedValue(1);
155
+
156
+ const liveInput = useCallback((): {
157
+ press: (action: string) => void;
158
+ release: (action: string) => void;
159
+ } | null => {
160
+ // GS-INPUT-03: terminal cleanup never touches a disposed session.
161
+ if (game.status === 'disposed') {
162
+ return null;
163
+ }
164
+ return input;
165
+ }, [game, input]);
166
+
167
+ const registerZone = useCallback(
168
+ (action: string, rect: { x: number; y: number; width: number; height: number }, node: unknown) => {
169
+ const owners = ownersRef.current;
170
+ const owner = owners.get(action);
171
+ if (owner !== undefined && owner !== node) {
172
+ throw new Error(
173
+ `GameButton action "${action}" is already registered by another mounted button. Use one button per action.`,
174
+ );
175
+ }
176
+ owners.set(action, node);
177
+ controllerRef.current?.setZone(action, rect);
178
+ },
179
+ [],
180
+ );
181
+
182
+ const context = useMemo<ButtonPadContextValue>(
183
+ () => ({
184
+ setZone: (action, x, y, width, height, node) => {
185
+ registerZone(action, { x, y, width, height }, node);
186
+ },
187
+ measureZone: (action, node) => {
188
+ const owners = ownersRef.current;
189
+ if (owners.has(action) && owners.get(action) !== node) {
190
+ throw new Error(
191
+ `GameButton action "${action}" is already registered by another mounted button. Use one button per action.`,
192
+ );
193
+ }
194
+ const measurable = node as MeasurableNode | null;
195
+ const padNode = padRef.current === null ? null : findNodeHandle(padRef.current);
196
+ if (measurable === null || typeof measurable.measureLayout !== 'function' || padNode === null) {
197
+ return;
198
+ }
199
+ measurable.measureLayout(
200
+ padNode,
201
+ (x, y, width, height) => {
202
+ // The button may have unmounted or been replaced while the
203
+ // native measurement was in flight: never resurrect a zone.
204
+ if (ownersRef.current.get(action) !== node) {
205
+ return;
206
+ }
207
+ registerZone(action, { x, y, width, height }, node);
208
+ },
209
+ () => {},
210
+ );
211
+ },
212
+ removeZone: (action, node) => {
213
+ if (ownersRef.current.get(action) !== node) {
214
+ return;
215
+ }
216
+ ownersRef.current.delete(action);
217
+ const released = controllerRef.current?.removeZone(action) ?? [];
218
+ const live = sessionRef.current;
219
+ if (live.game.status === 'disposed') {
220
+ return;
221
+ }
222
+ for (const actionReleased of released) {
223
+ live.input.release(actionReleased);
224
+ }
225
+ },
226
+ }),
227
+ [registerZone],
228
+ );
229
+
230
+ // GS-INPUT-04: one invalidation policy — pause, session replacement, and
231
+ // unmount bump the generation and clear pad ownership, so stale scheduled
232
+ // callbacks die at ingress and a fresh down is required after resume.
233
+ const invalidateOwnership = useCallback(() => {
234
+ generationRef.current += 1;
235
+ generationSV.value = generationRef.current;
236
+ controllerRef.current?.invalidateOwnership();
237
+ }, [generationSV]);
238
+
239
+ useEffect(() => {
240
+ invalidateOwnership();
241
+ if (game.status === 'disposed') {
242
+ return;
243
+ }
244
+ const subscription = game.addStatusListener((status) => {
245
+ if (status === 'paused') {
246
+ invalidateOwnership();
247
+ }
248
+ });
249
+ return () => {
250
+ subscription.remove();
251
+ };
252
+ }, [game, invalidateOwnership]);
253
+
254
+ useEffect(() => {
255
+ const controller = controllerRef.current;
256
+ return () => {
257
+ // Release held actions on the owning session first (unless it is
258
+ // already disposed), then invalidate so late callbacks die.
259
+ if (game.status !== 'disposed') {
260
+ for (const action of controller?.releaseAll() ?? []) {
261
+ input.release(action);
262
+ }
263
+ }
264
+ invalidateOwnership();
265
+ };
266
+ }, [game, input, invalidateOwnership]);
267
+
268
+ // JS-side handlers — called from the UI worklet via scheduleOnRN with the
269
+ // scheduling generation first. Stale generations die here, before the
270
+ // controller sees them.
271
+ const onDownJS = useCallback(
272
+ (generation: number, event: { allTouches: readonly { id: number; x: number; y: number }[] }) => {
273
+ if (generation !== generationRef.current) {
274
+ return;
275
+ }
276
+ const live = liveInput();
277
+ if (live === null) {
278
+ return;
279
+ }
280
+ const diff = controllerRef.current?.touchesDown(event.allTouches) ?? { pressed: [], released: [] };
281
+ for (const action of diff.released) live.release(action);
282
+ for (const action of diff.pressed) live.press(action);
283
+ },
284
+ [liveInput],
285
+ );
286
+ const onMoveJS = useCallback(
287
+ (generation: number, event: { allTouches: readonly { id: number; x: number; y: number }[] }) => {
288
+ if (generation !== generationRef.current) {
289
+ return;
290
+ }
291
+ const live = liveInput();
292
+ if (live === null) {
293
+ return;
294
+ }
295
+ const diff = controllerRef.current?.touchesMove(event.allTouches) ?? { pressed: [], released: [] };
296
+ for (const action of diff.released) live.release(action);
297
+ for (const action of diff.pressed) live.press(action);
298
+ },
299
+ [liveInput],
300
+ );
301
+ const onUpJS = useCallback(
302
+ (generation: number, event: { changedTouches: readonly { id: number; x: number; y: number }[] }) => {
303
+ if (generation !== generationRef.current) {
304
+ return;
305
+ }
306
+ const live = liveInput();
307
+ if (live === null) {
308
+ return;
309
+ }
310
+ const diff = controllerRef.current?.touchesUp(event.changedTouches) ?? { pressed: [], released: [] };
311
+ for (const action of diff.released) live.release(action);
312
+ },
313
+ [liveInput],
314
+ );
315
+ const onCancelJS = useCallback(
316
+ (generation: number, event: { changedTouches: readonly { id: number; x: number; y: number }[] }) => {
317
+ if (generation !== generationRef.current) {
318
+ return;
319
+ }
320
+ const live = liveInput();
321
+ if (live === null) {
322
+ return;
323
+ }
324
+ const diff = controllerRef.current?.touchesCancel(event.changedTouches) ?? { pressed: [], released: [] };
325
+ for (const action of diff.released) live.release(action);
326
+ },
327
+ [liveInput],
328
+ );
329
+
330
+ type ManualTouchHandler = NonNullable<ManualGestureConfig['onTouchesDown']>;
331
+ const handleTouchesDown = useCallback<ManualTouchHandler>(
332
+ (event) => {
333
+ 'worklet';
334
+ scheduleOnRN(onDownJS, generationSV.value, event as never);
335
+ },
336
+ [onDownJS, generationSV],
337
+ );
338
+ const handleTouchesMove = useCallback<ManualTouchHandler>(
339
+ (event) => {
340
+ 'worklet';
341
+ scheduleOnRN(onMoveJS, generationSV.value, event as never);
342
+ },
343
+ [onMoveJS, generationSV],
344
+ );
345
+ const handleTouchesUp = useCallback<ManualTouchHandler>(
346
+ (event) => {
347
+ 'worklet';
348
+ scheduleOnRN(onUpJS, generationSV.value, event as never);
349
+ },
350
+ [onUpJS, generationSV],
351
+ );
352
+ const handleTouchesCancel = useCallback<ManualTouchHandler>(
353
+ (event) => {
354
+ 'worklet';
355
+ scheduleOnRN(onCancelJS, generationSV.value, event as never);
356
+ },
357
+ [onCancelJS, generationSV],
358
+ );
359
+
360
+ const gestureConfig = useMemo<ManualGestureConfig>(
361
+ () => ({
362
+ shouldCancelWhenOutside: false,
363
+ onTouchesDown: handleTouchesDown,
364
+ onTouchesMove: handleTouchesMove,
365
+ onTouchesUp: handleTouchesUp,
366
+ onTouchesCancel: handleTouchesCancel,
367
+ }),
368
+ [handleTouchesCancel, handleTouchesDown, handleTouchesMove, handleTouchesUp],
369
+ );
370
+ const gesture = useManualGesture(gestureConfig);
371
+
372
+ // When a style is provided the caller owns the layout (e.g. an
373
+ // absolutely-positioned bottom row). Otherwise default to a full-surface
374
+ // invisible overlay so buttons can be placed anywhere via their own
375
+ // styles. Using a fallback instead of [absoluteFill, style] avoids the
376
+ // "tall pad" bug where merging top:0 from absoluteFill with a bottom-
377
+ // anchored row stretches the container to full height and vertically
378
+ // centers its children in the middle of the screen.
379
+ return (
380
+ <GestureDetector gesture={gesture}>
381
+ <View
382
+ ref={padRef as never}
383
+ pointerEvents="box-none"
384
+ style={style ?? StyleSheet.absoluteFill}
385
+ testID={testID}
386
+ >
387
+ <ButtonPadContext.Provider value={context}>{children}</ButtonPadContext.Provider>
388
+ </View>
389
+ </GestureDetector>
390
+ );
391
+ }
392
+
393
+ export interface GameButtonProps {
394
+ /** The declared button action this zone presses. */
395
+ readonly action: string;
396
+ /** Zone content (label, icon, art). */
397
+ readonly children?: ReactNode;
398
+ /** Zone layout + presentation styles. */
399
+ readonly style?: StyleProp<ViewStyle>;
400
+ /** Test id for end-to-end tests. */
401
+ readonly testID?: string;
402
+ /** Accessibility role announced to the OS. */
403
+ readonly accessibilityRole?: 'button';
404
+ }
405
+
406
+ /**
407
+ * One touch zone of the pad. Layout it however you like; its measured
408
+ * bounds become the multitouch hit area for `action`, in pad coordinates.
409
+ *
410
+ * GS-INPUT-03: the zone registers on layout (provisional, parent-relative)
411
+ * and re-resolves into pad space through native measurement whenever
412
+ * available; unmount and action replacement release the zone exactly once.
413
+ */
414
+ export function GameButton({ action, children, style, testID, accessibilityRole }: GameButtonProps) {
415
+ const context = useButtonPadContext();
416
+ const nodeRef = useRef<View | null>(null);
417
+ useEffect(() => {
418
+ context.measureZone(action, nodeRef.current);
419
+ return () => context.removeZone(action, nodeRef.current);
420
+ }, [context, action]);
421
+ return (
422
+ <View
423
+ ref={nodeRef as never}
424
+ style={style}
425
+ testID={testID}
426
+ accessibilityRole={accessibilityRole}
427
+ onLayout={(event) => {
428
+ const layout = event.nativeEvent.layout;
429
+ context.setZone(action, layout.x, layout.y, layout.width, layout.height, nodeRef.current);
430
+ context.measureZone(action, nodeRef.current);
431
+ }}
432
+ >
433
+ {children}
434
+ </View>
435
+ );
436
+ }
@@ -9,9 +9,10 @@ import {
9
9
  import { useFrameCallback, useSharedValue, type SharedValue } from 'react-native-reanimated';
10
10
  import { scheduleOnRN } from 'react-native-worklets';
11
11
 
12
+ import type { CameraCut2D } from '../camera2d';
12
13
  import type { InputMap, PointerInputAction, SceneMap } from '../definition/types';
13
14
  import type { GameSession } from '../core/session/types';
14
- import { GameViewportContext } from './GameView';
15
+ import { GameCameraContext, GameViewportContext } from './GameView';
15
16
  import {
16
17
  canBeginPrimaryPointer,
17
18
  cancelOnActiveFinalize,
@@ -29,6 +30,7 @@ import {
29
30
  type PointerCoalescerInput,
30
31
  type PointerCoalescerState,
31
32
  } from './pointerCoalescer';
33
+ import { packetCameraFor, type PointerCameraCapture2D } from './pointerCamera';
32
34
  import { PointerBinding, type PointerPacket } from './pointerBinding';
33
35
 
34
36
  /** Declared pointer action names of an input map. */
@@ -78,6 +80,7 @@ function TrailingFlushSampler({
78
80
  layoutEpoch,
79
81
  forwardSeq,
80
82
  generation,
83
+ camera,
81
84
  }: {
82
85
  readonly coalescerState: SharedValue<PointerCoalescerState>;
83
86
  readonly forwardEventOnJS: (packet: PointerPacket) => void;
@@ -85,6 +88,8 @@ function TrailingFlushSampler({
85
88
  readonly layoutEpoch: SharedValue<number>;
86
89
  readonly forwardSeq: SharedValue<number>;
87
90
  readonly generation: number;
91
+ /** The presented camera shared value, sampled at flush time (T12-F4). */
92
+ readonly camera: SharedValue<CameraCut2D | undefined> | undefined;
88
93
  }) {
89
94
  // F2 acceptance: report the sampler's mounted lifecycle so the lab can
90
95
  // prove no idle frame callback survives pointer exit, replacement, or
@@ -116,6 +121,10 @@ function TrailingFlushSampler({
116
121
  layoutEpoch: layoutEpoch.value,
117
122
  seq: forwardSeq.value,
118
123
  atMs: Date.now(),
124
+ // The deferred move carries the stamp captured at ITS native
125
+ // sample (T12-RF3, T12-SF1) — never the flush-time camera, and a
126
+ // captured undefined stays undefined.
127
+ camera: packetCameraFor(forwarded, camera?.value),
119
128
  });
120
129
  }
121
130
  });
@@ -150,6 +159,11 @@ export function GamePointerInput<TScenes extends SceneMap, TInput extends InputM
150
159
  if (viewportContext === null) {
151
160
  throw new Error('GamePointerInput must be rendered inside a GameView');
152
161
  }
162
+ // T12.4: the adapter discovers the presented camera from the mounted
163
+ // surface (never a repeated prop), so renderer and input share the same
164
+ // generation. Games without a camera get the exact current viewport path.
165
+ const cameraContext = useContext(GameCameraContext);
166
+ const presentedCamera = cameraContext?.presented;
153
167
  const viewportShared = viewportContext.viewport;
154
168
  const maxMoveIntervalMs = game.getRenderFrame().stepMs;
155
169
  const coalescerState = useSharedValue<PointerCoalescerState>(() =>
@@ -163,9 +177,19 @@ export function GamePointerInput<TScenes extends SceneMap, TInput extends InputM
163
177
  // stamp packets with that same generation, so the packet producer and
164
178
  // consumer agree by construction — no post-commit synchronization.
165
179
  const binding = useMemo(
166
- () => new PointerBinding(action, game.input, () => viewportBinding.resolved, nextBindingGeneration++),
180
+ () =>
181
+ new PointerBinding(
182
+ action,
183
+ game.input,
184
+ () => viewportBinding.resolved,
185
+ nextBindingGeneration++,
186
+ ),
167
187
  [action, game, viewportBinding],
168
188
  );
189
+ // T12-F4: the presented camera is sampled at EVENT TIME inside the touch
190
+ // worklets and stamped onto every packet; the binding inverts through
191
+ // that stamp, never a later JS-side read.
192
+ const cameraShared = presentedCamera;
169
193
  const generation = binding.generation;
170
194
  // F6/F3: the layout epoch is adapter-owned (ref + UI mirror), bumped only
171
195
  // on layout revisions and unmount; it never resets, so replacement cannot
@@ -197,7 +221,33 @@ export function GamePointerInput<TScenes extends SceneMap, TInput extends InputM
197
221
  [generation],
198
222
  );
199
223
 
200
- // F2 review: `autostart` is only consulted at creation (changing it later
224
+ // T10.4: the pause transition clears the UI-side coalescer queue for the
225
+ // active stream (the core input buffer already cancelled ownership), so a
226
+ // pre-pause pointer can never flush stale moves after resume. The gesture
227
+ // itself is not remounted and its key does not change.
228
+ // GS-INPUT-04: pause also advances the packet epoch (queued pre-pause
229
+ // packets die at RN ingress) and clears the React sampler mirror, so the
230
+ // trailing sampler unmounts even when no coalescer event ever arrives to
231
+ // finalize it. A fresh down is required after resume.
232
+ useEffect(() => {
233
+ if (game.status === 'disposed') {
234
+ return;
235
+ }
236
+ const subscription = game.addStatusListener((status) => {
237
+ if (status === 'paused') {
238
+ coalescerState.value = createPointerCoalescerState(maxMoveIntervalMs);
239
+ bumpLayoutEpoch();
240
+ setSamplerState((previous) =>
241
+ reduceSamplerMirrorState(generation, previous, { generation, active: false }),
242
+ );
243
+ }
244
+ });
245
+ return () => {
246
+ subscription.remove();
247
+ };
248
+ }, [bumpLayoutEpoch, coalescerState, game, generation, maxMoveIntervalMs]);
249
+
250
+ // F2 review: `autostart` is only consulted at creation (changing it later
201
251
  // re-registers but never activates) and `setActive` from an effect proved
202
252
  // unreliable in this stack, so the sampler is a conditionally mounted
203
253
  // component (see TrailingFlushSampler): it mounts with autostart active
@@ -213,10 +263,14 @@ export function GamePointerInput<TScenes extends SceneMap, TInput extends InputM
213
263
  // layout die here, before the binding sees them.
214
264
  if (packet.layoutEpoch !== layoutEpochRef.current) {
215
265
  instrumentation?.onDispatchResult?.(packet.seq, packet.atMs, false);
266
+ instrumentation?.onDispatchRejected?.('layout-epoch', packet.seq, packet.atMs);
216
267
  return;
217
268
  }
218
269
  const accepted = binding.dispatch(packet);
219
270
  instrumentation?.onDispatchResult?.(packet.seq, packet.atMs, accepted);
271
+ if (!accepted) {
272
+ instrumentation?.onDispatchRejected?.('binding', packet.seq, packet.atMs);
273
+ }
220
274
  },
221
275
  [binding, instrumentation],
222
276
  );
@@ -261,6 +315,7 @@ export function GamePointerInput<TScenes extends SceneMap, TInput extends InputM
261
315
  layoutEpoch: layoutEpoch.value,
262
316
  seq: forwardSeq.value,
263
317
  atMs: Date.now(),
318
+ camera: cameraShared?.value,
264
319
  });
265
320
  }
266
321
  const nextActive = samplerMirrorFromBatch(batch);
@@ -269,7 +324,7 @@ export function GamePointerInput<TScenes extends SceneMap, TInput extends InputM
269
324
  }
270
325
  GestureStateManager.activate(event.handlerTag);
271
326
  },
272
- [coalescerState, forwardEventOnJS, generation, instrumentation, layoutEpoch, reportSamplerState, viewportShared],
327
+ [cameraShared, coalescerState, forwardEventOnJS, generation, instrumentation, layoutEpoch, reportSamplerState, viewportShared],
273
328
  );
274
329
 
275
330
  const handleTouchesMove = useCallback<ManualTouchHandler>(
@@ -283,6 +338,7 @@ export function GamePointerInput<TScenes extends SceneMap, TInput extends InputM
283
338
  x: touch.x,
284
339
  y: touch.y,
285
340
  nowMs: Date.now(),
341
+ stamp: { captured: true, value: cameraShared?.value } satisfies PointerCameraCapture2D,
286
342
  });
287
343
  for (const forwarded of batch) {
288
344
  instrumentation?.onForwarded?.(
@@ -297,11 +353,12 @@ export function GamePointerInput<TScenes extends SceneMap, TInput extends InputM
297
353
  layoutEpoch: layoutEpoch.value,
298
354
  seq: forwardSeq.value,
299
355
  atMs: Date.now(),
356
+ camera: packetCameraFor(forwarded, cameraShared?.value),
300
357
  });
301
358
  }
302
359
  }
303
360
  },
304
- [coalescerState, forwardEventOnJS, generation, instrumentation, layoutEpoch],
361
+ [cameraShared, coalescerState, forwardEventOnJS, generation, instrumentation, layoutEpoch],
305
362
  );
306
363
 
307
364
  const handleTouchesUp = useCallback<ManualTouchHandler>(
@@ -330,6 +387,7 @@ export function GamePointerInput<TScenes extends SceneMap, TInput extends InputM
330
387
  layoutEpoch: layoutEpoch.value,
331
388
  seq: forwardSeq.value,
332
389
  atMs: Date.now(),
390
+ camera: packetCameraFor(forwarded, cameraShared?.value),
333
391
  });
334
392
  }
335
393
  const mirror = samplerMirrorFromBatch(batch);
@@ -344,7 +402,7 @@ export function GamePointerInput<TScenes extends SceneMap, TInput extends InputM
344
402
  GestureStateManager.deactivate(event.handlerTag);
345
403
  }
346
404
  },
347
- [coalescerState, forwardEventOnJS, generation, instrumentation, layoutEpoch, reportSamplerState],
405
+ [cameraShared, coalescerState, forwardEventOnJS, generation, instrumentation, layoutEpoch, reportSamplerState],
348
406
  );
349
407
 
350
408
  const handleTouchesCancel = useCallback<ManualTouchHandler>(
@@ -364,6 +422,7 @@ export function GamePointerInput<TScenes extends SceneMap, TInput extends InputM
364
422
  layoutEpoch: layoutEpoch.value,
365
423
  seq: forwardSeq.value,
366
424
  atMs: Date.now(),
425
+ camera: cameraShared?.value,
367
426
  });
368
427
  }
369
428
  const nextActive = samplerMirrorFromBatch(batch);
@@ -371,7 +430,7 @@ export function GamePointerInput<TScenes extends SceneMap, TInput extends InputM
371
430
  scheduleOnRN(reportSamplerState, { generation, active: nextActive });
372
431
  }
373
432
  },
374
- [coalescerState, forwardEventOnJS, generation, instrumentation, layoutEpoch, reportSamplerState],
433
+ [cameraShared, coalescerState, forwardEventOnJS, generation, instrumentation, layoutEpoch, reportSamplerState],
375
434
  );
376
435
 
377
436
  const handleFinalize = useCallback<ManualFinalizeHandler>(
@@ -394,6 +453,7 @@ export function GamePointerInput<TScenes extends SceneMap, TInput extends InputM
394
453
  layoutEpoch: layoutEpoch.value,
395
454
  seq: forwardSeq.value,
396
455
  atMs: Date.now(),
456
+ camera: cameraShared?.value,
397
457
  });
398
458
  }
399
459
  const nextActive = samplerMirrorFromBatch(batch);
@@ -441,8 +501,12 @@ export function GamePointerInput<TScenes extends SceneMap, TInput extends InputM
441
501
  // flight become harmless no-ops, neutralize old input ownership exactly
442
502
  // once, reset the coalescer, remove the old sampler (generation
443
503
  // mismatch makes it inactive by construction), and dispose the binding.
504
+ // The mirror reset bails out when already inactive so cleanup never
505
+ // schedules redundant renders (GS-INPUT-04).
444
506
  bumpLayoutEpoch();
445
- setSamplerState({ generation: -1, active: false });
507
+ setSamplerState((previous) =>
508
+ previous.active ? { generation: -1, active: false } : previous,
509
+ );
446
510
  if (game.status !== 'disposed') {
447
511
  binding.cancel();
448
512
  }
@@ -470,6 +534,7 @@ export function GamePointerInput<TScenes extends SceneMap, TInput extends InputM
470
534
  layoutEpoch={layoutEpoch}
471
535
  forwardSeq={forwardSeq}
472
536
  generation={generation}
537
+ camera={cameraShared}
473
538
  />
474
539
  ) : null}
475
540
  </View>