@nayan-ui/games 1.20.0 → 1.21.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 (552) hide show
  1. package/lib/module/games/arrows/Arrows.js +1 -1
  2. package/lib/module/games/arrows/ArrowsScene.js +1 -1
  3. package/lib/module/games/balloon-blaster/BalloonBlaster.js +1 -1
  4. package/lib/module/games/balloon-blaster/BalloonBlasterScene.js +1 -1
  5. package/lib/module/games/bike-racing/BikeMeshes.js +1 -1
  6. package/lib/module/games/bike-racing/BikeRacing.js +1 -1
  7. package/lib/module/games/bike-racing/BikeRacingConstants.js +1 -1
  8. package/lib/module/games/bike-racing/BikeRacingScene.js +1 -1
  9. package/lib/module/games/block-blast/BlockBlast.js +1 -1
  10. package/lib/module/games/block-blast/BlockBlastScene.js +1 -1
  11. package/lib/module/games/block-breaker/BlockBreaker.js +1 -1
  12. package/lib/module/games/block-breaker/BlockBreakerScene.js +1 -1
  13. package/lib/module/games/bluk/Bluk.js +1 -1
  14. package/lib/module/games/bluk/BlukScene.js +1 -1
  15. package/lib/module/games/bubble-shooter/BubbleShooter.js +1 -1
  16. package/lib/module/games/bubble-shooter/BubbleShooterScene.js +1 -1
  17. package/lib/module/games/candy-crush/CandyCrush.js +1 -1
  18. package/lib/module/games/candy-crush/CandyCrushScene.js +1 -1
  19. package/lib/module/games/car-racing/CarMeshes.js +1 -1
  20. package/lib/module/games/car-racing/CarRacing.js +1 -1
  21. package/lib/module/games/car-racing/CarRacingScene.js +1 -1
  22. package/lib/module/games/chameleon-run/ChameleonRun.js +1 -1
  23. package/lib/module/games/chameleon-run/ChameleonRunBackdrop.js +1 -1
  24. package/lib/module/games/chameleon-run/ChameleonRunMeshes.js +1 -1
  25. package/lib/module/games/checkers/Checkers.js +1 -1
  26. package/lib/module/games/checkers/CheckersScene.js +1 -1
  27. package/lib/module/games/color-switch/ColorSwitch.js +1 -1
  28. package/lib/module/games/color-switch/ColorSwitchScene.js +1 -1
  29. package/lib/module/games/colors-sort/ColorsSort.js +1 -1
  30. package/lib/module/games/colors-sort/ColorsSortScene.js +1 -1
  31. package/lib/module/games/connect-em-all/ConnectEmAll.js +1 -1
  32. package/lib/module/games/connect-em-all/ConnectEmAllScene.js +1 -1
  33. package/lib/module/games/crossy-road/CrossyRoad.js +1 -1
  34. package/lib/module/games/crossy-road/CrossyRoadMeshes.js +1 -1
  35. package/lib/module/games/crossy-road/CrossyRoadPerson.js +1 -1
  36. package/lib/module/games/dino-jump/DinoJump.js +1 -1
  37. package/lib/module/games/dino-jump/DinoJumpScene.js +1 -1
  38. package/lib/module/games/dots-and-boxes/DotsAndBoxes.js +1 -1
  39. package/lib/module/games/dots-and-boxes/DotsAndBoxesScene.js +1 -1
  40. package/lib/module/games/falling-sticks/FallingSticks.js +1 -1
  41. package/lib/module/games/falling-sticks/FallingSticksScene.js +1 -1
  42. package/lib/module/games/find-different-number/FindDifferentNumber.js +1 -1
  43. package/lib/module/games/find-different-number/FindDifferentNumberScene.js +1 -1
  44. package/lib/module/games/flappy-bird/FlappyBird.js +1 -1
  45. package/lib/module/games/flappy-bird/FlappyBirdScene.js +1 -1
  46. package/lib/module/games/fruit-merger/FruitMerger.js +1 -1
  47. package/lib/module/games/fruit-merger/FruitMergerScene.js +1 -1
  48. package/lib/module/games/fruit-ninja/FruitNinja.js +1 -1
  49. package/lib/module/games/fruit-ninja/FruitNinjaScene.js +1 -1
  50. package/lib/module/games/fruit-ninja/FruitNinjaWorld.js +1 -1
  51. package/lib/module/games/game-2048/Game2048.js +1 -1
  52. package/lib/module/games/game-2048/Game2048Scene.js +1 -1
  53. package/lib/module/games/knife-hit/KnifeHit.js +1 -1
  54. package/lib/module/games/knife-hit/KnifeHitScene.js +1 -1
  55. package/lib/module/games/ludo-king/LudoKing.js +1 -1
  56. package/lib/module/games/ludo-king/LudoKingScene.js +1 -1
  57. package/lib/module/games/maze-runner/MazeRunner.js +1 -1
  58. package/lib/module/games/maze-runner/MazeRunnerScene.js +1 -1
  59. package/lib/module/games/memory-match/MemoryMatch.js +1 -1
  60. package/lib/module/games/memory-match/MemoryMatchScene.js +1 -1
  61. package/lib/module/games/mine-sweeper/MineSweeper.js +1 -1
  62. package/lib/module/games/mine-sweeper/MineSweeperScene.js +1 -1
  63. package/lib/module/games/mob-control/MobControl.js +1 -1
  64. package/lib/module/games/mob-control/MobControlGame.js +1 -1
  65. package/lib/module/games/mob-control/MobControlMeshes.js +1 -1
  66. package/lib/module/games/mob-control/MobControlRenderer.js +1 -1
  67. package/lib/module/games/mob-control/MobControlRules.js +1 -1
  68. package/lib/module/games/mob-control/MobControlScene.js +1 -1
  69. package/lib/module/games/nuts-and-bolts/NutsAndBolts.js +1 -1
  70. package/lib/module/games/nuts-and-bolts/NutsAndBoltsScene.js +1 -1
  71. package/lib/module/games/pac-man/PacMan.js +1 -1
  72. package/lib/module/games/pac-man/PacManGame.js +1 -1
  73. package/lib/module/games/pac-man/PacManScene.js +1 -1
  74. package/lib/module/games/paper-io/PaperIo.js +1 -1
  75. package/lib/module/games/paper-io/PaperIoMeshes.js +1 -1
  76. package/lib/module/games/paper-io/PaperIoScene.js +1 -1
  77. package/lib/module/games/penguin-run/PenguinMeshes.js +1 -1
  78. package/lib/module/games/penguin-run/PenguinRun.js +1 -1
  79. package/lib/module/games/penguin-run/PenguinRunScene.js +1 -1
  80. package/lib/module/games/perfect-circle/PerfectCircle.js +1 -1
  81. package/lib/module/games/perfect-circle/PerfectCircleScene.js +1 -1
  82. package/lib/module/games/pipe-connect/PipeConnect.js +1 -1
  83. package/lib/module/games/pipe-connect/PipeConnectScene.js +1 -1
  84. package/lib/module/games/play-with-tua/PlayWithTua.js +1 -1
  85. package/lib/module/games/popit-fidget/PopitFidget.js +1 -1
  86. package/lib/module/games/popit-fidget/PopitFidgetScene.js +1 -1
  87. package/lib/module/games/reversi/Reversi.js +1 -1
  88. package/lib/module/games/reversi/ReversiScene.js +1 -1
  89. package/lib/module/games/search-grid/SearchGrid.js +1 -1
  90. package/lib/module/games/search-grid/SearchGridScene.js +1 -1
  91. package/lib/module/games/shared/BackdropCard.js +1 -1
  92. package/lib/module/games/shared/EngineBackdrop.js +1 -1
  93. package/lib/module/games/shared/EngineBlasts.js +1 -1
  94. package/lib/module/games/shared/EngineBlocks.js +1 -1
  95. package/lib/module/games/shared/EngineDice.js +1 -1
  96. package/lib/module/games/shared/EngineFruits.js +1 -1
  97. package/lib/module/games/shared/EngineGameLoader.js +1 -1
  98. package/lib/module/games/shared/EngineGameScreen.js +1 -1
  99. package/lib/module/games/shared/EngineGlyphs.js +1 -1
  100. package/lib/module/games/shared/EngineHud.js +1 -1
  101. package/lib/module/games/shared/EngineMeadow.js +1 -1
  102. package/lib/module/games/shared/EngineTouchSurface.js +1 -1
  103. package/lib/module/games/shared/GameActivity.js +1 -1
  104. package/lib/module/games/shared/GameLoading.js +1 -1
  105. package/lib/module/games/shared/GameLoadingTransition.js +1 -1
  106. package/lib/module/games/sliding-numbers/SlidingNumbers.js +1 -1
  107. package/lib/module/games/sliding-numbers/SlidingNumbersScene.js +1 -1
  108. package/lib/module/games/snake-3d/Snake3D.js +1 -1
  109. package/lib/module/games/snake-3d/Snake3DScene.js +1 -1
  110. package/lib/module/games/snakes-and-ladders/SnakesAndLadders.js +1 -1
  111. package/lib/module/games/snakes-and-ladders/SnakesAndLaddersScene.js +1 -1
  112. package/lib/module/games/space-fighter/SpaceFighter.js +1 -1
  113. package/lib/module/games/space-fighter/SpaceFighterScene.js +1 -1
  114. package/lib/module/games/spider-solitaire/SpiderSolitaire.js +1 -1
  115. package/lib/module/games/spider-solitaire/SpiderSolitaireScene.js +1 -1
  116. package/lib/module/games/stack-tower/StackTower.js +1 -1
  117. package/lib/module/games/stack-tower/StackTowerScene.js +1 -1
  118. package/lib/module/games/sudoku/Sudoku.js +1 -1
  119. package/lib/module/games/sudoku/SudokuScene.js +1 -1
  120. package/lib/module/games/tangram/Tangram.js +1 -1
  121. package/lib/module/games/tangram/TangramScene.js +1 -1
  122. package/lib/module/games/tank-1990/Tank1990.js +1 -1
  123. package/lib/module/games/tank-1990/Tank1990Scene.js +1 -1
  124. package/lib/module/games/tic-tac-toe/TicTacToe.js +1 -1
  125. package/lib/module/games/tic-tac-toe/TicTacToeScene.js +1 -1
  126. package/lib/module/games/tile-home/TileHome.js +1 -1
  127. package/lib/module/games/tile-home/TileHomeScene.js +1 -1
  128. package/lib/module/games/treasure-hunt/TreasureHunt.js +1 -1
  129. package/lib/module/games/treasure-hunt/TreasureHuntConstants.js +1 -1
  130. package/lib/module/games/treasure-hunt/TreasureHuntModel.js +1 -1
  131. package/lib/module/games/treasure-hunt/TreasureHuntScene.js +1 -1
  132. package/lib/module/games/whack-a-mole/WhackAMole.js +1 -1
  133. package/lib/module/games/whack-a-mole/WhackAMoleScene.js +1 -1
  134. package/lib/module/helpers/GameSettingsModal.js +1 -1
  135. package/lib/module/helpers/GameTheme.js +1 -1
  136. package/package.json +1 -2
  137. package/src/assets/fonts/Inter-SemiBold.ttf +0 -0
  138. package/src/assets/models/explorer/explorer.glb +0 -0
  139. package/src/assets/models/explorer/params.json +0 -51
  140. package/src/assets/models/explorer/source.json +0 -222
  141. package/src/assets/models/play-with-tua/play-with-tua.glb +0 -0
  142. package/src/assets/models/play-with-tua/sources.json +0 -141
  143. package/src/assets/sfx/balloon_pop.wav +0 -0
  144. package/src/assets/sfx/blocked.wav +0 -0
  145. package/src/assets/sfx/boing.wav +0 -0
  146. package/src/assets/sfx/bonus.wav +0 -0
  147. package/src/assets/sfx/boom.wav +0 -0
  148. package/src/assets/sfx/bump.wav +0 -0
  149. package/src/assets/sfx/cannon.wav +0 -0
  150. package/src/assets/sfx/capture.wav +0 -0
  151. package/src/assets/sfx/card_flip.wav +0 -0
  152. package/src/assets/sfx/card_place.wav +0 -0
  153. package/src/assets/sfx/catch.wav +0 -0
  154. package/src/assets/sfx/chime.wav +0 -0
  155. package/src/assets/sfx/chomp.wav +0 -0
  156. package/src/assets/sfx/chomp_alt.wav +0 -0
  157. package/src/assets/sfx/clack.wav +0 -0
  158. package/src/assets/sfx/clang.wav +0 -0
  159. package/src/assets/sfx/clash.wav +0 -0
  160. package/src/assets/sfx/click.wav +0 -0
  161. package/src/assets/sfx/coin.wav +0 -0
  162. package/src/assets/sfx/combo.wav +0 -0
  163. package/src/assets/sfx/crash.wav +0 -0
  164. package/src/assets/sfx/crunch.wav +0 -0
  165. package/src/assets/sfx/dance.wav +0 -0
  166. package/src/assets/sfx/deselect.wav +0 -0
  167. package/src/assets/sfx/dice_roll.wav +0 -0
  168. package/src/assets/sfx/die.wav +0 -0
  169. package/src/assets/sfx/draw.wav +0 -0
  170. package/src/assets/sfx/drop.wav +0 -0
  171. package/src/assets/sfx/eat.wav +0 -0
  172. package/src/assets/sfx/engine.wav +0 -0
  173. package/src/assets/sfx/error.wav +0 -0
  174. package/src/assets/sfx/flag.wav +0 -0
  175. package/src/assets/sfx/flap.wav +0 -0
  176. package/src/assets/sfx/flip_disc.wav +0 -0
  177. package/src/assets/sfx/footstep_run.wav +0 -0
  178. package/src/assets/sfx/footstep_walk.wav +0 -0
  179. package/src/assets/sfx/game_over.wav +0 -0
  180. package/src/assets/sfx/gate.wav +0 -0
  181. package/src/assets/sfx/hit.wav +0 -0
  182. package/src/assets/sfx/hop.wav +0 -0
  183. package/src/assets/sfx/hurt.wav +0 -0
  184. package/src/assets/sfx/invalid.wav +0 -0
  185. package/src/assets/sfx/jump.wav +0 -0
  186. package/src/assets/sfx/ladder.wav +0 -0
  187. package/src/assets/sfx/land.wav +0 -0
  188. package/src/assets/sfx/laugh.wav +0 -0
  189. package/src/assets/sfx/lose.wav +0 -0
  190. package/src/assets/sfx/merge.wav +0 -0
  191. package/src/assets/sfx/miss.wav +0 -0
  192. package/src/assets/sfx/nitro.wav +0 -0
  193. package/src/assets/sfx/pew.wav +0 -0
  194. package/src/assets/sfx/place.wav +0 -0
  195. package/src/assets/sfx/pling.wav +0 -0
  196. package/src/assets/sfx/point.wav +0 -0
  197. package/src/assets/sfx/pop.wav +0 -0
  198. package/src/assets/sfx/pop_soft.wav +0 -0
  199. package/src/assets/sfx/power_up.wav +0 -0
  200. package/src/assets/sfx/reveal.wav +0 -0
  201. package/src/assets/sfx/robot.wav +0 -0
  202. package/src/assets/sfx/rotate.wav +0 -0
  203. package/src/assets/sfx/run_steps.wav +0 -0
  204. package/src/assets/sfx/select.wav +0 -0
  205. package/src/assets/sfx/shuffle.wav +0 -0
  206. package/src/assets/sfx/six.wav +0 -0
  207. package/src/assets/sfx/slice.wav +0 -0
  208. package/src/assets/sfx/slide.wav +0 -0
  209. package/src/assets/sfx/snake.wav +0 -0
  210. package/src/assets/sfx/snap.wav +0 -0
  211. package/src/assets/sfx/splash.wav +0 -0
  212. package/src/assets/sfx/star.wav +0 -0
  213. package/src/assets/sfx/start.wav +0 -0
  214. package/src/assets/sfx/swoosh.wav +0 -0
  215. package/src/assets/sfx/tap.wav +0 -0
  216. package/src/assets/sfx/thud.wav +0 -0
  217. package/src/assets/sfx/thunk.wav +0 -0
  218. package/src/assets/sfx/tick.wav +0 -0
  219. package/src/assets/sfx/time_up.wav +0 -0
  220. package/src/assets/sfx/tock.wav +0 -0
  221. package/src/assets/sfx/walk_steps.wav +0 -0
  222. package/src/assets/sfx/warning.wav +0 -0
  223. package/src/assets/sfx/whack.wav +0 -0
  224. package/src/assets/sfx/whoosh.wav +0 -0
  225. package/src/assets/sfx/win.wav +0 -0
  226. package/src/assets/sfx/zap.wav +0 -0
  227. package/src/assets/textures/ad_bolt.png +0 -0
  228. package/src/assets/textures/ad_bubbles.png +0 -0
  229. package/src/assets/textures/ad_chevrons.png +0 -0
  230. package/src/assets/textures/asphalt.png +0 -0
  231. package/src/assets/textures/bark.jpg +0 -0
  232. package/src/assets/textures/explorer-atlas.png +0 -0
  233. package/src/assets/textures/facade.png +0 -0
  234. package/src/assets/textures/grass.png +0 -0
  235. package/src/assets/textures/leaves.png +0 -0
  236. package/src/assets/textures/limestone.jpg +0 -0
  237. package/src/assets/textures/meadow.jpg +0 -0
  238. package/src/assets/textures/nature-sources.json +0 -87
  239. package/src/assets/textures/pine.png +0 -0
  240. package/src/assets/textures/river.jpg +0 -0
  241. package/src/assets/textures/soft_glow.png +0 -0
  242. package/src/games/arrows/Arrows.tsx +0 -69
  243. package/src/games/arrows/ArrowsConstants.ts +0 -107
  244. package/src/games/arrows/ArrowsGame.ts +0 -735
  245. package/src/games/arrows/ArrowsScene.ts +0 -395
  246. package/src/games/balloon-blaster/BalloonBlaster.tsx +0 -75
  247. package/src/games/balloon-blaster/BalloonBlasterConstants.ts +0 -79
  248. package/src/games/balloon-blaster/BalloonBlasterGame.ts +0 -287
  249. package/src/games/balloon-blaster/BalloonBlasterPhysics.ts +0 -95
  250. package/src/games/balloon-blaster/BalloonBlasterScene.ts +0 -225
  251. package/src/games/bike-racing/BikeMeshes.ts +0 -273
  252. package/src/games/bike-racing/BikeRacing.tsx +0 -211
  253. package/src/games/bike-racing/BikeRacingConstants.ts +0 -103
  254. package/src/games/bike-racing/BikeRacingGame.ts +0 -557
  255. package/src/games/bike-racing/BikeRacingScene.ts +0 -119
  256. package/src/games/block-blast/BlockBlast.tsx +0 -73
  257. package/src/games/block-blast/BlockBlastConstants.ts +0 -179
  258. package/src/games/block-blast/BlockBlastGame.ts +0 -471
  259. package/src/games/block-blast/BlockBlastScene.ts +0 -156
  260. package/src/games/block-breaker/BlockBreaker.tsx +0 -79
  261. package/src/games/block-breaker/BlockBreakerConstants.ts +0 -139
  262. package/src/games/block-breaker/BlockBreakerGame.ts +0 -459
  263. package/src/games/block-breaker/BlockBreakerScene.ts +0 -257
  264. package/src/games/bluk/Bluk.tsx +0 -82
  265. package/src/games/bluk/BlukConstants.ts +0 -121
  266. package/src/games/bluk/BlukGame.ts +0 -323
  267. package/src/games/bluk/BlukRules.ts +0 -154
  268. package/src/games/bluk/BlukScene.ts +0 -514
  269. package/src/games/bubble-shooter/BubbleShooter.tsx +0 -76
  270. package/src/games/bubble-shooter/BubbleShooterConstants.ts +0 -103
  271. package/src/games/bubble-shooter/BubbleShooterGame.ts +0 -459
  272. package/src/games/bubble-shooter/BubbleShooterScene.ts +0 -158
  273. package/src/games/candy-crush/CandyCrush.tsx +0 -77
  274. package/src/games/candy-crush/CandyCrushConstants.ts +0 -94
  275. package/src/games/candy-crush/CandyCrushGame.ts +0 -477
  276. package/src/games/candy-crush/CandyCrushScene.ts +0 -209
  277. package/src/games/car-racing/CarMeshes.ts +0 -440
  278. package/src/games/car-racing/CarRacing.tsx +0 -211
  279. package/src/games/car-racing/CarRacingConstants.ts +0 -167
  280. package/src/games/car-racing/CarRacingFacade.ts +0 -40
  281. package/src/games/car-racing/CarRacingGame.ts +0 -539
  282. package/src/games/car-racing/CarRacingScene.ts +0 -1026
  283. package/src/games/car-racing/CarRacingScenery.ts +0 -265
  284. package/src/games/chameleon-run/ChameleonRun.tsx +0 -96
  285. package/src/games/chameleon-run/ChameleonRunBackdrop.ts +0 -186
  286. package/src/games/chameleon-run/ChameleonRunConstants.ts +0 -47
  287. package/src/games/chameleon-run/ChameleonRunGame.ts +0 -309
  288. package/src/games/chameleon-run/ChameleonRunMeshes.ts +0 -305
  289. package/src/games/chameleon-run/ChameleonRunRules.ts +0 -252
  290. package/src/games/chameleon-run/ChameleonRunScene.ts +0 -310
  291. package/src/games/checkers/Checkers.tsx +0 -83
  292. package/src/games/checkers/CheckersAI.ts +0 -255
  293. package/src/games/checkers/CheckersConstants.ts +0 -68
  294. package/src/games/checkers/CheckersGame.ts +0 -322
  295. package/src/games/checkers/CheckersRules.ts +0 -167
  296. package/src/games/checkers/CheckersScene.ts +0 -308
  297. package/src/games/color-switch/ColorSwitch.tsx +0 -75
  298. package/src/games/color-switch/ColorSwitchConstants.ts +0 -61
  299. package/src/games/color-switch/ColorSwitchGame.ts +0 -329
  300. package/src/games/color-switch/ColorSwitchScene.ts +0 -220
  301. package/src/games/colors-sort/ColorsSort.tsx +0 -82
  302. package/src/games/colors-sort/ColorsSortConstants.ts +0 -119
  303. package/src/games/colors-sort/ColorsSortGame.ts +0 -246
  304. package/src/games/colors-sort/ColorsSortScene.ts +0 -315
  305. package/src/games/connect-em-all/ConnectEmAll.tsx +0 -70
  306. package/src/games/connect-em-all/ConnectEmAllConstants.ts +0 -77
  307. package/src/games/connect-em-all/ConnectEmAllGame.ts +0 -401
  308. package/src/games/connect-em-all/ConnectEmAllScene.ts +0 -235
  309. package/src/games/crossy-road/CrossyRoad.tsx +0 -74
  310. package/src/games/crossy-road/CrossyRoadCamera.ts +0 -59
  311. package/src/games/crossy-road/CrossyRoadConstants.ts +0 -132
  312. package/src/games/crossy-road/CrossyRoadGame.ts +0 -293
  313. package/src/games/crossy-road/CrossyRoadMeshes.ts +0 -195
  314. package/src/games/crossy-road/CrossyRoadPerson.ts +0 -198
  315. package/src/games/crossy-road/CrossyRoadRules.ts +0 -99
  316. package/src/games/crossy-road/CrossyRoadScene.ts +0 -355
  317. package/src/games/dino-jump/DinoJump.tsx +0 -77
  318. package/src/games/dino-jump/DinoJumpConstants.ts +0 -80
  319. package/src/games/dino-jump/DinoJumpGame.ts +0 -344
  320. package/src/games/dino-jump/DinoJumpScene.ts +0 -686
  321. package/src/games/dots-and-boxes/DotsAndBoxes.tsx +0 -83
  322. package/src/games/dots-and-boxes/DotsAndBoxesConstants.ts +0 -77
  323. package/src/games/dots-and-boxes/DotsAndBoxesGame.ts +0 -271
  324. package/src/games/dots-and-boxes/DotsAndBoxesScene.ts +0 -249
  325. package/src/games/falling-sticks/FallingSticks.tsx +0 -72
  326. package/src/games/falling-sticks/FallingSticksConstants.ts +0 -62
  327. package/src/games/falling-sticks/FallingSticksGame.ts +0 -220
  328. package/src/games/falling-sticks/FallingSticksRules.ts +0 -145
  329. package/src/games/falling-sticks/FallingSticksScene.ts +0 -245
  330. package/src/games/find-different-number/FindDifferentNumber.tsx +0 -74
  331. package/src/games/find-different-number/FindDifferentNumberConstants.ts +0 -74
  332. package/src/games/find-different-number/FindDifferentNumberGame.ts +0 -174
  333. package/src/games/find-different-number/FindDifferentNumberRules.ts +0 -43
  334. package/src/games/find-different-number/FindDifferentNumberScene.ts +0 -193
  335. package/src/games/flappy-bird/FlappyBird.tsx +0 -73
  336. package/src/games/flappy-bird/FlappyBirdConstants.ts +0 -95
  337. package/src/games/flappy-bird/FlappyBirdGame.ts +0 -375
  338. package/src/games/flappy-bird/FlappyBirdScene.ts +0 -630
  339. package/src/games/fruit-merger/FruitMerger.tsx +0 -73
  340. package/src/games/fruit-merger/FruitMergerConstants.ts +0 -102
  341. package/src/games/fruit-merger/FruitMergerGame.ts +0 -285
  342. package/src/games/fruit-merger/FruitMergerRules.ts +0 -286
  343. package/src/games/fruit-merger/FruitMergerScene.ts +0 -498
  344. package/src/games/fruit-ninja/FruitNinja.tsx +0 -129
  345. package/src/games/fruit-ninja/FruitNinjaConstants.ts +0 -107
  346. package/src/games/fruit-ninja/FruitNinjaGame.ts +0 -391
  347. package/src/games/fruit-ninja/FruitNinjaScene.ts +0 -319
  348. package/src/games/fruit-ninja/FruitNinjaWorld.ts +0 -17
  349. package/src/games/game-2048/Game2048.tsx +0 -84
  350. package/src/games/game-2048/Game2048Constants.ts +0 -68
  351. package/src/games/game-2048/Game2048Game.ts +0 -181
  352. package/src/games/game-2048/Game2048Rules.ts +0 -101
  353. package/src/games/game-2048/Game2048Scene.ts +0 -206
  354. package/src/games/knife-hit/KnifeHit.tsx +0 -78
  355. package/src/games/knife-hit/KnifeHitConstants.ts +0 -89
  356. package/src/games/knife-hit/KnifeHitGame.ts +0 -264
  357. package/src/games/knife-hit/KnifeHitScene.ts +0 -360
  358. package/src/games/ludo-king/LudoKing.tsx +0 -86
  359. package/src/games/ludo-king/LudoKingConstants.ts +0 -212
  360. package/src/games/ludo-king/LudoKingGame.ts +0 -326
  361. package/src/games/ludo-king/LudoKingRules.ts +0 -143
  362. package/src/games/ludo-king/LudoKingScene.ts +0 -397
  363. package/src/games/maze-runner/MazeRunner.tsx +0 -88
  364. package/src/games/maze-runner/MazeRunnerConstants.ts +0 -65
  365. package/src/games/maze-runner/MazeRunnerGame.ts +0 -176
  366. package/src/games/maze-runner/MazeRunnerRules.ts +0 -161
  367. package/src/games/maze-runner/MazeRunnerScene.ts +0 -245
  368. package/src/games/memory-match/MemoryMatch.tsx +0 -77
  369. package/src/games/memory-match/MemoryMatchConstants.ts +0 -105
  370. package/src/games/memory-match/MemoryMatchGame.ts +0 -222
  371. package/src/games/memory-match/MemoryMatchRules.ts +0 -38
  372. package/src/games/memory-match/MemoryMatchScene.ts +0 -268
  373. package/src/games/mine-sweeper/MineSweeper.tsx +0 -77
  374. package/src/games/mine-sweeper/MineSweeperConstants.ts +0 -94
  375. package/src/games/mine-sweeper/MineSweeperGame.ts +0 -234
  376. package/src/games/mine-sweeper/MineSweeperRules.ts +0 -67
  377. package/src/games/mine-sweeper/MineSweeperScene.ts +0 -262
  378. package/src/games/mob-control/MobControl.tsx +0 -73
  379. package/src/games/mob-control/MobControlConstants.ts +0 -106
  380. package/src/games/mob-control/MobControlGame.ts +0 -176
  381. package/src/games/mob-control/MobControlMeshes.ts +0 -85
  382. package/src/games/mob-control/MobControlRenderer.ts +0 -108
  383. package/src/games/mob-control/MobControlRules.ts +0 -522
  384. package/src/games/mob-control/MobControlScene.ts +0 -399
  385. package/src/games/number-search/NumberSearch.tsx +0 -18
  386. package/src/games/number-search/NumberSearchNumbers.ts +0 -623
  387. package/src/games/nuts-and-bolts/NutsAndBolts.tsx +0 -85
  388. package/src/games/nuts-and-bolts/NutsAndBoltsConstants.ts +0 -75
  389. package/src/games/nuts-and-bolts/NutsAndBoltsGame.ts +0 -298
  390. package/src/games/nuts-and-bolts/NutsAndBoltsRules.ts +0 -474
  391. package/src/games/nuts-and-bolts/NutsAndBoltsScene.ts +0 -498
  392. package/src/games/pac-man/PacMan.tsx +0 -86
  393. package/src/games/pac-man/PacManConstants.ts +0 -169
  394. package/src/games/pac-man/PacManGame.ts +0 -406
  395. package/src/games/pac-man/PacManRules.ts +0 -255
  396. package/src/games/pac-man/PacManScene.ts +0 -387
  397. package/src/games/paper-io/PaperIo.tsx +0 -91
  398. package/src/games/paper-io/PaperIoConstants.ts +0 -86
  399. package/src/games/paper-io/PaperIoGame.ts +0 -193
  400. package/src/games/paper-io/PaperIoMeshes.ts +0 -233
  401. package/src/games/paper-io/PaperIoRules.ts +0 -315
  402. package/src/games/paper-io/PaperIoScene.ts +0 -310
  403. package/src/games/penguin-run/PenguinMeshes.ts +0 -269
  404. package/src/games/penguin-run/PenguinModel.ts +0 -712
  405. package/src/games/penguin-run/PenguinRun.tsx +0 -77
  406. package/src/games/penguin-run/PenguinRunConstants.ts +0 -69
  407. package/src/games/penguin-run/PenguinRunGame.ts +0 -406
  408. package/src/games/penguin-run/PenguinRunScene.ts +0 -949
  409. package/src/games/perfect-circle/PerfectCircle.tsx +0 -74
  410. package/src/games/perfect-circle/PerfectCircleConstants.ts +0 -52
  411. package/src/games/perfect-circle/PerfectCircleGame.ts +0 -153
  412. package/src/games/perfect-circle/PerfectCircleRules.ts +0 -111
  413. package/src/games/perfect-circle/PerfectCircleScene.ts +0 -176
  414. package/src/games/pipe-connect/PipeConnect.tsx +0 -69
  415. package/src/games/pipe-connect/PipeConnectConstants.ts +0 -101
  416. package/src/games/pipe-connect/PipeConnectGame.ts +0 -179
  417. package/src/games/pipe-connect/PipeConnectRules.ts +0 -326
  418. package/src/games/pipe-connect/PipeConnectScene.ts +0 -271
  419. package/src/games/play-with-tua/PlayWithTua.tsx +0 -123
  420. package/src/games/play-with-tua/PlayWithTuaConstants.ts +0 -70
  421. package/src/games/play-with-tua/PlayWithTuaGame.ts +0 -167
  422. package/src/games/play-with-tua/PlayWithTuaModel.ts +0 -42
  423. package/src/games/play-with-tua/PlayWithTuaScene.ts +0 -36
  424. package/src/games/popit-fidget/PopitFidget.tsx +0 -72
  425. package/src/games/popit-fidget/PopitFidgetConstants.ts +0 -71
  426. package/src/games/popit-fidget/PopitFidgetGame.ts +0 -148
  427. package/src/games/popit-fidget/PopitFidgetScene.ts +0 -242
  428. package/src/games/reversi/Reversi.tsx +0 -94
  429. package/src/games/reversi/ReversiConstants.ts +0 -67
  430. package/src/games/reversi/ReversiGame.ts +0 -266
  431. package/src/games/reversi/ReversiRules.ts +0 -176
  432. package/src/games/reversi/ReversiScene.ts +0 -240
  433. package/src/games/search-grid/SearchGrid.tsx +0 -92
  434. package/src/games/search-grid/SearchGridConstants.ts +0 -178
  435. package/src/games/search-grid/SearchGridGame.ts +0 -209
  436. package/src/games/search-grid/SearchGridScene.ts +0 -301
  437. package/src/games/shared/BackdropCard.tsx +0 -54
  438. package/src/games/shared/EngineBackdrop.ts +0 -300
  439. package/src/games/shared/EngineBlasts.ts +0 -136
  440. package/src/games/shared/EngineBlocks.ts +0 -91
  441. package/src/games/shared/EngineControlText.ts +0 -17
  442. package/src/games/shared/EngineDice.ts +0 -250
  443. package/src/games/shared/EngineFootprint.ts +0 -35
  444. package/src/games/shared/EngineFruits.ts +0 -1435
  445. package/src/games/shared/EngineGameLoader.tsx +0 -88
  446. package/src/games/shared/EngineGameScreen.tsx +0 -325
  447. package/src/games/shared/EngineGameWidth.ts +0 -3
  448. package/src/games/shared/EngineGameWidth.web.ts +0 -3
  449. package/src/games/shared/EngineGlyphs.ts +0 -178
  450. package/src/games/shared/EngineHud.ts +0 -815
  451. package/src/games/shared/EngineLoading.ts +0 -477
  452. package/src/games/shared/EngineMeadow.ts +0 -725
  453. package/src/games/shared/EngineMeshCache.ts +0 -15
  454. package/src/games/shared/EngineMeshes.ts +0 -10
  455. package/src/games/shared/EngineRacingChase.ts +0 -21
  456. package/src/games/shared/EngineRacingHandling.ts +0 -48
  457. package/src/games/shared/EngineReadyContext.ts +0 -4
  458. package/src/games/shared/EngineTilt.ts +0 -44
  459. package/src/games/shared/EngineTouchSurface.tsx +0 -103
  460. package/src/games/shared/EngineUtils.ts +0 -52
  461. package/src/games/shared/GameActivity.ts +0 -13
  462. package/src/games/shared/GameLoading.tsx +0 -202
  463. package/src/games/shared/GameLoadingTransition.tsx +0 -61
  464. package/src/games/shared/ProceduralCacheVersion.ts +0 -2
  465. package/src/games/shared/useEngineGame.ts +0 -240
  466. package/src/games/shared/useGameResource.ts +0 -21
  467. package/src/games/shared/useLiveState.ts +0 -58
  468. package/src/games/sliding-numbers/SlidingNumbers.tsx +0 -71
  469. package/src/games/sliding-numbers/SlidingNumbersConstants.ts +0 -65
  470. package/src/games/sliding-numbers/SlidingNumbersGame.ts +0 -192
  471. package/src/games/sliding-numbers/SlidingNumbersRules.ts +0 -79
  472. package/src/games/sliding-numbers/SlidingNumbersScene.ts +0 -193
  473. package/src/games/snake-3d/Snake3D.tsx +0 -76
  474. package/src/games/snake-3d/Snake3DConstants.ts +0 -61
  475. package/src/games/snake-3d/Snake3DGame.ts +0 -192
  476. package/src/games/snake-3d/Snake3DMotion.ts +0 -22
  477. package/src/games/snake-3d/Snake3DRules.ts +0 -116
  478. package/src/games/snake-3d/Snake3DScene.ts +0 -322
  479. package/src/games/snakes-and-ladders/SnakesAndLadders.tsx +0 -87
  480. package/src/games/snakes-and-ladders/SnakesAndLaddersConstants.ts +0 -136
  481. package/src/games/snakes-and-ladders/SnakesAndLaddersGame.ts +0 -310
  482. package/src/games/snakes-and-ladders/SnakesAndLaddersScene.ts +0 -515
  483. package/src/games/space-fighter/SpaceFighter.tsx +0 -74
  484. package/src/games/space-fighter/SpaceFighterConstants.ts +0 -86
  485. package/src/games/space-fighter/SpaceFighterGame.ts +0 -292
  486. package/src/games/space-fighter/SpaceFighterScene.ts +0 -550
  487. package/src/games/spider-solitaire/SpiderSolitaire.tsx +0 -80
  488. package/src/games/spider-solitaire/SpiderSolitaireConstants.ts +0 -107
  489. package/src/games/spider-solitaire/SpiderSolitaireGame.ts +0 -387
  490. package/src/games/spider-solitaire/SpiderSolitaireRules.ts +0 -123
  491. package/src/games/spider-solitaire/SpiderSolitaireScene.ts +0 -352
  492. package/src/games/stack-tower/StackTower.tsx +0 -76
  493. package/src/games/stack-tower/StackTowerConstants.ts +0 -74
  494. package/src/games/stack-tower/StackTowerGame.ts +0 -236
  495. package/src/games/stack-tower/StackTowerScene.ts +0 -186
  496. package/src/games/sudoku/Sudoku.tsx +0 -73
  497. package/src/games/sudoku/SudokuConstants.ts +0 -127
  498. package/src/games/sudoku/SudokuGame.ts +0 -250
  499. package/src/games/sudoku/SudokuRules.ts +0 -125
  500. package/src/games/sudoku/SudokuScene.ts +0 -304
  501. package/src/games/tangram/Tangram.tsx +0 -79
  502. package/src/games/tangram/TangramConstants.ts +0 -64
  503. package/src/games/tangram/TangramGame.ts +0 -356
  504. package/src/games/tangram/TangramRules.ts +0 -409
  505. package/src/games/tangram/TangramScene.ts +0 -203
  506. package/src/games/tank-1990/Tank1990.tsx +0 -78
  507. package/src/games/tank-1990/Tank1990Constants.ts +0 -81
  508. package/src/games/tank-1990/Tank1990Game.ts +0 -331
  509. package/src/games/tank-1990/Tank1990Rules.ts +0 -390
  510. package/src/games/tank-1990/Tank1990Scene.ts +0 -434
  511. package/src/games/tic-tac-toe/TicTacToe.tsx +0 -98
  512. package/src/games/tic-tac-toe/TicTacToeConstants.ts +0 -69
  513. package/src/games/tic-tac-toe/TicTacToeGame.ts +0 -200
  514. package/src/games/tic-tac-toe/TicTacToeRules.ts +0 -103
  515. package/src/games/tic-tac-toe/TicTacToeScene.ts +0 -245
  516. package/src/games/tile-home/TileHome.tsx +0 -92
  517. package/src/games/tile-home/TileHomeConstants.ts +0 -139
  518. package/src/games/tile-home/TileHomeGame.ts +0 -435
  519. package/src/games/tile-home/TileHomeScene.ts +0 -154
  520. package/src/games/treasure-hunt/TreasureHunt.tsx +0 -206
  521. package/src/games/treasure-hunt/TreasureHuntConstants.ts +0 -266
  522. package/src/games/treasure-hunt/TreasureHuntControls.ts +0 -280
  523. package/src/games/treasure-hunt/TreasureHuntGame.ts +0 -402
  524. package/src/games/treasure-hunt/TreasureHuntMeshes.ts +0 -422
  525. package/src/games/treasure-hunt/TreasureHuntModel.ts +0 -506
  526. package/src/games/treasure-hunt/TreasureHuntRules.ts +0 -1210
  527. package/src/games/treasure-hunt/TreasureHuntScene.ts +0 -586
  528. package/src/games/whack-a-mole/WhackAMole.tsx +0 -79
  529. package/src/games/whack-a-mole/WhackAMoleConstants.ts +0 -94
  530. package/src/games/whack-a-mole/WhackAMoleGame.ts +0 -233
  531. package/src/games/whack-a-mole/WhackAMoleScene.ts +0 -349
  532. package/src/games/word-search/WordSearch.tsx +0 -18
  533. package/src/games/word-search/WordSearchWords.ts +0 -1642
  534. package/src/helpers/GamePatternData.ts +0 -120
  535. package/src/helpers/GameSettingsModal.tsx +0 -384
  536. package/src/helpers/GameTheme.tsx +0 -37
  537. package/src/helpers/IconPaths.ts +0 -6
  538. package/src/helpers/index.ts +0 -2
  539. package/src/index.tsx +0 -60
  540. package/src/services/GameAssetLoader.ts +0 -111
  541. package/src/services/GameAssets.ts +0 -149
  542. package/src/services/GameLoaders.ts +0 -93
  543. package/src/services/GamesConstants.ts +0 -193
  544. package/src/services/GamesMetadata.ts +0 -711
  545. package/src/services/GamesService.ts +0 -116
  546. package/src/services/HapticsService.ts +0 -48
  547. package/src/services/ScoringService.ts +0 -19
  548. package/src/services/SfxSources.ts +0 -81
  549. package/src/services/SoundsService.ts +0 -582
  550. package/src/services/ThemeService.ts +0 -281
  551. package/src/services/UtilsService.ts +0 -134
  552. package/src/services/index.ts +0 -6
