@woosh/meep-engine 2.138.20 → 2.140.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 (584) hide show
  1. package/package.json +1 -1
  2. package/src/core/bvh2/bvh3/query/bvh_query_user_data_overlaps_aabb.d.ts +3 -3
  3. package/src/core/bvh2/bvh3/query/bvh_query_user_data_overlaps_aabb.d.ts.map +1 -1
  4. package/src/core/bvh2/bvh3/query/bvh_query_user_data_overlaps_aabb.js +4 -4
  5. package/src/core/collection/PairUint32Map.d.ts +100 -0
  6. package/src/core/collection/PairUint32Map.d.ts.map +1 -0
  7. package/src/core/collection/PairUint32Map.js +321 -0
  8. package/src/core/collection/Uint32Map.d.ts +119 -0
  9. package/src/core/collection/Uint32Map.d.ts.map +1 -0
  10. package/src/core/collection/Uint32Map.js +345 -0
  11. package/src/core/collection/array/array_shuffle.d.ts +10 -3
  12. package/src/core/collection/array/array_shuffle.d.ts.map +1 -1
  13. package/src/core/collection/array/array_shuffle.js +27 -22
  14. package/src/core/collection/heap/FibonacciHeap.d.ts +195 -0
  15. package/src/core/collection/heap/FibonacciHeap.d.ts.map +1 -0
  16. package/src/core/collection/heap/FibonacciHeap.js +586 -0
  17. package/src/core/collection/heap/Uint32Heap.js +1 -1
  18. package/src/core/collection/heap/Uint32Heap4.d.ts +169 -0
  19. package/src/core/collection/heap/Uint32Heap4.d.ts.map +1 -0
  20. package/src/core/collection/heap/Uint32Heap4.js +490 -0
  21. package/src/core/geom/3d/aabb/aabb3_transform_oriented.d.ts +30 -0
  22. package/src/core/geom/3d/aabb/aabb3_transform_oriented.d.ts.map +1 -0
  23. package/src/core/geom/3d/aabb/aabb3_transform_oriented.js +93 -0
  24. package/src/core/geom/3d/line/line3_closest_points_segment_segment.d.ts +27 -0
  25. package/src/core/geom/3d/line/line3_closest_points_segment_segment.d.ts.map +1 -0
  26. package/src/core/geom/3d/line/line3_closest_points_segment_segment.js +88 -0
  27. package/src/core/geom/3d/quaternion/quat3_to_matrix3.d.ts +54 -0
  28. package/src/core/geom/3d/quaternion/quat3_to_matrix3.d.ts.map +1 -0
  29. package/src/core/geom/3d/quaternion/quat3_to_matrix3.js +69 -0
  30. package/src/core/geom/3d/shape/AbstractShape3D.d.ts +24 -2
  31. package/src/core/geom/3d/shape/AbstractShape3D.d.ts.map +1 -1
  32. package/src/core/geom/3d/shape/AbstractShape3D.js +24 -1
  33. package/src/core/geom/3d/shape/BoxShape3D.d.ts +61 -0
  34. package/src/core/geom/3d/shape/BoxShape3D.d.ts.map +1 -0
  35. package/src/core/geom/3d/shape/BoxShape3D.js +158 -0
  36. package/src/core/geom/3d/shape/CapsuleShape3D.d.ts +11 -0
  37. package/src/core/geom/3d/shape/CapsuleShape3D.d.ts.map +1 -1
  38. package/src/core/geom/3d/shape/CapsuleShape3D.js +12 -0
  39. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts +148 -0
  40. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -0
  41. package/src/core/geom/3d/shape/HeightMapShape3D.js +451 -0
  42. package/src/core/geom/3d/shape/MeshShape3D.d.ts +210 -0
  43. package/src/core/geom/3d/shape/MeshShape3D.d.ts.map +1 -0
  44. package/src/core/geom/3d/shape/MeshShape3D.js +593 -0
  45. package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
  46. package/src/core/geom/3d/shape/TransformedShape3D.js +46 -2
  47. package/src/core/geom/3d/shape/Triangle3D.d.ts +95 -0
  48. package/src/core/geom/3d/shape/Triangle3D.d.ts.map +1 -0
  49. package/src/core/geom/3d/shape/Triangle3D.js +318 -0
  50. package/src/core/geom/3d/shape/UnionShape3D.js +13 -0
  51. package/src/core/geom/3d/shape/UnitCubeShape3D.d.ts +37 -9
  52. package/src/core/geom/3d/shape/UnitCubeShape3D.d.ts.map +1 -1
  53. package/src/core/geom/3d/shape/UnitCubeShape3D.js +45 -98
  54. package/src/core/geom/3d/shape/UnitSphereShape3D.d.ts +10 -0
  55. package/src/core/geom/3d/shape/UnitSphereShape3D.d.ts.map +1 -1
  56. package/src/core/geom/3d/shape/UnitSphereShape3D.js +11 -0
  57. package/src/core/geom/3d/shape/shape_mesh_from_geometry.d.ts +30 -0
  58. package/src/core/geom/3d/shape/shape_mesh_from_geometry.d.ts.map +1 -0
  59. package/src/core/geom/3d/shape/shape_mesh_from_geometry.js +64 -0
  60. package/src/core/geom/3d/shape/util/shape3d_voxelize_to_grid.d.ts +61 -0
  61. package/src/core/geom/3d/shape/util/shape3d_voxelize_to_grid.d.ts.map +1 -0
  62. package/src/core/geom/3d/shape/util/shape3d_voxelize_to_grid.js +148 -0
  63. package/src/core/geom/3d/tetrahedra/compute_tetrahedral_mesh_from_surface.d.ts +39 -0
  64. package/src/core/geom/3d/tetrahedra/compute_tetrahedral_mesh_from_surface.d.ts.map +1 -0
  65. package/src/core/geom/3d/tetrahedra/compute_tetrahedral_mesh_from_surface.js +147 -0
  66. package/src/core/geom/3d/tetrahedra/compute_tetrahedron_quality.d.ts +15 -0
  67. package/src/core/geom/3d/tetrahedra/compute_tetrahedron_quality.d.ts.map +1 -0
  68. package/src/core/geom/3d/tetrahedra/compute_tetrahedron_quality.js +22 -0
  69. package/src/core/geom/3d/tetrahedra/prototype_tetrahedrize_mesh.d.ts +2 -0
  70. package/src/core/geom/3d/tetrahedra/prototype_tetrahedrize_mesh.d.ts.map +1 -0
  71. package/src/core/geom/3d/tetrahedra/prototype_tetrahedrize_mesh.js +671 -0
  72. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.d.ts +28 -0
  73. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.d.ts.map +1 -0
  74. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.js +48 -0
  75. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_carve_outside_surface.d.ts +26 -0
  76. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_carve_outside_surface.d.ts.map +1 -0
  77. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_carve_outside_surface.js +222 -0
  78. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_find_tets_around_edge.d.ts +34 -0
  79. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_find_tets_around_edge.d.ts.map +1 -0
  80. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_find_tets_around_edge.js +146 -0
  81. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_23.d.ts +36 -0
  82. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_23.d.ts.map +1 -0
  83. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_23.js +232 -0
  84. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_32.d.ts +33 -0
  85. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_32.d.ts.map +1 -0
  86. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_32.js +255 -0
  87. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.d.ts +68 -0
  88. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.d.ts.map +1 -0
  89. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.js +387 -0
  90. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.d.ts +35 -0
  91. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.d.ts.map +1 -0
  92. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.js +140 -0
  93. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.d.ts +31 -0
  94. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.d.ts.map +1 -0
  95. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.js +97 -0
  96. package/src/core/geom/3d/tetrahedra/tetrahedron_compute_quality.d.ts +32 -0
  97. package/src/core/geom/3d/tetrahedra/tetrahedron_compute_quality.d.ts.map +1 -0
  98. package/src/core/geom/3d/tetrahedra/tetrahedron_compute_quality.js +66 -0
  99. package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.d.ts +41 -0
  100. package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.d.ts.map +1 -1
  101. package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.js +124 -13
  102. package/src/core/geom/3d/topology/struct/binary/BinaryTopology.d.ts +134 -0
  103. package/src/core/geom/3d/topology/struct/binary/BinaryTopology.d.ts.map +1 -1
  104. package/src/core/geom/3d/topology/struct/binary/BinaryTopology.js +276 -3
  105. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.d.ts +17 -0
  106. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.d.ts.map +1 -0
  107. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.js +135 -0
  108. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compact.d.ts +14 -0
  109. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compact.d.ts.map +1 -0
  110. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compact.js +177 -0
  111. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_decouple.d.ts.map +1 -1
  112. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_decouple.js +20 -4
  113. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts.map +1 -1
  114. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js +5 -3
  115. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_create.d.ts.map +1 -1
  116. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_create.js +9 -0
  117. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_get_or_create.d.ts.map +1 -1
  118. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_get_or_create.js +21 -45
  119. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill.d.ts.map +1 -1
  120. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill.js +7 -1
  121. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill_parallels.d.ts +8 -6
  122. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill_parallels.d.ts.map +1 -1
  123. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill_parallels.js +8 -6
  124. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_kill_short_edges.d.ts +22 -0
  125. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_kill_short_edges.d.ts.map +1 -0
  126. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_kill_short_edges.js +73 -0
  127. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_replace.d.ts.map +1 -1
  128. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_replace.js +51 -1
  129. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_get.d.ts +10 -0
  130. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_get.d.ts.map +1 -0
  131. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_get.js +42 -0
  132. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_sample_interior_grid_points.d.ts +28 -0
  133. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_sample_interior_grid_points.d.ts.map +1 -0
  134. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_sample_interior_grid_points.js +227 -0
  135. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_walk_boundary_loops.d.ts +13 -0
  136. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_walk_boundary_loops.d.ts.map +1 -0
  137. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_walk_boundary_loops.js +108 -0
  138. package/src/core/geom/3d/topology/struct/binary/query/bt_query_edge_is_boundary.d.ts +11 -0
  139. package/src/core/geom/3d/topology/struct/binary/query/bt_query_edge_is_boundary.d.ts.map +1 -0
  140. package/src/core/geom/3d/topology/struct/binary/query/bt_query_edge_is_boundary.js +20 -0
  141. package/src/core/geom/3d/triangle/triangle_mesh_compute_signed_volume.d.ts +20 -0
  142. package/src/core/geom/3d/triangle/triangle_mesh_compute_signed_volume.d.ts.map +1 -0
  143. package/src/core/geom/3d/triangle/triangle_mesh_compute_signed_volume.js +38 -0
  144. package/src/core/geom/3d/triangle/v3_compute_triangle_normal.d.ts +2 -2
  145. package/src/core/geom/3d/triangle/v3_compute_triangle_normal.d.ts.map +1 -1
  146. package/src/core/geom/3d/triangle/v3_compute_triangle_normal.js +1 -1
  147. package/src/core/geom/vec3/v3_dot_array_array.d.ts +3 -3
  148. package/src/core/geom/vec3/v3_dot_array_array.d.ts.map +1 -1
  149. package/src/core/geom/vec3/v3_dot_array_array.js +2 -2
  150. package/src/core/geom/vec3/v3_negate_array.d.ts +3 -3
  151. package/src/core/geom/vec3/v3_negate_array.d.ts.map +1 -1
  152. package/src/core/geom/vec3/v3_negate_array.js +2 -2
  153. package/src/core/geom/vec3/v3_quat3_apply.d.ts +29 -0
  154. package/src/core/geom/vec3/v3_quat3_apply.d.ts.map +1 -0
  155. package/src/core/geom/vec3/v3_quat3_apply.js +39 -0
  156. package/src/core/geom/vec3/v3_quat3_apply_inverse.d.ts +30 -0
  157. package/src/core/geom/vec3/v3_quat3_apply_inverse.d.ts.map +1 -0
  158. package/src/core/geom/vec3/v3_quat3_apply_inverse.js +41 -0
  159. package/src/core/geom/vec3/v3_triple_cross_product.d.ts +32 -0
  160. package/src/core/geom/vec3/v3_triple_cross_product.d.ts.map +1 -0
  161. package/src/core/geom/vec3/v3_triple_cross_product.js +45 -0
  162. package/src/core/graph/csr/CSRGraph.d.ts +168 -0
  163. package/src/core/graph/csr/CSRGraph.d.ts.map +1 -0
  164. package/src/core/graph/csr/CSRGraph.js +319 -0
  165. package/src/core/graph/metis/cluster_mesh_metis.d.ts +12 -0
  166. package/src/core/graph/metis/cluster_mesh_metis.d.ts.map +1 -1
  167. package/src/core/graph/metis/cluster_mesh_metis.js +12 -0
  168. package/src/core/graph/metis/metis.d.ts +19 -0
  169. package/src/core/graph/metis/metis.d.ts.map +1 -1
  170. package/src/core/graph/metis/metis.js +20 -0
  171. package/src/core/graph/metis/metis_cluster_bs.d.ts +11 -0
  172. package/src/core/graph/metis/metis_cluster_bs.d.ts.map +1 -1
  173. package/src/core/graph/metis/metis_cluster_bs.js +11 -0
  174. package/src/core/graph/metis/metis_options.d.ts +17 -2
  175. package/src/core/graph/metis/metis_options.d.ts.map +1 -1
  176. package/src/core/graph/metis/metis_options.js +17 -2
  177. package/src/core/graph/metis/native/MetisGraph.d.ts +144 -0
  178. package/src/core/graph/metis/native/MetisGraph.d.ts.map +1 -0
  179. package/src/core/graph/metis/native/MetisGraph.js +212 -0
  180. package/src/core/graph/metis/native/bisection/BisectionScratch.d.ts +72 -0
  181. package/src/core/graph/metis/native/bisection/BisectionScratch.d.ts.map +1 -0
  182. package/src/core/graph/metis/native/bisection/BisectionScratch.js +101 -0
  183. package/src/core/graph/metis/native/bisection/bisect_graph.d.ts +37 -0
  184. package/src/core/graph/metis/native/bisection/bisect_graph.d.ts.map +1 -0
  185. package/src/core/graph/metis/native/bisection/bisect_graph.js +100 -0
  186. package/src/core/graph/metis/native/bisection/compute_2way_params.d.ts +15 -0
  187. package/src/core/graph/metis/native/bisection/compute_2way_params.d.ts.map +1 -0
  188. package/src/core/graph/metis/native/bisection/compute_2way_params.js +84 -0
  189. package/src/core/graph/metis/native/bisection/fm_2way.d.ts +30 -0
  190. package/src/core/graph/metis/native/bisection/fm_2way.d.ts.map +1 -0
  191. package/src/core/graph/metis/native/bisection/fm_2way.js +290 -0
  192. package/src/core/graph/metis/native/bisection/grow_bisection.d.ts +23 -0
  193. package/src/core/graph/metis/native/bisection/grow_bisection.d.ts.map +1 -0
  194. package/src/core/graph/metis/native/bisection/grow_bisection.js +137 -0
  195. package/src/core/graph/metis/native/bisection/split_graph_two_way.d.ts +28 -0
  196. package/src/core/graph/metis/native/bisection/split_graph_two_way.d.ts.map +1 -0
  197. package/src/core/graph/metis/native/bisection/split_graph_two_way.js +119 -0
  198. package/src/core/graph/metis/native/coarsen/coarsen_graph.d.ts +20 -0
  199. package/src/core/graph/metis/native/coarsen/coarsen_graph.d.ts.map +1 -0
  200. package/src/core/graph/metis/native/coarsen/coarsen_graph.js +94 -0
  201. package/src/core/graph/metis/native/coarsen/create_coarse_graph.d.ts +24 -0
  202. package/src/core/graph/metis/native/coarsen/create_coarse_graph.d.ts.map +1 -0
  203. package/src/core/graph/metis/native/coarsen/create_coarse_graph.js +158 -0
  204. package/src/core/graph/metis/native/coarsen/match_shem.d.ts +41 -0
  205. package/src/core/graph/metis/native/coarsen/match_shem.d.ts.map +1 -0
  206. package/src/core/graph/metis/native/coarsen/match_shem.js +175 -0
  207. package/src/core/graph/metis/native/initial/initial_kway_bfs.d.ts +24 -0
  208. package/src/core/graph/metis/native/initial/initial_kway_bfs.d.ts.map +1 -0
  209. package/src/core/graph/metis/native/initial/initial_kway_bfs.js +122 -0
  210. package/src/core/graph/metis/native/initial/initial_kway_recursive_bisection.d.ts +29 -0
  211. package/src/core/graph/metis/native/initial/initial_kway_recursive_bisection.d.ts.map +1 -0
  212. package/src/core/graph/metis/native/initial/initial_kway_recursive_bisection.js +170 -0
  213. package/src/core/graph/metis/native/metis_partition_kway.d.ts +41 -0
  214. package/src/core/graph/metis/native/metis_partition_kway.d.ts.map +1 -0
  215. package/src/core/graph/metis/native/metis_partition_kway.js +126 -0
  216. package/src/core/graph/metis/native/refine/IndexedFloatMaxHeap.d.ts +62 -0
  217. package/src/core/graph/metis/native/refine/IndexedFloatMaxHeap.d.ts.map +1 -0
  218. package/src/core/graph/metis/native/refine/IndexedFloatMaxHeap.js +261 -0
  219. package/src/core/graph/metis/native/refine/RefinementScratch.d.ts +45 -0
  220. package/src/core/graph/metis/native/refine/RefinementScratch.d.ts.map +1 -0
  221. package/src/core/graph/metis/native/refine/RefinementScratch.js +53 -0
  222. package/src/core/graph/metis/native/refine/compute_kway_params.d.ts +18 -0
  223. package/src/core/graph/metis/native/refine/compute_kway_params.d.ts.map +1 -0
  224. package/src/core/graph/metis/native/refine/compute_kway_params.js +138 -0
  225. package/src/core/graph/metis/native/refine/fm_kway.d.ts +63 -0
  226. package/src/core/graph/metis/native/refine/fm_kway.d.ts.map +1 -0
  227. package/src/core/graph/metis/native/refine/fm_kway.js +462 -0
  228. package/src/core/graph/metis/native/refine/project_kway.d.ts +22 -0
  229. package/src/core/graph/metis/native/refine/project_kway.d.ts.map +1 -0
  230. package/src/core/graph/metis/native/refine/project_kway.js +43 -0
  231. package/src/core/graph/metis/native/refine/refine_kway.d.ts +34 -0
  232. package/src/core/graph/metis/native/refine/refine_kway.d.ts.map +1 -0
  233. package/src/core/graph/metis/native/refine/refine_kway.js +43 -0
  234. package/src/core/math/linalg/eigen/matrix_householder_in_place.d.ts +2 -2
  235. package/src/core/math/linalg/eigen/matrix_householder_in_place.js +2 -2
  236. package/src/core/math/linalg/eigen/matrix_qr_in_place.d.ts +6 -4
  237. package/src/core/math/linalg/eigen/matrix_qr_in_place.d.ts.map +1 -1
  238. package/src/core/math/linalg/eigen/matrix_qr_in_place.js +69 -23
  239. package/src/engine/EngineHarness.d.ts +3 -1
  240. package/src/engine/EngineHarness.d.ts.map +1 -1
  241. package/src/engine/EngineHarness.js +3 -0
  242. package/src/engine/control/first-person/DESIGN.md +30 -6
  243. package/src/engine/control/first-person/DESIGN_EXTENSIONS.md +563 -0
  244. package/src/engine/control/first-person/FirstPersonPlayerController.d.ts +115 -9
  245. package/src/engine/control/first-person/FirstPersonPlayerController.d.ts.map +1 -1
  246. package/src/engine/control/first-person/FirstPersonPlayerController.js +211 -176
  247. package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts +601 -8
  248. package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts.map +1 -1
  249. package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.js +349 -8
  250. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts +319 -23
  251. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts.map +1 -1
  252. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +1789 -799
  253. package/src/engine/control/first-person/TODO.md +173 -0
  254. package/src/engine/control/first-person/abilities/Ability.d.ts +101 -0
  255. package/src/engine/control/first-person/abilities/Ability.d.ts.map +1 -0
  256. package/src/engine/control/first-person/abilities/Ability.js +119 -0
  257. package/src/engine/control/first-person/abilities/AbilitySet.d.ts +86 -0
  258. package/src/engine/control/first-person/abilities/AbilitySet.d.ts.map +1 -0
  259. package/src/engine/control/first-person/abilities/AbilitySet.js +185 -0
  260. package/src/engine/control/first-person/abilities/LedgeGrab.d.ts +62 -0
  261. package/src/engine/control/first-person/abilities/LedgeGrab.d.ts.map +1 -0
  262. package/src/engine/control/first-person/abilities/LedgeGrab.js +199 -0
  263. package/src/engine/control/first-person/abilities/Mantle.d.ts +45 -0
  264. package/src/engine/control/first-person/abilities/Mantle.d.ts.map +1 -0
  265. package/src/engine/control/first-person/abilities/Mantle.js +188 -0
  266. package/src/engine/control/first-person/abilities/Slide.d.ts +33 -0
  267. package/src/engine/control/first-person/abilities/Slide.d.ts.map +1 -0
  268. package/src/engine/control/first-person/abilities/Slide.js +166 -0
  269. package/src/engine/control/first-person/abilities/WallJump.d.ts +45 -0
  270. package/src/engine/control/first-person/abilities/WallJump.d.ts.map +1 -0
  271. package/src/engine/control/first-person/abilities/WallJump.js +131 -0
  272. package/src/engine/control/first-person/abilities/WallRun.d.ts +44 -0
  273. package/src/engine/control/first-person/abilities/WallRun.d.ts.map +1 -0
  274. package/src/engine/control/first-person/abilities/WallRun.js +180 -0
  275. package/src/engine/control/first-person/composer/EyeOffsetStack.d.ts +49 -0
  276. package/src/engine/control/first-person/composer/EyeOffsetStack.d.ts.map +1 -0
  277. package/src/engine/control/first-person/composer/EyeOffsetStack.js +60 -0
  278. package/src/engine/control/first-person/mastery/BreathRhythmEvaluator.d.ts +100 -0
  279. package/src/engine/control/first-person/mastery/BreathRhythmEvaluator.d.ts.map +1 -0
  280. package/src/engine/control/first-person/mastery/BreathRhythmEvaluator.js +133 -0
  281. package/src/engine/control/first-person/mastery/DecisionPoint.d.ts +10 -0
  282. package/src/engine/control/first-person/mastery/DecisionPoint.d.ts.map +1 -0
  283. package/src/engine/control/first-person/mastery/DecisionPoint.js +30 -0
  284. package/src/engine/control/first-person/mastery/FootAsymmetryTurnEvaluator.d.ts +61 -0
  285. package/src/engine/control/first-person/mastery/FootAsymmetryTurnEvaluator.d.ts.map +1 -0
  286. package/src/engine/control/first-person/mastery/FootAsymmetryTurnEvaluator.js +109 -0
  287. package/src/engine/control/first-person/mastery/MasteryEvaluator.d.ts +40 -0
  288. package/src/engine/control/first-person/mastery/MasteryEvaluator.d.ts.map +1 -0
  289. package/src/engine/control/first-person/mastery/MasteryEvaluator.js +45 -0
  290. package/src/engine/control/first-person/mastery/MasteryScore.d.ts +68 -0
  291. package/src/engine/control/first-person/mastery/MasteryScore.d.ts.map +1 -0
  292. package/src/engine/control/first-person/mastery/MasteryScore.js +100 -0
  293. package/src/engine/control/first-person/mastery/MasterySet.d.ts +60 -0
  294. package/src/engine/control/first-person/mastery/MasterySet.d.ts.map +1 -0
  295. package/src/engine/control/first-person/mastery/MasterySet.js +86 -0
  296. package/src/engine/control/first-person/mastery/SlideInitiationTimingEvaluator.d.ts +58 -0
  297. package/src/engine/control/first-person/mastery/SlideInitiationTimingEvaluator.d.ts.map +1 -0
  298. package/src/engine/control/first-person/mastery/SlideInitiationTimingEvaluator.js +83 -0
  299. package/src/engine/control/first-person/mastery/StrideTimingJumpEvaluator.d.ts +69 -0
  300. package/src/engine/control/first-person/mastery/StrideTimingJumpEvaluator.d.ts.map +1 -0
  301. package/src/engine/control/first-person/mastery/StrideTimingJumpEvaluator.js +109 -0
  302. package/src/engine/control/first-person/math/Spring.d.ts +56 -0
  303. package/src/engine/control/first-person/math/Spring.d.ts.map +1 -0
  304. package/src/engine/control/first-person/math/Spring.js +71 -0
  305. package/src/engine/control/first-person/math/computeLRCBreathRate.d.ts +26 -0
  306. package/src/engine/control/first-person/math/computeLRCBreathRate.d.ts.map +1 -0
  307. package/src/engine/control/first-person/math/computeLRCBreathRate.js +41 -0
  308. package/src/engine/control/first-person/math/computeMassRatios.d.ts +35 -0
  309. package/src/engine/control/first-person/math/computeMassRatios.d.ts.map +1 -0
  310. package/src/engine/control/first-person/math/computeMassRatios.js +44 -0
  311. package/src/engine/control/first-person/pose/FirstPersonPose.d.ts +31 -1
  312. package/src/engine/control/first-person/pose/FirstPersonPose.d.ts.map +1 -1
  313. package/src/engine/control/first-person/pose/FirstPersonPose.js +49 -3
  314. package/src/engine/control/first-person/pose/FirstPersonPosture.d.ts +7 -0
  315. package/src/engine/control/first-person/pose/FirstPersonPosture.d.ts.map +1 -0
  316. package/src/engine/control/first-person/pose/FirstPersonPosture.js +27 -0
  317. package/src/engine/control/first-person/prototype_first_person_controller.js +637 -120
  318. package/src/engine/control/first-person/sensors/FirstPersonSensors.d.ts +58 -0
  319. package/src/engine/control/first-person/sensors/FirstPersonSensors.d.ts.map +1 -0
  320. package/src/engine/control/first-person/sensors/FirstPersonSensors.js +77 -0
  321. package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts +80 -0
  322. package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts.map +1 -0
  323. package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.js +196 -0
  324. package/src/engine/control/first-person/test/buildTestPlayer.d.ts +20 -0
  325. package/src/engine/control/first-person/test/buildTestPlayer.d.ts.map +1 -0
  326. package/src/engine/control/first-person/test/buildTestPlayer.js +36 -0
  327. package/src/engine/graphics/camera/testClippingPlaneComputation.js +0 -2
  328. package/src/engine/graphics/ecs/light/Light.d.ts.map +1 -1
  329. package/src/engine/graphics/ecs/light/Light.js +27 -0
  330. package/src/engine/graphics/ecs/light/LightSystem.js +1 -1
  331. package/src/engine/graphics/ecs/path/PathDisplaySystem.d.ts.map +1 -1
  332. package/src/engine/graphics/ecs/path/testPathDisplaySystem.js +0 -2
  333. package/src/engine/graphics/ecs/path/tube/prototypeAnimatedPathMask.js +0 -2
  334. package/src/engine/graphics/geometry/CapsuleGeometry.d.ts +42 -0
  335. package/src/engine/graphics/geometry/CapsuleGeometry.d.ts.map +1 -0
  336. package/src/engine/graphics/geometry/CapsuleGeometry.js +171 -0
  337. package/src/engine/graphics/render/buffer/buffers/prototypeNormalFrameBuffer.js +0 -2
  338. package/src/engine/graphics/render/forward_plus/plugin/ptototypeFPPlugin.js +0 -2
  339. package/src/engine/graphics/render/visibility/hiz/prototypeHiZ.js +0 -2
  340. package/src/engine/navigation/grid/find_path_on_grid_astar.d.ts.map +1 -1
  341. package/src/engine/navigation/grid/find_path_on_grid_astar.js +11 -2
  342. package/src/engine/navigation/mesh/bt_mesh_face_find_path.d.ts.map +1 -1
  343. package/src/engine/navigation/mesh/bt_mesh_face_find_path.js +11 -1
  344. package/src/engine/physics/BULLET_REVIEW.md +945 -0
  345. package/src/engine/physics/CANNON_REVIEW.md +1300 -0
  346. package/src/engine/physics/JOLT_REVIEW.md +913 -0
  347. package/src/engine/physics/PLAN.md +461 -0
  348. package/src/engine/physics/RAPIER_REVIEW.md +934 -0
  349. package/src/engine/physics/REVIEW_001_ACTION_PLAN.md +642 -0
  350. package/src/engine/physics/body/BodyStorage.d.ts +187 -0
  351. package/src/engine/physics/body/BodyStorage.d.ts.map +1 -0
  352. package/src/engine/physics/body/BodyStorage.js +427 -0
  353. package/src/engine/physics/broadphase/PairList.d.ts +62 -0
  354. package/src/engine/physics/broadphase/PairList.d.ts.map +1 -0
  355. package/src/engine/physics/broadphase/PairList.js +97 -0
  356. package/src/engine/physics/broadphase/compute_fat_world_aabb.d.ts +16 -0
  357. package/src/engine/physics/broadphase/compute_fat_world_aabb.d.ts.map +1 -0
  358. package/src/engine/physics/broadphase/compute_fat_world_aabb.js +61 -0
  359. package/src/engine/physics/broadphase/generate_pairs.d.ts +38 -0
  360. package/src/engine/physics/broadphase/generate_pairs.d.ts.map +1 -0
  361. package/src/engine/physics/broadphase/generate_pairs.js +101 -0
  362. package/src/engine/physics/contact/ManifoldStore.d.ts +299 -0
  363. package/src/engine/physics/contact/ManifoldStore.d.ts.map +1 -0
  364. package/src/engine/physics/contact/ManifoldStore.js +608 -0
  365. package/src/engine/physics/ecs/BodyKind.d.ts +23 -0
  366. package/src/engine/physics/ecs/BodyKind.d.ts.map +1 -0
  367. package/src/engine/physics/ecs/BodyKind.js +24 -0
  368. package/src/engine/physics/ecs/Collider.d.ts +98 -0
  369. package/src/engine/physics/ecs/Collider.d.ts.map +1 -0
  370. package/src/engine/physics/ecs/Collider.js +136 -0
  371. package/src/engine/physics/ecs/ColliderFlags.d.ts +14 -0
  372. package/src/engine/physics/ecs/ColliderFlags.d.ts.map +1 -0
  373. package/src/engine/physics/ecs/ColliderFlags.js +15 -0
  374. package/src/engine/physics/ecs/ColliderObserverSystem.d.ts +58 -0
  375. package/src/engine/physics/ecs/ColliderObserverSystem.d.ts.map +1 -0
  376. package/src/engine/physics/ecs/ColliderObserverSystem.js +103 -0
  377. package/src/engine/physics/ecs/ColliderSerializationAdapter.d.ts +25 -0
  378. package/src/engine/physics/ecs/ColliderSerializationAdapter.d.ts.map +1 -0
  379. package/src/engine/physics/ecs/ColliderSerializationAdapter.js +37 -0
  380. package/src/engine/physics/ecs/PhysicsEvents.d.ts +15 -0
  381. package/src/engine/physics/ecs/PhysicsEvents.d.ts.map +1 -0
  382. package/src/engine/physics/ecs/PhysicsEvents.js +16 -0
  383. package/src/engine/physics/ecs/PhysicsSystem.d.ts +628 -0
  384. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -0
  385. package/src/engine/physics/ecs/PhysicsSystem.js +1301 -0
  386. package/src/engine/physics/ecs/RigidBody.d.ts +197 -0
  387. package/src/engine/physics/ecs/RigidBody.d.ts.map +1 -0
  388. package/src/engine/physics/ecs/RigidBody.js +240 -0
  389. package/src/engine/physics/ecs/RigidBodyFlags.d.ts +21 -0
  390. package/src/engine/physics/ecs/RigidBodyFlags.d.ts.map +1 -0
  391. package/src/engine/physics/ecs/RigidBodyFlags.js +22 -0
  392. package/src/engine/physics/ecs/RigidBodySerializationAdapter.d.ts +28 -0
  393. package/src/engine/physics/ecs/RigidBodySerializationAdapter.d.ts.map +1 -0
  394. package/src/engine/physics/ecs/RigidBodySerializationAdapter.js +81 -0
  395. package/src/engine/physics/ecs/SleepState.d.ts +11 -0
  396. package/src/engine/physics/ecs/SleepState.d.ts.map +1 -0
  397. package/src/engine/physics/ecs/SleepState.js +12 -0
  398. package/src/engine/physics/events/ContactEventBuffer.d.ts +46 -0
  399. package/src/engine/physics/events/ContactEventBuffer.d.ts.map +1 -0
  400. package/src/engine/physics/events/ContactEventBuffer.js +83 -0
  401. package/src/engine/physics/events/diff_manifolds.d.ts +25 -0
  402. package/src/engine/physics/events/diff_manifolds.d.ts.map +1 -0
  403. package/src/engine/physics/events/diff_manifolds.js +50 -0
  404. package/src/engine/physics/fluid/FluidField.d.ts +294 -16
  405. package/src/engine/physics/fluid/FluidField.d.ts.map +1 -1
  406. package/src/engine/physics/fluid/FluidField.js +510 -66
  407. package/src/engine/physics/fluid/FluidSimulator.d.ts +188 -5
  408. package/src/engine/physics/fluid/FluidSimulator.d.ts.map +1 -1
  409. package/src/engine/physics/fluid/FluidSimulator.js +456 -95
  410. package/src/engine/physics/fluid/SliceVisualiser.d.ts +29 -6
  411. package/src/engine/physics/fluid/SliceVisualiser.d.ts.map +1 -1
  412. package/src/engine/physics/fluid/SliceVisualiser.js +190 -165
  413. package/src/engine/physics/fluid/ecs/FluidComponent.d.ts +154 -0
  414. package/src/engine/physics/fluid/ecs/FluidComponent.d.ts.map +1 -0
  415. package/src/engine/physics/fluid/ecs/FluidComponent.js +238 -0
  416. package/src/engine/physics/fluid/ecs/FluidEffectorsComponent.d.ts +45 -0
  417. package/src/engine/physics/fluid/ecs/FluidEffectorsComponent.d.ts.map +1 -0
  418. package/src/engine/physics/fluid/ecs/FluidEffectorsComponent.js +89 -0
  419. package/src/engine/physics/fluid/ecs/FluidSystem.d.ts +107 -0
  420. package/src/engine/physics/fluid/ecs/FluidSystem.d.ts.map +1 -0
  421. package/src/engine/physics/fluid/ecs/FluidSystem.js +278 -0
  422. package/src/engine/physics/fluid/effector/AbstractFluidEffector.d.ts +62 -1
  423. package/src/engine/physics/fluid/effector/AbstractFluidEffector.d.ts.map +1 -1
  424. package/src/engine/physics/fluid/effector/AbstractFluidEffector.js +81 -6
  425. package/src/engine/physics/fluid/effector/GlobalFluidEffector.d.ts +17 -4
  426. package/src/engine/physics/fluid/effector/GlobalFluidEffector.d.ts.map +1 -1
  427. package/src/engine/physics/fluid/effector/GlobalFluidEffector.js +105 -12
  428. package/src/engine/physics/fluid/effector/ImpulseFluidEffector.d.ts +43 -0
  429. package/src/engine/physics/fluid/effector/ImpulseFluidEffector.d.ts.map +1 -0
  430. package/src/engine/physics/fluid/effector/ImpulseFluidEffector.js +210 -0
  431. package/src/engine/physics/fluid/effector/WakeFluidEffector.d.ts +62 -1
  432. package/src/engine/physics/fluid/effector/WakeFluidEffector.d.ts.map +1 -1
  433. package/src/engine/physics/fluid/effector/WakeFluidEffector.js +302 -8
  434. package/src/engine/physics/fluid/prototype.js +102 -91
  435. package/src/engine/physics/fluid/solver/optimal_sor_omega.d.ts +33 -0
  436. package/src/engine/physics/fluid/solver/optimal_sor_omega.d.ts.map +1 -0
  437. package/src/engine/physics/fluid/solver/optimal_sor_omega.js +41 -0
  438. package/src/engine/physics/fluid/solver/v3_grid_apply_advection_forward.d.ts +20 -5
  439. package/src/engine/physics/fluid/solver/v3_grid_apply_advection_forward.d.ts.map +1 -1
  440. package/src/engine/physics/fluid/solver/v3_grid_apply_advection_forward.js +60 -38
  441. package/src/engine/physics/fluid/solver/v3_grid_apply_diffusion.d.ts +25 -4
  442. package/src/engine/physics/fluid/solver/v3_grid_apply_diffusion.d.ts.map +1 -1
  443. package/src/engine/physics/fluid/solver/v3_grid_apply_diffusion.js +93 -73
  444. package/src/engine/physics/fluid/solver/v3_grid_apply_scalar_advection.d.ts +23 -0
  445. package/src/engine/physics/fluid/solver/v3_grid_apply_scalar_advection.d.ts.map +1 -0
  446. package/src/engine/physics/fluid/solver/v3_grid_apply_scalar_advection.js +60 -0
  447. package/src/engine/physics/fluid/solver/v3_grid_compute_divergence.d.ts +23 -0
  448. package/src/engine/physics/fluid/solver/v3_grid_compute_divergence.d.ts.map +1 -0
  449. package/src/engine/physics/fluid/solver/v3_grid_compute_divergence.js +68 -0
  450. package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.d.ts +30 -0
  451. package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.d.ts.map +1 -0
  452. package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.js +66 -0
  453. package/src/engine/physics/fluid/solver/v3_grid_patch_edges_uniform.d.ts +26 -0
  454. package/src/engine/physics/fluid/solver/v3_grid_patch_edges_uniform.d.ts.map +1 -0
  455. package/src/engine/physics/fluid/solver/v3_grid_patch_edges_uniform.js +113 -0
  456. package/src/engine/physics/fluid/solver/v3_grid_shift_in_place.d.ts +30 -0
  457. package/src/engine/physics/fluid/solver/v3_grid_shift_in_place.d.ts.map +1 -0
  458. package/src/engine/physics/fluid/solver/v3_grid_shift_in_place.js +107 -0
  459. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.d.ts +49 -0
  460. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.d.ts.map +1 -0
  461. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.js +126 -0
  462. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.d.ts +93 -0
  463. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.d.ts.map +1 -0
  464. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.js +424 -0
  465. package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts +48 -0
  466. package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts.map +1 -0
  467. package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.js +92 -0
  468. package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts +6 -6
  469. package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts.map +1 -1
  470. package/src/engine/physics/gjk/expanding_polytope_algorithm.js +76 -32
  471. package/src/engine/physics/gjk/gjk.d.ts +28 -2
  472. package/src/engine/physics/gjk/gjk.d.ts.map +1 -1
  473. package/src/engine/physics/gjk/gjk.js +421 -378
  474. package/src/engine/physics/gjk/minkowski_support.d.ts +37 -0
  475. package/src/engine/physics/gjk/minkowski_support.d.ts.map +1 -0
  476. package/src/engine/physics/gjk/minkowski_support.js +75 -0
  477. package/src/engine/physics/gjk/mpr.d.ts +56 -0
  478. package/src/engine/physics/gjk/mpr.d.ts.map +1 -0
  479. package/src/engine/physics/gjk/mpr.js +344 -0
  480. package/src/engine/physics/inertia/world_inverse_inertia.d.ts +44 -0
  481. package/src/engine/physics/inertia/world_inverse_inertia.d.ts.map +1 -0
  482. package/src/engine/physics/inertia/world_inverse_inertia.js +77 -0
  483. package/src/engine/physics/integration/integrate_position.d.ts +34 -0
  484. package/src/engine/physics/integration/integrate_position.d.ts.map +1 -0
  485. package/src/engine/physics/integration/integrate_position.js +79 -0
  486. package/src/engine/physics/integration/integrate_velocity.d.ts +55 -0
  487. package/src/engine/physics/integration/integrate_velocity.d.ts.map +1 -0
  488. package/src/engine/physics/integration/integrate_velocity.js +160 -0
  489. package/src/engine/physics/integration/quat_integrate.d.ts +27 -0
  490. package/src/engine/physics/integration/quat_integrate.d.ts.map +1 -0
  491. package/src/engine/physics/integration/quat_integrate.js +62 -0
  492. package/src/engine/physics/island/IslandBuilder.d.ts +167 -0
  493. package/src/engine/physics/island/IslandBuilder.d.ts.map +1 -0
  494. package/src/engine/physics/island/IslandBuilder.js +411 -0
  495. package/src/engine/physics/island/union_find.d.ts +51 -0
  496. package/src/engine/physics/island/union_find.d.ts.map +1 -0
  497. package/src/engine/physics/island/union_find.js +76 -0
  498. package/src/engine/physics/narrowphase/PosedShape.d.ts +51 -0
  499. package/src/engine/physics/narrowphase/PosedShape.d.ts.map +1 -0
  500. package/src/engine/physics/narrowphase/PosedShape.js +108 -0
  501. package/src/engine/physics/narrowphase/box_box_manifold.d.ts +32 -0
  502. package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -0
  503. package/src/engine/physics/narrowphase/box_box_manifold.js +639 -0
  504. package/src/engine/physics/narrowphase/box_triangle_contact.d.ts +30 -0
  505. package/src/engine/physics/narrowphase/box_triangle_contact.d.ts.map +1 -0
  506. package/src/engine/physics/narrowphase/box_triangle_contact.js +811 -0
  507. package/src/engine/physics/narrowphase/capsule_contacts.d.ts +122 -0
  508. package/src/engine/physics/narrowphase/capsule_contacts.d.ts.map +1 -0
  509. package/src/engine/physics/narrowphase/capsule_contacts.js +462 -0
  510. package/src/engine/physics/narrowphase/capsule_triangle_contact.d.ts +71 -0
  511. package/src/engine/physics/narrowphase/capsule_triangle_contact.d.ts.map +1 -0
  512. package/src/engine/physics/narrowphase/capsule_triangle_contact.js +375 -0
  513. package/src/engine/physics/narrowphase/compute_penetration.d.ts +91 -0
  514. package/src/engine/physics/narrowphase/compute_penetration.d.ts.map +1 -0
  515. package/src/engine/physics/narrowphase/compute_penetration.js +396 -0
  516. package/src/engine/physics/narrowphase/decomposition/aabb_world_to_local.d.ts +35 -0
  517. package/src/engine/physics/narrowphase/decomposition/aabb_world_to_local.d.ts.map +1 -0
  518. package/src/engine/physics/narrowphase/decomposition/aabb_world_to_local.js +80 -0
  519. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.d.ts +31 -0
  520. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.d.ts.map +1 -0
  521. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.js +55 -0
  522. package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.d.ts +42 -0
  523. package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.d.ts.map +1 -0
  524. package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.js +204 -0
  525. package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts +42 -0
  526. package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts.map +1 -0
  527. package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.js +94 -0
  528. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.d.ts +37 -0
  529. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.d.ts.map +1 -0
  530. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.js +37 -0
  531. package/src/engine/physics/narrowphase/narrowphase_step.d.ts +17 -0
  532. package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -0
  533. package/src/engine/physics/narrowphase/narrowphase_step.js +1422 -0
  534. package/src/engine/physics/narrowphase/sphere_box_contact.d.ts +38 -0
  535. package/src/engine/physics/narrowphase/sphere_box_contact.d.ts.map +1 -0
  536. package/src/engine/physics/narrowphase/sphere_box_contact.js +123 -0
  537. package/src/engine/physics/narrowphase/sphere_sphere_contact.d.ts +26 -0
  538. package/src/engine/physics/narrowphase/sphere_sphere_contact.d.ts.map +1 -0
  539. package/src/engine/physics/narrowphase/sphere_sphere_contact.js +51 -0
  540. package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts +48 -0
  541. package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts.map +1 -0
  542. package/src/engine/physics/narrowphase/sphere_triangle_contact.js +143 -0
  543. package/src/engine/physics/queries/PhysicsSurfacePoint.d.ts +83 -0
  544. package/src/engine/physics/queries/PhysicsSurfacePoint.d.ts.map +1 -0
  545. package/src/engine/physics/queries/PhysicsSurfacePoint.js +100 -0
  546. package/src/engine/physics/queries/overlap_shape.d.ts +51 -0
  547. package/src/engine/physics/queries/overlap_shape.d.ts.map +1 -0
  548. package/src/engine/physics/queries/overlap_shape.js +183 -0
  549. package/src/engine/physics/queries/raycast.d.ts +20 -0
  550. package/src/engine/physics/queries/raycast.d.ts.map +1 -0
  551. package/src/engine/physics/queries/raycast.js +249 -0
  552. package/src/engine/physics/queries/shape_cast.d.ts +56 -0
  553. package/src/engine/physics/queries/shape_cast.d.ts.map +1 -0
  554. package/src/engine/physics/queries/shape_cast.js +387 -0
  555. package/src/engine/physics/solver/friction_cone.d.ts +16 -0
  556. package/src/engine/physics/solver/friction_cone.d.ts.map +1 -0
  557. package/src/engine/physics/solver/friction_cone.js +37 -0
  558. package/src/engine/physics/solver/solve_contacts.d.ts +122 -0
  559. package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -0
  560. package/src/engine/physics/solver/solve_contacts.js +1016 -0
  561. package/src/core/geom/3d/topology/struct/binary/io/edge/OrderedEdge.d.ts +0 -34
  562. package/src/core/geom/3d/topology/struct/binary/io/edge/OrderedEdge.d.ts.map +0 -1
  563. package/src/core/geom/3d/topology/struct/binary/io/edge/OrderedEdge.js +0 -66
  564. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_calc_edges.d.ts +0 -2
  565. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_calc_edges.d.ts.map +0 -1
  566. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_calc_edges.js +0 -54
  567. package/src/core/geom/3d/topology/struct/binary/io/edge/get_or_create_edge_map.d.ts +0 -2
  568. package/src/core/geom/3d/topology/struct/binary/io/edge/get_or_create_edge_map.d.ts.map +0 -1
  569. package/src/core/geom/3d/topology/struct/binary/io/edge/get_or_create_edge_map.js +0 -26
  570. package/src/engine/ecs/components/Motion.d.ts +0 -21
  571. package/src/engine/ecs/components/Motion.d.ts.map +0 -1
  572. package/src/engine/ecs/components/Motion.js +0 -27
  573. package/src/engine/ecs/components/MotionSerializationAdapter.d.ts +0 -20
  574. package/src/engine/ecs/components/MotionSerializationAdapter.d.ts.map +0 -1
  575. package/src/engine/ecs/components/MotionSerializationAdapter.js +0 -26
  576. package/src/engine/ecs/systems/MotionSystem.d.ts +0 -9
  577. package/src/engine/ecs/systems/MotionSystem.d.ts.map +0 -1
  578. package/src/engine/ecs/systems/MotionSystem.js +0 -29
  579. package/src/engine/physics/fluid/Fluid.d.ts +0 -26
  580. package/src/engine/physics/fluid/Fluid.d.ts.map +0 -1
  581. package/src/engine/physics/fluid/Fluid.js +0 -221
  582. package/src/engine/physics/fluid/solver/v3_grid_apply_advection_reverse.d.ts +0 -7
  583. package/src/engine/physics/fluid/solver/v3_grid_apply_advection_reverse.d.ts.map +0 -1
  584. package/src/engine/physics/fluid/solver/v3_grid_apply_advection_reverse.js +0 -8
