@origingame/create-game 0.0.0-stage → 0.1.1

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 (590) hide show
  1. package/README.md +57 -2
  2. package/archetypes/README.md +99 -0
  3. package/archetypes/idle/.origin/setup +14 -0
  4. package/archetypes/idle/LICENSES.md +12 -0
  5. package/archetypes/idle/README.md +234 -0
  6. package/archetypes/idle/assets/audio/buy.wav +0 -0
  7. package/archetypes/idle/assets/audio/milestone.wav +0 -0
  8. package/archetypes/idle/assets/audio/nova.wav +0 -0
  9. package/archetypes/idle/assets/audio/orbit.wav +0 -0
  10. package/archetypes/idle/assets/audio/tap.wav +0 -0
  11. package/archetypes/idle/assets/audio/ui-close.ogt +9 -0
  12. package/archetypes/idle/assets/audio/ui-error.ogt +9 -0
  13. package/archetypes/idle/assets/audio/ui-open.ogt +9 -0
  14. package/archetypes/idle/assets/audio/ui-press.ogt +9 -0
  15. package/archetypes/idle/assets/audio/ui-success.ogt +9 -0
  16. package/archetypes/idle/assets/fonts/NotoSansSC-subset.ttf +0 -0
  17. package/archetypes/idle/assets/fonts/NotoSansSC-subset.ttf.ogt +10 -0
  18. package/archetypes/idle/assets/fonts/OFL.txt +93 -0
  19. package/archetypes/idle/assets/fx/drift.ogt +21 -0
  20. package/archetypes/idle/assets/fx/milestone.ogt +21 -0
  21. package/archetypes/idle/assets/fx/nova.ogt +22 -0
  22. package/archetypes/idle/assets/fx/stars.ogt +20 -0
  23. package/archetypes/idle/assets/fx/tap.ogt +23 -0
  24. package/archetypes/idle/assets/looks/idle.ogt +17 -0
  25. package/archetypes/idle/assets/skins/sparkorb/ChakraPetch-BoldItalic.ttf +0 -0
  26. package/archetypes/idle/assets/skins/sparkorb/ChakraPetch-BoldItalic.ttf.ogt +9 -0
  27. package/archetypes/idle/assets/skins/sparkorb/LICENSES.md +19 -0
  28. package/archetypes/idle/assets/skins/sparkorb/OFL.txt +95 -0
  29. package/archetypes/idle/assets/skins/sparkorb/Orbitron-Medium.ttf +0 -0
  30. package/archetypes/idle/assets/skins/sparkorb/Orbitron-Medium.ttf.ogt +9 -0
  31. package/archetypes/idle/assets/skins/sparkorb/Rajdhani-Bold.ttf +0 -0
  32. package/archetypes/idle/assets/skins/sparkorb/Rajdhani-Bold.ttf.ogt +9 -0
  33. package/archetypes/idle/assets/skins/sparkorb/atlas.png +0 -0
  34. package/archetypes/idle/assets/skins/sparkorb/atlas.png.ogt +7 -0
  35. package/archetypes/idle/assets/skins/sparkorb/icons.png +0 -0
  36. package/archetypes/idle/assets/skins/sparkorb/icons.png.ogt +7 -0
  37. package/archetypes/idle/assets/skins/sparkorb/skin.ogt +113 -0
  38. package/archetypes/idle/assets/sprites/glow.png +0 -0
  39. package/archetypes/idle/assets/sprites/glow.png.ogt +8 -0
  40. package/archetypes/idle/assets/sprites/mote-crystal.png +0 -0
  41. package/archetypes/idle/assets/sprites/mote-crystal.png.ogt +7 -0
  42. package/archetypes/idle/assets/sprites/mote-ember.png +0 -0
  43. package/archetypes/idle/assets/sprites/mote-ember.png.ogt +7 -0
  44. package/archetypes/idle/assets/sprites/mote-star.png +0 -0
  45. package/archetypes/idle/assets/sprites/mote-star.png.ogt +7 -0
  46. package/archetypes/idle/assets/sprites/mote-storm.png +0 -0
  47. package/archetypes/idle/assets/sprites/mote-storm.png.ogt +7 -0
  48. package/archetypes/idle/assets/sprites/orb.png +0 -0
  49. package/archetypes/idle/assets/sprites/orb.png.ogt +7 -0
  50. package/archetypes/idle/assets/sprites/spark.png +0 -0
  51. package/archetypes/idle/assets/sprites/spark.png.ogt +7 -0
  52. package/archetypes/idle/assets/strings/en.ogt +62 -0
  53. package/archetypes/idle/assets/strings/zh-CN.ogt +62 -0
  54. package/archetypes/idle/gitattributes +22 -0
  55. package/archetypes/idle/gitignore +8 -0
  56. package/archetypes/idle/host.d.ts +46 -0
  57. package/archetypes/idle/package.json +18 -0
  58. package/archetypes/idle/project.ogt +14 -0
  59. package/archetypes/idle/references/metal/card.png +0 -0
  60. package/archetypes/idle/references/metal/orbit.png +0 -0
  61. package/archetypes/idle/references/metal/title.png +0 -0
  62. package/archetypes/idle/src/components.ts +139 -0
  63. package/archetypes/idle/src/main.ts +247 -0
  64. package/archetypes/idle/src/numbers.ts +126 -0
  65. package/archetypes/idle/src/systems.ts +340 -0
  66. package/archetypes/idle/src/ui.ts +412 -0
  67. package/archetypes/idle/tests/away.og.ts +42 -0
  68. package/archetypes/idle/tests/flow.og.ts +57 -0
  69. package/archetypes/idle/tests/frames.og.ts +33 -0
  70. package/archetypes/idle/tests/frames.ts +134 -0
  71. package/archetypes/idle/tests/lib.ts +74 -0
  72. package/archetypes/idle/tests/milestone.og.ts +32 -0
  73. package/archetypes/idle/tests/nova.og.ts +52 -0
  74. package/archetypes/idle/tests/offline.og.ts +60 -0
  75. package/archetypes/idle/tests/pad.og.ts +55 -0
  76. package/archetypes/idle/tests/pause.og.ts +68 -0
  77. package/archetypes/idle/tests/phone.og.ts +50 -0
  78. package/archetypes/idle/tests/replay.og.ts +28 -0
  79. package/archetypes/idle/tools/make-assets.py +448 -0
  80. package/archetypes/idle/tools/oglite.py +285 -0
  81. package/archetypes/idle/tsconfig.json +17 -0
  82. package/archetypes/match3/.origin/setup +14 -0
  83. package/archetypes/match3/LICENSES.md +7 -0
  84. package/archetypes/match3/README.md +205 -0
  85. package/archetypes/match3/assets/audio/cascade.wav +0 -0
  86. package/archetypes/match3/assets/audio/clear.wav +0 -0
  87. package/archetypes/match3/assets/audio/lost.wav +0 -0
  88. package/archetypes/match3/assets/audio/refused.wav +0 -0
  89. package/archetypes/match3/assets/audio/select.wav +0 -0
  90. package/archetypes/match3/assets/audio/swap.wav +0 -0
  91. package/archetypes/match3/assets/audio/won.wav +0 -0
  92. package/archetypes/match3/assets/fonts/OFL.txt +93 -0
  93. package/archetypes/match3/assets/fonts/ui.ttf +0 -0
  94. package/archetypes/match3/assets/fonts/ui.ttf.ogt +11 -0
  95. package/archetypes/match3/assets/scenes/main.ogt +24 -0
  96. package/archetypes/match3/assets/sprites/backdrop.png +0 -0
  97. package/archetypes/match3/assets/sprites/board.ogt +21 -0
  98. package/archetypes/match3/assets/sprites/cells.ogt +7 -0
  99. package/archetypes/match3/assets/sprites/cells.png +0 -0
  100. package/archetypes/match3/assets/sprites/cells.png.ogt +7 -0
  101. package/archetypes/match3/assets/sprites/gems.ogt +75 -0
  102. package/archetypes/match3/assets/sprites/gems.png +0 -0
  103. package/archetypes/match3/assets/sprites/panel.png +0 -0
  104. package/archetypes/match3/assets/sprites/ring.png +0 -0
  105. package/archetypes/match3/gitattributes +22 -0
  106. package/archetypes/match3/gitignore +8 -0
  107. package/archetypes/match3/host.d.ts +46 -0
  108. package/archetypes/match3/package.json +18 -0
  109. package/archetypes/match3/project.ogt +18 -0
  110. package/archetypes/match3/references/metal/cascade.png +0 -0
  111. package/archetypes/match3/references/metal/clearing.png +0 -0
  112. package/archetypes/match3/references/metal/dealt.png +0 -0
  113. package/archetypes/match3/src/art.ts +54 -0
  114. package/archetypes/match3/src/board.ts +112 -0
  115. package/archetypes/match3/src/components.ts +95 -0
  116. package/archetypes/match3/src/layout.ts +25 -0
  117. package/archetypes/match3/src/main.ts +199 -0
  118. package/archetypes/match3/src/systems.ts +631 -0
  119. package/archetypes/match3/tests/drag.og.ts +31 -0
  120. package/archetypes/match3/tests/endings.og.ts +40 -0
  121. package/archetypes/match3/tests/frames.og.ts +49 -0
  122. package/archetypes/match3/tests/frames.ts +134 -0
  123. package/archetypes/match3/tests/hidpi.og.ts +41 -0
  124. package/archetypes/match3/tests/lib.ts +64 -0
  125. package/archetypes/match3/tests/replay.og.ts +28 -0
  126. package/archetypes/match3/tests/sounds.og.ts +50 -0
  127. package/archetypes/match3/tests/swap.og.ts +67 -0
  128. package/archetypes/match3/tools/make-assets.py +404 -0
  129. package/archetypes/match3/tools/oglite.py +285 -0
  130. package/archetypes/match3/tsconfig.json +17 -0
  131. package/archetypes/rhythm/.origin/setup +14 -0
  132. package/archetypes/rhythm/LICENSES.md +13 -0
  133. package/archetypes/rhythm/README.md +235 -0
  134. package/archetypes/rhythm/assets/audio/bass0.wav +0 -0
  135. package/archetypes/rhythm/assets/audio/bass1.wav +0 -0
  136. package/archetypes/rhythm/assets/audio/bass2.wav +0 -0
  137. package/archetypes/rhythm/assets/audio/bass3.wav +0 -0
  138. package/archetypes/rhythm/assets/audio/chords0.wav +0 -0
  139. package/archetypes/rhythm/assets/audio/chords1.wav +0 -0
  140. package/archetypes/rhythm/assets/audio/chords2.wav +0 -0
  141. package/archetypes/rhythm/assets/audio/chords3.wav +0 -0
  142. package/archetypes/rhythm/assets/audio/clear.wav +0 -0
  143. package/archetypes/rhythm/assets/audio/click.wav +0 -0
  144. package/archetypes/rhythm/assets/audio/drums.wav +0 -0
  145. package/archetypes/rhythm/assets/audio/fail.wav +0 -0
  146. package/archetypes/rhythm/assets/audio/leadA.wav +0 -0
  147. package/archetypes/rhythm/assets/audio/leadB.wav +0 -0
  148. package/archetypes/rhythm/assets/audio/miss.wav +0 -0
  149. package/archetypes/rhythm/assets/audio/tap0.wav +0 -0
  150. package/archetypes/rhythm/assets/audio/tap1.wav +0 -0
  151. package/archetypes/rhythm/assets/audio/tap2.wav +0 -0
  152. package/archetypes/rhythm/assets/audio/ui-close.ogt +9 -0
  153. package/archetypes/rhythm/assets/audio/ui-error.ogt +9 -0
  154. package/archetypes/rhythm/assets/audio/ui-open.ogt +9 -0
  155. package/archetypes/rhythm/assets/audio/ui-press.ogt +9 -0
  156. package/archetypes/rhythm/assets/audio/ui-success.ogt +9 -0
  157. package/archetypes/rhythm/assets/charts/neon-pulse.ogt +919 -0
  158. package/archetypes/rhythm/assets/fonts/NotoSansSC-subset.ttf +0 -0
  159. package/archetypes/rhythm/assets/fonts/NotoSansSC-subset.ttf.ogt +10 -0
  160. package/archetypes/rhythm/assets/fonts/OFL.txt +93 -0
  161. package/archetypes/rhythm/assets/fx/hit.ogt +22 -0
  162. package/archetypes/rhythm/assets/skins/lanebeat/ChakraPetch-Medium.ttf +0 -0
  163. package/archetypes/rhythm/assets/skins/lanebeat/ChakraPetch-Medium.ttf.ogt +9 -0
  164. package/archetypes/rhythm/assets/skins/lanebeat/JetBrainsMono-Regular.ttf +0 -0
  165. package/archetypes/rhythm/assets/skins/lanebeat/JetBrainsMono-Regular.ttf.ogt +9 -0
  166. package/archetypes/rhythm/assets/skins/lanebeat/LICENSES.md +17 -0
  167. package/archetypes/rhythm/assets/skins/lanebeat/OFL.txt +95 -0
  168. package/archetypes/rhythm/assets/skins/lanebeat/Orbitron-Bold.ttf +0 -0
  169. package/archetypes/rhythm/assets/skins/lanebeat/Orbitron-Bold.ttf.ogt +9 -0
  170. package/archetypes/rhythm/assets/skins/lanebeat/atlas.png +0 -0
  171. package/archetypes/rhythm/assets/skins/lanebeat/atlas.png.ogt +7 -0
  172. package/archetypes/rhythm/assets/skins/lanebeat/icons.png +0 -0
  173. package/archetypes/rhythm/assets/skins/lanebeat/icons.png.ogt +7 -0
  174. package/archetypes/rhythm/assets/skins/lanebeat/skin.ogt +110 -0
  175. package/archetypes/rhythm/assets/sprites/lane.png +0 -0
  176. package/archetypes/rhythm/assets/sprites/lane.png.ogt +7 -0
  177. package/archetypes/rhythm/assets/sprites/note.png +0 -0
  178. package/archetypes/rhythm/assets/sprites/note.png.ogt +7 -0
  179. package/archetypes/rhythm/assets/sprites/ring.png +0 -0
  180. package/archetypes/rhythm/assets/sprites/ring.png.ogt +7 -0
  181. package/archetypes/rhythm/assets/sprites/spark.png +0 -0
  182. package/archetypes/rhythm/assets/sprites/spark.png.ogt +7 -0
  183. package/archetypes/rhythm/assets/strings/en.ogt +61 -0
  184. package/archetypes/rhythm/assets/strings/zh-CN.ogt +61 -0
  185. package/archetypes/rhythm/gitattributes +22 -0
  186. package/archetypes/rhythm/gitignore +8 -0
  187. package/archetypes/rhythm/host.d.ts +46 -0
  188. package/archetypes/rhythm/package.json +18 -0
  189. package/archetypes/rhythm/project.ogt +14 -0
  190. package/archetypes/rhythm/references/metal/highway.png +0 -0
  191. package/archetypes/rhythm/references/metal/results.png +0 -0
  192. package/archetypes/rhythm/references/metal/title.png +0 -0
  193. package/archetypes/rhythm/src/components.ts +147 -0
  194. package/archetypes/rhythm/src/main.ts +202 -0
  195. package/archetypes/rhythm/src/systems.ts +490 -0
  196. package/archetypes/rhythm/src/ui.ts +421 -0
  197. package/archetypes/rhythm/tests/band.og.ts +45 -0
  198. package/archetypes/rhythm/tests/calibrate.og.ts +49 -0
  199. package/archetypes/rhythm/tests/clear.og.ts +51 -0
  200. package/archetypes/rhythm/tests/fail.og.ts +40 -0
  201. package/archetypes/rhythm/tests/flow.og.ts +54 -0
  202. package/archetypes/rhythm/tests/frames.og.ts +35 -0
  203. package/archetypes/rhythm/tests/frames.ts +134 -0
  204. package/archetypes/rhythm/tests/lib.ts +123 -0
  205. package/archetypes/rhythm/tests/pad.og.ts +41 -0
  206. package/archetypes/rhythm/tests/pause.og.ts +77 -0
  207. package/archetypes/rhythm/tests/phone.og.ts +44 -0
  208. package/archetypes/rhythm/tests/replay.og.ts +27 -0
  209. package/archetypes/rhythm/tests/timing.og.ts +37 -0
  210. package/archetypes/rhythm/tools/make-assets.py +383 -0
  211. package/archetypes/rhythm/tools/oglite.py +285 -0
  212. package/archetypes/rhythm/tsconfig.json +17 -0
  213. package/archetypes/roguelike/.origin/setup +14 -0
  214. package/archetypes/roguelike/LICENSES.md +14 -0
  215. package/archetypes/roguelike/README.md +256 -0
  216. package/archetypes/roguelike/assets/ai/foe.ogt +28 -0
  217. package/archetypes/roguelike/assets/ai/guardian.ogt +26 -0
  218. package/archetypes/roguelike/assets/audio/bump.wav +0 -0
  219. package/archetypes/roguelike/assets/audio/drink.wav +0 -0
  220. package/archetypes/roguelike/assets/audio/fall.wav +0 -0
  221. package/archetypes/roguelike/assets/audio/hit.wav +0 -0
  222. package/archetypes/roguelike/assets/audio/hurt.wav +0 -0
  223. package/archetypes/roguelike/assets/audio/item.wav +0 -0
  224. package/archetypes/roguelike/assets/audio/kill.wav +0 -0
  225. package/archetypes/roguelike/assets/audio/pickup.wav +0 -0
  226. package/archetypes/roguelike/assets/audio/roar.wav +0 -0
  227. package/archetypes/roguelike/assets/audio/stairs.wav +0 -0
  228. package/archetypes/roguelike/assets/audio/step.ogt +9 -0
  229. package/archetypes/roguelike/assets/audio/telegraph.wav +0 -0
  230. package/archetypes/roguelike/assets/audio/tomb.wav +0 -0
  231. package/archetypes/roguelike/assets/audio/ui-close.ogt +9 -0
  232. package/archetypes/roguelike/assets/audio/ui-error.ogt +9 -0
  233. package/archetypes/roguelike/assets/audio/ui-open.ogt +9 -0
  234. package/archetypes/roguelike/assets/audio/ui-press.ogt +9 -0
  235. package/archetypes/roguelike/assets/audio/ui-success.ogt +9 -0
  236. package/archetypes/roguelike/assets/audio/won.wav +0 -0
  237. package/archetypes/roguelike/assets/fonts/NotoSansSC-subset.ttf +0 -0
  238. package/archetypes/roguelike/assets/fonts/NotoSansSC-subset.ttf.ogt +10 -0
  239. package/archetypes/roguelike/assets/fonts/OFL.txt +93 -0
  240. package/archetypes/roguelike/assets/maps/dive.ogt +106 -0
  241. package/archetypes/roguelike/assets/skins/lampdive/DotGothic16-Regular.ttf +0 -0
  242. package/archetypes/roguelike/assets/skins/lampdive/DotGothic16-Regular.ttf.ogt +9 -0
  243. package/archetypes/roguelike/assets/skins/lampdive/LICENSES.md +16 -0
  244. package/archetypes/roguelike/assets/skins/lampdive/OFL.txt +93 -0
  245. package/archetypes/roguelike/assets/skins/lampdive/atlas.png +0 -0
  246. package/archetypes/roguelike/assets/skins/lampdive/atlas.png.ogt +7 -0
  247. package/archetypes/roguelike/assets/skins/lampdive/icons.png +0 -0
  248. package/archetypes/roguelike/assets/skins/lampdive/icons.png.ogt +7 -0
  249. package/archetypes/roguelike/assets/skins/lampdive/skin.ogt +115 -0
  250. package/archetypes/roguelike/assets/sprites/dive.ogt +50 -0
  251. package/archetypes/roguelike/assets/sprites/dive.png +0 -0
  252. package/archetypes/roguelike/assets/sprites/dive.png.ogt +7 -0
  253. package/archetypes/roguelike/assets/sprites/glow.png +0 -0
  254. package/archetypes/roguelike/assets/sprites/glow.png.ogt +7 -0
  255. package/archetypes/roguelike/assets/strings/en.ogt +57 -0
  256. package/archetypes/roguelike/assets/strings/zh-CN.ogt +57 -0
  257. package/archetypes/roguelike/assets/tiles/tomb.ogt +31 -0
  258. package/archetypes/roguelike/assets/tiles/tomb.png +0 -0
  259. package/archetypes/roguelike/assets/tiles/tomb.png.ogt +7 -0
  260. package/archetypes/roguelike/gitattributes +22 -0
  261. package/archetypes/roguelike/gitignore +8 -0
  262. package/archetypes/roguelike/host.d.ts +46 -0
  263. package/archetypes/roguelike/package.json +18 -0
  264. package/archetypes/roguelike/project.ogt +14 -0
  265. package/archetypes/roguelike/references/metal/dive.png +0 -0
  266. package/archetypes/roguelike/references/metal/over.png +0 -0
  267. package/archetypes/roguelike/references/metal/title.png +0 -0
  268. package/archetypes/roguelike/src/art.ts +23 -0
  269. package/archetypes/roguelike/src/components.ts +169 -0
  270. package/archetypes/roguelike/src/dive.ts +144 -0
  271. package/archetypes/roguelike/src/foes.ts +174 -0
  272. package/archetypes/roguelike/src/main.ts +258 -0
  273. package/archetypes/roguelike/src/systems.ts +620 -0
  274. package/archetypes/roguelike/src/ui.ts +350 -0
  275. package/archetypes/roguelike/tests/flow.og.ts +54 -0
  276. package/archetypes/roguelike/tests/foes.og.ts +45 -0
  277. package/archetypes/roguelike/tests/frames.og.ts +32 -0
  278. package/archetypes/roguelike/tests/frames.ts +134 -0
  279. package/archetypes/roguelike/tests/lib.ts +83 -0
  280. package/archetypes/roguelike/tests/lose.og.ts +44 -0
  281. package/archetypes/roguelike/tests/pad.og.ts +54 -0
  282. package/archetypes/roguelike/tests/pause.og.ts +59 -0
  283. package/archetypes/roguelike/tests/phone.og.ts +54 -0
  284. package/archetypes/roguelike/tests/replay.og.ts +24 -0
  285. package/archetypes/roguelike/tests/win.og.ts +60 -0
  286. package/archetypes/roguelike/tools/make-assets.py +518 -0
  287. package/archetypes/roguelike/tools/oglite.py +285 -0
  288. package/archetypes/roguelike/tsconfig.json +17 -0
  289. package/archetypes/runner/.origin/setup +14 -0
  290. package/archetypes/runner/LICENSES.md +10 -0
  291. package/archetypes/runner/README.md +225 -0
  292. package/archetypes/runner/assets/audio/coin.wav +0 -0
  293. package/archetypes/runner/assets/audio/crash.wav +0 -0
  294. package/archetypes/runner/assets/audio/hold.ogt +9 -0
  295. package/archetypes/runner/assets/audio/jump.wav +0 -0
  296. package/archetypes/runner/assets/audio/start.wav +0 -0
  297. package/archetypes/runner/assets/fonts/OFL.txt +93 -0
  298. package/archetypes/runner/assets/fonts/ui.ttf +0 -0
  299. package/archetypes/runner/assets/fonts/ui.ttf.ogt +11 -0
  300. package/archetypes/runner/assets/maps/far.ogt +25 -0
  301. package/archetypes/runner/assets/maps/near.ogt +25 -0
  302. package/archetypes/runner/assets/maps/roofs.ogt +19 -0
  303. package/archetypes/runner/assets/scenes/main.ogt +35 -0
  304. package/archetypes/runner/assets/sprites/coin.ogt +11 -0
  305. package/archetypes/runner/assets/sprites/coin.png +0 -0
  306. package/archetypes/runner/assets/sprites/coin.png.ogt +7 -0
  307. package/archetypes/runner/assets/sprites/courier.ogt +32 -0
  308. package/archetypes/runner/assets/sprites/courier.png +0 -0
  309. package/archetypes/runner/assets/sprites/courier.png.ogt +7 -0
  310. package/archetypes/runner/assets/sprites/props.ogt +21 -0
  311. package/archetypes/runner/assets/sprites/props.png +0 -0
  312. package/archetypes/runner/assets/sprites/props.png.ogt +7 -0
  313. package/archetypes/runner/assets/sprites/sky.png +0 -0
  314. package/archetypes/runner/assets/sprites/sky.png.ogt +7 -0
  315. package/archetypes/runner/assets/tiles/city.ogt +18 -0
  316. package/archetypes/runner/assets/tiles/city.png +0 -0
  317. package/archetypes/runner/assets/tiles/city.png.ogt +7 -0
  318. package/archetypes/runner/gitattributes +22 -0
  319. package/archetypes/runner/gitignore +8 -0
  320. package/archetypes/runner/host.d.ts +46 -0
  321. package/archetypes/runner/package.json +18 -0
  322. package/archetypes/runner/project.ogt +18 -0
  323. package/archetypes/runner/references/metal/frames-coin.png +0 -0
  324. package/archetypes/runner/references/metal/frames-crash.png +0 -0
  325. package/archetypes/runner/references/metal/frames-title.png +0 -0
  326. package/archetypes/runner/src/art.ts +57 -0
  327. package/archetypes/runner/src/components.ts +94 -0
  328. package/archetypes/runner/src/main.ts +241 -0
  329. package/archetypes/runner/src/systems.ts +553 -0
  330. package/archetypes/runner/tests/coins.og.ts +30 -0
  331. package/archetypes/runner/tests/flow.og.ts +72 -0
  332. package/archetypes/runner/tests/frames.og.ts +37 -0
  333. package/archetypes/runner/tests/frames.ts +134 -0
  334. package/archetypes/runner/tests/jump.og.ts +44 -0
  335. package/archetypes/runner/tests/lib.ts +42 -0
  336. package/archetypes/runner/tests/records.og.ts +43 -0
  337. package/archetypes/runner/tests/replay.og.ts +36 -0
  338. package/archetypes/runner/tests/sounds.og.ts +43 -0
  339. package/archetypes/runner/tests/tune.og.ts +36 -0
  340. package/archetypes/runner/tools/make-assets.py +603 -0
  341. package/archetypes/runner/tools/oglite.py +285 -0
  342. package/archetypes/runner/tsconfig.json +17 -0
  343. package/archetypes/third-person/.origin/setup +14 -0
  344. package/archetypes/third-person/LICENSES.md +23 -0
  345. package/archetypes/third-person/README.md +249 -0
  346. package/archetypes/third-person/assets/ai/sentinel-east.ogt +69 -0
  347. package/archetypes/third-person/assets/ai/sentinel-north.ogt +69 -0
  348. package/archetypes/third-person/assets/ai/sentinel-west.ogt +69 -0
  349. package/archetypes/third-person/assets/anims/humanoid-clips.glb +0 -0
  350. package/archetypes/third-person/assets/audio/alarm.wav +0 -0
  351. package/archetypes/third-person/assets/audio/dawn.wav +0 -0
  352. package/archetypes/third-person/assets/audio/grab.wav +0 -0
  353. package/archetypes/third-person/assets/audio/ignite.wav +0 -0
  354. package/archetypes/third-person/assets/audio/jump.wav +0 -0
  355. package/archetypes/third-person/assets/audio/night.wav +0 -0
  356. package/archetypes/third-person/assets/audio/splash.wav +0 -0
  357. package/archetypes/third-person/assets/audio/step.ogt +9 -0
  358. package/archetypes/third-person/assets/audio/ui-close.ogt +9 -0
  359. package/archetypes/third-person/assets/audio/ui-error.ogt +9 -0
  360. package/archetypes/third-person/assets/audio/ui-open.ogt +9 -0
  361. package/archetypes/third-person/assets/audio/ui-press.ogt +9 -0
  362. package/archetypes/third-person/assets/audio/ui-success.ogt +9 -0
  363. package/archetypes/third-person/assets/audio/vale.wav +0 -0
  364. package/archetypes/third-person/assets/fonts/NotoSansSC-subset.ttf +0 -0
  365. package/archetypes/third-person/assets/fonts/NotoSansSC-subset.ttf.ogt +10 -0
  366. package/archetypes/third-person/assets/fonts/OFL.txt +93 -0
  367. package/archetypes/third-person/assets/fx/burst.ogt +25 -0
  368. package/archetypes/third-person/assets/fx/fire.ogt +24 -0
  369. package/archetypes/third-person/assets/fx/smoke.ogt +24 -0
  370. package/archetypes/third-person/assets/fx/splash.ogt +24 -0
  371. package/archetypes/third-person/assets/luts/teal-orange.cube +4917 -0
  372. package/archetypes/third-person/assets/models/stout.glb +0 -0
  373. package/archetypes/third-person/assets/models/strider.glb +0 -0
  374. package/archetypes/third-person/assets/models/strider.glb.ogt +9 -0
  375. package/archetypes/third-person/assets/nav/vale.ogt +43 -0
  376. package/archetypes/third-person/assets/rigs/strider.ogt +6 -0
  377. package/archetypes/third-person/assets/skins/ember/Cinzel-Bold.ttf +0 -0
  378. package/archetypes/third-person/assets/skins/ember/Cinzel-Bold.ttf.ogt +9 -0
  379. package/archetypes/third-person/assets/skins/ember/LICENSES.md +13 -0
  380. package/archetypes/third-person/assets/skins/ember/Newsreader-Medium.ttf +0 -0
  381. package/archetypes/third-person/assets/skins/ember/Newsreader-Medium.ttf.ogt +9 -0
  382. package/archetypes/third-person/assets/skins/ember/OFL.txt +94 -0
  383. package/archetypes/third-person/assets/skins/ember/atlas.png +0 -0
  384. package/archetypes/third-person/assets/skins/ember/atlas.png.ogt +7 -0
  385. package/archetypes/third-person/assets/skins/ember/icons.png +0 -0
  386. package/archetypes/third-person/assets/skins/ember/icons.png.ogt +7 -0
  387. package/archetypes/third-person/assets/skins/ember/skin.ogt +109 -0
  388. package/archetypes/third-person/assets/strings/en.ogt +47 -0
  389. package/archetypes/third-person/assets/strings/zh-CN.ogt +47 -0
  390. package/archetypes/third-person/assets/terrain/vale-control.png +0 -0
  391. package/archetypes/third-person/assets/terrain/vale.ogt +42 -0
  392. package/archetypes/third-person/assets/terrain/vale.png +0 -0
  393. package/archetypes/third-person/assets/textures/decals.png +0 -0
  394. package/archetypes/third-person/assets/textures/flame.png +0 -0
  395. package/archetypes/third-person/assets/textures/smoke.png +0 -0
  396. package/archetypes/third-person/assets/textures/spark.png +0 -0
  397. package/archetypes/third-person/assets/textures/terrain-grass.png +0 -0
  398. package/archetypes/third-person/assets/textures/terrain-path.png +0 -0
  399. package/archetypes/third-person/assets/textures/terrain-rock.png +0 -0
  400. package/archetypes/third-person/assets/textures/terrain-sand.png +0 -0
  401. package/archetypes/third-person/gitattributes +22 -0
  402. package/archetypes/third-person/gitignore +8 -0
  403. package/archetypes/third-person/host.d.ts +46 -0
  404. package/archetypes/third-person/package.json +18 -0
  405. package/archetypes/third-person/project.ogt +14 -0
  406. package/archetypes/third-person/references/metal/beacon.png +0 -0
  407. package/archetypes/third-person/references/metal/dawn.png +0 -0
  408. package/archetypes/third-person/references/metal/title.png +0 -0
  409. package/archetypes/third-person/references/webgl2/beacon.png +0 -0
  410. package/archetypes/third-person/src/anim.ts +87 -0
  411. package/archetypes/third-person/src/beacons.ts +80 -0
  412. package/archetypes/third-person/src/components.ts +148 -0
  413. package/archetypes/third-person/src/hero.ts +293 -0
  414. package/archetypes/third-person/src/main.ts +164 -0
  415. package/archetypes/third-person/src/sentinels.ts +159 -0
  416. package/archetypes/third-person/src/sound.ts +66 -0
  417. package/archetypes/third-person/src/ui.ts +451 -0
  418. package/archetypes/third-person/src/world.ts +212 -0
  419. package/archetypes/third-person/tests/flow.og.ts +70 -0
  420. package/archetypes/third-person/tests/frames.og.ts +35 -0
  421. package/archetypes/third-person/tests/frames.ts +134 -0
  422. package/archetypes/third-person/tests/hitstop.og.ts +26 -0
  423. package/archetypes/third-person/tests/lib.ts +87 -0
  424. package/archetypes/third-person/tests/lose.og.ts +44 -0
  425. package/archetypes/third-person/tests/pause.og.ts +64 -0
  426. package/archetypes/third-person/tests/phone.og.ts +65 -0
  427. package/archetypes/third-person/tests/replay.og.ts +32 -0
  428. package/archetypes/third-person/tests/restart.og.ts +28 -0
  429. package/archetypes/third-person/tests/sentinels.og.ts +48 -0
  430. package/archetypes/third-person/tests/win.og.ts +62 -0
  431. package/archetypes/third-person/tools/make-assets.py +353 -0
  432. package/archetypes/third-person/tools/oglite.py +285 -0
  433. package/archetypes/third-person/tsconfig.json +17 -0
  434. package/archetypes/topdown/.origin/setup +14 -0
  435. package/archetypes/topdown/LICENSES.md +10 -0
  436. package/archetypes/topdown/README.md +235 -0
  437. package/archetypes/topdown/assets/ai/slime.ogt +49 -0
  438. package/archetypes/topdown/assets/audio/coin.wav +0 -0
  439. package/archetypes/topdown/assets/audio/door.wav +0 -0
  440. package/archetypes/topdown/assets/audio/hit.wav +0 -0
  441. package/archetypes/topdown/assets/audio/hurt.wav +0 -0
  442. package/archetypes/topdown/assets/audio/key.wav +0 -0
  443. package/archetypes/topdown/assets/audio/step.ogt +9 -0
  444. package/archetypes/topdown/assets/fonts/OFL.txt +93 -0
  445. package/archetypes/topdown/assets/fonts/ui.ttf +0 -0
  446. package/archetypes/topdown/assets/fonts/ui.ttf.ogt +9 -0
  447. package/archetypes/topdown/assets/maps/tomb.tmj +2183 -0
  448. package/archetypes/topdown/assets/sprites/hero.ogt +58 -0
  449. package/archetypes/topdown/assets/sprites/hero.png +0 -0
  450. package/archetypes/topdown/assets/sprites/hero.png.ogt +7 -0
  451. package/archetypes/topdown/assets/sprites/items.ogt +15 -0
  452. package/archetypes/topdown/assets/sprites/items.png +0 -0
  453. package/archetypes/topdown/assets/sprites/items.png.ogt +7 -0
  454. package/archetypes/topdown/assets/sprites/slime.ogt +17 -0
  455. package/archetypes/topdown/assets/sprites/slime.png +0 -0
  456. package/archetypes/topdown/assets/sprites/slime.png.ogt +7 -0
  457. package/archetypes/topdown/assets/tiles/dungeon.ogt +65 -0
  458. package/archetypes/topdown/assets/tiles/dungeon.png +0 -0
  459. package/archetypes/topdown/assets/tiles/dungeon.png.ogt +7 -0
  460. package/archetypes/topdown/gitattributes +22 -0
  461. package/archetypes/topdown/gitignore +8 -0
  462. package/archetypes/topdown/host.d.ts +46 -0
  463. package/archetypes/topdown/package.json +18 -0
  464. package/archetypes/topdown/project.ogt +14 -0
  465. package/archetypes/topdown/references/metal/crit.png +0 -0
  466. package/archetypes/topdown/references/metal/dash.png +0 -0
  467. package/archetypes/topdown/references/metal/east.png +0 -0
  468. package/archetypes/topdown/references/metal/start.png +0 -0
  469. package/archetypes/topdown/src/art.ts +48 -0
  470. package/archetypes/topdown/src/components.ts +128 -0
  471. package/archetypes/topdown/src/main.ts +275 -0
  472. package/archetypes/topdown/src/systems.ts +503 -0
  473. package/archetypes/topdown/tests/ai.og.ts +64 -0
  474. package/archetypes/topdown/tests/combat.og.ts +72 -0
  475. package/archetypes/topdown/tests/door.og.ts +62 -0
  476. package/archetypes/topdown/tests/flee.og.ts +49 -0
  477. package/archetypes/topdown/tests/frames.og.ts +45 -0
  478. package/archetypes/topdown/tests/frames.ts +134 -0
  479. package/archetypes/topdown/tests/lib.ts +71 -0
  480. package/archetypes/topdown/tests/replay.og.ts +40 -0
  481. package/archetypes/topdown/tests/walk.og.ts +46 -0
  482. package/archetypes/topdown/tools/make-assets.py +623 -0
  483. package/archetypes/topdown/tools/oglite.py +285 -0
  484. package/archetypes/topdown/tsconfig.json +17 -0
  485. package/archetypes/tower-defense/.origin/setup +14 -0
  486. package/archetypes/tower-defense/LICENSES.md +15 -0
  487. package/archetypes/tower-defense/README.md +240 -0
  488. package/archetypes/tower-defense/assets/ai/plant.ogt +16 -0
  489. package/archetypes/tower-defense/assets/audio/clear.wav +0 -0
  490. package/archetypes/tower-defense/assets/audio/frost.wav +0 -0
  491. package/archetypes/tower-defense/assets/audio/leak.wav +0 -0
  492. package/archetypes/tower-defense/assets/audio/lost.wav +0 -0
  493. package/archetypes/tower-defense/assets/audio/night.wav +0 -0
  494. package/archetypes/tower-defense/assets/audio/plant.wav +0 -0
  495. package/archetypes/tower-defense/assets/audio/pop.wav +0 -0
  496. package/archetypes/tower-defense/assets/audio/thorn.wav +0 -0
  497. package/archetypes/tower-defense/assets/audio/ui-close.ogt +9 -0
  498. package/archetypes/tower-defense/assets/audio/ui-error.ogt +9 -0
  499. package/archetypes/tower-defense/assets/audio/ui-open.ogt +9 -0
  500. package/archetypes/tower-defense/assets/audio/ui-press.ogt +9 -0
  501. package/archetypes/tower-defense/assets/audio/ui-success.ogt +9 -0
  502. package/archetypes/tower-defense/assets/audio/wave.wav +0 -0
  503. package/archetypes/tower-defense/assets/audio/won.wav +0 -0
  504. package/archetypes/tower-defense/assets/fonts/NotoSansSC-subset.ttf +0 -0
  505. package/archetypes/tower-defense/assets/fonts/NotoSansSC-subset.ttf.ogt +10 -0
  506. package/archetypes/tower-defense/assets/fonts/OFL.txt +93 -0
  507. package/archetypes/tower-defense/assets/fx/fireflies.ogt +21 -0
  508. package/archetypes/tower-defense/assets/fx/pop.ogt +21 -0
  509. package/archetypes/tower-defense/assets/maps/garden.ogt +91 -0
  510. package/archetypes/tower-defense/assets/skins/pathguard/Fredoka-SemiBold.ttf +0 -0
  511. package/archetypes/tower-defense/assets/skins/pathguard/Fredoka-SemiBold.ttf.ogt +9 -0
  512. package/archetypes/tower-defense/assets/skins/pathguard/LICENSES.md +13 -0
  513. package/archetypes/tower-defense/assets/skins/pathguard/LilitaOne-Regular.ttf +0 -0
  514. package/archetypes/tower-defense/assets/skins/pathguard/LilitaOne-Regular.ttf.ogt +9 -0
  515. package/archetypes/tower-defense/assets/skins/pathguard/OFL.txt +94 -0
  516. package/archetypes/tower-defense/assets/skins/pathguard/atlas.png +0 -0
  517. package/archetypes/tower-defense/assets/skins/pathguard/atlas.png.ogt +7 -0
  518. package/archetypes/tower-defense/assets/skins/pathguard/icons.png +0 -0
  519. package/archetypes/tower-defense/assets/skins/pathguard/icons.png.ogt +7 -0
  520. package/archetypes/tower-defense/assets/skins/pathguard/skin.ogt +111 -0
  521. package/archetypes/tower-defense/assets/sprites/garden.ogt +66 -0
  522. package/archetypes/tower-defense/assets/sprites/garden.png +0 -0
  523. package/archetypes/tower-defense/assets/sprites/garden.png.ogt +7 -0
  524. package/archetypes/tower-defense/assets/sprites/glow.png +0 -0
  525. package/archetypes/tower-defense/assets/sprites/glow.png.ogt +7 -0
  526. package/archetypes/tower-defense/assets/sprites/ring.png +0 -0
  527. package/archetypes/tower-defense/assets/sprites/ring.png.ogt +7 -0
  528. package/archetypes/tower-defense/assets/sprites/spark.png +0 -0
  529. package/archetypes/tower-defense/assets/sprites/spark.png.ogt +7 -0
  530. package/archetypes/tower-defense/assets/strings/en.ogt +47 -0
  531. package/archetypes/tower-defense/assets/strings/zh-CN.ogt +47 -0
  532. package/archetypes/tower-defense/assets/tiles/garden.ogt +10 -0
  533. package/archetypes/tower-defense/assets/tiles/garden.png +0 -0
  534. package/archetypes/tower-defense/assets/tiles/garden.png.ogt +7 -0
  535. package/archetypes/tower-defense/gitattributes +22 -0
  536. package/archetypes/tower-defense/gitignore +8 -0
  537. package/archetypes/tower-defense/host.d.ts +46 -0
  538. package/archetypes/tower-defense/package.json +18 -0
  539. package/archetypes/tower-defense/project.ogt +14 -0
  540. package/archetypes/tower-defense/references/metal/end.png +0 -0
  541. package/archetypes/tower-defense/references/metal/title.png +0 -0
  542. package/archetypes/tower-defense/references/metal/wave.png +0 -0
  543. package/archetypes/tower-defense/src/art.ts +24 -0
  544. package/archetypes/tower-defense/src/components.ts +156 -0
  545. package/archetypes/tower-defense/src/main.ts +253 -0
  546. package/archetypes/tower-defense/src/systems.ts +658 -0
  547. package/archetypes/tower-defense/src/ui.ts +382 -0
  548. package/archetypes/tower-defense/tests/flow.og.ts +56 -0
  549. package/archetypes/tower-defense/tests/frames.og.ts +31 -0
  550. package/archetypes/tower-defense/tests/frames.ts +134 -0
  551. package/archetypes/tower-defense/tests/lib.ts +81 -0
  552. package/archetypes/tower-defense/tests/lose.og.ts +40 -0
  553. package/archetypes/tower-defense/tests/maze.og.ts +53 -0
  554. package/archetypes/tower-defense/tests/pad.og.ts +41 -0
  555. package/archetypes/tower-defense/tests/pause.og.ts +59 -0
  556. package/archetypes/tower-defense/tests/phone.og.ts +49 -0
  557. package/archetypes/tower-defense/tests/replay.og.ts +28 -0
  558. package/archetypes/tower-defense/tests/win.og.ts +58 -0
  559. package/archetypes/tower-defense/tools/make-assets.py +640 -0
  560. package/archetypes/tower-defense/tools/oglite.py +285 -0
  561. package/archetypes/tower-defense/tsconfig.json +17 -0
  562. package/bin/create.ts +503 -0
  563. package/looks/catalogue.json +1871 -0
  564. package/package.json +31 -4
  565. package/template/.origin/setup +14 -0
  566. package/template/LICENSES.md +23 -0
  567. package/template/README.md +37 -0
  568. package/template/assets/ai/guard.ogt +48 -0
  569. package/template/assets/audio/blip.wav +0 -0
  570. package/template/assets/audio/click.ogt +9 -0
  571. package/template/assets/fonts/NotoSansSC-Regular-subset.ttf +0 -0
  572. package/template/assets/fonts/NotoSansSC-Regular-subset.ttf.ogt +12 -0
  573. package/template/assets/fonts/OFL.txt +93 -0
  574. package/template/assets/looks/origin-game.ogt +12 -0
  575. package/template/assets/models/dropship.glb +0 -0
  576. package/template/assets/models/walker.glb +0 -0
  577. package/template/assets/nav/yard.ogt +27 -0
  578. package/template/assets/scenes/main.ogt +34 -0
  579. package/template/assets/strings/en.ogt +14 -0
  580. package/template/assets/textures/stone.png +0 -0
  581. package/template/assets/textures/stone_n.png +0 -0
  582. package/template/assets/textures/stone_n.png.ogt +7 -0
  583. package/template/gitattributes +22 -0
  584. package/template/gitignore +8 -0
  585. package/template/package.json +18 -0
  586. package/template/project.ogt +25 -0
  587. package/template/src/main.ts +246 -0
  588. package/template/tests/guard.og.ts +67 -0
  589. package/template/tests/smoke.og.ts +43 -0
  590. package/template/tsconfig.json +17 -0
