@origingame/create-game 0.0.0-stage → 0.1.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 (581) 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/host.d.ts +46 -0
  56. package/archetypes/idle/package.json +18 -0
  57. package/archetypes/idle/project.ogt +14 -0
  58. package/archetypes/idle/references/metal/card.png +0 -0
  59. package/archetypes/idle/references/metal/orbit.png +0 -0
  60. package/archetypes/idle/references/metal/title.png +0 -0
  61. package/archetypes/idle/src/components.ts +139 -0
  62. package/archetypes/idle/src/main.ts +247 -0
  63. package/archetypes/idle/src/numbers.ts +126 -0
  64. package/archetypes/idle/src/systems.ts +340 -0
  65. package/archetypes/idle/src/ui.ts +412 -0
  66. package/archetypes/idle/tests/away.og.ts +42 -0
  67. package/archetypes/idle/tests/flow.og.ts +57 -0
  68. package/archetypes/idle/tests/frames.og.ts +33 -0
  69. package/archetypes/idle/tests/frames.ts +134 -0
  70. package/archetypes/idle/tests/lib.ts +74 -0
  71. package/archetypes/idle/tests/milestone.og.ts +32 -0
  72. package/archetypes/idle/tests/nova.og.ts +52 -0
  73. package/archetypes/idle/tests/offline.og.ts +60 -0
  74. package/archetypes/idle/tests/pad.og.ts +55 -0
  75. package/archetypes/idle/tests/pause.og.ts +68 -0
  76. package/archetypes/idle/tests/phone.og.ts +50 -0
  77. package/archetypes/idle/tests/replay.og.ts +28 -0
  78. package/archetypes/idle/tools/make-assets.py +448 -0
  79. package/archetypes/idle/tools/oglite.py +285 -0
  80. package/archetypes/idle/tsconfig.json +17 -0
  81. package/archetypes/match3/.origin/setup +14 -0
  82. package/archetypes/match3/LICENSES.md +7 -0
  83. package/archetypes/match3/README.md +205 -0
  84. package/archetypes/match3/assets/audio/cascade.wav +0 -0
  85. package/archetypes/match3/assets/audio/clear.wav +0 -0
  86. package/archetypes/match3/assets/audio/lost.wav +0 -0
  87. package/archetypes/match3/assets/audio/refused.wav +0 -0
  88. package/archetypes/match3/assets/audio/select.wav +0 -0
  89. package/archetypes/match3/assets/audio/swap.wav +0 -0
  90. package/archetypes/match3/assets/audio/won.wav +0 -0
  91. package/archetypes/match3/assets/fonts/OFL.txt +93 -0
  92. package/archetypes/match3/assets/fonts/ui.ttf +0 -0
  93. package/archetypes/match3/assets/fonts/ui.ttf.ogt +11 -0
  94. package/archetypes/match3/assets/scenes/main.ogt +24 -0
  95. package/archetypes/match3/assets/sprites/backdrop.png +0 -0
  96. package/archetypes/match3/assets/sprites/board.ogt +21 -0
  97. package/archetypes/match3/assets/sprites/cells.ogt +7 -0
  98. package/archetypes/match3/assets/sprites/cells.png +0 -0
  99. package/archetypes/match3/assets/sprites/cells.png.ogt +7 -0
  100. package/archetypes/match3/assets/sprites/gems.ogt +75 -0
  101. package/archetypes/match3/assets/sprites/gems.png +0 -0
  102. package/archetypes/match3/assets/sprites/panel.png +0 -0
  103. package/archetypes/match3/assets/sprites/ring.png +0 -0
  104. package/archetypes/match3/gitattributes +22 -0
  105. package/archetypes/match3/host.d.ts +46 -0
  106. package/archetypes/match3/package.json +18 -0
  107. package/archetypes/match3/project.ogt +18 -0
  108. package/archetypes/match3/references/metal/cascade.png +0 -0
  109. package/archetypes/match3/references/metal/clearing.png +0 -0
  110. package/archetypes/match3/references/metal/dealt.png +0 -0
  111. package/archetypes/match3/src/art.ts +54 -0
  112. package/archetypes/match3/src/board.ts +112 -0
  113. package/archetypes/match3/src/components.ts +95 -0
  114. package/archetypes/match3/src/layout.ts +25 -0
  115. package/archetypes/match3/src/main.ts +199 -0
  116. package/archetypes/match3/src/systems.ts +631 -0
  117. package/archetypes/match3/tests/drag.og.ts +31 -0
  118. package/archetypes/match3/tests/endings.og.ts +40 -0
  119. package/archetypes/match3/tests/frames.og.ts +49 -0
  120. package/archetypes/match3/tests/frames.ts +134 -0
  121. package/archetypes/match3/tests/hidpi.og.ts +41 -0
  122. package/archetypes/match3/tests/lib.ts +64 -0
  123. package/archetypes/match3/tests/replay.og.ts +28 -0
  124. package/archetypes/match3/tests/sounds.og.ts +50 -0
  125. package/archetypes/match3/tests/swap.og.ts +67 -0
  126. package/archetypes/match3/tools/make-assets.py +404 -0
  127. package/archetypes/match3/tools/oglite.py +285 -0
  128. package/archetypes/match3/tsconfig.json +17 -0
  129. package/archetypes/rhythm/.origin/setup +14 -0
  130. package/archetypes/rhythm/LICENSES.md +13 -0
  131. package/archetypes/rhythm/README.md +235 -0
  132. package/archetypes/rhythm/assets/audio/bass0.wav +0 -0
  133. package/archetypes/rhythm/assets/audio/bass1.wav +0 -0
  134. package/archetypes/rhythm/assets/audio/bass2.wav +0 -0
  135. package/archetypes/rhythm/assets/audio/bass3.wav +0 -0
  136. package/archetypes/rhythm/assets/audio/chords0.wav +0 -0
  137. package/archetypes/rhythm/assets/audio/chords1.wav +0 -0
  138. package/archetypes/rhythm/assets/audio/chords2.wav +0 -0
  139. package/archetypes/rhythm/assets/audio/chords3.wav +0 -0
  140. package/archetypes/rhythm/assets/audio/clear.wav +0 -0
  141. package/archetypes/rhythm/assets/audio/click.wav +0 -0
  142. package/archetypes/rhythm/assets/audio/drums.wav +0 -0
  143. package/archetypes/rhythm/assets/audio/fail.wav +0 -0
  144. package/archetypes/rhythm/assets/audio/leadA.wav +0 -0
  145. package/archetypes/rhythm/assets/audio/leadB.wav +0 -0
  146. package/archetypes/rhythm/assets/audio/miss.wav +0 -0
  147. package/archetypes/rhythm/assets/audio/tap0.wav +0 -0
  148. package/archetypes/rhythm/assets/audio/tap1.wav +0 -0
  149. package/archetypes/rhythm/assets/audio/tap2.wav +0 -0
  150. package/archetypes/rhythm/assets/audio/ui-close.ogt +9 -0
  151. package/archetypes/rhythm/assets/audio/ui-error.ogt +9 -0
  152. package/archetypes/rhythm/assets/audio/ui-open.ogt +9 -0
  153. package/archetypes/rhythm/assets/audio/ui-press.ogt +9 -0
  154. package/archetypes/rhythm/assets/audio/ui-success.ogt +9 -0
  155. package/archetypes/rhythm/assets/charts/neon-pulse.ogt +919 -0
  156. package/archetypes/rhythm/assets/fonts/NotoSansSC-subset.ttf +0 -0
  157. package/archetypes/rhythm/assets/fonts/NotoSansSC-subset.ttf.ogt +10 -0
  158. package/archetypes/rhythm/assets/fonts/OFL.txt +93 -0
  159. package/archetypes/rhythm/assets/fx/hit.ogt +22 -0
  160. package/archetypes/rhythm/assets/skins/lanebeat/ChakraPetch-Medium.ttf +0 -0
  161. package/archetypes/rhythm/assets/skins/lanebeat/ChakraPetch-Medium.ttf.ogt +9 -0
  162. package/archetypes/rhythm/assets/skins/lanebeat/JetBrainsMono-Regular.ttf +0 -0
  163. package/archetypes/rhythm/assets/skins/lanebeat/JetBrainsMono-Regular.ttf.ogt +9 -0
  164. package/archetypes/rhythm/assets/skins/lanebeat/LICENSES.md +17 -0
  165. package/archetypes/rhythm/assets/skins/lanebeat/OFL.txt +95 -0
  166. package/archetypes/rhythm/assets/skins/lanebeat/Orbitron-Bold.ttf +0 -0
  167. package/archetypes/rhythm/assets/skins/lanebeat/Orbitron-Bold.ttf.ogt +9 -0
  168. package/archetypes/rhythm/assets/skins/lanebeat/atlas.png +0 -0
  169. package/archetypes/rhythm/assets/skins/lanebeat/atlas.png.ogt +7 -0
  170. package/archetypes/rhythm/assets/skins/lanebeat/icons.png +0 -0
  171. package/archetypes/rhythm/assets/skins/lanebeat/icons.png.ogt +7 -0
  172. package/archetypes/rhythm/assets/skins/lanebeat/skin.ogt +110 -0
  173. package/archetypes/rhythm/assets/sprites/lane.png +0 -0
  174. package/archetypes/rhythm/assets/sprites/lane.png.ogt +7 -0
  175. package/archetypes/rhythm/assets/sprites/note.png +0 -0
  176. package/archetypes/rhythm/assets/sprites/note.png.ogt +7 -0
  177. package/archetypes/rhythm/assets/sprites/ring.png +0 -0
  178. package/archetypes/rhythm/assets/sprites/ring.png.ogt +7 -0
  179. package/archetypes/rhythm/assets/sprites/spark.png +0 -0
  180. package/archetypes/rhythm/assets/sprites/spark.png.ogt +7 -0
  181. package/archetypes/rhythm/assets/strings/en.ogt +61 -0
  182. package/archetypes/rhythm/assets/strings/zh-CN.ogt +61 -0
  183. package/archetypes/rhythm/gitattributes +22 -0
  184. package/archetypes/rhythm/host.d.ts +46 -0
  185. package/archetypes/rhythm/package.json +18 -0
  186. package/archetypes/rhythm/project.ogt +14 -0
  187. package/archetypes/rhythm/references/metal/highway.png +0 -0
  188. package/archetypes/rhythm/references/metal/results.png +0 -0
  189. package/archetypes/rhythm/references/metal/title.png +0 -0
  190. package/archetypes/rhythm/src/components.ts +147 -0
  191. package/archetypes/rhythm/src/main.ts +202 -0
  192. package/archetypes/rhythm/src/systems.ts +490 -0
  193. package/archetypes/rhythm/src/ui.ts +421 -0
  194. package/archetypes/rhythm/tests/band.og.ts +45 -0
  195. package/archetypes/rhythm/tests/calibrate.og.ts +49 -0
  196. package/archetypes/rhythm/tests/clear.og.ts +51 -0
  197. package/archetypes/rhythm/tests/fail.og.ts +40 -0
  198. package/archetypes/rhythm/tests/flow.og.ts +54 -0
  199. package/archetypes/rhythm/tests/frames.og.ts +35 -0
  200. package/archetypes/rhythm/tests/frames.ts +134 -0
  201. package/archetypes/rhythm/tests/lib.ts +123 -0
  202. package/archetypes/rhythm/tests/pad.og.ts +41 -0
  203. package/archetypes/rhythm/tests/pause.og.ts +77 -0
  204. package/archetypes/rhythm/tests/phone.og.ts +44 -0
  205. package/archetypes/rhythm/tests/replay.og.ts +27 -0
  206. package/archetypes/rhythm/tests/timing.og.ts +37 -0
  207. package/archetypes/rhythm/tools/make-assets.py +383 -0
  208. package/archetypes/rhythm/tools/oglite.py +285 -0
  209. package/archetypes/rhythm/tsconfig.json +17 -0
  210. package/archetypes/roguelike/.origin/setup +14 -0
  211. package/archetypes/roguelike/LICENSES.md +14 -0
  212. package/archetypes/roguelike/README.md +256 -0
  213. package/archetypes/roguelike/assets/ai/foe.ogt +28 -0
  214. package/archetypes/roguelike/assets/ai/guardian.ogt +26 -0
  215. package/archetypes/roguelike/assets/audio/bump.wav +0 -0
  216. package/archetypes/roguelike/assets/audio/drink.wav +0 -0
  217. package/archetypes/roguelike/assets/audio/fall.wav +0 -0
  218. package/archetypes/roguelike/assets/audio/hit.wav +0 -0
  219. package/archetypes/roguelike/assets/audio/hurt.wav +0 -0
  220. package/archetypes/roguelike/assets/audio/item.wav +0 -0
  221. package/archetypes/roguelike/assets/audio/kill.wav +0 -0
  222. package/archetypes/roguelike/assets/audio/pickup.wav +0 -0
  223. package/archetypes/roguelike/assets/audio/roar.wav +0 -0
  224. package/archetypes/roguelike/assets/audio/stairs.wav +0 -0
  225. package/archetypes/roguelike/assets/audio/step.ogt +9 -0
  226. package/archetypes/roguelike/assets/audio/telegraph.wav +0 -0
  227. package/archetypes/roguelike/assets/audio/tomb.wav +0 -0
  228. package/archetypes/roguelike/assets/audio/ui-close.ogt +9 -0
  229. package/archetypes/roguelike/assets/audio/ui-error.ogt +9 -0
  230. package/archetypes/roguelike/assets/audio/ui-open.ogt +9 -0
  231. package/archetypes/roguelike/assets/audio/ui-press.ogt +9 -0
  232. package/archetypes/roguelike/assets/audio/ui-success.ogt +9 -0
  233. package/archetypes/roguelike/assets/audio/won.wav +0 -0
  234. package/archetypes/roguelike/assets/fonts/NotoSansSC-subset.ttf +0 -0
  235. package/archetypes/roguelike/assets/fonts/NotoSansSC-subset.ttf.ogt +10 -0
  236. package/archetypes/roguelike/assets/fonts/OFL.txt +93 -0
  237. package/archetypes/roguelike/assets/maps/dive.ogt +106 -0
  238. package/archetypes/roguelike/assets/skins/lampdive/DotGothic16-Regular.ttf +0 -0
  239. package/archetypes/roguelike/assets/skins/lampdive/DotGothic16-Regular.ttf.ogt +9 -0
  240. package/archetypes/roguelike/assets/skins/lampdive/LICENSES.md +16 -0
  241. package/archetypes/roguelike/assets/skins/lampdive/OFL.txt +93 -0
  242. package/archetypes/roguelike/assets/skins/lampdive/atlas.png +0 -0
  243. package/archetypes/roguelike/assets/skins/lampdive/atlas.png.ogt +7 -0
  244. package/archetypes/roguelike/assets/skins/lampdive/icons.png +0 -0
  245. package/archetypes/roguelike/assets/skins/lampdive/icons.png.ogt +7 -0
  246. package/archetypes/roguelike/assets/skins/lampdive/skin.ogt +115 -0
  247. package/archetypes/roguelike/assets/sprites/dive.ogt +50 -0
  248. package/archetypes/roguelike/assets/sprites/dive.png +0 -0
  249. package/archetypes/roguelike/assets/sprites/dive.png.ogt +7 -0
  250. package/archetypes/roguelike/assets/sprites/glow.png +0 -0
  251. package/archetypes/roguelike/assets/sprites/glow.png.ogt +7 -0
  252. package/archetypes/roguelike/assets/strings/en.ogt +57 -0
  253. package/archetypes/roguelike/assets/strings/zh-CN.ogt +57 -0
  254. package/archetypes/roguelike/assets/tiles/tomb.ogt +31 -0
  255. package/archetypes/roguelike/assets/tiles/tomb.png +0 -0
  256. package/archetypes/roguelike/assets/tiles/tomb.png.ogt +7 -0
  257. package/archetypes/roguelike/gitattributes +22 -0
  258. package/archetypes/roguelike/host.d.ts +46 -0
  259. package/archetypes/roguelike/package.json +18 -0
  260. package/archetypes/roguelike/project.ogt +14 -0
  261. package/archetypes/roguelike/references/metal/dive.png +0 -0
  262. package/archetypes/roguelike/references/metal/over.png +0 -0
  263. package/archetypes/roguelike/references/metal/title.png +0 -0
  264. package/archetypes/roguelike/src/art.ts +23 -0
  265. package/archetypes/roguelike/src/components.ts +169 -0
  266. package/archetypes/roguelike/src/dive.ts +144 -0
  267. package/archetypes/roguelike/src/foes.ts +174 -0
  268. package/archetypes/roguelike/src/main.ts +258 -0
  269. package/archetypes/roguelike/src/systems.ts +620 -0
  270. package/archetypes/roguelike/src/ui.ts +350 -0
  271. package/archetypes/roguelike/tests/flow.og.ts +54 -0
  272. package/archetypes/roguelike/tests/foes.og.ts +45 -0
  273. package/archetypes/roguelike/tests/frames.og.ts +32 -0
  274. package/archetypes/roguelike/tests/frames.ts +134 -0
  275. package/archetypes/roguelike/tests/lib.ts +83 -0
  276. package/archetypes/roguelike/tests/lose.og.ts +44 -0
  277. package/archetypes/roguelike/tests/pad.og.ts +54 -0
  278. package/archetypes/roguelike/tests/pause.og.ts +59 -0
  279. package/archetypes/roguelike/tests/phone.og.ts +54 -0
  280. package/archetypes/roguelike/tests/replay.og.ts +24 -0
  281. package/archetypes/roguelike/tests/win.og.ts +60 -0
  282. package/archetypes/roguelike/tools/make-assets.py +518 -0
  283. package/archetypes/roguelike/tools/oglite.py +285 -0
  284. package/archetypes/roguelike/tsconfig.json +17 -0
  285. package/archetypes/runner/.origin/setup +14 -0
  286. package/archetypes/runner/LICENSES.md +10 -0
  287. package/archetypes/runner/README.md +225 -0
  288. package/archetypes/runner/assets/audio/coin.wav +0 -0
  289. package/archetypes/runner/assets/audio/crash.wav +0 -0
  290. package/archetypes/runner/assets/audio/hold.ogt +9 -0
  291. package/archetypes/runner/assets/audio/jump.wav +0 -0
  292. package/archetypes/runner/assets/audio/start.wav +0 -0
  293. package/archetypes/runner/assets/fonts/OFL.txt +93 -0
  294. package/archetypes/runner/assets/fonts/ui.ttf +0 -0
  295. package/archetypes/runner/assets/fonts/ui.ttf.ogt +11 -0
  296. package/archetypes/runner/assets/maps/far.ogt +25 -0
  297. package/archetypes/runner/assets/maps/near.ogt +25 -0
  298. package/archetypes/runner/assets/maps/roofs.ogt +19 -0
  299. package/archetypes/runner/assets/scenes/main.ogt +35 -0
  300. package/archetypes/runner/assets/sprites/coin.ogt +11 -0
  301. package/archetypes/runner/assets/sprites/coin.png +0 -0
  302. package/archetypes/runner/assets/sprites/coin.png.ogt +7 -0
  303. package/archetypes/runner/assets/sprites/courier.ogt +32 -0
  304. package/archetypes/runner/assets/sprites/courier.png +0 -0
  305. package/archetypes/runner/assets/sprites/courier.png.ogt +7 -0
  306. package/archetypes/runner/assets/sprites/props.ogt +21 -0
  307. package/archetypes/runner/assets/sprites/props.png +0 -0
  308. package/archetypes/runner/assets/sprites/props.png.ogt +7 -0
  309. package/archetypes/runner/assets/sprites/sky.png +0 -0
  310. package/archetypes/runner/assets/sprites/sky.png.ogt +7 -0
  311. package/archetypes/runner/assets/tiles/city.ogt +18 -0
  312. package/archetypes/runner/assets/tiles/city.png +0 -0
  313. package/archetypes/runner/assets/tiles/city.png.ogt +7 -0
  314. package/archetypes/runner/gitattributes +22 -0
  315. package/archetypes/runner/host.d.ts +46 -0
  316. package/archetypes/runner/package.json +18 -0
  317. package/archetypes/runner/project.ogt +18 -0
  318. package/archetypes/runner/references/metal/frames-coin.png +0 -0
  319. package/archetypes/runner/references/metal/frames-crash.png +0 -0
  320. package/archetypes/runner/references/metal/frames-title.png +0 -0
  321. package/archetypes/runner/src/art.ts +57 -0
  322. package/archetypes/runner/src/components.ts +94 -0
  323. package/archetypes/runner/src/main.ts +241 -0
  324. package/archetypes/runner/src/systems.ts +553 -0
  325. package/archetypes/runner/tests/coins.og.ts +30 -0
  326. package/archetypes/runner/tests/flow.og.ts +72 -0
  327. package/archetypes/runner/tests/frames.og.ts +37 -0
  328. package/archetypes/runner/tests/frames.ts +134 -0
  329. package/archetypes/runner/tests/jump.og.ts +44 -0
  330. package/archetypes/runner/tests/lib.ts +42 -0
  331. package/archetypes/runner/tests/records.og.ts +43 -0
  332. package/archetypes/runner/tests/replay.og.ts +36 -0
  333. package/archetypes/runner/tests/sounds.og.ts +43 -0
  334. package/archetypes/runner/tests/tune.og.ts +36 -0
  335. package/archetypes/runner/tools/make-assets.py +603 -0
  336. package/archetypes/runner/tools/oglite.py +285 -0
  337. package/archetypes/runner/tsconfig.json +17 -0
  338. package/archetypes/third-person/.origin/setup +14 -0
  339. package/archetypes/third-person/LICENSES.md +23 -0
  340. package/archetypes/third-person/README.md +249 -0
  341. package/archetypes/third-person/assets/ai/sentinel-east.ogt +69 -0
  342. package/archetypes/third-person/assets/ai/sentinel-north.ogt +69 -0
  343. package/archetypes/third-person/assets/ai/sentinel-west.ogt +69 -0
  344. package/archetypes/third-person/assets/anims/humanoid-clips.glb +0 -0
  345. package/archetypes/third-person/assets/audio/alarm.wav +0 -0
  346. package/archetypes/third-person/assets/audio/dawn.wav +0 -0
  347. package/archetypes/third-person/assets/audio/grab.wav +0 -0
  348. package/archetypes/third-person/assets/audio/ignite.wav +0 -0
  349. package/archetypes/third-person/assets/audio/jump.wav +0 -0
  350. package/archetypes/third-person/assets/audio/night.wav +0 -0
  351. package/archetypes/third-person/assets/audio/splash.wav +0 -0
  352. package/archetypes/third-person/assets/audio/step.ogt +9 -0
  353. package/archetypes/third-person/assets/audio/ui-close.ogt +9 -0
  354. package/archetypes/third-person/assets/audio/ui-error.ogt +9 -0
  355. package/archetypes/third-person/assets/audio/ui-open.ogt +9 -0
  356. package/archetypes/third-person/assets/audio/ui-press.ogt +9 -0
  357. package/archetypes/third-person/assets/audio/ui-success.ogt +9 -0
  358. package/archetypes/third-person/assets/audio/vale.wav +0 -0
  359. package/archetypes/third-person/assets/fonts/NotoSansSC-subset.ttf +0 -0
  360. package/archetypes/third-person/assets/fonts/NotoSansSC-subset.ttf.ogt +10 -0
  361. package/archetypes/third-person/assets/fonts/OFL.txt +93 -0
  362. package/archetypes/third-person/assets/fx/burst.ogt +25 -0
  363. package/archetypes/third-person/assets/fx/fire.ogt +24 -0
  364. package/archetypes/third-person/assets/fx/smoke.ogt +24 -0
  365. package/archetypes/third-person/assets/fx/splash.ogt +24 -0
  366. package/archetypes/third-person/assets/luts/teal-orange.cube +4917 -0
  367. package/archetypes/third-person/assets/models/stout.glb +0 -0
  368. package/archetypes/third-person/assets/models/strider.glb +0 -0
  369. package/archetypes/third-person/assets/models/strider.glb.ogt +9 -0
  370. package/archetypes/third-person/assets/nav/vale.ogt +43 -0
  371. package/archetypes/third-person/assets/rigs/strider.ogt +6 -0
  372. package/archetypes/third-person/assets/skins/ember/Cinzel-Bold.ttf +0 -0
  373. package/archetypes/third-person/assets/skins/ember/Cinzel-Bold.ttf.ogt +9 -0
  374. package/archetypes/third-person/assets/skins/ember/LICENSES.md +13 -0
  375. package/archetypes/third-person/assets/skins/ember/Newsreader-Medium.ttf +0 -0
  376. package/archetypes/third-person/assets/skins/ember/Newsreader-Medium.ttf.ogt +9 -0
  377. package/archetypes/third-person/assets/skins/ember/OFL.txt +94 -0
  378. package/archetypes/third-person/assets/skins/ember/atlas.png +0 -0
  379. package/archetypes/third-person/assets/skins/ember/atlas.png.ogt +7 -0
  380. package/archetypes/third-person/assets/skins/ember/icons.png +0 -0
  381. package/archetypes/third-person/assets/skins/ember/icons.png.ogt +7 -0
  382. package/archetypes/third-person/assets/skins/ember/skin.ogt +109 -0
  383. package/archetypes/third-person/assets/strings/en.ogt +47 -0
  384. package/archetypes/third-person/assets/strings/zh-CN.ogt +47 -0
  385. package/archetypes/third-person/assets/terrain/vale-control.png +0 -0
  386. package/archetypes/third-person/assets/terrain/vale.ogt +42 -0
  387. package/archetypes/third-person/assets/terrain/vale.png +0 -0
  388. package/archetypes/third-person/assets/textures/decals.png +0 -0
  389. package/archetypes/third-person/assets/textures/flame.png +0 -0
  390. package/archetypes/third-person/assets/textures/smoke.png +0 -0
  391. package/archetypes/third-person/assets/textures/spark.png +0 -0
  392. package/archetypes/third-person/assets/textures/terrain-grass.png +0 -0
  393. package/archetypes/third-person/assets/textures/terrain-path.png +0 -0
  394. package/archetypes/third-person/assets/textures/terrain-rock.png +0 -0
  395. package/archetypes/third-person/assets/textures/terrain-sand.png +0 -0
  396. package/archetypes/third-person/gitattributes +22 -0
  397. package/archetypes/third-person/host.d.ts +46 -0
  398. package/archetypes/third-person/package.json +18 -0
  399. package/archetypes/third-person/project.ogt +14 -0
  400. package/archetypes/third-person/references/metal/beacon.png +0 -0
  401. package/archetypes/third-person/references/metal/dawn.png +0 -0
  402. package/archetypes/third-person/references/metal/title.png +0 -0
  403. package/archetypes/third-person/references/webgl2/beacon.png +0 -0
  404. package/archetypes/third-person/src/anim.ts +87 -0
  405. package/archetypes/third-person/src/beacons.ts +80 -0
  406. package/archetypes/third-person/src/components.ts +148 -0
  407. package/archetypes/third-person/src/hero.ts +293 -0
  408. package/archetypes/third-person/src/main.ts +164 -0
  409. package/archetypes/third-person/src/sentinels.ts +159 -0
  410. package/archetypes/third-person/src/sound.ts +66 -0
  411. package/archetypes/third-person/src/ui.ts +451 -0
  412. package/archetypes/third-person/src/world.ts +212 -0
  413. package/archetypes/third-person/tests/flow.og.ts +70 -0
  414. package/archetypes/third-person/tests/frames.og.ts +35 -0
  415. package/archetypes/third-person/tests/frames.ts +134 -0
  416. package/archetypes/third-person/tests/hitstop.og.ts +26 -0
  417. package/archetypes/third-person/tests/lib.ts +87 -0
  418. package/archetypes/third-person/tests/lose.og.ts +44 -0
  419. package/archetypes/third-person/tests/pause.og.ts +64 -0
  420. package/archetypes/third-person/tests/phone.og.ts +65 -0
  421. package/archetypes/third-person/tests/replay.og.ts +32 -0
  422. package/archetypes/third-person/tests/restart.og.ts +28 -0
  423. package/archetypes/third-person/tests/sentinels.og.ts +48 -0
  424. package/archetypes/third-person/tests/win.og.ts +62 -0
  425. package/archetypes/third-person/tools/make-assets.py +353 -0
  426. package/archetypes/third-person/tools/oglite.py +285 -0
  427. package/archetypes/third-person/tsconfig.json +17 -0
  428. package/archetypes/topdown/.origin/setup +14 -0
  429. package/archetypes/topdown/LICENSES.md +10 -0
  430. package/archetypes/topdown/README.md +235 -0
  431. package/archetypes/topdown/assets/ai/slime.ogt +49 -0
  432. package/archetypes/topdown/assets/audio/coin.wav +0 -0
  433. package/archetypes/topdown/assets/audio/door.wav +0 -0
  434. package/archetypes/topdown/assets/audio/hit.wav +0 -0
  435. package/archetypes/topdown/assets/audio/hurt.wav +0 -0
  436. package/archetypes/topdown/assets/audio/key.wav +0 -0
  437. package/archetypes/topdown/assets/audio/step.ogt +9 -0
  438. package/archetypes/topdown/assets/fonts/OFL.txt +93 -0
  439. package/archetypes/topdown/assets/fonts/ui.ttf +0 -0
  440. package/archetypes/topdown/assets/fonts/ui.ttf.ogt +9 -0
  441. package/archetypes/topdown/assets/maps/tomb.tmj +2183 -0
  442. package/archetypes/topdown/assets/sprites/hero.ogt +58 -0
  443. package/archetypes/topdown/assets/sprites/hero.png +0 -0
  444. package/archetypes/topdown/assets/sprites/hero.png.ogt +7 -0
  445. package/archetypes/topdown/assets/sprites/items.ogt +15 -0
  446. package/archetypes/topdown/assets/sprites/items.png +0 -0
  447. package/archetypes/topdown/assets/sprites/items.png.ogt +7 -0
  448. package/archetypes/topdown/assets/sprites/slime.ogt +17 -0
  449. package/archetypes/topdown/assets/sprites/slime.png +0 -0
  450. package/archetypes/topdown/assets/sprites/slime.png.ogt +7 -0
  451. package/archetypes/topdown/assets/tiles/dungeon.ogt +65 -0
  452. package/archetypes/topdown/assets/tiles/dungeon.png +0 -0
  453. package/archetypes/topdown/assets/tiles/dungeon.png.ogt +7 -0
  454. package/archetypes/topdown/gitattributes +22 -0
  455. package/archetypes/topdown/host.d.ts +46 -0
  456. package/archetypes/topdown/package.json +18 -0
  457. package/archetypes/topdown/project.ogt +14 -0
  458. package/archetypes/topdown/references/metal/crit.png +0 -0
  459. package/archetypes/topdown/references/metal/dash.png +0 -0
  460. package/archetypes/topdown/references/metal/east.png +0 -0
  461. package/archetypes/topdown/references/metal/start.png +0 -0
  462. package/archetypes/topdown/src/art.ts +48 -0
  463. package/archetypes/topdown/src/components.ts +128 -0
  464. package/archetypes/topdown/src/main.ts +275 -0
  465. package/archetypes/topdown/src/systems.ts +503 -0
  466. package/archetypes/topdown/tests/ai.og.ts +64 -0
  467. package/archetypes/topdown/tests/combat.og.ts +72 -0
  468. package/archetypes/topdown/tests/door.og.ts +62 -0
  469. package/archetypes/topdown/tests/flee.og.ts +49 -0
  470. package/archetypes/topdown/tests/frames.og.ts +45 -0
  471. package/archetypes/topdown/tests/frames.ts +134 -0
  472. package/archetypes/topdown/tests/lib.ts +71 -0
  473. package/archetypes/topdown/tests/replay.og.ts +40 -0
  474. package/archetypes/topdown/tests/walk.og.ts +46 -0
  475. package/archetypes/topdown/tools/make-assets.py +623 -0
  476. package/archetypes/topdown/tools/oglite.py +285 -0
  477. package/archetypes/topdown/tsconfig.json +17 -0
  478. package/archetypes/tower-defense/.origin/setup +14 -0
  479. package/archetypes/tower-defense/LICENSES.md +15 -0
  480. package/archetypes/tower-defense/README.md +240 -0
  481. package/archetypes/tower-defense/assets/ai/plant.ogt +16 -0
  482. package/archetypes/tower-defense/assets/audio/clear.wav +0 -0
  483. package/archetypes/tower-defense/assets/audio/frost.wav +0 -0
  484. package/archetypes/tower-defense/assets/audio/leak.wav +0 -0
  485. package/archetypes/tower-defense/assets/audio/lost.wav +0 -0
  486. package/archetypes/tower-defense/assets/audio/night.wav +0 -0
  487. package/archetypes/tower-defense/assets/audio/plant.wav +0 -0
  488. package/archetypes/tower-defense/assets/audio/pop.wav +0 -0
  489. package/archetypes/tower-defense/assets/audio/thorn.wav +0 -0
  490. package/archetypes/tower-defense/assets/audio/ui-close.ogt +9 -0
  491. package/archetypes/tower-defense/assets/audio/ui-error.ogt +9 -0
  492. package/archetypes/tower-defense/assets/audio/ui-open.ogt +9 -0
  493. package/archetypes/tower-defense/assets/audio/ui-press.ogt +9 -0
  494. package/archetypes/tower-defense/assets/audio/ui-success.ogt +9 -0
  495. package/archetypes/tower-defense/assets/audio/wave.wav +0 -0
  496. package/archetypes/tower-defense/assets/audio/won.wav +0 -0
  497. package/archetypes/tower-defense/assets/fonts/NotoSansSC-subset.ttf +0 -0
  498. package/archetypes/tower-defense/assets/fonts/NotoSansSC-subset.ttf.ogt +10 -0
  499. package/archetypes/tower-defense/assets/fonts/OFL.txt +93 -0
  500. package/archetypes/tower-defense/assets/fx/fireflies.ogt +21 -0
  501. package/archetypes/tower-defense/assets/fx/pop.ogt +21 -0
  502. package/archetypes/tower-defense/assets/maps/garden.ogt +91 -0
  503. package/archetypes/tower-defense/assets/skins/pathguard/Fredoka-SemiBold.ttf +0 -0
  504. package/archetypes/tower-defense/assets/skins/pathguard/Fredoka-SemiBold.ttf.ogt +9 -0
  505. package/archetypes/tower-defense/assets/skins/pathguard/LICENSES.md +13 -0
  506. package/archetypes/tower-defense/assets/skins/pathguard/LilitaOne-Regular.ttf +0 -0
  507. package/archetypes/tower-defense/assets/skins/pathguard/LilitaOne-Regular.ttf.ogt +9 -0
  508. package/archetypes/tower-defense/assets/skins/pathguard/OFL.txt +94 -0
  509. package/archetypes/tower-defense/assets/skins/pathguard/atlas.png +0 -0
  510. package/archetypes/tower-defense/assets/skins/pathguard/atlas.png.ogt +7 -0
  511. package/archetypes/tower-defense/assets/skins/pathguard/icons.png +0 -0
  512. package/archetypes/tower-defense/assets/skins/pathguard/icons.png.ogt +7 -0
  513. package/archetypes/tower-defense/assets/skins/pathguard/skin.ogt +111 -0
  514. package/archetypes/tower-defense/assets/sprites/garden.ogt +66 -0
  515. package/archetypes/tower-defense/assets/sprites/garden.png +0 -0
  516. package/archetypes/tower-defense/assets/sprites/garden.png.ogt +7 -0
  517. package/archetypes/tower-defense/assets/sprites/glow.png +0 -0
  518. package/archetypes/tower-defense/assets/sprites/glow.png.ogt +7 -0
  519. package/archetypes/tower-defense/assets/sprites/ring.png +0 -0
  520. package/archetypes/tower-defense/assets/sprites/ring.png.ogt +7 -0
  521. package/archetypes/tower-defense/assets/sprites/spark.png +0 -0
  522. package/archetypes/tower-defense/assets/sprites/spark.png.ogt +7 -0
  523. package/archetypes/tower-defense/assets/strings/en.ogt +47 -0
  524. package/archetypes/tower-defense/assets/strings/zh-CN.ogt +47 -0
  525. package/archetypes/tower-defense/assets/tiles/garden.ogt +10 -0
  526. package/archetypes/tower-defense/assets/tiles/garden.png +0 -0
  527. package/archetypes/tower-defense/assets/tiles/garden.png.ogt +7 -0
  528. package/archetypes/tower-defense/gitattributes +22 -0
  529. package/archetypes/tower-defense/host.d.ts +46 -0
  530. package/archetypes/tower-defense/package.json +18 -0
  531. package/archetypes/tower-defense/project.ogt +14 -0
  532. package/archetypes/tower-defense/references/metal/end.png +0 -0
  533. package/archetypes/tower-defense/references/metal/title.png +0 -0
  534. package/archetypes/tower-defense/references/metal/wave.png +0 -0
  535. package/archetypes/tower-defense/src/art.ts +24 -0
  536. package/archetypes/tower-defense/src/components.ts +156 -0
  537. package/archetypes/tower-defense/src/main.ts +253 -0
  538. package/archetypes/tower-defense/src/systems.ts +658 -0
  539. package/archetypes/tower-defense/src/ui.ts +382 -0
  540. package/archetypes/tower-defense/tests/flow.og.ts +56 -0
  541. package/archetypes/tower-defense/tests/frames.og.ts +31 -0
  542. package/archetypes/tower-defense/tests/frames.ts +134 -0
  543. package/archetypes/tower-defense/tests/lib.ts +81 -0
  544. package/archetypes/tower-defense/tests/lose.og.ts +40 -0
  545. package/archetypes/tower-defense/tests/maze.og.ts +53 -0
  546. package/archetypes/tower-defense/tests/pad.og.ts +41 -0
  547. package/archetypes/tower-defense/tests/pause.og.ts +59 -0
  548. package/archetypes/tower-defense/tests/phone.og.ts +49 -0
  549. package/archetypes/tower-defense/tests/replay.og.ts +28 -0
  550. package/archetypes/tower-defense/tests/win.og.ts +58 -0
  551. package/archetypes/tower-defense/tools/make-assets.py +640 -0
  552. package/archetypes/tower-defense/tools/oglite.py +285 -0
  553. package/archetypes/tower-defense/tsconfig.json +17 -0
  554. package/bin/create.ts +496 -0
  555. package/looks/catalogue.json +1871 -0
  556. package/package.json +31 -4
  557. package/template/.origin/setup +14 -0
  558. package/template/LICENSES.md +23 -0
  559. package/template/README.md +37 -0
  560. package/template/assets/ai/guard.ogt +48 -0
  561. package/template/assets/audio/blip.wav +0 -0
  562. package/template/assets/audio/click.ogt +9 -0
  563. package/template/assets/fonts/NotoSansSC-Regular-subset.ttf +0 -0
  564. package/template/assets/fonts/NotoSansSC-Regular-subset.ttf.ogt +12 -0
  565. package/template/assets/fonts/OFL.txt +93 -0
  566. package/template/assets/looks/origin-game.ogt +12 -0
  567. package/template/assets/models/dropship.glb +0 -0
  568. package/template/assets/models/walker.glb +0 -0
  569. package/template/assets/nav/yard.ogt +27 -0
  570. package/template/assets/scenes/main.ogt +34 -0
  571. package/template/assets/strings/en.ogt +14 -0
  572. package/template/assets/textures/stone.png +0 -0
  573. package/template/assets/textures/stone_n.png +0 -0
  574. package/template/assets/textures/stone_n.png.ogt +7 -0
  575. package/template/gitattributes +22 -0
  576. package/template/package.json +18 -0
  577. package/template/project.ogt +25 -0
  578. package/template/src/main.ts +246 -0
  579. package/template/tests/guard.og.ts +67 -0
  580. package/template/tests/smoke.og.ts +43 -0
  581. package/template/tsconfig.json +17 -0