@@ -1,9 +1,15 @@
1
- import { BoxGeometry, MeshStandardMaterial } from "three";
1
+ import { BoxGeometry, MeshStandardMaterial, TorusKnotGeometry } from "three";
2
+ import { Ray3 } from "../../../core/geom/3d/ray/Ray3.js";
3
+ import { BoxShape3D } from "../../../core/geom/3d/shape/BoxShape3D.js";
4
+ import { CapsuleShape3D } from "../../../core/geom/3d/shape/CapsuleShape3D.js";
5
+ import { TransformedShape3D } from "../../../core/geom/3d/shape/TransformedShape3D.js";
6
+ import { shape_mesh_from_geometry } from "../../../core/geom/3d/shape/shape_mesh_from_geometry.js";
2
7
  import Vector2 from "../../../core/geom/Vector2.js";
3
8
  import Vector3 from "../../../core/geom/Vector3.js";
4
9
  import { SerializationMetadata } from "../../ecs/components/SerializationMetadata.js";
5
10
  import { Tag } from "../../ecs/components/Tag.js";
6
11
  import Entity from "../../ecs/Entity.js";
12
+ import { obtainTerrain } from "../../ecs/terrain/util/obtainTerrain.js";
7
13
  import { Transform } from "../../ecs/transform/Transform.js";
