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,368 @@
1
+
2
+ import type {
3
+ PlatformerContact2D,
4
+ PlatformerMoveResult2D,
5
+ TileCell2D,
6
+ TileMap2D,
7
+ } from './types';
8
+ import { cellsInAabb, cellsInSweptBounds } from './queries';
9
+ import { tileError } from './errors';
10
+ import type { Aabb2D, Vector2D } from '../geometry/types';
11
+ import { assertValidAabb2D, assertValidVector2D } from '../geometry/validation';
12
+
13
+ /** Frozen v1 movement semantics (T16.0). */
14
+ export const PLATFORMER_MAX_ITERATIONS = 4;
15
+ export const PLATFORMER_SKIN = 1e-6;
16
+ /** Previous-bottom must be at or above the platform top by this tolerance. */
17
+ export const ONE_WAY_TOLERANCE = 1e-4;
18
+
19
+ export interface PlatformerMoveOptions2D {
20
+ /** Explicit per-call drop-through intent; the helper owns no timer. */
21
+ readonly dropThroughOneWay?: boolean;
22
+ /**
23
+ * Snap downward onto floor tops within this many world units when the
24
+ * body ends the step falling or resting and no floor contact was resolved.
25
+ * Default 0 (disabled).
26
+ */
27
+ readonly floorSnapDistance?: number;
28
+ }
29
+
30
+ function isSolid(kind: string | undefined): boolean {
31
+ return kind === 'solid';
32
+ }
33
+
34
+ function isOneWay(kind: string | undefined): boolean {
35
+ return kind === 'one-way-up';
36
+ }
37
+
38
+ function overlaps(a: Aabb2D, b: Aabb2D): boolean {
39
+ return (
40
+ a.x < b.x + b.width && a.x + a.width > b.x &&
41
+ a.y < b.y + b.height && a.y + a.height > b.y
42
+ );
43
+ }
44
+
45
+ /**
46
+ * Pure fixed-step AABB platformer movement (T16.3, T16-F5).
47
+ *
48
+ * Frozen semantics:
49
+ * - Axis order X then Y.
50
+ * - Solid tiles block both axes; one-way tiles block descent only when the
51
+ * previous bottom was at/above the platform top (ONE_WAY_TOLERANCE) and
52
+ * `dropThroughOneWay` is not set.
53
+ * - Starting overlap resolves along the least-penetration face preferring UP
54
+ * on ties (up = negative y in y-down space), up to PLATFORMER_MAX_ITERATIONS;
55
+ * unresolved spawn overlap throws deterministically. The reported contact is
56
+ * always the tile that supplied the winning face.
57
+ * - Each axis resolves against the NEAREST blocking plane and reports contacts
58
+ * ONLY for tiles touching that winning plane — farther crossed rows/columns
59
+ * are never reported.
60
+ * - Floor snap pulls a falling/resting body down onto tops within
61
+ * `floorSnapDistance`.
62
+ * - Candidate order is layer order then row-major cell order; equal physical
63
+ * candidates preserve that deterministic order.
64
+ *
65
+ * Displacement contract (GS-TILE-03): `displacement` reports requested-motion
66
+ * resolution only — starting-overlap recovery moves the body and reports its
67
+ * contact, but never contributes to displacement. The caller's body object is
68
+ * never mutated or frozen; every returned body/contact/normal is an owned
69
+ * frozen value.
70
+ */
71
+ export function movePlatformerBody2D(options: {
72
+ readonly body: Aabb2D;
73
+ readonly velocity: Vector2D;
74
+ readonly deltaSeconds: number;
75
+ readonly map: TileMap2D;
76
+ /** Layer ids that contribute collision; others render only. */
77
+ readonly collisionLayers: readonly string[];
78
+ } & PlatformerMoveOptions2D): PlatformerMoveResult2D {
79
+ const { map, deltaSeconds, collisionLayers } = options;
80
+ // GS-TILE-03: validate author inputs once at the public boundary, and
81
+ // copy the body — the caller's object is never mutated or frozen.
82
+ assertValidAabb2D(options.body, 'body');
83
+ assertValidVector2D(options.velocity, 'velocity');
84
+ let body: Aabb2D = { ...options.body };
85
+ let vx = options.velocity.x;
86
+ let vy = options.velocity.y;
87
+ if (!Number.isFinite(deltaSeconds) || deltaSeconds < 0) {
88
+ throw tileError('deltaSeconds', `must be a finite number >= 0; got ${String(deltaSeconds)}`);
89
+ }
90
+ const dropThrough = options.dropThroughOneWay ?? false;
91
+ if (typeof dropThrough !== 'boolean') {
92
+ throw tileError('dropThroughOneWay', `must be a boolean or omitted; got ${String(options.dropThroughOneWay)}`);
93
+ }
94
+ const snapDistance = options.floorSnapDistance ?? 0;
95
+ if (typeof snapDistance !== 'number' || !Number.isFinite(snapDistance) || snapDistance < 0) {
96
+ throw tileError('floorSnapDistance', `must be a finite number >= 0 or omitted; got ${String(options.floorSnapDistance)}`);
97
+ }
98
+ // GS-TILE-02: layer selection is explicit and exact — unknown ids fail
99
+ // here instead of silently colliding with nothing (a typo would otherwise
100
+ // drop the player through the world). Duplicates are harmless: queries
101
+ // iterate map layers, so an id can never report twice.
102
+ if (!Array.isArray(collisionLayers)) {
103
+ throw tileError('collisionLayers', `must be an array of layer ids; got ${String(collisionLayers)}`);
104
+ }
105
+ for (const layerId of collisionLayers) {
106
+ if (typeof layerId !== 'string' || map.layerById[layerId] === undefined) {
107
+ throw tileError('collisionLayers', `unknown collision layer ${JSON.stringify(layerId)}`);
108
+ }
109
+ }
110
+ const intendedX = vx * deltaSeconds;
111
+ const intendedY = vy * deltaSeconds;
112
+
113
+ const contacts: PlatformerContact2D[] = [];
114
+ let appliedX = 0;
115
+ let appliedY = 0;
116
+
117
+ /** Report one owned frozen contact (GS-TILE-03: nested values immutable). */
118
+ const report = (cell: TileCell2D, normal: { x: number; y: number }): void => {
119
+ contacts.push(
120
+ Object.freeze({ cell, normal: Object.freeze({ ...normal }) }),
121
+ );
122
+ };
123
+
124
+ // --- Starting-overlap recovery ---
125
+ // Track the winning tile TOGETHER with its face so the reported contact
126
+ // always names the tile that supplied the resolved plane (T16-F5).
127
+ for (let iter = 0; iter < PLATFORMER_MAX_ITERATIONS; iter++) {
128
+ const overlapping = cellsInAabb(map, body, collisionLayers)
129
+ .filter((c) => isSolid(c.collision) && overlaps(body, c.aabb));
130
+ if (overlapping.length === 0) break;
131
+ // Least-penetration FACE wins; ties prefer pushing UP (negative y).
132
+ let bestAxis: 'x' | 'y' = 'y';
133
+ let bestSign = -1;
134
+ let bestDepth = Number.POSITIVE_INFINITY;
135
+ let bestCell: TileCell2D | undefined;
136
+ const consider = (axis: 'x' | 'y', sign: number, depth: number, cell: TileCell2D): void => {
137
+ // Deterministic tie rule: prefer y over x, and prefer negative-y (up)
138
+ // over positive-y. Otherwise first-evaluated (row-major) wins.
139
+ const better =
140
+ depth < bestDepth ||
141
+ (depth === bestDepth &&
142
+ (axis === 'y' ? bestAxis !== 'y' || sign === -1 : false));
143
+ if (better) {
144
+ bestAxis = axis;
145
+ bestSign = sign;
146
+ bestDepth = depth;
147
+ bestCell = cell;
148
+ }
149
+ };
150
+ for (const c of overlapping) {
151
+ const pushLeft = (c.aabb.x - body.width) - body.x;
152
+ const pushRight = (c.aabb.x + c.aabb.width) - body.x;
153
+ const pushUp = c.aabb.y - body.height - body.y;
154
+ const pushDown = (c.aabb.y + c.aabb.height) - body.y;
155
+ consider('x', -1, Math.abs(pushLeft), c);
156
+ consider('x', 1, Math.abs(pushRight), c);
157
+ consider('y', -1, Math.abs(pushUp), c);
158
+ consider('y', 1, Math.abs(pushDown), c);
159
+ }
160
+ if (bestCell === undefined) break;
161
+ // Closure mutation defeats TS narrowing here; assert the union.
162
+ const axis = bestAxis as 'x' | 'y';
163
+ body =
164
+ axis === 'x'
165
+ ? { ...body, x: body.x + bestSign * bestDepth }
166
+ : { ...body, y: body.y + bestSign * bestDepth };
167
+ // Negative-y push lands the body ON a tile top (floor); positive-y hits
168
+ // a ceiling. The contact names the winning tile.
169
+ if (axis === 'y') {
170
+ report(bestCell, { x: 0, y: bestSign });
171
+ vy = 0;
172
+ } else {
173
+ report(bestCell, { x: bestSign, y: 0 });
174
+ vx = 0;
175
+ }
176
+ }
177
+ // Final check: unresolved spawn overlap is a deterministic error.
178
+ const stillOverlapping = cellsInAabb(map, body, collisionLayers).some(
179
+ (c) => isSolid(c.collision) && overlaps(body, c.aabb),
180
+ );
181
+ if (stillOverlapping) {
182
+ throw tileError('body', 'starting overlap could not be resolved within PLATFORMER_MAX_ITERATIONS');
183
+ }
184
+
185
+ const prevBottom = body.y + body.height;
186
+ // --- X axis sweep ---
187
+ {
188
+ const dx = intendedX;
189
+ if (dx !== 0) {
190
+ const newX = body.x + dx;
191
+ let clamped = newX;
192
+ const yOverlap = (aabb: Aabb2D): boolean =>
193
+ body.y < aabb.y + aabb.height && body.y + body.height > aabb.y;
194
+ const solids = cellsInSweptBounds(map, body, { x: dx, y: 0 }, collisionLayers)
195
+ .filter((c) => isSolid(c.collision));
196
+ // Phase 1: resolve against the NEAREST blocking plane only.
197
+ if (dx > 0) {
198
+ let planeX = Number.POSITIVE_INFINITY;
199
+ for (const c of solids) {
200
+ if (!yOverlap(c.aabb)) continue;
201
+ if (body.x + body.width > c.aabb.x + PLATFORMER_SKIN) continue;
202
+ if (c.aabb.x < planeX) planeX = c.aabb.x;
203
+ }
204
+ if (planeX !== Number.POSITIVE_INFINITY) {
205
+ clamped = Math.min(clamped, planeX - body.width);
206
+ // Phase 2: emit contacts ONLY for tiles touching the winning plane.
207
+ for (const c of solids) {
208
+ if (!yOverlap(c.aabb)) continue;
209
+ if (Math.abs(c.aabb.x - planeX) > PLATFORMER_SKIN) continue;
210
+ report(c, { x: -1, y: 0 });
211
+ }
212
+ }
213
+ } else {
214
+ let planeX = Number.NEGATIVE_INFINITY;
215
+ for (const c of solids) {
216
+ if (!yOverlap(c.aabb)) continue;
217
+ if (body.x < c.aabb.x + c.aabb.width - PLATFORMER_SKIN) continue;
218
+ if (c.aabb.x + c.aabb.width > planeX) planeX = c.aabb.x + c.aabb.width;
219
+ }
220
+ if (planeX !== Number.NEGATIVE_INFINITY) {
221
+ clamped = Math.max(clamped, planeX);
222
+ for (const c of solids) {
223
+ if (!yOverlap(c.aabb)) continue;
224
+ if (Math.abs(c.aabb.x + c.aabb.width - planeX) > PLATFORMER_SKIN) continue;
225
+ report(c, { x: 1, y: 0 });
226
+ }
227
+ }
228
+ }
229
+ if (clamped !== newX) vx = 0;
230
+ appliedX = clamped - body.x;
231
+ body = { ...body, x: clamped };
232
+ }
233
+ }
234
+ // --- Y axis sweep ---
235
+ {
236
+ const dy = intendedY;
237
+ if (dy !== 0) {
238
+ const newY = body.y + dy;
239
+ let clamped = newY;
240
+ const prevBottomY = body.y + body.height;
241
+ const prevTop = body.y;
242
+ const xOverlap = (aabb: Aabb2D): boolean =>
243
+ body.x < aabb.x + aabb.width && body.x + body.width > aabb.x;
244
+ const candidates = cellsInSweptBounds(map, body, { x: 0, y: dy }, collisionLayers);
245
+ interface FloorCandidate { cell: TileCell2D }
246
+ const floors: FloorCandidate[] = [];
247
+ const ceilings: TileCell2D[] = [];
248
+ for (const c of candidates) {
249
+ const solid = isSolid(c.collision);
250
+ const oneWay = isOneWay(c.collision);
251
+ if (!solid && !oneWay) continue;
252
+ if (!xOverlap(c.aabb)) continue;
253
+ if (dy > 0) {
254
+ // Descending: floor candidate.
255
+ if (oneWay && (dropThrough || prevBottomY > c.aabb.y + ONE_WAY_TOLERANCE)) continue;
256
+ // Swept crossing: started above/on the top and reached past it.
257
+ if (prevBottomY <= c.aabb.y + PLATFORMER_SKIN && newY + body.height >= c.aabb.y) {
258
+ floors.push({ cell: c });
259
+ }
260
+ } else {
261
+ // Ascending: only solids block ceilings.
262
+ if (!solid) continue;
263
+ if (
264
+ prevTop >= c.aabb.y + c.aabb.height - PLATFORMER_SKIN &&
265
+ newY <= c.aabb.y + c.aabb.height
266
+ ) {
267
+ ceilings.push(c);
268
+ }
269
+ }
270
+ }
271
+ if (dy > 0 && floors.length > 0) {
272
+ // Nearest plane wins; report only tiles touching it.
273
+ let planeY = Number.POSITIVE_INFINITY;
274
+ for (const f of floors) {
275
+ if (f.cell.aabb.y < planeY) planeY = f.cell.aabb.y;
276
+ }
277
+ clamped = Math.min(clamped, planeY - body.height);
278
+ for (const f of floors) {
279
+ if (f.cell.aabb.y !== planeY) continue;
280
+ report(f.cell, { x: 0, y: -1 });
281
+ }
282
+ } else if (dy < 0 && ceilings.length > 0) {
283
+ let planeBottom = Number.NEGATIVE_INFINITY;
284
+ for (const c of ceilings) {
285
+ if (c.aabb.y + c.aabb.height > planeBottom) planeBottom = c.aabb.y + c.aabb.height;
286
+ }
287
+ clamped = Math.max(clamped, planeBottom);
288
+ for (const c of ceilings) {
289
+ if (Math.abs(c.aabb.y + c.aabb.height - planeBottom) > PLATFORMER_SKIN) continue;
290
+ report(c, { x: 0, y: 1 });
291
+ }
292
+ }
293
+ if (clamped !== newY) vy = 0;
294
+ appliedY = clamped - body.y;
295
+ body = { ...body, y: clamped };
296
+ }
297
+ }
298
+
299
+ // --- Floor snap ---
300
+ // GS-TILE-03: drop-through suppresses one-way floors without disabling
301
+ // snap onto solid floors.
302
+ {
303
+ const hasFloor = contacts.some((c) => c.normal.y === -1);
304
+ if (!hasFloor && vy >= 0 && snapDistance > 0) {
305
+ const probeBottom = body.y + body.height + snapDistance;
306
+ const probe: Aabb2D = { x: body.x, y: body.y + body.height, width: body.width, height: snapDistance };
307
+ const near = cellsInAabb(map, probe, collisionLayers);
308
+ let bestTop = Number.POSITIVE_INFINITY;
309
+ let bestCell: TileCell2D | undefined;
310
+ for (const c of near) {
311
+ const top = c.aabb.y;
312
+ if (top < body.y + body.height - PLATFORMER_SKIN) continue;
313
+ if (top > probeBottom) continue;
314
+ const ok = isSolid(c.collision) ||
315
+ (!dropThrough && isOneWay(c.collision) && prevBottom <= top + ONE_WAY_TOLERANCE);
316
+ if (!ok) continue;
317
+ if (top < bestTop) {
318
+ bestTop = top;
319
+ bestCell = c;
320
+ }
321
+ }
322
+ if (bestCell !== undefined && bestTop < Number.POSITIVE_INFINITY) {
323
+ const snapDelta = bestTop - body.height - body.y;
324
+ body = { ...body, y: bestTop - body.height };
325
+ report(bestCell, { x: 0, y: -1 });
326
+ vy = 0;
327
+ appliedY += snapDelta;
328
+ }
329
+ }
330
+ }
331
+
332
+ const classified = classify(contacts);
333
+ return Object.freeze({
334
+ body: Object.freeze(body),
335
+ velocity: Object.freeze({ x: vx, y: vy }),
336
+ displacement: Object.freeze({ x: appliedX, y: appliedY }),
337
+ remainingDisplacement: Object.freeze({
338
+ x: intendedX - appliedX,
339
+ y: intendedY - appliedY,
340
+ }),
341
+ contacts: Object.freeze({
342
+ floor: classified.floor,
343
+ ceiling: classified.ceiling,
344
+ leftWall: classified.leftWall,
345
+ rightWall: classified.rightWall,
346
+ all: Object.freeze(contacts),
347
+ }),
348
+ });
349
+ }
350
+
351
+ function classify(contacts: PlatformerContact2D[]): {
352
+ floor: PlatformerContact2D | undefined;
353
+ ceiling: PlatformerContact2D | undefined;
354
+ leftWall: PlatformerContact2D | undefined;
355
+ rightWall: PlatformerContact2D | undefined;
356
+ } {
357
+ let floor: PlatformerContact2D | undefined;
358
+ let ceiling: PlatformerContact2D | undefined;
359
+ let leftWall: PlatformerContact2D | undefined;
360
+ let rightWall: PlatformerContact2D | undefined;
361
+ for (const c of contacts) {
362
+ if (c.normal.y === -1 && floor === undefined) floor = c;
363
+ else if (c.normal.y === 1 && ceiling === undefined) ceiling = c;
364
+ else if (c.normal.x === 1 && leftWall === undefined) leftWall = c;
365
+ else if (c.normal.x === -1 && rightWall === undefined) rightWall = c;
366
+ }
367
+ return { floor, ceiling, leftWall, rightWall };
368
+ }
@@ -0,0 +1,136 @@
1
+ import type { Aabb2D, Point2D } from '../geometry/types';
2
+ import type { TileCell2D, TileMap2D } from './types';
3
+ import { forEachCellInSpan, makeCell } from './definitions';
4
+
5
+ /**
6
+ * Bounded cell queries (T16.2, T16-F6).
7
+ *
8
+ * Deterministic order: layer order (map declaration), then row-major within
9
+ * a layer. Spans are clamped to the finite map before iteration; queries
10
+ * outside the map return an empty frozen array. All reads flow through the
11
+ * private chunk index — each overlapped 16x16 chunk region is fetched once.
12
+ */
13
+
14
+ function clampSpan(
15
+ layer: { width: number; height: number },
16
+ minCx: number, minCy: number, maxCx: number, maxCy: number,
17
+ ): { x0: number; y0: number; x1: number; y1: number } | undefined {
18
+ const x0 = Math.max(0, minCx);
19
+ const y0 = Math.max(0, minCy);
20
+ const x1 = Math.min(layer.width - 1, maxCx);
21
+ const y1 = Math.min(layer.height - 1, maxCy);
22
+ if (x0 > x1 || y0 > y1) return undefined;
23
+ return { x0, y0, x1, y1 };
24
+ }
25
+
26
+ function collectSpan(
27
+ map: TileMap2D,
28
+ layerId: string,
29
+ span: { x0: number; y0: number; x1: number; y1: number },
30
+ out: TileCell2D[],
31
+ ): void {
32
+ forEachCellInSpan(map, layerId, span.x0, span.y0, span.x1, span.y1, (cx, cy, id) => {
33
+ out.push(makeCell(map, layerId, cx, cy, id));
34
+ });
35
+ }
36
+
37
+ /** All non-empty cells in the given layers overlapping the point. */
38
+ export function cellsAtPoint(
39
+ map: TileMap2D,
40
+ point: Point2D,
41
+ layerIds?: readonly string[],
42
+ ): readonly TileCell2D[] {
43
+ const out: TileCell2D[] = [];
44
+ const cx = Math.floor((point.x - map.origin.x) / map.cellSize.width);
45
+ const cy = Math.floor((point.y - map.origin.y) / map.cellSize.height);
46
+ for (const layer of map.layers) {
47
+ if (layerIds !== undefined && !layerIds.includes(layer.id)) continue;
48
+ const span = clampSpan(layer, cx, cy, cx, cy);
49
+ if (span === undefined) continue;
50
+ collectSpan(map, layer.id, span, out);
51
+ }
52
+ return Object.freeze(out);
53
+ }
54
+
55
+ /** All non-empty cells whose AABBs intersect the query AABB. */
56
+ export function cellsInAabb(
57
+ map: TileMap2D,
58
+ aabb: Aabb2D,
59
+ layerIds?: readonly string[],
60
+ ): readonly TileCell2D[] {
61
+ const out: TileCell2D[] = [];
62
+ const minCx = Math.floor((aabb.x - map.origin.x) / map.cellSize.width);
63
+ const minCy = Math.floor((aabb.y - map.origin.y) / map.cellSize.height);
64
+ // GS-TILE-05: half-open tile areas, exact arithmetic — no shared epsilon.
65
+ // The maximum corner selects ceil(max/cell) - 1, so exact boundaries
66
+ // exclude the next cell and tiny positive extents still select their
67
+ // own. A zero/negative extent is a point query for the cell containing
68
+ // the minimum corner (documented divergence from inclusive contact).
69
+ const maxCx = maxCellIndex(aabb.x, aabb.width, map.origin.x, map.cellSize.width);
70
+ const maxCy = maxCellIndex(aabb.y, aabb.height, map.origin.y, map.cellSize.height);
71
+ for (const layer of map.layers) {
72
+ if (layerIds !== undefined && !layerIds.includes(layer.id)) continue;
73
+ const span = clampSpan(layer, minCx, minCy, maxCx, maxCy);
74
+ if (span === undefined) continue;
75
+ collectSpan(map, layer.id, span, out);
76
+ }
77
+ return Object.freeze(out);
78
+ }
79
+
80
+ /**
81
+ * Last cell index covered by the half-open span [min, min + extent).
82
+ * Exact boundaries exclude the next cell; zero/negative extents fall back
83
+ * to the cell containing the minimum corner (point-query semantics).
84
+ */
85
+ function maxCellIndex(min: number, extent: number, origin: number, cell: number): number {
86
+ if (!(min + extent > min)) {
87
+ return Math.floor((min - origin) / cell);
88
+ }
89
+ return Math.ceil((min + extent - origin) / cell) - 1;
90
+ }
91
+
92
+ /**
93
+ * Cells overlapped by the swept bounds of a body moving by `displacement`.
94
+ * The swept bounds are the union AABB of start and end positions — bounded,
95
+ * never unbounded even for very large displacements (clamped to the map).
96
+ */
97
+ export function cellsInSweptBounds(
98
+ map: TileMap2D,
99
+ body: Aabb2D,
100
+ displacement: { x: number; y: number },
101
+ layerIds?: readonly string[],
102
+ ): readonly TileCell2D[] {
103
+ const minX = Math.min(body.x, body.x + displacement.x);
104
+ const minY = Math.min(body.y, body.y + displacement.y);
105
+ return cellsInAabb(
106
+ map,
107
+ { x: minX, y: minY, width: body.width + Math.abs(displacement.x), height: body.height + Math.abs(displacement.y) },
108
+ layerIds,
109
+ );
110
+ }
111
+
112
+ /**
113
+ * Non-empty cells inside the visible region (camera bounds + overscan).
114
+ * Same deterministic ordering as `cellsInAabb`.
115
+ */
116
+ export function visibleCells(
117
+ map: TileMap2D,
118
+ visibleBounds: Aabb2D,
119
+ overscanCells: number,
120
+ layerIds?: readonly string[],
121
+ ): readonly TileCell2D[] {
122
+ const out: TileCell2D[] = [];
123
+ const minCx = Math.floor((visibleBounds.x - map.origin.x) / map.cellSize.width) - overscanCells;
124
+ const minCy = Math.floor((visibleBounds.y - map.origin.y) / map.cellSize.height) - overscanCells;
125
+ const maxCx =
126
+ Math.ceil((visibleBounds.x + visibleBounds.width - map.origin.x) / map.cellSize.width) - 1 + overscanCells;
127
+ const maxCy =
128
+ Math.ceil((visibleBounds.y + visibleBounds.height - map.origin.y) / map.cellSize.height) - 1 + overscanCells;
129
+ for (const layer of map.layers) {
130
+ if (layerIds !== undefined && !layerIds.includes(layer.id)) continue;
131
+ const span = clampSpan(layer, minCx, minCy, maxCx, maxCy);
132
+ if (span === undefined) continue;
133
+ collectSpan(map, layer.id, span, out);
134
+ }
135
+ return Object.freeze(out);
136
+ }
@@ -0,0 +1,165 @@
1
+ import { tileError } from './errors';
2
+ import type { TileMapInput2D, TileSet2D } from './types';
3
+
4
+ /**
5
+ * Narrow Tiled JSON adapter (T16.5, hardened per T16-F6).
6
+ *
7
+ * Accepts ONLY the finite orthogonal subset used by the reference level:
8
+ * - `orientation: "orthogonal"`, `infinite: false`
9
+ * - `layers` of `type: "tilelayer"` with inline numeric `data` (no base64,
10
+ * no compression, no chunks)
11
+ * - EVERY other layer type (objectgroup, group, imagelayer, ...) is rejected
12
+ * with its exact source path — nothing silently disappears
13
+ * - Nonzero `offsetx`/`offsety` are rejected (v1 has no layer offsets)
14
+ * - No per-tile flips (high GID bits) — rejected with a source path
15
+ *
16
+ * The root value, options object, and every layer entry are validated BEFORE
17
+ * any property access, so malformed input always surfaces as a structured
18
+ * TileMapError instead of a TypeError. Raw Tiled JSON never enters
19
+ * fixed-step or rendering hot paths; the output is a normalized Gamekit map
20
+ * input.
21
+ */
22
+ export interface TiledAdapterOptions {
23
+ /**
24
+ * Map Tiled global tile ids to Gamekit tile names. The referenced names
25
+ * must exist on the provided tileset.
26
+ */
27
+ readonly gidToTileName: Readonly<Record<number, string>>;
28
+ /** Layers to convert, in order. Defaults to every tilelayer. */
29
+ readonly layerIds?: readonly string[];
30
+ /** Layers that contribute collision; unlisted layers stay decorative. */
31
+ readonly collidableLayers?: readonly string[];
32
+ }
33
+
34
+ function reject(cond: boolean, path: string, message: string): void {
35
+ if (cond) throw tileError(path, message);
36
+ }
37
+
38
+ function assertPlainObject(value: unknown, path: string): asserts value is Record<string, unknown> {
39
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
40
+ throw tileError(path, `expected an object; got ${value === null ? 'null' : Array.isArray(value) ? 'array' : typeof value}`);
41
+ }
42
+ }
43
+
44
+ export function parseTiledMap2D(
45
+ json: unknown,
46
+ tileset: TileSet2D,
47
+ options: TiledAdapterOptions,
48
+ ): TileMapInput2D {
49
+ // Validate inputs BEFORE any property access (T16-F6).
50
+ assertPlainObject(json, 'root');
51
+ assertPlainObject(options as unknown, 'options');
52
+ const gidMap = options.gidToTileName as Record<string, string> | null;
53
+ if (gidMap === null || typeof gidMap !== 'object' || Array.isArray(gidMap)) {
54
+ throw tileError('options.gidToTileName', 'must be an object mapping gids to tile names');
55
+ }
56
+ if (options.layerIds !== undefined) {
57
+ if (!Array.isArray(options.layerIds)) throw tileError('options.layerIds', 'must be an array of layer names');
58
+ for (let i = 0; i < options.layerIds.length; i++) {
59
+ if (typeof options.layerIds[i] !== 'string') {
60
+ throw tileError(`options.layerIds[${i}]`, 'must be a string');
61
+ }
62
+ }
63
+ }
64
+ if (options.collidableLayers !== undefined) {
65
+ if (!Array.isArray(options.collidableLayers)) throw tileError('options.collidableLayers', 'must be an array of layer names');
66
+ for (let i = 0; i < options.collidableLayers.length; i++) {
67
+ if (typeof options.collidableLayers[i] !== 'string') {
68
+ throw tileError(`options.collidableLayers[${i}]`, 'must be a string');
69
+ }
70
+ }
71
+ }
72
+ if (tileset === undefined || tileset.idOfName === undefined) {
73
+ throw tileError('tileset', 'must be a value produced by defineTileSet2D');
74
+ }
75
+
76
+ const root = json as Record<string, unknown>;
77
+ reject(root.orientation !== 'orthogonal', 'root.orientation', `only "orthogonal" is supported; got ${JSON.stringify(root.orientation)}`);
78
+ reject(root.infinite === true, 'root.infinite', 'infinite maps are not supported in v1');
79
+ const width = root.width;
80
+ const height = root.height;
81
+ reject(typeof width !== 'number' || !Number.isSafeInteger(width) || width <= 0, 'root.width', 'must be a positive integer');
82
+ reject(typeof height !== 'number' || !Number.isSafeInteger(height) || height <= 0, 'root.height', 'must be a positive integer');
83
+ const tw = root.tilewidth;
84
+ const th = root.tileheight;
85
+ reject(typeof tw !== 'number' || !Number.isFinite(tw) || tw <= 0, 'root.tilewidth', 'must be finite and > 0');
86
+ reject(typeof th !== 'number' || !Number.isFinite(th) || th <= 0, 'root.tileheight', 'must be finite and > 0');
87
+
88
+ reject(!Array.isArray(root.layers), 'root.layers', 'must be an array of layer objects');
89
+ const layersRaw = root.layers as unknown[];
90
+ const wanted = options.layerIds;
91
+ const collidableLayers = options.collidableLayers;
92
+
93
+ const layers: TileMapInput2D['layers'] = [];
94
+ layersRaw.forEach((rawLayerUnknown, index) => {
95
+ assertPlainObject(rawLayerUnknown, `layers[${index}]`);
96
+ const layer = rawLayerUnknown as Record<string, unknown>;
97
+ const rawType = layer.type;
98
+ reject(
99
+ typeof rawType !== 'string',
100
+ `layers[${index}].type`,
101
+ `must be a string layer type; got ${JSON.stringify(rawType)}`,
102
+ );
103
+ // T16-F6: tilelayer is the ONLY accepted type — every other layer type
104
+ // (objectgroup, group, imagelayer, unknown future types) is rejected
105
+ // with its exact source path instead of being silently skipped.
106
+ reject(
107
+ rawType !== 'tilelayer',
108
+ `layers[${index}](${JSON.stringify(String(layer.name ?? ''))}).type`,
109
+ `"${String(rawType)}" layers are not supported in v1`,
110
+ );
111
+ const name = typeof layer.name === 'string' && layer.name.length > 0 ? layer.name : `layer${index}`;
112
+ if (wanted !== undefined && !wanted.includes(name)) return;
113
+ const p = `layers[${index}](${JSON.stringify(name)})`;
114
+ // T16-F6: nonzero layer offsets change cell placement — rejected, not ignored.
115
+ reject(layer.offsetx !== undefined && layer.offsetx !== 0, `${p}.offsetx`, `nonzero layer offsets are not supported in v1; got ${JSON.stringify(layer.offsetx)}`);
116
+ reject(layer.offsety !== undefined && layer.offsety !== 0, `${p}.offsety`, `nonzero layer offsets are not supported in v1; got ${JSON.stringify(layer.offsety)}`);
117
+ reject(layer.encoding !== undefined && layer.encoding !== 'csv', `${p}.encoding`, `only inline CSV-free numeric arrays are supported; got ${JSON.stringify(layer.encoding)}`);
118
+ reject(layer.compression !== undefined, `${p}.compression`, 'compressed layers are not supported in v1');
119
+ reject(layer.chunks !== undefined, `${p}.chunks`, 'chunked (infinite) layers are not supported in v1');
120
+ reject(!Array.isArray(layer.data), `${p}.data`, 'must be an inline array of gids');
121
+ const layerWidth = layer.width;
122
+ const layerHeight = layer.height;
123
+ reject(layerWidth !== width, `${p}.width`, `must equal map width ${String(width)}`);
124
+ reject(layerHeight !== height, `${p}.height`, `must equal map height ${String(height)}`);
125
+
126
+ const gidData = layer.data as unknown[];
127
+ reject(gidData.length !== (width as number) * (height as number), `${p}.data`, `length must equal width*height (${String(width)}*${String(height)})`);
128
+
129
+ // Convert gids -> Gamekit ids. Flip bits unsupported.
130
+ const out: number[] = new Array(gidData.length);
131
+ for (let i = 0; i < gidData.length; i++) {
132
+ const rawUnknown = gidData[i];
133
+ const raw = typeof rawUnknown === 'number' ? rawUnknown : -1;
134
+ reject(!Number.isSafeInteger(raw) || raw < 0, `${p}.data[${i}]`, `must be a non-negative safe integer; got ${JSON.stringify(rawUnknown)}`);
135
+ if (raw === 0) {
136
+ out[i] = 0;
137
+ continue;
138
+ }
139
+ const FLIP_BITS = 0x80000000 | 0x40000000 | 0x20000000;
140
+ reject((raw & FLIP_BITS) !== 0, `${p}.data[${i}]`, 'per-tile flip/rotation flags are not supported in v1');
141
+ const mapped = gidMap[raw];
142
+ reject(mapped === undefined, `${p}.data[${i}]`, `gid ${raw} has no mapping in gidToTileName`);
143
+ const gamekitId = tileset.idOfName[mapped as string] ?? -1;
144
+ reject(gamekitId === -1, `${p}.data[${i}]`, `mapped tile name ${JSON.stringify(mapped)} is missing from the bound tileset`);
145
+ out[i] = gamekitId;
146
+ }
147
+
148
+ layers.push({
149
+ id: name,
150
+ width: width as number,
151
+ height: height as number,
152
+ data: out,
153
+ collidable: collidableLayers ? collidableLayers.includes(name) : true,
154
+ });
155
+ });
156
+
157
+ reject(layers.length === 0, 'layers', 'no tilelayers matched; at least one is required');
158
+
159
+ return {
160
+ cellSize: { width: tw as number, height: th as number },
161
+ tileset,
162
+ layers,
163
+ origin: { x: 0, y: 0 },
164
+ };
165
+ }