@@ -0,0 +1,70 @@
1
+ // The core loop through real input: Set out on the title starts a run at
2
+ // the south end of the vale with three hearts, no beacon lit and the clock
3
+ // at half past three; W walks the hero north on the character controller,
4
+ // Shift runs, and on into the lake it wades (slower, splashing). Then by
5
+ // the first beacon the prompt shows, holding F kindles it: the right hand
6
+ // reaches into the bowl (og.IkChain, solved within its tolerance) and after
7
+ // Tuning.kindleSeconds the beacon catches, the HUD counts it and says so.
8
+ import { playtest } from "@origingame/engine/playtest";
9
+ import { game, PLAY, position, tap, texts, TITLE, value } from "./lib.ts";
10
+
11
+ export default playtest({
12
+ name: "Set out, walk, run and wade on the terrain, kindle a beacon with the hand in the bowl",
13
+ commands: [
14
+ "0.3:get:Game:title.json",
15
+ tap("play", 0.5),
16
+ "0.7:get:Game:started.json",
17
+ "0.7:get:hero.og.Transform:start.json",
18
+ "0.8:key_down:KeyW",
19
+ "1.8:get:Game:walk.json",
20
+ "1.9:key_down:ShiftLeft",
21
+ "2.9:get:Game:run.json",
22
+ "7.6:get:Game:wade.json",
23
+ "7.6:get:hero.og.Transform:wade-at.json",
24
+ "7.7:key_up:ShiftLeft",
25
+ "7.7:key_up:KeyW",
26
+ "7.8:warp:0",
27
+ "8.1:get:Game:near.json",
28
+ "8.1:dump_ui:prompt.json",
29
+ "8.2:key_down:KeyF",
30
+ "9.3:get:hero-reach.og.IkChain:reach.json",
31
+ "9.3:get:Game:kindling.json",
32
+ "10.4:key_up:KeyF",
33
+ "10.5:get:Game:lit.json",
34
+ "10.5:get:beacon-0.Beacon:beacon.json",
35
+ "10.5:dump_ui:lit-ui.json",
36
+ "10.6:quit",
37
+ ].join(";"),
38
+ check(run, t) {
39
+ t.equal(run.warnings.join("; "), "", "the script ran without warnings");
40
+ t.ok(run.quit, "the script reached quit");
41
+ t.equal(game(run, "title.json").phase, TITLE, "the game opens on the title");
42
+ const started = game(run, "started.json");
43
+ t.equal(started.phase, PLAY, "Set out started a run");
44
+ t.ok(started.hearts === 3 && started.lit === 0 && started.runs === 1, "with three hearts, no beacon lit, the first run");
45
+ t.ok(started.hour >= 15.5 && started.hour < 15.6, `at half past three (${started.hour.toFixed(2)})`);
46
+ const walk = game(run, "walk.json");
47
+ t.ok(walk.grounded && Math.abs(walk.speed - 3) < 0.1, `W walks on the ground at the walk speed (${walk.speed.toFixed(2)} m/s)`);
48
+ t.ok(Math.abs(game(run, "run.json").speed - 6) < 0.1, "Shift runs at the run speed");
49
+ const wade = game(run, "wade.json");
50
+ t.ok(wade.wading, "on into the lake the hero wades");
51
+ t.ok(Math.abs(wade.speed - 6 * 0.45) < 0.15, `slowed to Tuning.wade of a run (${wade.speed.toFixed(2)} m/s)`);
52
+ t.ok(wade.splashes >= 1, `and splashes (${wade.splashes})`);
53
+ const [, , z0] = position(run, "start.json");
54
+ const [x1, , z1] = position(run, "wade-at.json");
55
+ t.ok(z0 - z1 > 20 && Math.abs(x1) < 0.5, `the hero went north across the terrain (z ${z0.toFixed(1)} → ${z1.toFixed(1)})`);
56
+ t.equal(game(run, "near.json").near, 0, "by the first beacon it is within reach");
57
+ t.ok(texts(run, "prompt.json").includes("Hold F to kindle the beacon"), "and the prompt says how to kindle it");
58
+ const k = game(run, "kindling.json");
59
+ t.ok(k.kindling && k.kindle > 0.5, `holding F kindles it (${k.kindle.toFixed(2)} s)`);
60
+ const reach = value<{ reached: boolean; error: number; weight: number }>(run, "reach.json");
61
+ t.ok(reach.weight > 0.9, `the arm's IK chain is on (weight ${reach.weight.toFixed(2)})`);
62
+ t.ok(reach.reached && reach.error <= 0.02, `the hand reached into the bowl (${(reach.error * 100).toFixed(1)} cm off)`);
63
+ const lit = game(run, "lit.json");
64
+ t.ok(lit.lit === 1 && lit.litMask === 1, "the beacon caught");
65
+ const b = value<{ lit: boolean; litAt: number }>(run, "beacon.json");
66
+ t.ok(b.lit && b.litAt - k.time > 0 && b.litAt < k.time + 1.6, `after Tuning.kindleSeconds of kindling (at ${b.litAt.toFixed(2)} s)`);
67
+ const words = texts(run, "lit-ui.json");
68
+ t.ok(words.includes("1/4") && words.includes("Beacon 1 of 4 burns"), "the HUD counts it and says so");
69
+ },
70
+ });
@@ -0,0 +1,35 @@
1
+ // Three frames at 1280×720 compared with references/<tier>/: the title over
2
+ // the vale at late afternoon, the first beacon just lit with the hero's
3
+ // hand in the bowl (fire, smoke, the ember burst, its light, the toast),
4
+ // and the dawn summary once the other beacons' toasts have gone (the last
5
+ // beacon raises none). `OG_FX_CAPTURE=1 og test .` records this tier's
6
+ // references.
7
+ import { playtest } from "@origingame/engine/playtest";
8
+ import { checkFrames } from "./frames.ts";
9
+
10
+ const FILES = ["title.png", "beacon.png", "dawn.png"];
11
+
12
+ export default playtest({
13
+ name: "the title, a beacon catching and the dawn summary match their reference frames",
14
+ size: "1280x720",
15
+ commands: [
16
+ "0.9:screenshot:title.png",
17
+ "1.0:run",
18
+ "1.1:warp:0",
19
+ "1.3:key_down:KeyF",
20
+ "3.4:screenshot:beacon.png",
21
+ "3.5:key_up:KeyF",
22
+ "3.6:light:1",
23
+ "3.7:light:2",
24
+ "3.8:light:3",
25
+ "7.2:screenshot:dawn.png",
26
+ "7.3:quit",
27
+ ].join(";"),
28
+ check(run, t) {
29
+ t.equal(run.warnings.join("; "), "", "the script ran without warnings");
30
+ t.frame(FILES, () => {
31
+ for (const f of FILES) t.equal(run.png(f).width, 1280, `${f} is 1280 wide`);
32
+ });
33
+ checkFrames(run, t, FILES, "");
34
+ },
35
+ });
@@ -0,0 +1,134 @@
1
+ // The reference-frame check: decode the PNGs a run wrote and compare them
2
+ // with references/<tier>/ under og-tests' tolerance (a channel off by more
3
+ // than 32/255 is an outlier; fail above 0.5 % outliers or an RGB RMSE above
4
+ // 0.01). A tier without its own references compares with `metal` and says
5
+ // so. `OG_FX_CAPTURE=1 og test .` records this tier's references instead.
6
+ // (Copied from templates/fx-showcase/tests/frames.ts so the archetype stands alone.)
7
+
8
+ import { copyFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
9
+ import { dirname, join, resolve } from "node:path";
10
+ import { fileURLToPath } from "node:url";
11
+ import { inflateSync } from "node:zlib";
12
+ import type { PlaytestChecks, PlaytestRun } from "@origingame/engine/playtest";
13
+
14
+ export interface Image {
15
+ width: number;
16
+ height: number;
17
+ /** RGBA8 rows. */
18
+ rgba: Uint8Array;
19
+ }
20
+
21
+ /** An 8-bit RGB or RGBA, non-interlaced PNG, unfiltered. */
22
+ export function decodePng(bytes: Uint8Array): Image {
23
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
24
+ let at = 8;
25
+ let width = 0;
26
+ let height = 0;
27
+ let channels = 4;
28
+ const idat: Uint8Array[] = [];
29
+ while (at < bytes.length) {
30
+ const len = view.getUint32(at);
31
+ const type = String.fromCharCode(...bytes.subarray(at + 4, at + 8));
32
+ const data = bytes.subarray(at + 8, at + 8 + len);
33
+ if (type === "IHDR") {
34
+ width = view.getUint32(at + 8);
35
+ height = view.getUint32(at + 12);
36
+ const depth = data[8]!;
37
+ const color = data[9]!;
38
+ if (depth !== 8 || (color !== 6 && color !== 2) || data[12] !== 0) throw new Error(`unsupported PNG (depth ${depth}, colour ${color}, interlace ${data[12]})`);
39
+ channels = color === 6 ? 4 : 3;
40
+ } else if (type === "IDAT") idat.push(data);
41
+ else if (type === "IEND") break;
42
+ at += 12 + len;
43
+ }
44
+ const raw = inflateSync(Buffer.concat(idat));
45
+ const stride = width * channels;
46
+ const out = new Uint8Array(width * height * 4);
47
+ const prev = new Uint8Array(stride);
48
+ const cur = new Uint8Array(stride);
49
+ for (let y = 0; y < height; y++) {
50
+ const filter = raw[y * (stride + 1)]!;
51
+ const line = raw.subarray(y * (stride + 1) + 1, (y + 1) * (stride + 1));
52
+ for (let i = 0; i < stride; i++) {
53
+ const a = i >= channels ? cur[i - channels]! : 0;
54
+ const b = prev[i]!;
55
+ const c = i >= channels ? prev[i - channels]! : 0;
56
+ let v = line[i]!;
57
+ if (filter === 1) v += a;
58
+ else if (filter === 2) v += b;
59
+ else if (filter === 3) v += (a + b) >> 1;
60
+ else if (filter === 4) {
61
+ const p = a + b - c;
62
+ const pa = Math.abs(p - a), pb = Math.abs(p - b), pc = Math.abs(p - c);
63
+ v += pa <= pb && pa <= pc ? a : pb <= pc ? b : c;
64
+ }
65
+ cur[i] = v & 255;
66
+ }
67
+ for (let x = 0; x < width; x++) {
68
+ for (let k = 0; k < 4; k++) out[(y * width + x) * 4 + k] = k < channels ? cur[x * channels + k]! : 255;
69
+ }
70
+ prev.set(cur);
71
+ }
72
+ return { width, height, rgba: out };
73
+ }
74
+
75
+ /** og-tests' rule: outlier fraction and RGB RMSE between two equal-size frames. */
76
+ export function difference(a: Image, b: Image): { outliers: number; rmse: number } {
77
+ if (a.width !== b.width || a.height !== b.height) return { outliers: 1, rmse: 1 };
78
+ let outliers = 0;
79
+ let sum = 0;
80
+ const n = a.width * a.height;
81
+ for (let p = 0; p < n; p++) {
82
+ let out = false;
83
+ for (let k = 0; k < 3; k++) {
84
+ const d = Math.abs(a.rgba[p * 4 + k]! - b.rgba[p * 4 + k]!);
85
+ if (d > 32) out = true;
86
+ const f = d / 255;
87
+ sum += f * f;
88
+ }
89
+ if (out) outliers++;
90
+ }
91
+ return { outliers: outliers / n, rmse: Math.sqrt(sum / (n * 3)) };
92
+ }
93
+
94
+ /** The playtest's output directory (the bundled playtest runs from it). */
95
+ export function outDir(): string {
96
+ return dirname(fileURLToPath(import.meta.url));
97
+ }
98
+
99
+ /** The project's references/, from its og-out/test/<name>/. */
100
+ export function referencesDir(): string {
101
+ return resolve(outDir(), "..", "..", "..", "references");
102
+ }
103
+
104
+ /** Compares each screenshot with its reference (`<prefix><file>`), or records it. */
105
+ export function checkFrames(run: PlaytestRun, t: PlaytestChecks, files: readonly string[], prefix: string): void {
106
+ // Read only where there is a renderer: without one (tier `none`) each frame is
107
+ // reported "no renderer, frame unchecked" and every other check still runs.
108
+ t.frame(files, () => {
109
+ const refs = referencesDir();
110
+ t.ok(existsSync(refs) || process.env.OG_FX_CAPTURE !== undefined, `the references directory exists at ${refs}`);
111
+ const own = join(refs, run.tier);
112
+ const capture = process.env.OG_FX_CAPTURE !== undefined;
113
+ for (const f of files) {
114
+ const actual = join(outDir(), f);
115
+ // Each file on its own: this tier's reference where it has one, metal's otherwise.
116
+ const mine = join(own, prefix + f);
117
+ const ref = capture || existsSync(mine) ? mine : join(refs, "metal", prefix + f);
118
+ if (ref !== mine) process.stdout.write(` note: no ${run.tier} reference for ${prefix}${f}; comparing with metal\n`);
119
+ if (capture) {
120
+ mkdirSync(own, { recursive: true });
121
+ copyFileSync(actual, ref);
122
+ t.ok(true, `recorded ${ref}`);
123
+ continue;
124
+ }
125
+ if (!existsSync(ref)) {
126
+ t.ok(false, `${ref} is missing: record it with OG_FX_CAPTURE=1 og test .`);
127
+ continue;
128
+ }
129
+ const d = difference(decodePng(readFileSync(actual)), decodePng(readFileSync(ref)));
130
+ process.stdout.write(` ${f}: ${(d.outliers * 100).toFixed(3)} % outliers, RMSE ${d.rmse.toFixed(5)} against ${ref}\n`);
131
+ t.ok(d.outliers <= 0.005 && d.rmse <= 0.01, `${f} matches ${prefix}${f}: ${(d.outliers * 100).toFixed(3)} % outliers (≤ 0.5 %), RMSE ${d.rmse.toFixed(5)} (≤ 0.01)`);
132
+ }
133
+ });
134
+ }
@@ -0,0 +1,26 @@
1
+ // A pause pressed during a hit-stop: a grab freezes scaled time
2
+ // (og.time.hitStop, here held 20 ticks), and Escape pressed inside that
3
+ // freeze still pauses the run.
4
+ import { playtest } from "@origingame/engine/playtest";
5
+ import { game, press } from "./lib.ts";
6
+
7
+ export default playtest({
8
+ name: "Escape pressed during a grab's hit-stop pauses the run",
9
+ commands: [
10
+ "0.3:run",
11
+ "0.4:set:Tuning.hitStop=20",
12
+ "0.5:warp:-27:13.2",
13
+ "0.6:grab:0",
14
+ press("Escape", 0.65),
15
+ "1.0:get:Game:after.json",
16
+ "1.0:get:og.TimeScale.scale:scale.json",
17
+ "1.1:quit",
18
+ ].join(";"),
19
+ check(run, t) {
20
+ t.equal(run.warnings.join("; "), "", "the script ran without warnings");
21
+ const g = game(run, "after.json");
22
+ t.equal(g.hearts, 2, "the west sentinel's grab landed (the hit-stop ran)");
23
+ t.equal(g.paused, true, "Escape inside the hit-stop paused the run");
24
+ t.equal(run.json<{ value: number }>("scale.json").value, 0, "and the run holds: time scale 0");
25
+ },
26
+ });
@@ -0,0 +1,87 @@
1
+ // Shared readers for Ember Vale's playtests. `get:Game:<file>` writes the
2
+ // run's resource, the state the game's `facts` (src/main.ts) report to the
3
+ // probe; `get:<entity>.<component>:<file>` one component of a named row;
4
+ // `dump_ui:<file>` the laid-out interface; `dump_platform:<file>` the calls
5
+ // to og.platform.
6
+ import type { PlaytestRun } from "@origingame/engine/playtest";
7
+
8
+ export interface Game {
9
+ phase: number;
10
+ ending: number;
11
+ paused: boolean;
12
+ settingsOpen: boolean;
13
+ runs: number;
14
+ wins: number;
15
+ best: number;
16
+ recordConflicts: number;
17
+ newBest: boolean;
18
+ hearts: number;
19
+ hurts: number;
20
+ lit: number;
21
+ litMask: number;
22
+ time: number;
23
+ hour: number;
24
+ near: number;
25
+ kindle: number;
26
+ kindling: boolean;
27
+ wading: boolean;
28
+ grounded: boolean;
29
+ speed: number;
30
+ jumps: number;
31
+ splashes: number;
32
+ grabs: number;
33
+ alarms: number;
34
+ language: number;
35
+ }
36
+
37
+ /** Phase.* and Ending.* of src/components.ts. */
38
+ export const TITLE = 0;
39
+ export const PLAY = 1;
40
+ export const OVER = 2;
41
+ export const DAWN = 1;
42
+ export const NIGHT = 2;
43
+ export const CAUGHT = 3;
44
+
45
+ /** The summary's buttons answer once its entrance has played (about 1.6 s after the run ends). */
46
+ export const SUMMARY_READY = 1.7;
47
+
48
+ /** The Game resource from a `get:Game:<file>` step. */
49
+ export function game(run: PlaytestRun, file: string): Game {
50
+ return run.json<{ value: Game }>(file).value;
51
+ }
52
+
53
+ /** The value of a `get:<target>:<file>` step. */
54
+ export function value<T>(run: PlaytestRun, file: string): T {
55
+ return run.json<{ value: T }>(file).value;
56
+ }
57
+
58
+ /** A position from a `get:<entity>.og.Transform:<file>` step. */
59
+ export function position(run: PlaytestRun, file: string): [number, number, number] {
60
+ return value<{ position: [number, number, number] }>(run, file).position;
61
+ }
62
+
63
+ export interface PlatformDump {
64
+ host: string;
65
+ calls: Array<{ method: string; args: Record<string, unknown>; tick: number; state: string; result?: { ok: boolean; data?: Record<string, unknown>; error?: { code: string } } }>;
66
+ }
67
+
68
+ export interface Element { id: string; parent: string; kind: string; text: string; x: number; y: number; width: number; height: number }
69
+
70
+ /** The laid-out elements from a `dump_ui:<file>` step. */
71
+ export function elements(run: PlaytestRun, file: string): Element[] {
72
+ return run.json<{ elements: Element[] }>(file).elements;
73
+ }
74
+
75
+ /** The UI's labels from a `dump_ui:<file>` step. */
76
+ export function texts(run: PlaytestRun, file: string): string[] {
77
+ return elements(run, file).filter((e) => e.kind === "label" && e.text !== "").map((e) => e.text);
78
+ }
79
+
80
+ /** A key pressed at t (down, then up two ticks later). */
81
+ export const press = (key: string, t: number) => `${t}:key_down:${key};${(t + 0.034).toFixed(3)}:key_up:${key}`;
82
+
83
+ /** A key held from t for `seconds`. */
84
+ export const hold = (key: string, t: number, seconds: number) => `${t}:key_down:${key};${(t + seconds).toFixed(3)}:key_up:${key}`;
85
+
86
+ /** A tap on a UI element by its id path (pressed, released a tick later). */
87
+ export const tap = (id: string, t: number) => `${t}:touch_move_to:${id};${(t + 0.017).toFixed(3)}:touch_start;${(t + 0.05).toFixed(3)}:touch_end`;
@@ -0,0 +1,44 @@
1
+ // The two losses and the way back: the last heart gone ends the run caught
2
+ // (the summary with the skull); Again starts afresh; the clock run to
3
+ // Tuning.duskSeconds brings night (the vale goes dark, the summary says
4
+ // night took it); Title goes back to the title, the vale reset behind it.
5
+ import { playtest } from "@origingame/engine/playtest";
6
+ import { CAUGHT, game, NIGHT, OVER, PLAY, SUMMARY_READY, tap, texts, TITLE } from "./lib.ts";
7
+
8
+ export default playtest({
9
+ name: "caught with no hearts left, night at dusk's end, Again and Title",
10
+ commands: [
11
+ "0.3:run",
12
+ "0.5:hearts:0",
13
+ "0.6:get:Game:caught.json",
14
+ "2.3:get:Game:caught-over.json",
15
+ "2.6:dump_ui:caught-ui.json",
16
+ tap("Again", 2.1 + SUMMARY_READY),
17
+ "4.1:get:Game:again.json",
18
+ "4.2:dusk:149.6",
19
+ "4.7:get:Game:night.json",
20
+ "6.4:get:Game:night-over.json",
21
+ "6.7:dump_ui:night-ui.json",
22
+ tap("Menu", 6.2 + SUMMARY_READY),
23
+ "8.6:get:Game:title.json",
24
+ "8.7:quit",
25
+ ].join(";"),
26
+ check(run, t) {
27
+ t.equal(run.warnings.join("; "), "", "the script ran without warnings");
28
+ t.equal(game(run, "caught.json").ending, CAUGHT, "no hearts left: caught");
29
+ const co = game(run, "caught-over.json");
30
+ t.equal(co.phase, OVER, "the summary after a beat");
31
+ t.ok(texts(run, "caught-ui.json").includes("The sentinels caught you"), "it says the sentinels caught you");
32
+ const again = game(run, "again.json");
33
+ t.ok(again.phase === PLAY && again.hearts === 3 && again.ending === 0 && again.time < 0.5, "Again started a fresh run");
34
+ const night = game(run, "night.json");
35
+ t.equal(night.ending, NIGHT, "dusk's end brings night");
36
+ t.ok(night.hour >= 19.5, `at half past seven (${night.hour.toFixed(2)})`);
37
+ t.equal(game(run, "night-over.json").phase, OVER, "the summary after a beat");
38
+ const words = texts(run, "night-ui.json");
39
+ t.ok(words.includes("Night took the vale") && words.includes("0/4"), "it says night took the vale, no beacon lit");
40
+ const title = game(run, "title.json");
41
+ t.equal(title.phase, TITLE, "Title went back to the title");
42
+ t.ok(title.hour > 16 && title.hour < 17, "where the vale is in late afternoon again");
43
+ },
44
+ });
@@ -0,0 +1,64 @@
1
+ // Pause and settings: Escape holds the run (time scale 0, the clock stops,
2
+ // W moves nothing), the pause menu opens Settings, 简体中文 switches every
3
+ // string, the music slider moves, Done saves the settings through
4
+ // og.platform, and Resume gives the run back, in the chosen language.
5
+ import { playtest } from "@origingame/engine/playtest";
6
+ import { game, type PlatformDump, position, press, tap, texts } from "./lib.ts";
7
+
8
+ export default playtest({
9
+ name: "Escape pauses, settings switch the language and save, Resume gives the run back",
10
+ commands: [
11
+ "0.3:run",
12
+ press("Escape", 0.8),
13
+ "1.0:get:Game:paused.json",
14
+ "1.0:get:og.TimeScale.scale:scale.json",
15
+ "1.0:get:hero.og.Transform:before.json",
16
+ "1.05:key_down:KeyW",
17
+ "1.6:key_up:KeyW",
18
+ "1.7:get:Game:held.json",
19
+ "1.7:get:hero.og.Transform:after.json",
20
+ "1.75:screenshot:pause.png",
21
+ tap("settings", 1.8),
22
+ "2.4:move_to_text:简体中文",
23
+ "2.42:touch_start",
24
+ "2.45:touch_end",
25
+ "2.7:touch_move_to:Music/Slider",
26
+ "2.72:touch_start",
27
+ "2.75:touch_end",
28
+ "3.2:screenshot:settings-zh.png",
29
+ tap("Done", 3.4),
30
+ "3.9:get:Prefs:prefs.json",
31
+ "3.9:dump_platform:platform.json",
32
+ tap("resume", 4.0),
33
+ "4.3:warp:0",
34
+ "4.8:dump_ui:play-zh.json",
35
+ "5.0:get:Game:resumed.json",
36
+ "5.1:screenshot:play-zh.png",
37
+ "5.2:quit",
38
+ ].join(";"),
39
+ check(run, t) {
40
+ t.equal(run.warnings.join("; "), "", "the script ran without warnings");
41
+ const paused = game(run, "paused.json");
42
+ t.equal(paused.paused, true, "Escape paused the run");
43
+ t.equal(run.json<{ value: number }>("scale.json").value, 0, "the run holds: time scale 0");
44
+ const held = game(run, "held.json");
45
+ t.equal(held.time, paused.time, "the clock stopped");
46
+ const [bx, , bz] = position(run, "before.json");
47
+ const [ax, , az] = position(run, "after.json");
48
+ t.ok(Math.hypot(ax - bx, az - bz) < 1e-3, "W while paused moves nothing");
49
+ const prefs = run.json<{ value: { language: number; music: number; saves: number } }>("prefs.json").value;
50
+ t.equal(prefs.language, 1, "简体中文 was chosen");
51
+ t.ok(prefs.music < 0.7, `the music slider moved (${prefs.music.toFixed(2)})`);
52
+ t.equal(prefs.saves, 1, "Done saved once");
53
+ const p = run.json<PlatformDump>("platform.json");
54
+ const saved = p.calls.find((c) => c.method === "store.set" && c.args["key"] === "og.settings");
55
+ t.equal((saved?.args["value"] as { language?: string } | undefined)?.language, "zh-CN", "the settings went to og.platform with the language");
56
+ t.ok(texts(run, "play-zh.json").includes("按住 F 点燃烽火"), "the HUD speaks 简体中文");
57
+ const resumed = game(run, "resumed.json");
58
+ t.equal(resumed.paused, false, "Resume closed the pause menu");
59
+ t.ok(resumed.time > held.time + 0.5, "and the clock runs again");
60
+ t.frame(["pause.png", "settings-zh.png", "play-zh.png"], () => {
61
+ for (const f of ["pause.png", "settings-zh.png", "play-zh.png"]) t.equal(run.png(f).width, 1280, `${f} captured`);
62
+ });
63
+ },
64
+ });
@@ -0,0 +1,65 @@
1
+ // The phone layout (390×844 at 3×, a notch and a home bar): the title's
2
+ // menu inside the safe area with the touch footer; the HUD in it too, the
3
+ // hearts, the beacons, the clock and Pause along the top and the joystick,
4
+ // Kindle and Jump along the bottom, all touch-sized. The joystick walks
5
+ // the hero, Jump jumps, and holding Kindle by a beacon lights it.
6
+ import { playtest } from "@origingame/engine/playtest";
7
+ import { elements, game, PLAY, position, tap, texts } from "./lib.ts";
8
+
9
+ export default playtest({
10
+ name: "the phone layout: a HUD inside the safe area, a joystick that walks, Jump and Kindle",
11
+ size: "1170x2532",
12
+ commands: [
13
+ "0.1:viewport:phone",
14
+ "0.9:dump_ui:title.json",
15
+ tap("play", 1.0),
16
+ "1.1:touch_move:200,400",
17
+ "1.7:get:Game:started.json",
18
+ "1.7:get:hero.og.Transform:from.json",
19
+ "1.8:dump_ui:hud.json",
20
+ "1.9:touch_move_to:Stick",
21
+ "1.92:touch_start",
22
+ "2.0:touch_move:86,664",
23
+ "2.5:get:Game:walking.json",
24
+ "3.0:touch_end",
25
+ "3.0:get:hero.og.Transform:to.json",
26
+ tap("Jump", 3.3),
27
+ "3.5:get:Game:jumped.json",
28
+ "4.4:warp:0",
29
+ "4.6:touch_move_to:Kindle",
30
+ "4.62:touch_start",
31
+ "6.6:touch_end",
32
+ "6.7:get:Game:lit.json",
33
+ "6.75:screenshot:play.png",
34
+ "6.8:quit",
35
+ ].join(";"),
36
+ check(run, t) {
37
+ t.equal(run.warnings.join("; "), "", "the script ran without warnings");
38
+ const title = elements(run, "title.json");
39
+ t.ok(title.some((e) => e.id === "play") && title.every((e) => e.y + e.height <= 844 - 34 && e.y >= 47), "the title's menu sits inside the safe area");
40
+ t.ok(texts(run, "title.json").includes("The stick moves · Jump jumps · hold Kindle by a beacon · the view swings behind you"), "with the touch controls in its footer");
41
+ t.equal(game(run, "started.json").phase, PLAY, "a tap on Set out started a run");
42
+ const ui = run.json<{ surface: { width: number } }>("hud.json");
43
+ const hud = elements(run, "hud.json");
44
+ const el = (id: string) => hud.find((e) => e.id === id);
45
+ t.equal(ui.surface.width, 390, "laid out for a 390 px wide phone");
46
+ for (const id of ["Hearts", "Beacons", "Clock", "Pause"]) t.ok((el(id)?.y ?? 0) >= 47, `${id} sits below the notch`);
47
+ for (const id of ["Stick", "Kindle", "Jump"]) {
48
+ const e = el(id);
49
+ t.ok(e !== undefined && e.y + e.height <= 844 - 34, `${id} sits above the home bar`);
50
+ }
51
+ for (const id of ["Pause", "Stick", "Kindle", "Jump"]) {
52
+ const e = el(id);
53
+ t.ok(e !== undefined && e.width >= 44 && e.height >= 44, `${id} is at least 44 px square (${e?.width}×${e?.height})`);
54
+ }
55
+ t.ok(hud.every((e) => e.x >= 0 && e.x + e.width <= 390), "nothing runs off the sides");
56
+ t.ok(game(run, "walking.json").speed > 2, "the joystick pushed up walks the hero");
57
+ const [, , z0] = position(run, "from.json");
58
+ const [, , z1] = position(run, "to.json");
59
+ t.ok(z0 - z1 > 1.5, `north, away from the camera (${(z0 - z1).toFixed(2)} m)`);
60
+ const jumped = game(run, "jumped.json");
61
+ t.ok(jumped.jumps === 1 && !jumped.grounded, "Jump jumped");
62
+ t.equal(game(run, "lit.json").lit, 1, "holding Kindle by a beacon lit it");
63
+ t.frame("play.png", () => t.equal(run.png("play.png").width, 1170, "play.png captured at the phone's device pixels"));
64
+ },
65
+ });
@@ -0,0 +1,32 @@
1
+ // Determinism: a run with walking, running, a jump, a beacon kindled and
2
+ // a sentinel's chase and grab (the character controller, the animation
3
+ // graph and IK, og.nav and og.ai), run twice in fresh processes, ends on
4
+ // the same world hash.
5
+ import { playtest } from "@origingame/engine/playtest";
6
+ import { game, press, tap } from "./lib.ts";
7
+
8
+ export default playtest({
9
+ name: "a run with movement, a jump, kindling and a sentinel's grab replays to the same hash",
10
+ runs: 2,
11
+ commands: [
12
+ tap("play", 0.7),
13
+ "1.0:key_down:KeyW",
14
+ "1.2:key_down:ShiftLeft",
15
+ press("Space", 1.6),
16
+ "2.2:key_up:ShiftLeft",
17
+ "2.4:key_up:KeyW",
18
+ "2.5:warp:0",
19
+ "2.7:key_down:KeyF",
20
+ "4.6:key_up:KeyF",
21
+ "4.7:warp:-29.5:1",
22
+ "8.0:get:Game:end.json",
23
+ "8.1:quit",
24
+ ].join(";"),
25
+ check(run, t) {
26
+ t.equal(run.warnings.join("; "), "", "the script ran without warnings");
27
+ t.equal(run.hashes.length, 2, "two runs");
28
+ t.equal(run.hashes[1], run.hashes[0], `both runs end on hash ${run.hashes[0]}`);
29
+ const end = game(run, "end.json");
30
+ t.ok(end.jumps === 1 && end.lit === 1 && end.grabs >= 1, `the run jumped, lit a beacon and was grabbed (${end.jumps}, ${end.lit}, ${end.grabs})`);
31
+ },
32
+ });
@@ -0,0 +1,28 @@
1
+ // A new run calms the sentinels: one chasing the hero when the run
2
+ // restarts goes back to its round in `patrol` (its machine started afresh)
3
+ // instead of searching where it last saw the hero.
4
+ import { playtest } from "@origingame/engine/playtest";
5
+ import { value } from "./lib.ts";
6
+
7
+ interface Agent { state: number }
8
+ interface Sentinel { alert: boolean }
9
+
10
+ export default playtest({
11
+ name: "a sentinel chasing when the run restarts is back on patrol",
12
+ commands: [
13
+ "0.3:run",
14
+ "2.6:warp:-28.5:3",
15
+ "3.25:get:sentinel-0.og.AiAgent:chasing.json",
16
+ "3.3:run",
17
+ "3.6:get:sentinel-0.og.AiAgent:restarted.json",
18
+ "3.6:get:sentinel-0.Sentinel:calm.json",
19
+ "3.7:quit",
20
+ ].join(";"),
21
+ check(run, t) {
22
+ t.equal(run.warnings.join("; "), "", "the script ran without warnings");
23
+ const chasing = value<Agent>(run, "chasing.json").state;
24
+ t.ok(chasing === 1 || chasing === 2, `the west sentinel saw the hero and left its patrol (state ${chasing})`);
25
+ t.equal(value<Agent>(run, "restarted.json").state, 0, "after the restart it patrols (state 0), not searching");
26
+ t.equal(value<Sentinel>(run, "calm.json").alert, false, "and its eye is calm");
27
+ },
28
+ });
@@ -0,0 +1,48 @@
1
+ // The sentinels on the navmesh: the west one walks its round (og.NavAgent
2
+ // moving it at the patrol speed); the hero standing ahead on its round raises
3
+ // its alarm (og.ai's behaviour enters `alert` and commands `alarm`), its
4
+ // eye burns red and it chases; within reach its grab costs a heart, throws
5
+ // the hero back and spares it a moment (no second grab while spared).
6
+ import { playtest } from "@origingame/engine/playtest";
7
+ import { game, position, value } from "./lib.ts";
8
+
9
+ interface Sentinel { alarms: number; grabs: number; alert: boolean; speed: number }
10
+
11
+ export default playtest({
12
+ name: "a sentinel on its round sees the hero, raises the alarm, chases and grabs a heart",
13
+ commands: [
14
+ "0.3:run",
15
+ "2.0:get:sentinel-0.og.Transform:round-a.json",
16
+ "2.5:get:sentinel-0.og.Transform:round-b.json",
17
+ "2.5:get:sentinel-0.Sentinel:calm.json",
18
+ "2.6:warp:-28.5:3",
19
+ "3.2:get:Game:seen.json",
20
+ "3.2:get:sentinel-0.Sentinel:alert.json",
21
+ "3.2:get:sentinel-0-eye.og.PointLight:eye.json",
22
+ "3.25:screenshot:alarm.png",
23
+ "7.6:get:Game:grabbed.json",
24
+ "7.6:get:sentinel-0.Sentinel:grabber.json",
25
+ "7.7:quit",
26
+ ].join(";"),
27
+ check(run, t) {
28
+ t.equal(run.warnings.join("; "), "", "the script ran without warnings");
29
+ const [ax, , az] = position(run, "round-a.json");
30
+ const [bx, , bz] = position(run, "round-b.json");
31
+ const walked = Math.hypot(bx - ax, bz - az);
32
+ t.ok(walked > 0.4 && walked < 1.2, `the west sentinel walks its round at the patrol pace (${walked.toFixed(2)} m in 0.5 s, after its first wait)`);
33
+ const calm = value<Sentinel>(run, "calm.json");
34
+ t.ok(!calm.alert && calm.alarms === 0, "calm while the hero is far");
35
+ const seen = game(run, "seen.json");
36
+ const alert = value<Sentinel>(run, "alert.json");
37
+ t.ok(alert.alert && alert.alarms === 1 && seen.alarms >= 1, "the hero in its sight: the alarm, and it gives chase");
38
+ t.ok(value<{ intensity: number }>(run, "eye.json").intensity > 5, "its eye burns red");
39
+ const grabbed = game(run, "grabbed.json");
40
+ const grabber = value<Sentinel>(run, "grabber.json");
41
+ t.ok(grabber.grabs >= 1, `it caught the hero (${grabber.grabs} grabs)`);
42
+ t.equal(grabbed.hearts, 3 - grabbed.grabs, `each grab cost a heart (${grabbed.hearts} left)`);
43
+ t.equal(grabbed.hurts, grabbed.grabs, "and counts as a heart lost");
44
+ t.ok(grabbed.grabs <= 2, `spared a moment after each grab: at most two in 4.4 s (${grabbed.grabs})`);
45
+ t.equal(grabbed.phase, 1, "the run goes on with hearts left");
46
+ t.frame("alarm.png", () => t.equal(run.png("alarm.png").width, 1280, "alarm.png captured"));
47
+ },
48
+ });