8
14
  import { EngineHarness } from "../../EngineHarness.js";
9
15
  import { CameraSystem } from "../../graphics/ecs/camera/CameraSystem.js";
@@ -12,11 +18,29 @@ import { ShadedGeometryFlags } from "../../graphics/ecs/mesh-v2/ShadedGeometryFl
12
18
  import { ShadedGeometrySystem } from "../../graphics/ecs/mesh-v2/ShadedGeometrySystem.js";
13
19
  import InputController from "../../input/ecs/components/InputController.js";
14
20
  import InputControllerSystem from "../../input/ecs/systems/InputControllerSystem.js";
21
+ import { BodyKind } from "../../physics/ecs/BodyKind.js";
22
+ import { Collider } from "../../physics/ecs/Collider.js";
23
+ import { ColliderObserverSystem } from "../../physics/ecs/ColliderObserverSystem.js";
24
+ import { PhysicsSystem } from "../../physics/ecs/PhysicsSystem.js";
25
+ import { RigidBody } from "../../physics/ecs/RigidBody.js";
26
+ import { PhysicsSurfacePoint } from "../../physics/queries/PhysicsSurfacePoint.js";
27
+ import { LedgeGrab } from "./abilities/LedgeGrab.js";
28
+ import { Mantle } from "./abilities/Mantle.js";
29
+ import { Slide } from "./abilities/Slide.js";
30
+ import { WallJump } from "./abilities/WallJump.js";
31
+ import { WallRun } from "./abilities/WallRun.js";
15
32
  import { FirstPersonPlayerController } from "./FirstPersonPlayerController.js";