@@ -1,949 +0,0 @@
1
- // Penguin Run's world: an ancient stone causeway running across a jungle chasm, built tile by tile
2
- // ahead of the penguin. Mossy paving between carved kerbs, fluted columns (some broken), stone idols,
3
- // flaming torches, giant jungle trees and the canopy far below, an archway now and then. Also the
4
- // obstacles (broken gaps to jump, spiked gateways to slide under), coins, gems and
5
- // the finish line, all laid out fresh for every run by `generateTrack`.
6
- import {
7
- type Color,
8
- effects,
9
- type Entity,
10
- type GeometryPart,
11
- lathe,
12
- type Light,
13
- merge,
14
- type Model,
15
- quat,
16
- recolor,
17
- shapeGeometry,
18
- tube,
19
- type Vec3,
20
- type World
21
- } from '@nayan-ui/engine';
22
- import type { GameTheme } from '../../services/ThemeService';
23
- import { noise, random } from '../shared/EngineMeadow';
24
- import { cacheGameAsset, createGameMesh as createShape } from '../shared/EngineMeshes';
25
- import { FINISH_CLEAR, LANE_X, SLAB, START_CLEAR, TILE_LENGTH, TILES_BEFORE_START, TILES_PAST_FINISH, TRACK_LENGTH } from './PenguinRunConstants';
26
-
27
- /** The last tile of the causeway: a few past the finish, ending at the far temple. */
28
- export const LAST_TILE = Math.ceil(TRACK_LENGTH / TILE_LENGTH) + TILES_PAST_FINISH - 1;
29
-
30
- const night = (theme: GameTheme) => theme.mode === 'dark';
31
-
32
- const DAY_LIGHT: Light = { direction: [0.4, 0.9, 0.5], ambient: 0.72, color: [1.3, 1.24, 1.12], shadows: true, shadowExtent: 16 };
33
- const NIGHT_LIGHT: Light = { direction: [-0.3, 0.85, 0.5], ambient: 0.66, color: [0.95, 1.0, 1.25], shadows: true, shadowExtent: 16 };
34
- export const lightFor = (theme: GameTheme): Light => (night(theme) ? NIGHT_LIGHT : DAY_LIGHT);
35
- /** Hazy jungle air by day, deep blue by night; also the fog's color. */
36
- export const backgroundFor = (theme: GameTheme): readonly [number, number, number] => (night(theme) ? [0.16, 0.2, 0.32] : [0.78, 0.88, 0.92]);
37
- export const FOG = 0.02;
38
- /** The top of the jungle canopy far below the causeway, where the trees rise from. */
39
- const CANOPY_Y = -8;
40
- /** How far a tree's or palm's crown can reach from its trunk, at its largest scale (metres). */
41
- const TREE_SPREAD = 7.5;
42
-
43
- // ── Meshes ────────────────────────────────────────────────────────────
44
-
45
- type RGB = [number, number, number];
46
- const rgba = (c: RGB, a = 1): Color => [c[0], c[1], c[2], a];
47
- const mix = (a: RGB, b: RGB, t: number): RGB => {
48
- const k = Math.min(1, Math.max(0, t));
49
- return [a[0] + (b[0] - a[0]) * k, a[1] + (b[1] - a[1]) * k, a[2] + (b[2] - a[2]) * k];
50
- };
51
- const STONE: RGB = [0.82, 0.77, 0.66];
52
- const STONE_DARK: RGB = [0.56, 0.52, 0.45];
53
- const MOSS: RGB = [0.42, 0.56, 0.26];
54
- const BARK: RGB = [0.48, 0.34, 0.2];
55
- const LEAF: RGB = [0.26, 0.52, 0.18];
56
- const LEAF_LIGHT: RGB = [0.5, 0.72, 0.28];
57
- const GOLD: RGB = [1, 0.76, 0.2];
58
- const LEAF_DARK: RGB = [0.12, 0.3, 0.1];
59
- const PALM: RGB = [0.34, 0.6, 0.2];
60
-
61
- /** The walkway along each side of the lanes: its centre, width and outer edge (x). Props stand on it. */
62
- const WALK_X = 3.78;
63
- const WALK_WIDTH = 1.44;
64
- const WALK_OUTER = WALK_X + WALK_WIDTH / 2;
65
-
66
- const meshes = new Map<string, Model>();
67
- const cached = (key: string, build: () => Model) => {
68
- let m = meshes.get(key);
69
- if (!m) {
70
- m = cacheGameAsset('penguin-run/PenguinRunScene:' + key, build);
71
- meshes.set(key, m);
72
- }
73
- return m;
74
- };
75
-
76
- /** Weathered stone: darker in the cracks, moss creeping up from below and over the tops. */
77
- const weathered =
78
- (seed: number, mossLevel = 0.5) =>
79
- ({ position: [x, y, z] }: { position: Vec3 }): Color => {
80
- const grain = noise(x * 3.1 + seed, z * 3.1 + y * 2.3);
81
- let c = mix(STONE_DARK, STONE, 0.35 + grain * 0.8);
82
- c = mix(c, MOSS, Math.max(0, noise(x * 1.3 + seed * 2, z * 1.3 + y) - (1 - mossLevel)) * 2.2);
83
- return rgba(c);
84
- };
85
-
86
- /**
87
- * One causeway tile (origin at its near end, running to -TILE_LENGTH): kerbs, a paved walkway along
88
- * each side (where the columns, idols, arches and bushes stand) behind a low broken parapet, vines
89
- * trailing down, and the deep stone pier. The paving is laid separately, slab by slab, so slabs can be
90
- * missing (gaps to jump); under them is a black bed, so a missing slab reads as a crack into the depths.
91
- */
92
- const pathMesh = (variant: number) =>
93
- cached(`pr-path:${variant}`, () => {
94
- const rand = random(40 + variant);
95
- const box = shapeGeometry('roundedBox');
96
- const parts: GeometryPart[] = [
97
- { geometry: shapeGeometry('cube'), position: [0, -0.31, -TILE_LENGTH / 2], scale: [5.3, 0.04, TILE_LENGTH], color: [0.03, 0.025, 0.02, 1] }
98
- ];
99
- for (const side of [-1, 1]) {
100
- // Carved kerbs along the lanes
101
- for (let i = 0; i < 4; i++)
102
- parts.push({
103
- geometry: recolor(box, weathered(i + side * 5, 0.6)),
104
- position: [side * 2.85, 0.1, -1.5 - i * 3],
105
- scale: [0.42, 0.5, 2.94],
106
- color: [1, 1, 1, 1]
107
- });
108
- // The walkway: big worn flagstones, a little below the paving
109
- for (let i = 0; i < 6; i++)
110
- parts.push({
111
- geometry: recolor(box, weathered(20 + i + side * 7 + variant * 13, 0.55)),
112
- position: [side * WALK_X, -0.12 + (rand() - 0.5) * 0.04, -1 - i * 2],
113
- scale: [WALK_WIDTH - 0.04, 0.26, 1.94],
114
- color: [1, 1, 1, 1]
115
- });
116
- // A low parapet on the outer edge, broken here and there
117
- for (let i = 0; i < 4; i++) {
118
- if (rand() < 0.25) continue;
119
- const h = 0.35 + rand() * 0.2;
120
- parts.push({
121
- geometry: recolor(box, weathered(30 + i + side * 3, 0.7)),
122
- position: [side * (WALK_OUTER - 0.18), h / 2, -1.5 - i * 3],
123
- scale: [0.34, h, 2.4 + rand() * 0.4],
124
- color: [1, 1, 1, 1]
125
- });
126
- }
127
- // Vines trailing over the edge and down the pier
128
- for (let i = 0; i < 3; i++) {
129
- const z = -1 - rand() * (TILE_LENGTH - 2);
130
- const len = 2.5 + rand() * 3.5;
131
- const x = side * (WALK_OUTER + 0.05);
132
- parts.push({
133
- geometry: tube(
134
- [
135
- [x - side * 0.3, 0.05, z],
136
- [x + side * 0.08, -0.3, z + 0.1],
137
- [x + side * 0.12, -len * 0.5, z - 0.1],
138
- [x + side * 0.05, -len, z + 0.15]
139
- ],
140
- {
141
- radius: (t) => 0.06 - t * 0.035,
142
- sides: 4,
143
- smoothness: 3,
144
- color: ({ v }) => rgba(mix(LEAF, LEAF_LIGHT, v * 0.6))
145
- }
146
- )
147
- });
148
- }
149
- }
150
- // A carved band under the deck, then the pier falling away into the chasm
151
- parts.push({
152
- geometry: recolor(box, weathered(3, 0.3)),
153
- position: [0, -0.48, -TILE_LENGTH / 2],
154
- scale: [WALK_OUTER * 2 + 0.3, 0.36, TILE_LENGTH + 0.02],
155
- color: [0.85, 0.82, 0.78, 1]
156
- });
157
- parts.push({
158
- geometry: recolor(shapeGeometry('cube'), ({ position: [x, y, z] }) =>
159
- rgba(mix(mix(STONE_DARK, [0.3, 0.3, 0.27], -y * 0.08), MOSS, Math.max(0, noise(x * 0.8, z * 0.8 + y) - 0.55) * 1.6))
160
- ),
161
- position: [0, -4.6, -TILE_LENGTH / 2],
162
- scale: [WALK_OUTER * 2 - 0.4, 8, TILE_LENGTH],
163
- color: [1, 1, 1, 1]
164
- });
165
- return createShape(merge(parts), { center: false, shine: 0.15 });
166
- });
167
-
168
- /** A paving slab (one lane wide, SLAB long), worn at the edges; tinted per slab. */
169
- const slabMesh = (variant: number) =>
170
- cached(`pr-slab:${variant}`, () =>
171
- createShape(
172
- merge([
173
- {
174
- geometry: recolor(shapeGeometry('roundedBox'), weathered(90 + variant * 3, 0.35)),
175
- position: [0, -0.15, 0],
176
- scale: [1.66, 0.3, SLAB - 0.08],
177
- color: [1, 1, 1, 1]
178
- }
179
- ]),
180
- {
181
- center: false,
182
- shine: 0.15
183
- }
184
- )
185
- );
186
-
187
- /** Broken slab ends either side of a gap: jagged stone teeth and loose chips. */
188
- const brokenEdgeMesh = () =>
189
- cached('pr-broken', () => {
190
- const rand = random(97);
191
- const parts: GeometryPart[] = [];
192
- for (const side of [-1, 1])
193
- for (let i = 0; i < 5; i++)
194
- parts.push({
195
- geometry: recolor(shapeGeometry('roundedBox'), weathered(100 + i, 0.3)),
196
- position: [-0.66 + i * 0.33 + (rand() - 0.5) * 0.1, -0.12 - rand() * 0.1, side * (SLAB / 2 - 0.08 - rand() * 0.12)],
197
- rotation: quat.y(rand() * 6.3),
198
- scale: [0.22 + rand() * 0.12, 0.2, 0.18 + rand() * 0.12],
199
- color: [1, 1, 1, 1]
200
- });
201
- return createShape(merge(parts), { center: false, shine: 0.1 });
202
- });
203
-
204
- /** The finish line: a checkered band across the causeway under a golden gateway with a red banner and pennants. */
205
- const finishMesh = () =>
206
- cached('pr-finish', () => {
207
- const box = shapeGeometry('roundedBox');
208
- const checker = recolor(shapeGeometry('cube'), ({ position: [x, , z] }) =>
209
- rgba((Math.floor((x + 0.5) * 12) + Math.floor((z + 0.5) * 3)) % 2 ? [0.96, 0.96, 0.94] : [0.08, 0.08, 0.1])
210
- );
211
- const pennant = (x: number, color: RGB): GeometryPart => ({
212
- geometry: tube(
213
- [
214
- [x, 6.0, 0.4],
215
- [x + 0.06, 5.45, 0.41]
216
- ],
217
- { radius: (t) => 0.14 * (1 - t), shape: (s) => 0.2 + 0.8 * Math.abs(Math.cos(s * Math.PI * 2)), sides: 6, color: rgba(color) }
218
- )
219
- });
220
- return createShape(
221
- merge([
222
- { geometry: checker, position: [0, 0.015, 0], scale: [5.3, 0.03, 1.2], color: [1, 1, 1, 1] },
223
- { geometry: box, position: [-WALK_X, 0.2, 0], scale: [1.1, 0.4, 1.1], color: rgba([0.8, 0.6, 0.16]) },
224
- { geometry: box, position: [WALK_X, 0.2, 0], scale: [1.1, 0.4, 1.1], color: rgba([0.8, 0.6, 0.16]) },
225
- { geometry: box, position: [-WALK_X, 3.1, 0], scale: [0.6, 6.2, 0.6], color: rgba(GOLD) },
226
- { geometry: box, position: [WALK_X, 3.1, 0], scale: [0.6, 6.2, 0.6], color: rgba(GOLD) },
227
- { geometry: box, position: [0, 6.25, 0], scale: [WALK_X * 2 + 0.8, 0.5, 0.7], color: rgba(GOLD) },
228
- { geometry: box, position: [0, 5.35, 0.1], scale: [5.6, 1.1, 0.12], color: rgba([0.85, 0.12, 0.14]) },
229
- { geometry: box, position: [0, 5.35, 0.17], scale: [4.6, 0.12, 0.04], color: rgba([1, 0.9, 0.5]) },
230
- ...[-2.6, -1.3, 0, 1.3, 2.6].map((x, i) =>
231
- pennant(
232
- x,
233
- [
234
- [0.2, 0.5, 0.95],
235
- [1, 0.8, 0.15],
236
- [0.2, 0.75, 0.3]
237
- ][i % 3] as RGB
238
- )
239
- )
240
- ]),
241
- { center: false, shine: 0.5 }
242
- );
243
- });
244
-
245
- /** The start line: a white band across the causeway. */
246
- const startMesh = () =>
247
- cached('pr-start', () =>
248
- createShape(merge([{ geometry: shapeGeometry('cube'), position: [0, 0.015, 0], scale: [5.3, 0.03, 0.5], color: rgba([0.96, 0.96, 0.92]) }]), {
249
- center: false,
250
- shine: 0.3
251
- })
252
- );
253
-
254
- /** A stepped jungle temple filling the causeway's end, so the track never just stops in the air. */
255
- const templeMesh = () =>
256
- cached('pr-temple', () => {
257
- const box = shapeGeometry('roundedBox');
258
- const stone = (seed: number, moss = 0.5) => recolor(box, weathered(seed, moss));
259
- const steps: GeometryPart[] = [0, 1, 2, 3].map((i) => ({
260
- geometry: stone(150 + i, 0.6 - i * 0.1),
261
- position: [0, -2 + i * 2.6, 0],
262
- scale: [22 - i * 4.5, 2.6, 10 - i * 1.8],
263
- color: [1, 1, 1, 1]
264
- }));
265
- return createShape(
266
- merge([
267
- { geometry: stone(160, 0.3), position: [0, -8, 0], scale: [24, 12, 12], color: [0.85, 0.82, 0.78, 1] },
268
- ...steps,
269
- { geometry: stone(161, 0.3), position: [0, 10.2, 0], scale: [6, 3.2, 4.4], color: [1, 1, 1, 1] },
270
- { geometry: box, position: [0, 9.6, 2.22], scale: [2, 2.4, 0.1], color: [0.05, 0.04, 0.03, 1] },
271
- { geometry: box, position: [0, 12.1, 0], scale: [6.6, 0.5, 5], color: rgba(GOLD) }
272
- ]),
273
- { center: false, shine: 0.15 }
274
- );
275
- });
276
-
277
- /** A fluted column on a square plinth with a carved capital; `broken` snaps it off part way up. */
278
- const columnMesh = (broken: boolean) =>
279
- cached(`pr-column:${broken}`, () => {
280
- const top = broken ? 2.2 : 4.4;
281
- const box = shapeGeometry('roundedBox');
282
- const shaft = lathe(
283
- [
284
- [0.001, top],
285
- [0.36, top - (broken ? 0.25 : 0)],
286
- [0.38, top - 0.3],
287
- [0.4, 0.8],
288
- [0.44, 0.55]
289
- ],
290
- {
291
- segments: 24,
292
- displace: (u, v) => Math.cos(u * Math.PI * 2 * 12) * 0.018 + (broken && v < 0.15 ? (noise(u * 9, 1) - 0.5) * 0.4 : 0),
293
- color: weathered(broken ? 7 : 2, 0.45)
294
- }
295
- );
296
- const parts: GeometryPart[] = [
297
- { geometry: recolor(box, weathered(1, 0.7)), position: [0, 0.28, 0], scale: [1.2, 0.56, 1.2], color: [1, 1, 1, 1] },
298
- { geometry: shaft }
299
- ];
300
- if (!broken) {
301
- parts.push({ geometry: recolor(box, weathered(4, 0.3)), position: [0, top + 0.12, 0], scale: [1.1, 0.24, 1.1], color: [1, 1, 1, 1] });
302
- parts.push({
303
- geometry: recolor(shapeGeometry('cylinder'), weathered(5, 0.3)),
304
- position: [0, top - 0.1, 0],
305
- scale: [0.95, 0.22, 0.95],
306
- color: [1, 1, 1, 1]
307
- });
308
- } else {
309
- // Fallen drum lying at its foot
310
- parts.push({
311
- geometry: recolor(shapeGeometry('cylinder'), weathered(8, 0.6)),
312
- position: [0.55, 0.32, 0.5],
313
- rotation: quat.z(Math.PI / 2),
314
- scale: [0.72, 0.8, 0.72],
315
- color: [1, 1, 1, 1]
316
- });
317
- }
318
- return createShape(merge(parts), { center: false, shine: 0.1 });
319
- });
320
-
321
- /** A stone idol: a great carved head with heavy brows, hollow eyes and a grim mouth, on a plinth. */
322
- const idolMesh = () =>
323
- cached('pr-idol', () => {
324
- const box = shapeGeometry('roundedBox');
325
- const stone = (seed: number) => recolor(box, weathered(seed, 0.55));
326
- const dark: Color = [0.12, 0.11, 0.1, 1];
327
- return createShape(
328
- merge([
329
- { geometry: stone(1), position: [0, 0.35, 0], scale: [1.5, 0.7, 1.3], color: [1, 1, 1, 1] },
330
- { geometry: stone(2), position: [0, 1.5, 0], scale: [1.2, 1.6, 1.05], color: [1, 1, 1, 1] },
331
- { geometry: stone(3), position: [0, 2.05, 0.5], scale: [1.15, 0.22, 0.2], color: [1, 1, 1, 1] },
332
- { geometry: box, position: [-0.28, 1.82, 0.52], scale: [0.3, 0.18, 0.08], color: dark },
333
- { geometry: box, position: [0.28, 1.82, 0.52], scale: [0.3, 0.18, 0.08], color: dark },
334
- { geometry: stone(4), position: [0, 1.5, 0.58], scale: [0.26, 0.5, 0.2], color: [1, 1, 1, 1] },
335
- { geometry: box, position: [0, 1.05, 0.53], scale: [0.62, 0.12, 0.08], color: dark },
336
- { geometry: stone(5), position: [-0.64, 1.55, 0], scale: [0.14, 0.6, 0.35], color: [1, 1, 1, 1] },
337
- { geometry: stone(6), position: [0.64, 1.55, 0], scale: [0.14, 0.6, 0.35], color: [1, 1, 1, 1] }
338
- ]),
339
- { center: false, shine: 0.1 }
340
- );
341
- });
342
-
343
- /** A bronze torch stand: a twisted post and a bowl at the top (the flame is a separate glowing sprite). */
344
- const torchMesh = () =>
345
- cached('pr-torch', () =>
346
- createShape(
347
- merge([
348
- {
349
- geometry: tube(
350
- [
351
- [0, 0, 0],
352
- [0.03, 0.6, 0.02],
353
- [-0.02, 1.2, 0],
354
- [0, 1.55, 0]
355
- ],
356
- { radius: (t) => 0.07 - t * 0.025, sides: 8, color: rgba([0.35, 0.24, 0.12]) }
357
- )
358
- },
359
- {
360
- geometry: lathe(
361
- [
362
- [0.001, 1.5],
363
- [0.18, 1.55],
364
- [0.26, 1.75],
365
- [0.22, 1.76],
366
- [0.001, 1.65]
367
- ],
368
- { segments: 14, color: rgba([0.55, 0.38, 0.16]) }
369
- )
370
- },
371
- { geometry: shapeGeometry('cylinder'), position: [0, 0.05, 0], scale: [0.35, 0.1, 0.35], color: rgba([0.3, 0.22, 0.12]) }
372
- ]),
373
- { center: false, shine: 0.6 }
374
- )
375
- );
376
-
377
- /**
378
- * A lumpy clump of leaves about one unit across: a sphere pushed in and out (the same every time for a
379
- * seed), dark underneath and inside, catching the light on top.
380
- */
381
- const leafClump = (seed: number, dark: RGB = LEAF_DARK, light: RGB = LEAF_LIGHT) => {
382
- const profile: [number, number][] = Array.from({ length: 7 }, (_, i) => {
383
- const a = Math.PI / 2 - (i / 6) * Math.PI;
384
- return [Math.max(0.001, Math.cos(a) * 0.5), Math.sin(a) * 0.5];
385
- });
386
- return lathe(profile, {
387
- segments: 10,
388
- // Periodic around (no seam): big lumps, then smaller ones
389
- displace: (u, v) =>
390
- 0.08 * Math.sin(u * Math.PI * 2 * 3 + seed) * Math.sin(v * Math.PI * 3 + seed * 0.7) +
391
- 0.04 * Math.sin(u * Math.PI * 2 * 7 + seed * 1.9) * Math.sin(v * Math.PI * 6),
392
- color: ({ position: [x, y, z] }) => rgba(mix(dark, light, 0.35 + y * 1.1 + (noise(x * 5 + seed, z * 5 - seed) - 0.5) * 0.5))
393
- });
394
- };
395
-
396
- /** A small round bead one unit across (blossoms, coconuts): a handful of triangles, unlike the built-in sphere. */
397
- const bead = () =>
398
- lathe(
399
- [
400
- [0.001, 0.5],
401
- [0.35, 0.35],
402
- [0.5, 0],
403
- [0.35, -0.35],
404
- [0.001, -0.5]
405
- ],
406
- { segments: 6 }
407
- );
408
-
409
- /** Bark: deep grooves running up the trunk, moss on its lower part. */
410
- const bark =
411
- (seed: number) =>
412
- ({ u, v }: { u: number; v: number }) =>
413
- rgba(mix(mix([0.3, 0.21, 0.13], BARK, 0.4 + noise(u * 14 + seed, v * 3) * 0.8), MOSS, Math.max(0, 0.35 - v) * 1.6 * noise(u * 6, v * 9 + seed)));
414
-
415
- /**
416
- * A giant jungle tree rising out of the canopy below (its base at the origin, about 15 high): a tall,
417
- * slightly leaning trunk flaring into buttress roots, limbs branching off its upper half, each ending in
418
- * lumpy clumps of leaves, and lianas hanging from them.
419
- */
420
- const treeMesh = (variant: number) =>
421
- cached(`pr-tree:${variant}`, () => {
422
- const rand = random(70 + variant);
423
- const lean = (rand() - 0.5) * 0.8;
424
- const height = 13 + rand() * 2;
425
- const at = (t: number): Vec3 => [lean * t * t, t * height, Math.sin(t * 3 + variant) * 0.3];
426
- const parts: GeometryPart[] = [
427
- {
428
- geometry: tube(
429
- Array.from({ length: 6 }, (_, i) => at(i / 5)),
430
- { radius: (t) => 0.62 * (1 - t * 0.6) + Math.max(0, 0.12 - t) * 4, sides: 10, smoothness: 4, color: bark(variant) }
431
- )
432
- }
433
- ];
434
- // Buttress roots flaring out at the foot
435
- for (let i = 0; i < 5; i++) {
436
- const a = (i / 5) * Math.PI * 2 + rand() * 0.5;
437
- const r = 1.8 + rand() * 0.8;
438
- parts.push({
439
- geometry: tube(
440
- [
441
- [0, 2.4, 0],
442
- [Math.cos(a) * 0.8, 0.9, Math.sin(a) * 0.8],
443
- [Math.cos(a) * r, 0, Math.sin(a) * r]
444
- ],
445
- { radius: (t) => 0.4 - t * 0.3, sides: 6, smoothness: 3, color: bark(variant + i) }
446
- )
447
- });
448
- }
449
- // Limbs off the upper trunk, each with its clumps of leaves (and the crown on top)
450
- const crowns: Vec3[] = [at(1)];
451
- const limbs = 3 + Math.floor(rand() * 2);
452
- for (let i = 0; i < limbs; i++) {
453
- const t = 0.55 + (i / limbs) * 0.35 + rand() * 0.05;
454
- const from = at(t);
455
- const a = (i / limbs) * Math.PI * 2 + rand();
456
- const reach = 1.6 + rand() * 1.0;
457
- const to: Vec3 = [from[0] + Math.cos(a) * reach, from[1] + 1.4 + rand() * 1.6, from[2] + Math.sin(a) * reach];
458
- parts.push({
459
- geometry: tube([from, [(from[0] + to[0]) / 2, from[1] + 0.9, (from[2] + to[2]) / 2], to], {
460
- radius: (k) => 0.28 * (1 - t * 0.5) * (1 - k * 0.6),
461
- sides: 6,
462
- smoothness: 3,
463
- color: bark(variant + 10 + i)
464
- })
465
- });
466
- crowns.push(to);
467
- }
468
- crowns.forEach((c, i) => {
469
- const clumps = i === 0 ? 4 : 3;
470
- for (let k = 0; k < clumps; k++) {
471
- const a = rand() * Math.PI * 2;
472
- const r = k === 0 ? 0 : 0.6 + rand() * 0.5;
473
- const size = (i === 0 ? 3.6 : 2.8) + rand() * 1.2;
474
- parts.push({
475
- geometry: leafClump(variant * 10 + i * 3 + k),
476
- position: [c[0] + Math.cos(a) * r, c[1] + 0.4 + rand() * 0.6, c[2] + Math.sin(a) * r],
477
- rotation: quat.y(rand() * 6.3),
478
- scale: [size, size * (0.5 + rand() * 0.15), size],
479
- color: [1, 1, 1, 1]
480
- });
481
- }
482
- // A liana or two hanging from the limb
483
- if (i > 0 && rand() < 0.8) {
484
- const len = 4 + rand() * 4;
485
- parts.push({
486
- geometry: tube(
487
- [
488
- [c[0], c[1], c[2]],
489
- [c[0] + 0.15, c[1] - len * 0.5, c[2] - 0.1],
490
- [c[0] - 0.1, c[1] - len, c[2] + 0.12]
491
- ],
492
- {
493
- radius: 0.045,
494
- sides: 4,
495
- smoothness: 3,
496
- color: rgba(mix(BARK, LEAF, 0.55))
497
- }
498
- )
499
- });
500
- }
501
- });
502
- return createShape(merge(parts), { center: false, shine: 0.05 });
503
- });
504
-
505
- /** A palm rising out of the canopy (base at the origin, about 11 high): a curving ringed trunk and a crown of drooping fronds, with coconuts. */
506
- const palmMesh = (variant: number) =>
507
- cached(`pr-palm:${variant}`, () => {
508
- const rand = random(170 + variant);
509
- const bend = (rand() < 0.5 ? -1 : 1) * (0.6 + rand() * 0.6);
510
- const height = 10 + rand() * 2;
511
- const trunkAt = (t: number): Vec3 => [bend * t * t, t * height, 0];
512
- const top = trunkAt(1);
513
- const parts: GeometryPart[] = [
514
- {
515
- geometry: tube(
516
- Array.from({ length: 6 }, (_, i) => trunkAt(i / 5)),
517
- {
518
- radius: (t) => 0.32 - t * 0.1 + Math.max(0, 0.08 - t) * 2,
519
- sides: 8,
520
- smoothness: 5,
521
- color: ({ v }) => rgba(mix([0.42, 0.33, 0.22], [0.6, 0.5, 0.34], 0.5 + Math.sin(v * 90) * 0.5))
522
- }
523
- )
524
- }
525
- ];
526
- const fronds = 9;
527
- for (let i = 0; i < fronds; i++) {
528
- const a = (i / fronds) * Math.PI * 2 + rand() * 0.3;
529
- const reach = 3.6 + rand() * 1.2;
530
- const dir: Vec3 = [Math.cos(a), 0, Math.sin(a)];
531
- const p = (k: number, up: number): Vec3 => [top[0] + dir[0] * reach * k, top[1] + up, top[2] + dir[2] * reach * k];
532
- parts.push({
533
- geometry: tube([p(0, 0), p(0.35, 0.7), p(0.7, 0.2), p(1, -1.3 - rand() * 0.6)], {
534
- radius: (t) => 0.42 * Math.max(0.05, Math.sin(Math.PI * Math.min(1, t * 1.15))) ** 0.6,
535
- // A flat blade with a ridge down the middle
536
- shape: (s) => 0.08 + 0.92 * Math.abs(Math.cos(s * Math.PI * 2)) ** 3,
537
- sides: 6,
538
- smoothness: 5,
539
- color: ({ v }) => rgba(mix(PALM, [0.62, 0.74, 0.3], v * 0.8))
540
- })
541
- });
542
- }
543
- for (let i = 0; i < 3; i++) {
544
- const a = (i / 3) * Math.PI * 2;
545
- parts.push({
546
- geometry: bead(),
547
- position: [top[0] + Math.cos(a) * 0.28, top[1] - 0.25, top[2] + Math.sin(a) * 0.28],
548
- scale: 0.32,
549
- color: rgba([0.4, 0.27, 0.12])
550
- });
551
- }
552
- return createShape(merge(parts), { center: false, shine: 0.1 });
553
- });
554
-
555
- /** A flowering bush for the walkway: a few clumps of leaves dotted with blossoms. */
556
- const bushMesh = (variant: number) =>
557
- cached(`pr-bush:${variant}`, () => {
558
- const rand = random(210 + variant);
559
- const parts: GeometryPart[] = [];
560
- for (let i = 0; i < 5; i++) {
561
- const a = (i / 5) * Math.PI * 2 + rand();
562
- const r = i === 0 ? 0 : 0.35 + rand() * 0.2;
563
- const size = 0.7 + rand() * 0.35;
564
- parts.push({
565
- geometry: leafClump(300 + variant * 7 + i),
566
- position: [Math.cos(a) * r, size * 0.32, Math.sin(a) * r],
567
- scale: [size, size * 0.75, size],
568
- color: [1, 1, 1, 1]
569
- });
570
- }
571
- const blossom: RGB = variant ? [1, 0.45, 0.6] : [1, 0.82, 0.25];
572
- for (let i = 0; i < 7; i++) {
573
- const a = rand() * Math.PI * 2;
574
- const r = 0.2 + rand() * 0.45;
575
- parts.push({ geometry: bead(), position: [Math.cos(a) * r, 0.45 + rand() * 0.35, Math.sin(a) * r], scale: 0.11, color: rgba(blossom) });
576
- }
577
- return createShape(merge(parts), { center: false, shine: 0.1 });
578
- });
579
-
580
- /** The jungle canopy far below: a wide bed of leafy crowns in several greens. */
581
- const canopyMesh = (variant: number) =>
582
- cached(`pr-canopy:${variant}`, () => {
583
- const rand = random(250 + variant);
584
- const parts: GeometryPart[] = [];
585
- for (let i = 0; i < 9; i++) {
586
- const size = 4 + rand() * 3.5;
587
- const shade = rand();
588
- parts.push({
589
- geometry: leafClump(400 + variant * 11 + i, mix(LEAF_DARK, [0.08, 0.2, 0.08], shade), mix(LEAF, LEAF_LIGHT, shade)),
590
- position: [(rand() - 0.5) * 12, rand() * 1.5, (rand() - 0.5) * 12],
591
- scale: [size, size * 0.55, size],
592
- color: [1, 1, 1, 1]
593
- });
594
- }
595
- return createShape(merge(parts), { center: false, shine: 0.03 });
596
- });
597
-
598
- /** An archway over the causeway: two square piers standing on the walkways and a carved lintel, hung with vines. */
599
- const archMesh = () =>
600
- cached('pr-arch', () => {
601
- const box = shapeGeometry('roundedBox');
602
- const vine = (x: number, len: number) => ({
603
- geometry: tube(
604
- [
605
- [x, 5.3, 0.45],
606
- [x + 0.05, 5.3 - len * 0.5, 0.5],
607
- [x - 0.04, 5.3 - len, 0.48]
608
- ],
609
- { radius: 0.04, sides: 5, color: rgba(LEAF) }
610
- )
611
- });
612
- return createShape(
613
- merge([
614
- { geometry: recolor(box, weathered(15, 0.8)), position: [-WALK_X, 0.25, 0], scale: [1.35, 0.5, 1.35], color: [1, 1, 1, 1] },
615
- { geometry: recolor(box, weathered(16, 0.8)), position: [WALK_X, 0.25, 0], scale: [1.35, 0.5, 1.35], color: [1, 1, 1, 1] },
616
- { geometry: recolor(box, weathered(11, 0.6)), position: [-WALK_X, 2.8, 0], scale: [1.05, 5.0, 1.05], color: [1, 1, 1, 1] },
617
- { geometry: recolor(box, weathered(12, 0.6)), position: [WALK_X, 2.8, 0], scale: [1.05, 5.0, 1.05], color: [1, 1, 1, 1] },
618
- { geometry: recolor(box, weathered(13, 0.4)), position: [0, 5.6, 0], scale: [WALK_OUTER * 2 - 0.2, 0.9, 1.3], color: [1, 1, 1, 1] },
619
- { geometry: recolor(box, weathered(14, 0.2)), position: [0, 6.25, 0], scale: [WALK_OUTER * 2 + 0.2, 0.4, 1.5], color: [1, 1, 1, 1] },
620
- { geometry: box, position: [0, 5.6, 0.66], scale: [1.2, 0.5, 0.06], color: rgba(GOLD) },
621
- vine(-2.2, 1.6),
622
- vine(-1.1, 0.9),
623
- vine(0.9, 1.3),
624
- vine(2.4, 2.0)
625
- ]),
626
- { center: false, shine: 0.15 }
627
- );
628
- });
629
-
630
- // Obstacles: gateways span the whole causeway (gaps are missing slabs, laid with the paving)
631
-
632
- /**
633
- * A gateway spanning the whole causeway: massive end piers, a long carved lintel fringed with stone
634
- * spikes and vines, its underside about 1 m up: the only way through is to slide.
635
- */
636
- const wideGateMesh = () =>
637
- cached('pr-wide-gate', () => {
638
- const box = shapeGeometry('roundedBox');
639
- const cone = shapeGeometry('cone');
640
- const spikes: GeometryPart[] = Array.from({ length: 17 }, (_, i) => ({
641
- geometry: recolor(cone, weathered(120 + i, 0.2)),
642
- position: [-2.4 + i * 0.3, 0.95, 0.04],
643
- rotation: quat.x(Math.PI),
644
- scale: [0.13, 0.2 + (i % 2) * 0.06, 0.13],
645
- color: [1, 1, 1, 1]
646
- }));
647
- const vines: GeometryPart[] = [-2.1, -1.2, -0.4, 0.7, 1.5, 2.3].map((x, i) => ({
648
- geometry: tube(
649
- [
650
- [x, 1.08, 0.32],
651
- [x + 0.03, 0.95 - (i % 3) * 0.04, 0.35],
652
- [x - 0.02, 0.84 - (i % 2) * 0.1, 0.33]
653
- ],
654
- { radius: 0.022, sides: 5, color: rgba(LEAF) }
655
- )
656
- }));
657
- const glyphs: GeometryPart[] = [-1.8, -0.9, 0, 0.9, 1.8].map((x) => ({
658
- geometry: shapeGeometry('cylinder'),
659
- position: [x, 1.32, 0.31],
660
- rotation: quat.x(Math.PI / 2),
661
- scale: [0.22, 0.04, 0.22],
662
- color: rgba(GOLD)
663
- }));
664
- return createShape(
665
- merge([
666
- ...[-2.85, 2.85].flatMap((x, i): GeometryPart[] => [
667
- { geometry: recolor(box, weathered(130 + i, 0.75)), position: [x, 0.25, 0], scale: [0.7, 0.5, 0.8], color: [1, 1, 1, 1] },
668
- { geometry: recolor(box, weathered(132 + i, 0.5)), position: [x, 1.25, 0], scale: [0.5, 2.5, 0.6], color: [1, 1, 1, 1] }
669
- ]),
670
- { geometry: recolor(box, weathered(134, 0.35)), position: [0, 1.38, 0], scale: [6.3, 0.6, 0.62], color: [1, 1, 1, 1] },
671
- { geometry: recolor(box, weathered(135, 0.25)), position: [0, 1.06, 0], scale: [5.4, 0.14, 0.5], color: [0.9, 0.86, 0.8, 1] },
672
- { geometry: recolor(box, weathered(136, 0.2)), position: [0, 1.76, 0], scale: [6.6, 0.18, 0.72], color: [1, 1, 1, 1] },
673
- ...glyphs,
674
- ...spikes,
675
- ...vines
676
- ]),
677
- { center: false, shine: 0.15 }
678
- );
679
- });
680
-
681
- const coinMesh = () =>
682
- cached('pr-coin', () =>
683
- createShape(
684
- merge([
685
- { geometry: shapeGeometry('cylinder'), rotation: quat.x(Math.PI / 2), scale: [0.5, 0.08, 0.5], color: rgba(GOLD) },
686
- { geometry: shapeGeometry('cylinder'), rotation: quat.x(Math.PI / 2), scale: [0.34, 0.1, 0.34], color: rgba([1, 0.86, 0.36]) }
687
- ]),
688
- { shine: 1 }
689
- )
690
- );
691
-
692
- const gemMesh = () =>
693
- cached('pr-gem', () =>
694
- createShape(
695
- lathe(
696
- [
697
- [0.001, 0.3],
698
- [0.3, 0.14],
699
- [0.34, 0.06],
700
- [0.001, -0.38]
701
- ],
702
- { segments: 8, color: [1, 1, 1, 1] }
703
- ),
704
- { shine: 1 }
705
- )
706
- );
707
-
708
- /** Builds every mesh ahead of time (each is made once per app). */
709
- export function prepareRunMeshes() {
710
- for (const build of warmAssets()) build();
711
- }
712
-
713
- /** Each cached scenery mesh can be warmed independently without constructing a track. */
714
- export function* warmAssets(): Iterable<() => unknown> {
715
- yield () => pathMesh(0);
716
- yield () => pathMesh(1);
717
- yield () => columnMesh(false);
718
- yield () => columnMesh(true);
719
- yield () => idolMesh();
720
- yield () => torchMesh();
721
- yield () => treeMesh(0);
722
- yield () => treeMesh(1);
723
- yield () => treeMesh(2);
724
- yield () => palmMesh(0);
725
- yield () => palmMesh(1);
726
- yield () => bushMesh(0);
727
- yield () => bushMesh(1);
728
- yield () => canopyMesh(0);
729
- yield () => canopyMesh(1);
730
- yield () => archMesh();
731
- yield () => slabMesh(0);
732
- yield () => slabMesh(1);
733
- yield () => slabMesh(2);
734
- yield () => brokenEdgeMesh();
735
- yield () => finishMesh();
736
- yield () => startMesh();
737
- yield () => templeMesh();
738
- yield () => wideGateMesh();
739
- yield () => coinMesh();
740
- yield () => gemMesh();
741
- }
742
-
743
- // ── Track plan ────────────────────────────────────────────────────────
744
-
745
- export type ObstacleKind = 'gap' | 'beam';
746
- /** `wide`: part of a row spanning all three lanes (drawn once, as one piece, on the middle lane). */
747
- export type PlanObstacle = { kind: ObstacleKind; lane: number; z: number; wide?: boolean };
748
- export type PlanPickup = { lane: number; z: number; gem: boolean };
749
- export type TrackPlan = { seed: number; obstacles: PlanObstacle[]; pickups: PlanPickup[] };
750
-
751
- /** Centre z of paving row `k` (rows are SLAB long, from the start line toward -z). */
752
- const slabZ = (k: number) => -(k + 0.5) * SLAB;
753
-
754
- /**
755
- * Lays out the TRACK_LENGTH metres in rows of obstacles. Some rows span the whole causeway (a crack
756
- * you must jump, or a gateway you must slide under: gateways always span it); the rest break one or
757
- * two lanes with gaps, never all three. Rows come closer and harder the further along the track, always
758
- * leaving room to land and react. A trail of coins hops between lanes, steering clear of obstacles.
759
- * A new seed gives a new layout every attempt.
760
- */
761
- export function generateTrack(seed: number): TrackPlan {
762
- const rand = random(seed);
763
- const obstacles: PlanObstacle[] = [];
764
- const lastRow = Math.floor((TRACK_LENGTH - FINISH_CLEAR) / SLAB);
765
- for (let k = Math.ceil(START_CLEAR / SLAB); k <= lastRow;) {
766
- const z = slabZ(k);
767
- const progress = (k * SLAB) / TRACK_LENGTH;
768
- if (rand() < 0.4 + progress * 0.15) {
769
- const kind: ObstacleKind = rand() < 0.5 ? 'gap' : 'beam';
770
- for (let lane = 0; lane < 3; lane++) obstacles.push({ kind, lane, z, wide: true });
771
- } else {
772
- const lanes = [0, 1, 2].sort(() => rand() - 0.5);
773
- const blocked = rand() < 0.15 + progress * 0.5 ? 2 : 1;
774
- for (let i = 0; i < blocked; i++) obstacles.push({ kind: 'gap', lane: lanes[i]!, z });
775
- }
776
- // 10-22 m to the next row early on, 8-14 m near the finish: room to land a jump and react
777
- k += Math.max(4, 5 - Math.floor(progress * 2)) + Math.floor(rand() * (4 - Math.floor(progress * 2)));
778
- }
779
-
780
- const near = (lane: number, z: number, margin: number) =>
781
- obstacles.some((o) => o.lane === lane && Math.abs(o.z - z) < (o.kind === 'gap' ? SLAB / 2 : 0.5) + margin);
782
- const pickups: PlanPickup[] = [];
783
- let lane = Math.floor(rand() * 3);
784
- let nextHop = 8 + rand() * 6;
785
- for (let d = 6; d < TRACK_LENGTH - 2; d += 1.6) {
786
- const z = -d;
787
- if (d >= nextHop) {
788
- lane = (lane + 1 + Math.floor(rand() * 2)) % 3;
789
- nextHop = d + 6 + rand() * 8;
790
- }
791
- if (near(lane, z, 0.9)) {
792
- const free = [0, 1, 2].filter((l) => !near(l, z, 1.6));
793
- if (!free.length) continue;
794
- lane = free[Math.floor(rand() * free.length)]!;
795
- }
796
- pickups.push({ lane, z, gem: rand() < 0.04 });
797
- }
798
- return { seed, obstacles, pickups };
799
- }
800
-
801
- // ── Tiles ─────────────────────────────────────────────────────────────
802
-
803
- export type Obstacle = PlanObstacle & { e: Entity | null };
804
- export type Pickup = { gem: boolean; lane: number; y: number; z: number; e: Entity; taken: boolean };
805
- export type Tile = { index: number; z: number; entities: Entity[]; obstacles: Obstacle[]; pickups: Pickup[] };
806
-
807
- /**
808
- * Builds tile `index` (its near end at z = -index * TILE_LENGTH) from the run's plan: the causeway
809
- * slab by slab (broken out where there is a gap), its scenery, and the plan's obstacles, coins and
810
- * finish line that fall in it.
811
- */
812
- export function spawnTile(world: World, index: number, theme: GameTheme, plan: TrackPlan): Tile {
813
- const isNight = night(theme);
814
- const z0 = -index * TILE_LENGTH;
815
- const z1 = z0 - TILE_LENGTH;
816
- const inTile = (z: number) => z <= z0 && z > z1;
817
- const rand = random(plan.seed * 31 + index * 17);
818
- const tint: Color = isNight ? [0.8, 0.85, 1, 1] : [1, 1, 1, 1];
819
- const entities: Entity[] = [];
820
- const add = (
821
- mesh: Model | 'sphere' | 'puff' | 'flame',
822
- position: Vec3,
823
- scale: Vec3 | number = 1,
824
- yaw = 0,
825
- color: Color = tint,
826
- extra: object = {}
827
- ) => {
828
- const e = world.spawn({ mesh, position, scale, rotation: quat.y(yaw), color, pickable: false, ...extra });
829
- entities.push(e);
830
- return e;
831
- };
832
-
833
- add(pathMesh(Math.abs(index) % 2), [0, 0, z0]);
834
-
835
- // Paving: every slab its own shade, a jagged hole where the plan breaks the track
836
- const rowsPerTile = Math.round(TILE_LENGTH / SLAB);
837
- for (let r = 0; r < rowsPerTile; r++) {
838
- const z = slabZ(index * rowsPerTile + r);
839
- for (let lane = 0; lane < 3; lane++) {
840
- const gap = plan.obstacles.some((o) => o.kind === 'gap' && o.lane === lane && Math.abs(o.z - z) < 0.01);
841
- if (gap) {
842
- add(brokenEdgeMesh(), [LANE_X[lane]!, 0, z], 1, 0);
843
- continue;
844
- }
845
- const shade = 0.88 + rand() * 0.2;
846
- add(slabMesh(Math.floor(rand() * 3)), [LANE_X[lane]!, (rand() - 0.5) * 0.02, z], 1, (rand() - 0.5) * 0.015, [
847
- tint[0] * shade,
848
- tint[1] * shade,
849
- tint[2] * shade,
850
- 1
851
- ]);
852
- }
853
- }
854
-
855
- // Scenery: columns, idols and flowering bushes on the walkways, torches on the kerbs; jungle giants
856
- // and palms rising out of the canopy beyond
857
- for (const side of [-1, 1] as const) {
858
- const pick = rand();
859
- const z = z0 - 2 - rand() * 8;
860
- if (pick < 0.45) add(columnMesh(rand() < 0.35), [side * WALK_X, -0.02, z], 0.85 + rand() * 0.15, rand() * 6.3);
861
- else if (pick < 0.65) add(idolMesh(), [side * WALK_X, -0.02, z], 0.72, side < 0 ? 0.9 : -0.9);
862
- for (let i = 0; i < 2; i++) {
863
- const bz = z0 - 1 - rand() * 10;
864
- if (rand() < 0.6 && Math.abs(bz - z) > 1.6)
865
- add(bushMesh(Math.floor(rand() * 2)), [side * (WALK_X + (rand() - 0.3) * 0.35), -0.02, bz], 0.75 + rand() * 0.35, rand() * 6.3);
866
- }
867
- const tz = z0 - 1 - rand() * 10;
868
- if (rand() < 0.75) {
869
- add(torchMesh(), [side * 2.85, 0.35, tz]);
870
- add('flame', [side * 2.85, 2.05, tz], 0.55, 0, [2.6, 1.3, 0.45, 1], {
871
- bob: { amplitude: [0.02, 0.06, 0.02], speed: 9 + rand() * 4, phase: rand() * 6 }
872
- });
873
- add('puff', [side * 2.85, 2.3, tz], 0.6, 0, [1.4, 0.8, 0.4, 0.35], { bob: { amplitude: [0.03, 0.12, 0.03], speed: 3, phase: rand() * 6 } });
874
- }
875
- // Far enough out that even the widest crown (about TREE_SPREAD across its trunk) stays clear of the walkway
876
- for (let i = 0; i < 2; i++) {
877
- const at: Vec3 = [side * (WALK_OUTER + TREE_SPREAD + rand() * 7), CANOPY_Y - 1, z0 - rand() * TILE_LENGTH];
878
- if (rand() < 0.35) add(palmMesh(Math.floor(rand() * 2)), at, 0.9 + rand() * 0.3, rand() * 6.3);
879
- else add(treeMesh(Math.floor(rand() * 3)), at, 0.85 + rand() * 0.35, rand() * 6.3);
880
- }
881
- add(canopyMesh(Math.floor(rand() * 2)), [side * (10 + rand() * 6), CANOPY_Y, z0 - 6], 1 + rand() * 0.4, rand() * 6.3, tint);
882
- }
883
- if (index > 0 && index % 5 === 0) add(archMesh(), [0, 0, z0 - 6]);
884
- if (rand() < 0.5)
885
- add('puff', [(rand() - 0.5) * 16, -3, z0 - rand() * 12], 6 + rand() * 4, 0, isNight ? [0.4, 0.45, 0.6, 0.25] : [1.1, 1.15, 1.1, 0.35]);
886
- if (inTile(-TRACK_LENGTH)) add(finishMesh(), [0, 0, -TRACK_LENGTH]);
887
- if (inTile(0)) add(startMesh(), [0, 0, -0.3]);
888
- // The temples closing both ends of the causeway
889
- if (index === -TILES_BEFORE_START) add(templeMesh(), [0, 0, z0 + 4]);
890
- if (index === LAST_TILE) add(templeMesh(), [0, 0, z1 - 4]);
891
-
892
- const obstacles: Obstacle[] = plan.obstacles
893
- .filter((o) => inTile(o.z))
894
- .map((o) => {
895
- if (o.kind === 'gap' || o.lane !== 1) return { ...o, e: null };
896
- return { ...o, e: add(wideGateMesh(), [0, 0, o.z], 1, 0) };
897
- });
898
- const pickups = plan.pickups.filter((p) => inTile(p.z)).map((p) => spawnPickup(world, entities, p.lane, p.gem ? 1.0 : 0.9, p.z, p.gem));
899
- return { index, z: z0, entities, obstacles, pickups };
900
- }
901
-
902
- function spawnPickup(world: World, entities: Entity[], lane: number, y: number, z: number, gem: boolean): Pickup {
903
- const e = world.spawn({
904
- mesh: gem ? gemMesh() : coinMesh(),
905
- position: [LANE_X[lane]!, y, z],
906
- scale: gem ? 0.9 : 0.85,
907
- color: gem ? [0.35, 0.8, 1.6, 1] : [1.25, 1.1, 0.75, 1],
908
- spin: [0, gem ? 1.8 : 3.2, 0],
909
- bob: { amplitude: [0, 0.1, 0], speed: 3, phase: z },
910
- pickable: false
911
- });
912
- entities.push(e);
913
- return { gem, lane, y, z, e, taken: false };
914
- }
915
-
916
- export function despawnTile(world: World, tile: Tile) {
917
- for (const e of tile.entities) world.despawn(e);
918
- }
919
-
920
- // ── Effects ───────────────────────────────────────────────────────────
921
-
922
- export function coinSparkle(world: World, position: Vec3, gem: boolean) {
923
- world.burst({
924
- position,
925
- count: gem ? 16 : 7,
926
- mesh: 'flame',
927
- size: gem ? 0.22 : 0.14,
928
- speed: gem ? 3 : 2,
929
- lifetime: 0.35,
930
- gravity: 0,
931
- color: gem ? [0.5, 1.2, 2.4, 1] : [2.4, 1.9, 0.7, 1],
932
- endSize: 0
933
- });
934
- }
935
-
936
- export function crashDust(world: World, position: Vec3) {
937
- world.burst({
938
- position,
939
- count: 14,
940
- mesh: 'sphere',
941
- size: 0.1,
942
- speed: 4,
943
- lifetime: 0.6,
944
- gravity: -9,
945
- color: [rgba(STONE), rgba(STONE_DARK)],
946
- endSize: 0.05
947
- });
948
- effects.dust(world, { position, count: 6, size: 0.6, speed: 1.2, lifetime: 0.9, color: [0.8, 0.75, 0.65, 0.7], endSize: 2.2 });
949
- }