@@ -0,0 +1,87 @@
1
+ // The two characters' blend trees (og.anim.graph) and where their clips
2
+ // come from. The hero is the strider: its import settings retarget the
3
+ // clip library (anims/humanoid-clips.glb) at cook time. The sentinels are
4
+ // the stout, whose Unreal-named bones get the library's clips retargeted
5
+ // here at run time (og.anim.retarget).
6
+
7
+ import { og } from "@origingame/engine";
8
+ import type { ClipInfo, GraphAsset, SkeletonAsset, World } from "@origingame/engine";
9
+ import { must } from "./world.ts";
10
+
11
+ /**
12
+ * The hero's graph: p0 is the speed across the ground, p1 is 1 in the air.
13
+ * On the ground a 1D blend runs idle, walk and run by speed (the clips'
14
+ * phases kept in step); a jump or a fall cross-fades to the run held
15
+ * mid-stride, slowed.
16
+ */
17
+ const HERO_GRAPH = `type animgraph
18
+ version 1
19
+ ---
20
+ param speed
21
+ param air
22
+ clip idle "idle"
23
+ clip walk "walk" {
24
+ speed 1.1
25
+ }
26
+ clip run "run"
27
+ blend1d move speed {
28
+ child idle 0
29
+ child walk 2.2
30
+ child run 5.6
31
+ }
32
+ layer base {
33
+ state ground move
34
+ state air run {
35
+ speed 0.3
36
+ }
37
+ entry ground
38
+ transition ground air {
39
+ when air gt 0.5
40
+ fade 0.15
41
+ }
42
+ transition air ground {
43
+ when air lt 0.5
44
+ fade 0.1
45
+ }
46
+ }
47
+ `;
48
+
49
+ /** A sentinel's graph: p0 is its speed (a patrol walks, a chase runs). */
50
+ const SENTINEL_GRAPH = `type animgraph
51
+ version 1
52
+ ---
53
+ param speed
54
+ clip idle "idle"
55
+ clip walk "walk" {
56
+ speed 0.9
57
+ }
58
+ clip run "run"
59
+ blend1d move speed {
60
+ child idle 0
61
+ child walk 1.6
62
+ child run 4.2
63
+ }
64
+ layer base {
65
+ state move move
66
+ entry move
67
+ }
68
+ `;
69
+
70
+ export interface Rigged {
71
+ skeleton: SkeletonAsset;
72
+ graph: GraphAsset;
73
+ }
74
+
75
+ /** The strider's skeleton and the hero's graph. */
76
+ export function heroRig(world: World): Rigged {
77
+ const anim = must(og.load.animation(world, "models/strider.glb"));
78
+ return { skeleton: anim.skeleton, graph: must(og.anim.graph(world, { text: HERO_GRAPH, clips: anim.clips })) };
79
+ }
80
+
81
+ /** The stout's skeleton and the sentinels' graph, its clips retargeted from the library. */
82
+ export function sentinelRig(world: World): Rigged {
83
+ const anim = must(og.load.animation(world, "models/stout.glb"));
84
+ const library = must(og.load.animation(world, "anims/humanoid-clips.glb"));
85
+ const clips: ClipInfo[] = library.clips.map((c) => ({ ...c, clip: must(og.anim.retarget(world, { clip: c.clip, source: library.skeleton, target: anim.skeleton })).clip }));
86
+ return { skeleton: anim.skeleton, graph: must(og.anim.graph(world, { text: SENTINEL_GRAPH, clips })) };
87
+ }
@@ -0,0 +1,80 @@
1
+ // The beacons: the hero kindles the nearest one by holding F (X, Kindle)
2
+ // within reach; Tuning.kindleSeconds later it catches (src/world.ts
3
+ // lightBeacon: fire, smoke, a burst of embers, the scorch and the rune),
4
+ // the camera punches and a toast counts it. Every burning beacon's light
5
+ // swells in and flickers. All four lit, dawn will find the vale lit: the
6
+ // run is won.
7
+
8
+ import { og, defineSystem } from "@origingame/engine";
9
+ import type { Entity, Value, World } from "@origingame/engine";
10
+ import { Beacon, BEACONS, Ending, Game, Phase, Sound, Tuning } from "./components.ts";
11
+ import { entity, STAND } from "./hero.ts";
12
+ import { say } from "./ui.ts";
13
+ import { BEACON_AT, douseBeacons, lightBeacon, must, part } from "./world.ts";
14
+
15
+ /** Beacon `i` catches: its fire, the punch, the toast; the last one wins the run (no toast). */
16
+ export function catchBeacon(world: World, g: Value<typeof Game>, i: number): void {
17
+ if ((g.litMask & (1 << i)) !== 0) return;
18
+ lightBeacon(world, i, g.time);
19
+ g.litMask |= 1 << i;
20
+ g.lit += 1;
21
+ g.kindle = 0;
22
+ g.near = -1;
23
+ g.sfx |= Sound.Ignite;
24
+ must(og.camera.punch(world, 0.35));
25
+ // The last one's news is the dawn stinger and the summary, not a toast under it.
26
+ if (g.lit < BEACONS) og.ui.toast(world, say(world, "hud.lit", { n: g.lit, total: BEACONS }));
27
+ if (g.lit >= BEACONS) {
28
+ g.ending = Ending.Dawn;
29
+ g.sfx |= Sound.Dawn;
30
+ }
31
+ }
32
+
33
+ /** A burning beacon's light at full flame (og.PointLight.intensity). */
34
+ const FLAME = 30;
35
+
36
+ export const Beacons = defineSystem(
37
+ "Beacons",
38
+ { reads: [Tuning, og.Transform], writes: [Game, Beacon, og.PointLight, og.ParticleSystem, og.Decal, og.Mesh], after: ["Hero"] },
39
+ (_q, time, world) => {
40
+ const g = must(world.resource(Game));
41
+ const t = must(world.resource(Tuning));
42
+ if (g.start) {
43
+ // Behind the title the four burn; a run starts with them dark.
44
+ douseBeacons(world);
45
+ if (g.phase === Phase.Title) for (let i = 0; i < BEACONS; i++) lightBeacon(world, i, -1);
46
+ }
47
+ const playing = g.phase === Phase.Play && g.ending === Ending.None;
48
+ const p = must(world.get(g.hero as unknown as Entity, og.Transform)).position;
49
+ let near = -1;
50
+ let nearest = Infinity;
51
+ if (playing) {
52
+ BEACON_AT.forEach((b, i) => {
53
+ if ((g.litMask & (1 << i)) !== 0) return;
54
+ const d = Math.hypot(p.x - b.x, p.z - b.z);
55
+ if (d < t.reach + 0.8 && d < nearest) {
56
+ nearest = d;
57
+ near = i;
58
+ }
59
+ });
60
+ }
61
+ if (near !== g.near) {
62
+ g.near = near;
63
+ g.kindle = 0;
64
+ }
65
+ // Kindling counts once the hero stands at the plinth with its hand in the bowl.
66
+ if (playing && g.kindling && near >= 0 && nearest <= STAND + 0.3) g.kindle += time.scaledDt;
67
+ else if (g.kindle > 0) g.kindle = Math.max(0, g.kindle - time.scaledDt * 2);
68
+ if (playing && near >= 0 && g.kindle >= t.kindleSeconds) catchBeacon(world, g, near);
69
+ // The lights: each swells in over half a second and flickers.
70
+ for (let i = 0; i < BEACONS; i++) {
71
+ const b = must(world.get(entity(world, `beacon-${i}`), Beacon));
72
+ if (!b.lit) continue;
73
+ const since = g.phase === Phase.Title ? 10 : g.time - b.litAt;
74
+ const swell = Math.min(1, since / 0.5);
75
+ const e = time.elapsed;
76
+ const flicker = 0.86 + 0.08 * Math.sin(e * 13 + i * 2.1) + 0.06 * Math.sin(e * 29 + i * 5.3);
77
+ must(world.set(part(world, i, "light"), og.PointLight, { intensity: FLAME * swell * flicker }));
78
+ }
79
+ },
80
+ );
@@ -0,0 +1,148 @@
1
+ // Every piece of Ember Vale's state lives in these reflected types: dumps,
2
+ // the probe, Tune and the command channel's `get` and `set` read and write
3
+ // them by name.
4
+ //
5
+ // The run (Game), the knobs (Tuning) and the player's settings (Prefs) are
6
+ // resources. The actors are rows spawned in `start`: the hero (Hero), the
7
+ // four beacons (Beacon), the three sentinels (Sentinel) and one voice per
8
+ // sound (Sfx).
9
+
10
+ import { bool, defineComponent, defineResource, f32, f64, i32, u32 } from "@origingame/engine";
11
+
12
+ /** The screen the game is on. */
13
+ export const Phase = { Title: 0, Play: 1, Over: 2 } as const;
14
+
15
+ /** How a run ended (Game.ending). */
16
+ export const Ending = { None: 0, Dawn: 1, Night: 2, Caught: 3 } as const;
17
+
18
+ /**
19
+ * Sound requests: bits of Game.sfx that the Voices system plays once each.
20
+ * `Tap` is the touch HUD's stick, Jump and Kindle answering on the ui bus, as
21
+ * og.ui's own buttons do (og-ui's touch controls play no press sound of their
22
+ * own).
23
+ */
24
+ export const Sound = { Step: 1, Jump: 2, Splash: 4, Ignite: 8, Alarm: 16, Grab: 32, Dawn: 64, Night: 128, Tap: 256 } as const;
25
+
26
+ /** The beacons a run lights. */
27
+ export const BEACONS = 4;
28
+ /** The hearts a run starts with. */
29
+ export const HEARTS = 3;
30
+
31
+ /** The run: the hero's progress, the clock and the effects' bookkeeping. */
32
+ export const Game = defineResource("Game", {
33
+ phase: u32(Phase.Title, { doc: "Phase.* of src/components.ts." }),
34
+ leftTitle: bool(false, { doc: "The player has left the title the game opened on: from then on the title enters with the skin's entrance (the opening one is built settled, og.ui.mainMenu's instant: the web draws it behind the play gate)." }),
35
+ ending: u32(Ending.None, { doc: "Ending.* once a run is over." }),
36
+ paused: bool(false),
37
+ settingsOpen: bool(false),
38
+ held: bool(false, { doc: "The time scale is held at 0 (paused or in the settings)." }),
39
+ runs: u32(0, { doc: "Runs started on this device (the store's `record`)." }),
40
+ wins: u32(0, { doc: "Runs that lit every beacon (the store's `record`)." }),
41
+ best: f32(0, { unit: "s", doc: "The fastest win (the store's `record`; 0 none)." }),
42
+ recordRequest: u32(0, { doc: "The store request reading `record`." }),
43
+ recordRevision: u32(0, { doc: "The revision `record` was read or written at." }),
44
+ recordWrite: u32(0, { doc: "The store request writing `record`." }),
45
+ recordMerge: bool(false, { doc: "A write was refused as stale: the record is read again and the merge written." }),
46
+ recordRuns: u32(0, { doc: "Runs in the record as last read or written: Game.runs less this is what this device adds." }),
47
+ recordWins: u32(0, { doc: "Wins in the record as last read or written." }),
48
+ recordConflicts: u32(0, { doc: "Record writes refused as stale (another device or tab wrote first)." }),
49
+ recordDirty: bool(false, { doc: "The record changed while a read or write of it was in flight: written when that answers." }),
50
+ recordSentRuns: u32(0, { doc: "Runs in the record write in flight (what the store holds once it answers ok)." }),
51
+ recordSentWins: u32(0, { doc: "Wins in the record write in flight." }),
52
+ newBest: bool(false),
53
+ hearts: i32(HEARTS),
54
+ hurts: u32(0, { doc: "Hearts lost this run." }),
55
+ lit: u32(0, { doc: "Beacons lit this run." }),
56
+ litMask: u32(0, { doc: "Bit i: beacon i burns." }),
57
+ time: f32(0, { unit: "s", doc: "Seconds played this run (paused time excluded)." }),
58
+ hour: f32(0, { doc: "The time of day (og.atmosphere.timeOfDay): night falls at Tuning.nightHour." }),
59
+ near: i32(-1, { doc: "The beacon within reach (-1 none)." }),
60
+ kindle: f32(0, { unit: "s", doc: "How long the near beacon has been kindled." }),
61
+ kindling: bool(false, { doc: "The hero is kindling (holding F, X or Kindle by a beacon)." }),
62
+ wading: bool(false, { doc: "The hero's feet are in the lake." }),
63
+ grounded: bool(false),
64
+ speed: f32(0, { unit: "m/s", doc: "The hero's speed across the ground." }),
65
+ jumps: u32(0),
66
+ splashes: u32(0),
67
+ grabs: u32(0, { doc: "Sentinel grabs that cost a heart." }),
68
+ alarms: u32(0, { doc: "Times a sentinel called the alarm." }),
69
+ invulnerable: f32(0, { unit: "s", doc: "Seconds before the hero can be grabbed again." }),
70
+ knock: f32(0, { unit: "s", doc: "Seconds the hero is thrown back (no control)." }),
71
+ knockX: f32(0),
72
+ knockZ: f32(0),
73
+ yaw: f32(0, { unit: "rad", doc: "The view's heading: 0 looks north (-z)." }),
74
+ facing: f32(0, { unit: "rad", doc: "The hero's heading." }),
75
+ padJump: bool(false, { doc: "The HUD's Jump was pressed this tick." }),
76
+ padKindle: bool(false, { doc: "The HUD's Kindle is held." }),
77
+ stickX: f32(0, { doc: "The HUD joystick (written by Ui)." }),
78
+ stickY: f32(0),
79
+ lookId: i32(-1, { doc: "The finger turning the view on a touch screen (an og.Pointers id; -1 none)." }),
80
+ lookX: f32(0, { unit: "cssPx", doc: "Where that finger was last tick." }),
81
+ lookTurn: f32(0, { unit: "rad", doc: "How far the finger turned the view this tick (written by Ui, applied by View)." }),
82
+ overTicks: u32(0, { unit: "ticks", doc: "Ticks since the run ended; the summary comes after a beat." }),
83
+ start: bool(false, { doc: "Every system resets its part of the vale this tick (a new run or the title)." }),
84
+ sfx: u32(0),
85
+ hero: f64(0, { doc: "The hero's entity." }),
86
+ terrain: f64(0, { doc: "The vale's og.terrain data." }),
87
+ nav: f64(0, { doc: "The sentinels' navmesh." }),
88
+ viewW: f32(0, { unit: "cssPx" }),
89
+ viewH: f32(0, { unit: "cssPx" }),
90
+ language: u32(0, { doc: "Prefs.language, mirrored for the facts." }),
91
+ });
92
+
93
+ /** The knobs. Tune reads the tunable fields; `set:Tuning.<field>=<value>` writes them. */
94
+ export const Tuning = defineResource("Tuning", {
95
+ walkSpeed: f32(3, { min: 1, max: 8, step: 0.1, unit: "m/s", tunable: true, label: "Walk speed" }),
96
+ runSpeed: f32(6, { min: 2, max: 12, step: 0.1, unit: "m/s", tunable: true, label: "Run speed" }),
97
+ wade: f32(0.45, { min: 0.1, max: 1, step: 0.05, tunable: true, label: "Wading slows to", doc: "The hero's speed in the lake, as a fraction." }),
98
+ jumpSpeed: f32(5.2, { min: 2, max: 12, step: 0.1, unit: "m/s", tunable: true, label: "Jump speed" }),
99
+ kindleSeconds: f32(1.6, { min: 0.2, max: 6, step: 0.1, unit: "s", tunable: true, label: "Kindling takes" }),
100
+ reach: f32(2.4, { min: 1, max: 5, step: 0.1, unit: "m", tunable: true, label: "Kindling reach", doc: "How near a beacon's centre the hero kindles it." }),
101
+ duskSeconds: f32(150, { min: 20, max: 900, step: 5, unit: "s", tunable: true, label: "Dusk lasts", doc: "Seconds of play from the first light to night." }),
102
+ patrolSpeed: f32(1.6, { min: 0.5, max: 6, step: 0.1, unit: "m/s", tunable: true, label: "Sentinel patrol speed" }),
103
+ chaseSpeed: f32(4.2, { min: 1, max: 9, step: 0.1, unit: "m/s", tunable: true, label: "Sentinel chase speed" }),
104
+ sight: f32(13, { min: 2, max: 40, step: 0.5, unit: "m", tunable: true, label: "Sentinel sight" }),
105
+ hitStop: u32(5, { min: 0, max: 20, unit: "ticks", tunable: true, label: "Hit stop" }),
106
+ cameraDistance: f32(6.2, { min: 2, max: 14, step: 0.1, unit: "m", tunable: true, label: "Camera distance" }),
107
+ });
108
+
109
+ /** The player's settings: kept in og.platform's settings, restored on the first ticks. */
110
+ export const Prefs = defineResource("Prefs", {
111
+ master: f32(1, { min: 0, max: 1 }),
112
+ music: f32(0.7, { min: 0, max: 1 }),
113
+ effects: f32(1, { min: 0, max: 1 }),
114
+ language: u32(0, { doc: "0 English, 1 简体中文 (LANGUAGES in src/ui.ts)." }),
115
+ reducedMotion: bool(false),
116
+ loadRequest: u32(0),
117
+ saves: u32(0),
118
+ });
119
+
120
+ /** The hero, on the capsule (its body, the strider, is a child). */
121
+ export const Hero = defineComponent("Hero", {
122
+ steps: u32(0, { doc: "Footsteps: one per stride of ground covered." }),
123
+ airTime: f32(0, { unit: "s", doc: "Seconds since the capsule last stood on the ground." }),
124
+ stepClock: f32(0, { unit: "m", doc: "Ground covered since the last footstep." }),
125
+ });
126
+
127
+ /** A beacon: its plinth, bowl, fire, smoke, light and decals hang off it. */
128
+ export const Beacon = defineComponent("Beacon", {
129
+ index: u32(0),
130
+ lit: bool(false),
131
+ litAt: f32(0, { unit: "s", doc: "Game.time when it caught." }),
132
+ top: f32(0, { unit: "m", doc: "The bowl's rim, world y." }),
133
+ });
134
+
135
+ /** A sentinel: a stone stout on the navmesh, thinking with assets/ai/sentinel-<name>.ogt. */
136
+ export const Sentinel = defineComponent("Sentinel", {
137
+ index: u32(0),
138
+ grabs: u32(0),
139
+ alarms: u32(0),
140
+ speed: f32(0, { unit: "m/s", doc: "Its speed across the ground (the graph's p0)." }),
141
+ alert: bool(false, { doc: "It is chasing or grabbing (its eye burns red)." }),
142
+ });
143
+
144
+ /** One voice: plays its sound once when Game.sfx asks for its bit. */
145
+ export const Sfx = defineComponent("Sfx", {
146
+ bit: u32(0),
147
+ plays: u32(0),
148
+ });
@@ -0,0 +1,293 @@
1
+ // The hero: a capsule on og.CharacterController carrying the strider
2
+ // (skinned, run by the graph of src/anim.ts), and the camera that follows
3
+ // it. WASD or the arrows (the left stick, the touch joystick) walk
4
+ // relative to the view, Shift (a full stick) runs, Space (A, Jump) jumps,
5
+ // holding F (X, Kindle) by a beacon walks up to it and reaches into the
6
+ // bowl with the right hand (an og.IkChain on the arm) until it catches.
7
+ // Wading in the lake slows the hero and splashes; the sentinels cannot
8
+ // follow there. Q and E (the right stick, a finger dragged across the
9
+ // touch HUD's look zone) turn the view; walking swings it slowly behind
10
+ // the hero.
11
+
12
+ import { og, defineSystem, SHAPE_CAPSULE } from "@origingame/engine";
13
+ import type { Entity, Value, Vector3, World } from "@origingame/engine";
14
+ import { heroRig } from "./anim.ts";
15
+ import { Beacon, Ending, Game, Hero, Phase, Sound, Tuning } from "./components.ts";
16
+ import { BEACON_AT, ground, LAKE, must, START, WATER_Y } from "./world.ts";
17
+
18
+ type GameV = Value<typeof Game>;
19
+ type TuneV = Value<typeof Tuning>;
20
+
21
+ /** The capsule's centre above the feet (radius 0.3, a 0.9 m segment). */
22
+ export const FEET = 0.75;
23
+ /** The strider's scale (about 1.6 m tall). */
24
+ const BODY_SCALE = 0.92;
25
+ /** The strider's right hand: its index among the skin's joints (Hips is 0). */
26
+ export const RIGHT_HAND = 12;
27
+ /** How near a beacon's centre the hero stands to reach into its bowl. */
28
+ export const STAND = 1.12;
29
+ const UP = { x: 0, y: 1, z: 0 };
30
+
31
+ /** Tuning, read on its first use in this call (a resource read encodes every field). */
32
+ export function lazyTuning(world: World): TuneV {
33
+ let v: TuneV | null = null;
34
+ return new Proxy({} as TuneV, { get: (_, k) => ((v ??= must(world.resource(Tuning))) as unknown as Record<string | symbol, unknown>)[k] });
35
+ }
36
+
37
+ export function entity(world: World, name: string): Entity {
38
+ const e = world.find(name);
39
+ if (e === null) throw new Error(`${name} is missing`);
40
+ return e;
41
+ }
42
+
43
+ /** Spawns the hero at the start, its body, its reach and its splash. */
44
+ export function spawnHero(world: World): void {
45
+ const g = must(world.resource(Game));
46
+ const rig = heroRig(world);
47
+ const h = ground(world, g, START.x, START.z).height;
48
+ const hero = must(world.spawn({ name: "hero", position: { x: START.x, y: h + FEET + 0.05, z: START.z } }));
49
+ must(world.insert(hero, og.Collider, { shape: SHAPE_CAPSULE, radius: 0.3, height: 0.9 }));
50
+ must(world.insert(hero, og.CharacterController, { maxSlope: 42, stepHeight: 0.35, snapDistance: 0.3, coyoteTime: 0.12 }));
51
+ must(world.insert(hero, Hero));
52
+ must(world.insert(hero, og.Perceivable, { team: 1, eyeHeight: 0.6 }));
53
+ g.hero = hero as unknown as number;
54
+ const body = must(world.instantiate(must(og.load.gltf(world, "models/strider.glb")), { name: "hero-body", parent: hero, position: { x: 0, y: -FEET, z: 0 }, scale: { x: BODY_SCALE, y: BODY_SCALE, z: BODY_SCALE } }));
55
+ must(world.insert(body, og.Skeleton, { asset: rig.skeleton, rootMotion: "off" }));
56
+ must(world.insert(body, og.AnimationGraph, { asset: rig.graph }));
57
+ const reach = must(world.spawn({ name: "hero-reach" }));
58
+ must(world.insert(reach, og.IkChain, { skeleton: body, tip: RIGHT_HAND, bones: 3, solver: "fabrik", tolerance: 0.02, space: "world", weight: 0 }));
59
+ const splash = must(world.spawn({ name: "hero-splash", position: { x: 0, y: WATER_Y, z: 0 } }));
60
+ must(world.insert(splash, og.ParticleSystem, { emitter: must(og.load.emitter(world, "fx/splash.ogt")), seed: 5, visible: false }));
61
+ }
62
+
63
+ function wrap(a: number): number {
64
+ while (a > Math.PI) a -= 2 * Math.PI;
65
+ while (a < -Math.PI) a += 2 * Math.PI;
66
+ return a;
67
+ }
68
+
69
+ /** Turns `from` towards `to` by at most `step` radians. */
70
+ export function turn(from: number, to: number, step: number): number {
71
+ const d = wrap(to - from);
72
+ return wrap(from + Math.max(-step, Math.min(step, d)));
73
+ }
74
+
75
+ function splash(world: World, g: GameV, at: Vector3): void {
76
+ must(world.set(entity(world, "hero-splash"), og.Transform, { position: { x: at.x, y: WATER_Y + 0.02, z: at.z } }));
77
+ must(world.set(entity(world, "hero-splash"), og.ParticleSystem, { visible: true, startTick: world.tick }));
78
+ g.sfx |= Sound.Splash;
79
+ g.splashes += 1;
80
+ }
81
+
82
+ /** Puts the hero back at the start, facing north, the view behind it. */
83
+ function reset(world: World, g: GameV, hero: Entity): void {
84
+ const h = ground(world, g, START.x, START.z).height;
85
+ must(world.set(hero, og.Transform, { position: { x: START.x, y: h + FEET + 0.05, z: START.z } }));
86
+ must(world.set(hero, og.CharacterController, { move: { x: 0, y: 0, z: 0 }, jump: false }));
87
+ must(world.set(hero, Hero, { airTime: 0, stepClock: 0 }));
88
+ g.facing = Math.PI;
89
+ g.yaw = 0;
90
+ g.knock = 0;
91
+ g.invulnerable = 0;
92
+ g.kindling = false;
93
+ }
94
+
95
+ /**
96
+ * Per tick: reads the controls, moves the capsule, turns the body, drives
97
+ * the graph and the reach, and makes the steps, the splashes and the jumps
98
+ * heard.
99
+ */
100
+ export const HeroMove = defineSystem(
101
+ "Hero",
102
+ { reads: [og.Input, og.Gamepad, Tuning, Beacon], writes: [Game, Hero, og.CharacterController, og.Transform, og.AnimationGraph, og.IkChain, og.ParticleSystem, og.PostEffects], after: ["Ui"] },
103
+ (_q, time, world) => {
104
+ const g = must(world.resource(Game));
105
+ const t = lazyTuning(world);
106
+ const hero = g.hero as unknown as Entity;
107
+ if (g.start) reset(world, g, hero);
108
+ const inp = must(world.resource(og.Input));
109
+ const gp = must(world.resource(og.Gamepad));
110
+ const playing = g.phase === Phase.Play && g.ending === Ending.None;
111
+ // Pause before the time gate: a press during a hit-stop (scaledDt 0)
112
+ // still pauses. Not while held: the press that resumed from the menu
113
+ // this tick (Ui runs first) would open it again.
114
+ if (playing && !g.held && (inp.pressed("Escape") || inp.pressed("KeyP") || gp.pressed("start"))) g.paused = true;
115
+ const dt = time.scaledDt;
116
+ if (dt === 0) return;
117
+ const body = entity(world, "hero-body");
118
+ const reach = entity(world, "hero-reach");
119
+ const cc = must(world.get(hero, og.CharacterController));
120
+ const me = must(world.get(hero, Hero));
121
+ const p = must(world.get(hero, og.Transform)).position;
122
+
123
+ // The controls, relative to the view: forward is where the camera looks.
124
+ let ix = inp.axis("KeyA", "KeyD") + inp.axis("ArrowLeft", "ArrowRight");
125
+ let iy = inp.axis("KeyS", "KeyW") + inp.axis("ArrowDown", "ArrowUp");
126
+ let analog = false;
127
+ const sx = gp.axis("leftX");
128
+ const sy = -gp.axis("leftY");
129
+ if (Math.hypot(sx, sy) > 0.2) {
130
+ ix += sx;
131
+ iy += sy;
132
+ analog = true;
133
+ }
134
+ if (g.stickX !== 0 || g.stickY !== 0) {
135
+ ix += g.stickX;
136
+ iy -= g.stickY;
137
+ analog = true;
138
+ }
139
+ const mag = Math.min(1, Math.hypot(ix, iy));
140
+ const run = analog ? mag > 0.85 : inp.down("ShiftLeft") || inp.down("ShiftRight");
141
+ const fwd = { x: -Math.sin(g.yaw), z: -Math.cos(g.yaw) };
142
+ const right = { x: Math.cos(g.yaw), z: -Math.sin(g.yaw) };
143
+ let dx = 0;
144
+ let dz = 0;
145
+ if (mag > 0.01) {
146
+ const n = Math.hypot(ix, iy);
147
+ dx = (right.x * ix + fwd.x * iy) / n;
148
+ dz = (right.z * ix + fwd.z * iy) / n;
149
+ }
150
+
151
+ const feet = p.y - FEET;
152
+ g.wading = feet < WATER_Y - 0.08 && Math.hypot(p.x - LAKE.x, p.z - LAKE.z) < 18;
153
+ const kindleHeld = playing && (inp.down("KeyF") || gp.down("x") || g.padKindle);
154
+ g.kindling = kindleHeld && g.near >= 0;
155
+
156
+ let speed = 0;
157
+ let face: number | null = null;
158
+ if (g.knock > 0) {
159
+ const k = g.knock / 0.35;
160
+ speed = 7 * k;
161
+ dx = g.knockX;
162
+ dz = g.knockZ;
163
+ g.knock = Math.max(0, g.knock - dt);
164
+ } else if (g.kindling) {
165
+ // Walk up to the plinth, then stand and reach.
166
+ const b = BEACON_AT[g.near]!;
167
+ const vx = b.x - p.x;
168
+ const vz = b.z - p.z;
169
+ const d = Math.hypot(vx, vz);
170
+ face = Math.atan2(vx, vz);
171
+ if (d > STAND) {
172
+ dx = vx / d;
173
+ dz = vz / d;
174
+ speed = t.walkSpeed;
175
+ }
176
+ } else if (playing && mag > 0.01) {
177
+ speed = (run ? t.runSpeed : t.walkSpeed) * (analog && !run ? mag / 0.85 : 1);
178
+ face = Math.atan2(dx, dz);
179
+ }
180
+ if (g.wading) speed *= t.wade;
181
+ const jump = playing && !g.kindling && g.knock === 0 && (inp.down("Space") || gp.down("a") || g.padJump);
182
+ must(world.set(hero, og.CharacterController, { move: { x: dx * speed, y: 0, z: dz * speed }, jump, jumpSpeed: t.jumpSpeed }));
183
+ g.padJump = false;
184
+ if (g.invulnerable > 0) g.invulnerable = Math.max(0, g.invulnerable - dt);
185
+ const post = must(world.resource(og.PostEffects));
186
+ if (post.flash > 0) post.flash = Math.max(0, post.flash - dt * 2.5);
187
+
188
+ // The body turns towards where it goes (not while thrown back).
189
+ if (face !== null) g.facing = turn(g.facing, face, 11 * dt);
190
+ must(world.set(body, og.Transform, { quaternion: og.transform.fromAxisAngle(UP, g.facing) }));
191
+
192
+ // The graph: speed across the ground, and whether it is in the air.
193
+ const v = cc.velocity;
194
+ const hs = Math.hypot(v.x, v.z);
195
+ g.speed = hs;
196
+ const airTime = cc.grounded ? 0 : me.airTime + dt;
197
+ must(world.set(body, og.AnimationGraph, { p0: cc.grounded || airTime < 0.12 ? hs : 0, p1: airTime > 0.12 ? 1 : 0 }));
198
+
199
+ // The reach: the right hand into the near bowl while kindling.
200
+ const ik = must(world.get(reach, og.IkChain));
201
+ let weight = ik.weight;
202
+ const want = g.kindling ? 1 : 0;
203
+ weight += Math.max(-dt * 5, Math.min(dt * 5, want - weight));
204
+ if (g.near >= 0) {
205
+ const b = BEACON_AT[g.near]!;
206
+ const bowl = world.find(`beacon-${g.near}`);
207
+ const top = bowl === null ? p.y : must(world.get(bowl, Beacon)).top;
208
+ const vx = p.x - b.x;
209
+ const vz = p.z - b.z;
210
+ const d = Math.max(0.01, Math.hypot(vx, vz));
211
+ // IkChain's "world" space is the frame the body's Transform is in: the hero's (unrotated) capsule.
212
+ must(world.set(reach, og.IkChain, { weight, target: { x: b.x + (vx / d) * 0.5 - p.x, y: top + 0.05 - p.y, z: b.z + (vz / d) * 0.5 - p.z } }));
213
+ } else if (weight !== ik.weight) must(world.set(reach, og.IkChain, { weight }));
214
+
215
+ // Steps, splashes, jumps and landings.
216
+ let steps = me.steps;
217
+ let stepClock = me.stepClock;
218
+ if (cc.grounded && hs > 0.5) {
219
+ stepClock += hs * dt;
220
+ const stride = hs > 4 ? 1.6 : 1.15;
221
+ if (stepClock >= stride) {
222
+ stepClock -= stride;
223
+ steps += 1;
224
+ if (g.wading) splash(world, g, p);
225
+ else g.sfx |= Sound.Step;
226
+ }
227
+ }
228
+ if (!cc.grounded && me.airTime === 0 && v.y > 1) {
229
+ g.jumps += 1;
230
+ g.sfx |= Sound.Jump;
231
+ }
232
+ if (cc.grounded && me.airTime > 0.3) {
233
+ if (feet < WATER_Y) splash(world, g, p);
234
+ else g.sfx |= Sound.Step;
235
+ }
236
+ g.grounded = cc.grounded;
237
+ must(world.set(hero, Hero, { airTime, steps, stepClock }));
238
+ },
239
+ );
240
+
241
+ /** How far the camera's eye stays above the ground and the water. */
242
+ const CLEARANCE = 0.6;
243
+ /** The view's pitch, down from level. */
244
+ const PITCH = 0.36;
245
+
246
+ /**
247
+ * The camera: behind the title an orbit of the vale; in a run it follows
248
+ * the hero, turned by Q and E (the right stick) and swung slowly behind it
249
+ * as it walks, kept above the ground and the lake.
250
+ */
251
+ export const View = defineSystem("View", { reads: [og.Input, og.Gamepad, og.Transform, Tuning], writes: [Game, og.Camera], after: ["Hero"] }, (_q, time, world) => {
252
+ const g = must(world.resource(Game));
253
+ const cam = must(world.resource(og.Camera));
254
+ const t = lazyTuning(world);
255
+ const inp = must(world.resource(og.Input));
256
+ const gp = must(world.resource(og.Gamepad));
257
+ const dt = time.dt;
258
+ let eye: Vector3;
259
+ let focus: Vector3;
260
+ if (g.phase === Phase.Title) {
261
+ const a = time.elapsed * 0.035 + 0.6;
262
+ eye = { x: LAKE.x + Math.sin(a) * 42, y: 17, z: LAKE.z + Math.cos(a) * 42 };
263
+ focus = { x: LAKE.x, y: 3, z: LAKE.z };
264
+ } else {
265
+ const p = must(world.get(g.hero as unknown as Entity, og.Transform)).position;
266
+ if (time.scaledDt > 0) {
267
+ let turnBy = inp.axis("KeyQ", "KeyE") * 2.2 + (Math.abs(gp.axis("rightX")) > 0.2 ? gp.axis("rightX") * 2.6 : 0);
268
+ // Swing behind a walking hero (not one walking at the camera), unless a finger holds the view.
269
+ const behind = g.facing + Math.PI;
270
+ if (turnBy === 0 && g.lookId < 0 && g.speed > 0.5 && Math.abs(wrap(behind - g.yaw)) < 2.2) turnBy = (wrap(behind - g.yaw) * Math.min(1, g.speed / 6)) * 0.9;
271
+ // A finger dragging across the look zone (Ui's touch turn): dragging right turns the view right.
272
+ g.yaw = wrap(g.yaw + turnBy * dt - g.lookTurn);
273
+ if (g.lookTurn !== 0) g.lookTurn = 0;
274
+ }
275
+ const d = t.cameraDistance;
276
+ focus = { x: p.x, y: p.y + 0.9, z: p.z };
277
+ eye = {
278
+ x: focus.x + Math.sin(g.yaw) * d * Math.cos(PITCH),
279
+ y: focus.y + d * Math.sin(PITCH),
280
+ z: focus.z + Math.cos(g.yaw) * d * Math.cos(PITCH),
281
+ };
282
+ const floor = Math.max(ground(world, g, eye.x, eye.z).height, WATER_Y) + CLEARANCE;
283
+ if (eye.y < floor) eye.y = floor;
284
+ }
285
+ const at = cam.position;
286
+ const far = Math.hypot(eye.x - at.x, eye.y - at.y, eye.z - at.z) > 12 || g.start;
287
+ const k = far ? 1 : 1 - Math.exp(-dt * 9);
288
+ const pos = { x: at.x + (eye.x - at.x) * k, y: at.y + (eye.y - at.y) * k, z: at.z + (eye.z - at.z) * k };
289
+ cam.position = pos;
290
+ cam.quaternion = og.transform.lookAt(pos, focus);
291
+ cam.fov = 55;
292
+ cam.far = 400;
293
+ });