16
33
  import { FirstPersonPlayerControllerSystem } from "./FirstPersonPlayerControllerSystem.js";
34
+ import { BreathRhythmEvaluator } from "./mastery/BreathRhythmEvaluator.js";
35
+ import { DecisionPoint } from "./mastery/DecisionPoint.js";
36
+ import { FootAsymmetryTurnEvaluator } from "./mastery/FootAsymmetryTurnEvaluator.js";
37
+ import { SlideInitiationTimingEvaluator } from "./mastery/SlideInitiationTimingEvaluator.js";
38
+ import { StrideTimingJumpEvaluator } from "./mastery/StrideTimingJumpEvaluator.js";
39
+ import { FirstPersonSensorsSystem } from "./sensors/FirstPersonSensorsSystem.js";
17
40
 
18
41
  /**
19
- * Prototype harness for {@link FirstPersonPlayerController}.
42
+ * Prototype harness for {@link FirstPersonPlayerController} — a parkour
43
+ * "gym" laid out as discrete test stations around the spawn point.
20
44
  *
21
45
  * Controls:
22
46
  * - Click to capture mouse (pointer lock); ESC to release
@@ -24,12 +48,38 @@ import { FirstPersonPlayerControllerSystem } from "./FirstPersonPlayerController
24
48
  * - Mouse — look
25
49
  * - Space — jump (tap; holding does not auto-repeat)
26
50
  * - Shift — sprint (hold while moving forward)
27
- * - C — crouch (hold)
51
+ * - C — crouch (hold; sprint+crouch press → slide)
28
52
  *
29
- * Scene: a flat ground at y=0 with a handful of coloured cubes for spatial
30
- * reference, lit by the harness's default sun.
53
+ * Stations (compass directions from spawn):
54
+ *
55
+ * N Mantle row four obstacles from too-low to too-tall
56
+ * NE Slide tunnel sprint run-up + low ceiling
57
+ * E Wall-run wall one long wall to run alongside
58
+ * SE Gap jump raised platforms with a gap to clear
59
+ * S Ledge-grab wall too tall to mantle; jump + catch the lip
60
+ * SW (open)
61
+ * W Wall-jump chimney two parallel walls for back-and-forth jumps
62
+ * NW (open)
63
+ *
64
+ * Each station is colour-coded at the entrance. Look around: the
65
+ * coloured pad on the ground tells you what's there.
66
+ *
67
+ * @author Alex Goldring
68
+ * @copyright Company Named Limited (c) 2026
31
69
  */
32
70
 
71
+ const SPAWN_X = 100;
72
+ const SPAWN_Z = 100;
73
+
74
+ // Station palette — used for entry pads + matching obstacle colour.
75
+ const COLOR_MANTLE = 0x55ff55;
76
+ const COLOR_WALLRUN = 0xff5555;
77
+ const COLOR_WALLJUMP = 0xaa55ff;
78
+ const COLOR_LEDGE = 0xffaa44;
79
+ const COLOR_SLIDE = 0x44ddee;
80
+ const COLOR_GAP = 0xff66cc;
81
+ const COLOR_NEUTRAL = 0xaaaaaa;
82
+
33
83
  const eh = new EngineHarness();
34
84
 
35
85
  async function main(engine) {
@@ -43,6 +93,8 @@ async function main(engine) {
43
93
  engine,
44
94
  sunIntensity: 1.0,
45
95
  ambientIntensity: 0.25,
96
+ shadowmapResolution: 2048,
97
+ sunShadowDistance: 30,
46
98
  });
47
99
 
48
100
  if (em.getSystem(CameraSystem) === null) {
@@ -55,11 +107,30 @@ async function main(engine) {
55
107
  await em.addSystem(new ShadedGeometrySystem(engine));
56
108
  }
57
109
 
110
+ // Physics — owns RigidBody+Collider lifecycle, fires contact events,
111
+ // and serves spatial queries (raycast). Sensors and the ground
112
+ // resolver both go through it. Added BEFORE the controller so the
113
+ // sensors and ground resolver find it in their startup auto-acquire.
114
+ const physicsSystem = new PhysicsSystem();
115
+ await em.addSystem(physicsSystem);
116
+ // ColliderObserver wires Collider components into PhysicsSystem
117
+ // bodies as entities are built. Without it, every collider would
118
+ // need an explicit attach_collider call.
119
+ await em.addSystem(new ColliderObserverSystem(physicsSystem));
120
+
58
121
  const fpsSystem = new FirstPersonPlayerControllerSystem();
59
- fpsSystem.useBuiltInFlatGround = true;
60
- fpsSystem.groundY = 0;
122
+ // Built-in flat-ground OFF — the gym uses the physics-backed ground
123
+ // resolver below (which finds the highest static surface under the
124
+ // player from BVH raycasts). Leaving the flat baseline on would
125
+ // pin the player to y=0 even when standing on a 2 m platform.
126
+ fpsSystem.useBuiltInFlatGround = false;
61
127
  await em.addSystem(fpsSystem);
62
128
 
129
+ // Sensors system — populates wall/obstacle/ledge probes via
130
+ // PhysicsSystem.raycast. Auto-acquires the physics system in startup.
131
+ const sensorsSystem = new FirstPersonSensorsSystem();
132
+ await em.addSystem(sensorsSystem);
133
+
63
134
  if (!ecd.isComponentTypeRegistered(Tag)) {
64
135
  ecd.registerComponentType(Tag);
65
136
  }
@@ -76,11 +147,22 @@ async function main(engine) {
76
147
  diffuse0: "data/textures/materials/terrain_township_set/512/Grass_3.png",
77
148
  });
78
149
 
79
- addReferenceMarkers(ecd);
150
+ obtainTerrain(ecd); // no-op call to ensure terrain is built
151
+
152
+ buildGym(ecd);
80
153
 
81
154
  // -- Player ----------------------------------------------------------
82
155
  const player = buildPlayerEntity(ecd);
83
156
 
157
+ // Ground resolver: downward raycast through PhysicsSystem, filtered
158
+ // to exclude the player's own capsule. Wired AFTER the player is
159
+ // built so the filter has the right entity id. The controller's
160
+ // vertical integrator falls through when the resolver returns null
161
+ // (no platform below) — set up with `useBuiltInFlatGround = false`
162
+ // above so that means actual airborne / void, not a hidden y=0
163
+ // safety floor.
164
+ fpsSystem.groundResolver = makePhysicsGroundResolver(physicsSystem, player.entity);
165
+
84
166
  // -- Inputs ----------------------------------------------------------
85
167
  buildInputBindings(ecd, player);
86
168
 
@@ -89,26 +171,545 @@ async function main(engine) {
89
171
  logHelp();
90
172
  }
91
173
 
174
+ /**
175
+ * Downward-raycast ground resolver, backed by {@link PhysicsSystem.raycast}.
176
+ * Each call: from just above the player's feet, cast straight down
177
+ * through static + dynamic broadphase; filter out the player's own
178
+ * body; return the hit y, or null if no surface below.
179
+ *
180
+ * The controller's vertical integrator picks max(useBuiltInFlatGround,
181
+ * resolver). In the gym we leave useBuiltInFlatGround off so the
182
+ * resolver is the sole source of truth — gaps in the floor are real
183
+ * gaps (the player falls into them).
184
+ *
185
+ * @param {PhysicsSystem} physicsSystem
186
+ * @param {number} playerEntity excluded from raycasts to skip the player capsule
187
+ * @returns {(x:number, y:number, z:number) => number|null}
188
+ */
189
+ function makePhysicsGroundResolver(physicsSystem, playerEntity) {
190
+ // Probe starts slightly above the player feet so a grounded player
191
+ // standing right on a platform still raycasts cleanly. Length is
192
+ // longer than any reasonable gym platform.
193
+ const PROBE_LIFT = 0.1;
194
+ const PROBE_DOWN = 50;
195
+ const filter = (entity) => entity !== playerEntity;
196
+ // Reuse a single ray and surface-point across calls — the resolver
197
+ // can fire many times per frame as the controller hunts for foothold.
198
+ const ray = Ray3.from(0, 0, 0, 0, -1, 0, PROBE_DOWN);
199
+ const hit = new PhysicsSurfacePoint();
200
+ return function physicsGroundResolver(x, y, z) {
201
+ ray.setOrigin(x, y + PROBE_LIFT, z);
202
+ if (!physicsSystem.raycast(ray, hit, filter)) return null;
203
+ // Direction is unit (0,-1,0); t is the distance to the surface
204
+ // along that direction. Surface Y = origin Y minus t.
205
+ return (y + PROBE_LIFT) - hit.t;
206
+ };
207
+ }
208
+
209
+ // =====================================================================
210
+ // Gym construction
211
+ // =====================================================================
212
+
213
+ function buildGym(ecd) {
214
+ buildGroundPlane(ecd);
215
+ buildSpawnPlaza(ecd);
216
+ buildMantleStation(ecd);
217
+ buildWallRunStation(ecd);
218
+ buildWallJumpChimney(ecd);
219
+ buildLedgeGrabStation(ecd);
220
+ buildSlideTunnel(ecd);
221
+ buildGapJumpStation(ecd);
222
+ buildMeshShapeShowcase(ecd);
223
+ }
224
+
225
+ /**
226
+ * Drop a torus-knot prop off to the side as a smoke test for
227
+ * {@link MeshShape3D}: arbitrary indexed triangle mesh → tetrahedralised
228
+ * collider via {@link shape_mesh_from_geometry}.
229
+ *
230
+ * Dynamic body, so the player capsule (kinematic) can bump it around.
231
+ * KinematicPosition bodies don't receive impulses themselves (the
232
+ * controller writes the Transform), so a static knot vs. kinematic
233
+ * capsule produces zero solver response and the capsule clips through.
234
+ * Making the knot dynamic flips the relationship: when the capsule
235
+ * penetrates, the solver applies impulse to the knot — it bounces away
236
+ * and the player gets a visible reaction.
237
+ *
238
+ * Placed off the spawn plaza's main travel path (radius 18 north-east of
239
+ * spawn) so it's visible but not in the way of the parkour drills.
240
+ */
241
+ function buildMeshShapeShowcase(ecd) {
242
+ // TorusKnotGeometry (radius, tube, tubularSegments, radialSegments).
243
+ // 64 × 8 = 512 segments → ~512 verts / ~1024 tris — plenty of detail
244
+ // for visual interest, light enough that the per-vertex `support()`
245
+ // scan stays fast.
246
+ const geom = new TorusKnotGeometry(0.5, 0.15, 64, 8);
247
+ geom.computeVertexNormals();
248
+
249
+ // Three packs positions as Float32Array (interleaved x, y, z) and
250
+ // indices as Uint16Array by default. The MeshShape3D factory wants
251
+ // a Uint32Array index buffer — convert once at construction.
252
+ const positions = geom.attributes.position.array;
253
+ const src_indices = geom.index.array;
254
+ const indices = src_indices instanceof Uint32Array
255
+ ? src_indices
256
+ : Uint32Array.from(src_indices);
257
+
258
+ const shape = shape_mesh_from_geometry(positions, indices);
259
+
260
+ const material = new MeshStandardMaterial({ color: 0xffaa66, roughness: 0.5 });
261
+ const sg = ShadedGeometry.from(geom, material);
262
+ sg.setFlag(ShadedGeometryFlags.CastShadow);
263
+ sg.setFlag(ShadedGeometryFlags.ReceiveShadow);
264
+ sg.setFlag(ShadedGeometryFlags.Visible);
265
+
266
+ const transform = new Transform();
267
+ // North-east of spawn, lifted ~2m off the ground so the knot drops
268
+ // onto the floor when the sim starts and the player can walk over,
269
+ // bump it, and watch it tumble.
270
+ transform.position.set(SPAWN_X + 18, 2, SPAWN_Z - 18);
271
+
272
+ const rigidBody = new RigidBody();
273
+ rigidBody.kind = BodyKind.Dynamic;
274
+ rigidBody.mass = 1;
275
+ // Bounding radius ≈ main_radius + tube_radius = 0.65. For a chunky
276
+ // bumpable body matching the existing 1m cube reference, use the
277
+ // same `(6, 6, 6)` inverse inertia — it's slightly under-rotational
278
+ // for the torus knot's mass distribution but reads as solid.
279
+ rigidBody.inverseInertiaLocal.set(6, 6, 6);
280
+ rigidBody.linearDamping = 0.5;
281
+ rigidBody.angularDamping = 0.5;
282
+
283
+ const collider = new Collider();
284
+ collider.shape = shape;
285
+ collider.friction = 0.5;
286
+ collider.restitution = 0.2;
287
+
288
+ new Entity()
289
+ .add(sg)
290
+ .add(rigidBody)
291
+ .add(collider)
292
+ .add(transform)
293
+ .add(Tag.fromJSON(["GymObstacle"]))
294
+ .add(SerializationMetadata.Transient)
295
+ .build(ecd);
296
+ }
297
+
298
+ /**
299
+ * Reference markers around spawn so motion is visually obvious — a ring
300
+ * of coloured cubes (circle-strafe targets) and a few tall pillars at
301
+ * radius 22 for parallax-at-distance.
302
+ */
303
+ function buildSpawnPlaza(ecd) {
304
+ const unitCubeGeom = new BoxGeometry(1, 1, 1);
305
+ const palette = [
306
+ 0xff5555, 0x55ff55, 0x5577ff, 0xffaa44,
307
+ 0xaa55ff, 0x44ddee, 0xff66cc, 0x66ffcc,
308
+ ];
309
+
310
+ // Inner ring at radius 6 — close enough to circle-strafe around.
311
+ const RING_N = 8;
312
+ for (let i = 0; i < RING_N; i++) {
313
+ const a = (i / RING_N) * Math.PI * 2;
314
+ spawnBox(ecd, {
315
+ center: new Vector3(SPAWN_X + Math.cos(a) * 6, 0.5, SPAWN_Z + Math.sin(a) * 6),
316
+ size: new Vector3(1, 1, 1),
317
+ geometry: unitCubeGeom,
318
+ color: palette[i % palette.length],
319
+ });
320
+ }
321
+
322
+ // Tall pillars further out — parallax depth perception when running.
323
+ const pillarGeom = new BoxGeometry(1, 6, 1);
324
+ const PILLAR_N = 6;
325
+ for (let i = 0; i < PILLAR_N; i++) {
326
+ const a = (i / PILLAR_N) * Math.PI * 2 + 0.3;
327
+ const r = 22;
328
+ spawnBox(ecd, {
329
+ center: new Vector3(SPAWN_X + Math.cos(a) * r, 3, SPAWN_Z + Math.sin(a) * r),
330
+ size: new Vector3(1, 6, 1),
331
+ geometry: pillarGeom,
332
+ color: 0x888888,
333
+ roughness: 0.9,
334
+ });
335
+ }
336
+ }
337
+
338
+ /**
339
+ * Mantle row — directly north of spawn. Four obstacles in line, heights
340
+ * stepping past the mantle range:
341
+ *
342
+ * - 0.6m: well within mantle range; lowest "step-up" test
343
+ * - 1.0m: mid mantle range
344
+ * - 1.3m: near top of mantle range
345
+ * - 2.0m: too tall to mantle from the ground (heightDiff > maxHeight)
346
+ *
347
+ * The 2.0m block is also the canonical "LedgeGrab if you jump at it"
348
+ * candidate, but the dedicated LedgeGrab station to the south has a
349
+ * cleaner approach path.
350
+ */
351
+ function buildMantleStation(ecd) {
352
+ const baseZ = SPAWN_Z + 15;
353
+ spawnPad(ecd, SPAWN_X, baseZ - 3, COLOR_MANTLE);
354
+
355
+ const heights = [0.6, 1.0, 1.3, 2.0];
356
+ for (let i = 0; i < heights.length; i++) {
357
+ const h = heights[i];
358
+ spawnBox(ecd, {
359
+ center: new Vector3(SPAWN_X, h / 2, baseZ + i * 5),
360
+ size: new Vector3(3, h, 1),
361
+ color: COLOR_MANTLE,
362
+ roughness: 0.6,
363
+ });
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Wall-run wall — a long wall on the east side. Player runs east past
369
+ * spawn, jumps near the wall, runs along it.
370
+ *
371
+ * Two walls actually — left and right — so the player can wall-run on
372
+ * either side without needing to circle back.
373
+ */
374
+ function buildWallRunStation(ecd) {
375
+ const baseX = SPAWN_X + 15;
376
+ spawnPad(ecd, baseX - 3, SPAWN_Z, COLOR_WALLRUN);
377
+
378
+ // South wall (run along its north face).
379
+ spawnBox(ecd, {
380
+ center: new Vector3(baseX + 5, 2, SPAWN_Z - 2),
381
+ size: new Vector3(10, 4, 0.5),
382
+ color: COLOR_WALLRUN,
383
+ roughness: 0.6,
384
+ });
385
+ // North wall (run along its south face) — separated by 4m so the
386
+ // wall-runner picks one side, not both.
387
+ spawnBox(ecd, {
388
+ center: new Vector3(baseX + 5, 2, SPAWN_Z + 2),
389
+ size: new Vector3(10, 4, 0.5),
390
+ color: COLOR_WALLRUN,
391
+ roughness: 0.6,
392
+ });
393
+ }
394
+
395
+ /**
396
+ * Wall-jump chimney — two parallel walls on the west side, ~3m apart,
397
+ * 6m tall. Player walks into the gap, jumps, hits one wall, wall-jumps
398
+ * to the other side. Repeat to climb.
399
+ *
400
+ * The walls are wide enough that wall-jumping straight back to the
401
+ * other side stays inside the chimney.
402
+ */
403
+ function buildWallJumpChimney(ecd) {
404
+ const baseX = SPAWN_X - 15;
405
+ spawnPad(ecd, baseX + 3, SPAWN_Z, COLOR_WALLJUMP);
406
+
407
+ // North wall of the chimney (pushes south).
408
+ spawnBox(ecd, {
409
+ center: new Vector3(baseX - 2, 3, SPAWN_Z + 2),
410
+ size: new Vector3(4, 6, 0.5),
411
+ color: COLOR_WALLJUMP,
412
+ roughness: 0.6,
413
+ });
414
+ // South wall of the chimney (pushes north). 3m gap to the north wall.
415
+ spawnBox(ecd, {
416
+ center: new Vector3(baseX - 2, 3, SPAWN_Z - 1.25),
417
+ size: new Vector3(4, 6, 0.5),
418
+ color: COLOR_WALLJUMP,
419
+ roughness: 0.6,
420
+ });
421
+ }
422
+
423
+ /**
424
+ * Ledge-grab station — a single 2m-tall wall just south of spawn.
425
+ * Too tall to mantle from the ground (heightDiff = 2 > maxHeight 1.4),
426
+ * but the player can run-jump at it and catch the lip on descent:
427
+ *
428
+ * 1. sprint south
429
+ * 2. jump just before the wall
430
+ * 3. on the way down, chest probe hits the wall face;
431
+ * ledgeAhead probe finds the top → LedgeGrab fires
432
+ * 4. press jump to chain into Mantle (mantle-up release)
433
+ *
434
+ * The wall is 6m wide so the player doesn't have to aim perfectly.
435
+ */
436
+ function buildLedgeGrabStation(ecd) {
437
+ const baseZ = SPAWN_Z - 15;
438
+ spawnPad(ecd, SPAWN_X, baseZ + 3, COLOR_LEDGE);
439
+
440
+ spawnBox(ecd, {
441
+ center: new Vector3(SPAWN_X, 1.0, baseZ - 1),
442
+ size: new Vector3(6, 2.0, 2),
443
+ color: COLOR_LEDGE,
444
+ roughness: 0.6,
445
+ });
446
+
447
+ // A larger top-of-wall platform behind the lip, so once the player
448
+ // climbs up they have somewhere to stand and look around.
449
+ spawnBox(ecd, {
450
+ center: new Vector3(SPAWN_X, 1.0, baseZ - 4.5),
451
+ size: new Vector3(6, 2.0, 5),
452
+ color: COLOR_LEDGE,
453
+ roughness: 0.6,
454
+ });
455
+ }
456
+
457
+ /**
458
+ * Slide tunnel — a sprint run-up followed by a low ceiling that forces
459
+ * the player to slide under. NE of spawn.
460
+ *
461
+ * Layout: a long flat lane (just an entry pad / marker, ground is
462
+ * already flat), with an overhead box at 1m height. Approaching at
463
+ * sprint + crouch press → slide → fits under the gap.
464
+ */
465
+ function buildSlideTunnel(ecd) {
466
+ const baseX = SPAWN_X + 11;
467
+ const baseZ = SPAWN_Z + 11;
468
+ spawnPad(ecd, baseX, baseZ, COLOR_SLIDE);
469
+
470
+ // The "ceiling" — a slab suspended 1.0m above the ground. Sliding
471
+ // brings the eye height down to ~0.4m; standing-eye-height would
472
+ // collide.
473
+ spawnBox(ecd, {
474
+ center: new Vector3(baseX + 4, 1.5, baseZ + 4),
475
+ size: new Vector3(4, 1.0, 4),
476
+ color: COLOR_SLIDE,
477
+ roughness: 0.6,
478
+ });
479
+
480
+ // Side pillars to make the tunnel feel like a tunnel.
481
+ spawnBox(ecd, {
482
+ center: new Vector3(baseX + 4, 1.0, baseZ + 2.0),
483
+ size: new Vector3(4, 2.0, 0.5),
484
+ color: COLOR_SLIDE,
485
+ roughness: 0.7,
486
+ });
487
+ spawnBox(ecd, {
488
+ center: new Vector3(baseX + 4, 1.0, baseZ + 6.0),
489
+ size: new Vector3(4, 2.0, 0.5),
490
+ color: COLOR_SLIDE,
491
+ roughness: 0.7,
492
+ });
493
+ }
494
+
495
+ /**
496
+ * Gap jump — two elevated platforms with a 3m gap between them. Player
497
+ * approaches via a mantle stair, runs across the first platform,
498
+ * sprints + jumps the gap.
499
+ *
500
+ * The platforms sit 1.5m above the spawn ground (mantleable). The gap
501
+ * doesn't actually break the floor — landing flat would still catch
502
+ * the player on the y=0 floor below. That's fine for testing the
503
+ * mid-flight feel of a jump; survival of misses is a feature here.
504
+ */
505
+ function buildGapJumpStation(ecd) {
506
+ const baseX = SPAWN_X + 11;
507
+ const baseZ = SPAWN_Z - 11;
508
+ spawnPad(ecd, baseX, baseZ, COLOR_GAP);
509
+
510
+ // Approach mantle (step up to the first platform).
511
+ spawnBox(ecd, {
512
+ center: new Vector3(baseX + 2, 0.5, baseZ - 2),
513
+ size: new Vector3(2, 1.0, 2),
514
+ color: COLOR_GAP,
515
+ roughness: 0.7,
516
+ });
517
+
518
+ // First platform — 4m long along Z.
519
+ spawnBox(ecd, {
520
+ center: new Vector3(baseX + 5, 0.75, baseZ - 4),
521
+ size: new Vector3(4, 1.5, 4),
522
+ color: COLOR_GAP,
523
+ roughness: 0.6,
524
+ });
525
+
526
+ // Second platform — same height, 3m gap.
527
+ spawnBox(ecd, {
528
+ center: new Vector3(baseX + 5, 0.75, baseZ - 11),
529
+ size: new Vector3(4, 1.5, 4),
530
+ color: COLOR_GAP,
531
+ roughness: 0.6,
532
+ });
533
+ }
534
+
535
+ // =====================================================================
536
+ // Spawn helpers
537
+ // =====================================================================
538
+
539
+ /**
540
+ * Add a box-shaped obstacle to the world. Position is the box centre.
541
+ *
542
+ * Single entity carrying both visual (ShadedGeometry) and physical
543
+ * (RigidBody + Collider) representations so the player can stand on it,
544
+ * sensors can raycast against it, and dynamic crates (when added) bump
545
+ * off it. Bodies are {@link BodyKind.Static} — gym geometry never moves.
546
+ *
547
+ * @param {EntityComponentDataset} ecd
548
+ * @param {object} p
549
+ * @param {Vector3} p.center
550
+ * @param {Vector3} p.size w, h, d (box dimensions)
551
+ * @param {number} p.color
552
+ * @param {number} [p.roughness]
553
+ * @param {THREE.BufferGeometry} [p.geometry] reuse to avoid new geometry per spawn
554
+ * @param {string} [p.tag]
555
+ * @param {boolean} [p.collide] default true; set false for decorative pads
556
+ */
557
+ function spawnBox(ecd, { center, size, color, roughness = 0.7, geometry, tag = "GymObstacle", collide = true }) {
558
+ const geom = geometry !== undefined ? geometry : new BoxGeometry(size.x, size.y, size.z);
559
+ const material = new MeshStandardMaterial({ color, roughness });
560
+ const sg = ShadedGeometry.from(geom, material);
561
+ sg.setFlag(ShadedGeometryFlags.CastShadow);
562
+ sg.setFlag(ShadedGeometryFlags.ReceiveShadow);
563
+ sg.setFlag(ShadedGeometryFlags.Visible);
564
+
565
+ const transform = new Transform();
566
+ transform.position.copy(center);
567
+
568
+ const eb = new Entity()
569
+ .add(sg)
570
+ .add(Tag.fromJSON([tag]))
571
+ .add(SerializationMetadata.Transient);
572
+
573
+ if (collide) {
574
+ const rigidBody = new RigidBody();
575
+ rigidBody.kind = BodyKind.Static;
576
+ const collider = new Collider();
577
+ // BoxShape3D with explicit half-extents — routes through the
578
+ // closed-form sphere-box / box-box / capsule-box narrowphase paths
579
+ // instead of degenerating into GJK+EPA, which is unreliable on
580
+ // smooth-surface pairs like capsule-vs-box.
581
+ collider.shape = BoxShape3D.from_size(size.x, size.y, size.z);
582
+ // Transform is added LAST so the (RigidBody, Transform) tuple
583
+ // completes only after the body is present — PhysicsSystem.link
584
+ // is the trigger, not the rigidBody add.
585
+ eb.add(rigidBody).add(collider).add(transform).build(ecd);
586
+ } else {
587
+ eb.add(transform).build(ecd);
588
+ }
589
+ }
590
+
591
+ /**
592
+ * Lay a thin coloured "entry pad" on the ground at (x, z). Visual
593
+ * landmark only — flush with the floor, no collision (a 5 cm-thick
594
+ * static body in the player's walking path would bump the camera).
595
+ */
596
+ function spawnPad(ecd, x, z, color) {
597
+ spawnBox(ecd, {
598
+ center: new Vector3(x, 0.025, z),
599
+ size: new Vector3(4, 0.05, 4),
600
+ color,
601
+ roughness: 0.95,
602
+ tag: "GymEntryPad",
603
+ collide: false,
604
+ });
605
+ }
606
+
607
+ /**
608
+ * Lay a physics-only ground body underneath the entire gym so the
609
+ * player has something to stand on. The terrain mesh provides the
610
+ * grass visual at y=0; this is its physics counterpart (the player
611
+ * controller's vertical integrator and sensors only look at physics
612
+ * bodies, not the terrain heightfield).
613
+ *
614
+ * Sized to comfortably encompass all stations (the gym spans ~30 m
615
+ * radius around spawn). The slab top sits at y=0 — same as the
616
+ * terrain visual — so standing on it puts the player's feet on the
617
+ * grass.
618
+ */
619
+ function buildGroundPlane(ecd) {
620
+ const SIZE = 120;
621
+ const THICKNESS = 1.0;
622
+
623
+ const rigidBody = new RigidBody();
624
+ rigidBody.kind = BodyKind.Static;
625
+ const collider = new Collider();
626
+ collider.shape = BoxShape3D.from_size(SIZE, THICKNESS, SIZE);
627
+
628
+ const transform = new Transform();
629
+ // Top face at y=0 → centre at y = -thickness/2.
630
+ transform.position.set(SPAWN_X, -THICKNESS / 2, SPAWN_Z);
631
+
632
+ new Entity()
633
+ .add(rigidBody)
634
+ .add(collider)
635
+ .add(Tag.fromJSON(["GymGroundPlane"]))
636
+ .add(SerializationMetadata.Transient)
637
+ .add(transform)
638
+ .build(ecd);
639
+ }
640
+
641
+ // =====================================================================
642
+ // Player + input
643
+ // =====================================================================
644
+
92
645
  /**
93
646
  * @param {EntityComponentDataset} ecd
94
647
  * @returns {{entity:number, controller:FirstPersonPlayerController}}
95
648
  */
96
649
  function buildPlayerEntity(ecd) {
97
650
  const transform = new Transform();
98
- // Spawn standing on the ground (body origin sits at the feet)
99
- transform.position.set(100, 0, 100);
651
+ // Spawn standing on the ground at the centre of the plaza.
652
+ transform.position.set(SPAWN_X, 0, SPAWN_Z);
100
653
 
101
654
  const controller = new FirstPersonPlayerController();
102
- // Most tuning lives at config defaults; mass-tweak example shown for
103
- // documentation. Comment out for the default soldier feel.
104
- // controller.config.body.mass = 120;
105
- // controller.config.bob.verticalAmpAtWalk = 0.08;
106
655
 
656
+ // Movement abilities (priority order — see DESIGN_EXTENSIONS §2.2):
657
+ // Slide(10) < Mantle(30) < LedgeGrab(40) < WallRun(50) < WallJump(60).
658
+ controller.abilities.add(new Slide());
659
+ controller.abilities.add(new Mantle());
660
+ controller.abilities.add(new LedgeGrab());
661
+ controller.abilities.add(new WallRun());
662
+ controller.abilities.add(new WallJump());
663
+
664
+ // Mastery evaluators — small bonuses/penalties tied to gait phase,
665
+ // foot asymmetry, and breath rhythm. Each fires on one (or a few)
666
+ // DecisionPoint(s); contributions compose multiplicatively.
667
+ controller.mastery.add(new StrideTimingJumpEvaluator());
668
+ controller.mastery.add(new SlideInitiationTimingEvaluator());
669
+ controller.mastery.add(new FootAsymmetryTurnEvaluator());
670
+ controller.mastery.add(new BreathRhythmEvaluator());
671
+ controller.mastery.add(new BreathRhythmEvaluator({
672
+ decisionPoints: [DecisionPoint.GroundAccel],
673
+ peakBonus: 0.015,
674
+ troughPenalty: 0.005,
675
+ requireExertionAbove: 0.3,
676
+ }));
677
+
678
+ // Kinematic capsule body: the controller writes Transform directly,
679
+ // physics derives velocity from the per-step delta. Capsule centred
680
+ // on the Transform — bottom hemisphere extends below the feet (this
681
+ // is fine for spatial queries since sensor probes start at chest
682
+ // height and the ground resolver filters out the player's own body).
683
+ const bodyCfg = controller.config.body;
684
+ const radius = bodyCfg.radius;
685
+ const totalHeight = bodyCfg.height; // standing eye height ≈ body extent
686
+ const cylinderHeight = Math.max(0, totalHeight - 2 * radius);
687
+ const rigidBody = new RigidBody();
688
+ rigidBody.kind = BodyKind.KinematicPosition;
689
+ rigidBody.mass = bodyCfg.mass;
690
+ const collider = new Collider();
691
+ // Capsule lives in [feet .. head] in world space. CapsuleShape3D is
692
+ // centred at its local origin, so wrap it in a TransformedShape3D
693
+ // with a +Y translation of totalHeight/2 — that puts the capsule
694
+ // bottom at the player's feet (Transform.position) and the top at
695
+ // the head. Without this, shape_cast queries against the ground
696
+ // would stop the capsule centre at floor + half-extent, leaving
697
+ // the player visibly floating half a body height above the floor.
698
+ collider.shape = TransformedShape3D.from_translation(
699
+ CapsuleShape3D.from(radius, cylinderHeight),
700
+ [0, totalHeight / 2, 0],
701
+ );
702
+
703
+ // Build order matters: Transform is added LAST so the FirstPerson
704
+ // controller system's link() sees the RigidBody already attached.
705
+ // (Same pattern as the spec helpers.)
107
706
  const eb = new Entity()
108
- .add(transform)
109
707
  .add(controller)
708
+ .add(rigidBody)
709
+ .add(collider)
110
710
  .add(Tag.fromJSON(["Player"]))
111
- .add(SerializationMetadata.Transient);
711
+ .add(SerializationMetadata.Transient)
712
+ .add(transform);
112
713
 
113
714
  eb.build(ecd);
114
715
 
@@ -116,9 +717,7 @@ function buildPlayerEntity(ecd) {
116
717
  }
117
718
 
118
719
  /**
119
- * Wire keyboard + mouse to the controller's intent surface. Uses the same
120
- * `InputController` plumbing as the orbital-camera controller, so the
121
- * standard `InputControllerSystem` does the device subscription bookkeeping.
720
+ * Wire keyboard + mouse to the controller's intent surface.
122
721
  *
123
722
  * @param {EntityComponentDataset} ecd
124
723
  * @param {{entity:number, controller:FirstPersonPlayerController}} player
@@ -128,14 +727,9 @@ function buildInputBindings(ecd, player) {
128
727
  const intent = player.controller.intent;
129
728
 
130
729
  // Mouse sensitivity — radians of view rotation per pixel of mouse delta.
131
- // ~1200px sweep = one full 360° turn. Comparable to Source-engine
132
- // "sensitivity 2" at 800 DPI; calmer than typical web demos.
133
730
  const SENSITIVITY = (Math.PI * 2) / 1200;
134
731
 
135
732
  // -- WASD movement --------------------------------------------------
136
- // Keep a small "held" mirror so we can recompute intent.move whenever
137
- // any of the four keys change. We can't read intent.move directly
138
- // because both keys can be pressed simultaneously.
139
733
  const held = { w: false, a: false, s: false, d: false };
140
734
 
141
735
  function recomputeMove() {
@@ -148,15 +742,22 @@ function buildInputBindings(ecd, player) {
148
742
  }
149
743
 
150
744
  function bindMoveKey(key, prop) {
151
- ic.bind(`keyboard/keys/${key}/down`, () => { held[prop] = true; recomputeMove(); });
152
- ic.bind(`keyboard/keys/${key}/up`, () => { held[prop] = false; recomputeMove(); });
745
+ ic.bind(`keyboard/keys/${key}/down`, () => {
746
+ held[prop] = true;
747
+ recomputeMove();
748
+ });
749
+ ic.bind(`keyboard/keys/${key}/up`, () => {
750
+ held[prop] = false;
751
+ recomputeMove();
752
+ });
153
753
  }
754
+
154
755
  bindMoveKey("w", "w");
155
756
  bindMoveKey("a", "a");
156
757
  bindMoveKey("s", "s");
157
758
  bindMoveKey("d", "d");
158
759
 
159
- // -- Jump (hold-state; the system does the edge detection) -----------
760
+ // -- Jump ------------------------------------------------------------
160
761
  ic.bind("keyboard/keys/space/down", () => { intent.jump = true; });
161
762
  ic.bind("keyboard/keys/space/up", () => { intent.jump = false; });
162
763
 
@@ -169,16 +770,11 @@ function buildInputBindings(ecd, player) {
169
770
  ic.bind("keyboard/keys/c/up", () => { intent.crouch = false; });
170
771
 
171
772
  // -- Mouse look ------------------------------------------------------
172
- // Only accumulate look delta while the pointer is captured — otherwise
173
- // moving the cursor over the page would steer the camera, which is
174
- // jarring before the user has "claimed" the input surface.
175
773
  ic.bind("pointer/on/move", (position, event, delta) => {
176
774
  if (document.pointerLockElement === null) return;
177
775
  intent.look._add(delta.x * SENSITIVITY, delta.y * SENSITIVITY);
178
776
  });
179
777
 
180
- // When the InputController unlinks (e.g. system shutdown), zero
181
- // everything so a held key doesn't get stuck.
182
778
  ic.on.unlinked.add(() => {
183
779
  intent.move.set(0, 0);
184
780
  intent.look.set(0, 0);
@@ -200,10 +796,6 @@ function buildInputBindings(ecd, player) {
200
796
  */
201
797
  function setupPointerLock(engine) {
202
798
  const el = engine.graphics.domElement;
203
-
204
- // The harness wires the engine into the page body via viewStack; the
205
- // canvas is the actual pointer-lock target. Listen on the viewStack
206
- // element so clicks anywhere inside the canvas trigger the request.
207
799
  const captureEl = engine.viewStack.el;
208
800
 
209
801
  captureEl.addEventListener("click", () => {
@@ -212,98 +804,23 @@ function setupPointerLock(engine) {
212
804
  }
213
805
  });
214
806
 
215
- // Suppress the right-click context menu so the canvas stays usable.
216
807
  captureEl.addEventListener("contextmenu", (e) => e.preventDefault());
217
808
  }
218
809
 
219
- /**
220
- * Spawn a handful of marker entities around the player so motion is
221
- * visually obvious. All markers go through the standard ECS pipeline
222
- * (Transform + ShadedGeometry) — same path as gameplay geometry — so
223
- * shadows and lighting work correctly.
224
- *
225
- * @param {EntityComponentDataset} ecd
226
- */
227
- function addReferenceMarkers(ecd) {
228
- const SPAWN_X = 100, SPAWN_Z = 100;
229
-
230
- // Share one unit-cube geometry across the ring cubes — cheap and lets
231
- // the renderer's instancing path kick in if it wants.
232
- const unitCubeGeom = new BoxGeometry(1, 1, 1);
233
- const palette = [0xff5555, 0x55ff55, 0x5577ff, 0xffaa44, 0xaa55ff, 0x44ddee, 0xff66cc, 0x66ffcc];
234
-
235
- // Ring of coloured cubes at radius 6 — circle-strafe targets.
236
- const RING_N = 8;
237
- for (let i = 0; i < RING_N; i++) {
238
- const a = (i / RING_N) * Math.PI * 2;
239
- spawnMarker({
240
- ecd,
241
- geometry: unitCubeGeom,
242
- color: palette[i % palette.length],
243
- roughness: 0.8,
244
- position: new Vector3(SPAWN_X + Math.cos(a) * 6, 0.5, SPAWN_Z + Math.sin(a) * 6),
245
- });
246
- }
247
-
248
- // Tall pillars further out — for parallax depth-perception while
249
- // testing bob and breath at distance.
250
- const pillarGeom = new BoxGeometry(1, 6, 1);
251
- const PILLAR_N = 6;
252
- for (let i = 0; i < PILLAR_N; i++) {
253
- const a = (i / PILLAR_N) * Math.PI * 2 + 0.3;
254
- const r = 22;
255
- spawnMarker({
256
- ecd,
257
- geometry: pillarGeom,
258
- color: 0x888888,
259
- roughness: 0.9,
260
- position: new Vector3(SPAWN_X + Math.cos(a) * r, 3, SPAWN_Z + Math.sin(a) * r),
261
- });
262
- }
263
-
264
- // A low platform next to spawn — easy hop target, plus a chance to see
265
- // the landing dip when stepping back down.
266
- spawnMarker({
267
- ecd,
268
- geometry: new BoxGeometry(2, 0.4, 2),
269
- color: 0xddddee,
270
- roughness: 0.7,
271
- position: new Vector3(SPAWN_X + 3, 0.2, SPAWN_Z),
272
- });
273
- }
274
-
275
- /**
276
- * @param {object} p
277
- * @param {EntityComponentDataset} p.ecd
278
- * @param {THREE.BufferGeometry} p.geometry
279
- * @param {number} p.color Hex color, e.g. 0xff5555
280
- * @param {number} [p.roughness]
281
- * @param {Vector3} p.position
282
- */
283
- function spawnMarker({ ecd, geometry, color, roughness = 0.8, position }) {
284
- const material = new MeshStandardMaterial({ color, roughness });
285
- const sg = ShadedGeometry.from(geometry, material);
286
- sg.setFlag(ShadedGeometryFlags.CastShadow);
287
- sg.setFlag(ShadedGeometryFlags.ReceiveShadow);
288
- sg.setFlag(ShadedGeometryFlags.Visible);
289
-
290
- const transform = new Transform();
291
- transform.position.copy(position);
292
-
293
- new Entity()
294
- .add(transform)
295
- .add(sg)
296
- .add(Tag.fromJSON(["ReferenceMarker"]))
297
- .add(SerializationMetadata.Transient)
298
- .build(ecd);
299
- }
300
-
301
810
  function logHelp() {
302
811
  /* eslint-disable no-console */
303
- console.log("%cFirstPersonPlayerController prototype",
812
+ console.log("%cFirstPersonPlayerController — Parkour Gym",
304
813
  "color:#88ddff;font-weight:bold;font-size:14px");
305
814
  console.log("Click the canvas to capture mouse. ESC to release.");
306
815
  console.log("WASD = move | Mouse = look | Space = jump | Shift = sprint | C = crouch");
816
+ console.log("");
817
+ console.log("%cStations:", "font-weight:bold");
818
+ console.log(" N Mantle row — four obstacles, last one is too tall");
819
+ console.log(" NE Slide tunnel — sprint + C to slide under the low ceiling");
820
+ console.log(" E Wall-run — sprint along, jump near the wall");
821
+ console.log(" SE Gap jump — mantle up the stair, sprint, jump the gap");
822
+ console.log(" S Ledge-grab — too tall to mantle; jump at it, catch the lip");
823
+ console.log(" W Wall-jump chimney — alternate walls to climb");
307
824
  }
308
825
 
309
826
  async function init() {