@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,40 @@
1
+ // The two endings, through Tune writes: with the target at 30 the first line
2
+ // wins the round; with one move and a target out of reach, that move loses
3
+ // it. Each end card waits for a press, which deals the next round.
4
+ import { playtest } from "@origingame/engine/playtest";
5
+ import { CASCADE_BOARD, PHASE, matchOf, tap } from "./lib.ts";
6
+
7
+ export default playtest({
8
+ name: "reaching the target wins, running out of moves loses, a press deals again",
9
+ commands: [
10
+ "0.1:set:board.Tuning.target=30",
11
+ tap(0.5),
12
+ `0.8:deal:${CASCADE_BOARD}`,
13
+ "1.0:swap:2,0:2,1",
14
+ "3.0:dump_entity:board:won.json",
15
+ "3.0:screenshot:won.png",
16
+ "3.1:set:board.Tuning.target=10000",
17
+ "3.1:set:board.Tuning.moves=1",
18
+ tap(3.2),
19
+ "3.3:dump_entity:board:second.json",
20
+ `3.5:deal:${CASCADE_BOARD}`,
21
+ "3.7:swap:2,0:2,1",
22
+ "5.7:dump_entity:board:lost.json",
23
+ "5.7:screenshot:lost.png",
24
+ "5.8:quit",
25
+ ].join(";"),
26
+ check(run, t) {
27
+ t.equal(run.warnings.join("; "), "", "the script ran without warnings");
28
+ const won = matchOf(run, "won.json");
29
+ t.equal(won.phase, PHASE.won, "the first line reached the target of 30");
30
+ t.equal(won.best, won.score, "the best score took the round's");
31
+ const second = matchOf(run, "second.json");
32
+ t.equal(second.phase, PHASE.idle, "a press after the card's hold dealt round two");
33
+ t.equal(second.rounds, 2, "two rounds");
34
+ t.equal(second.movesLeft, 1, "round two has the tuned single move");
35
+ const lost = matchOf(run, "lost.json");
36
+ t.equal(lost.phase, PHASE.lost, "the last move ran out below the target");
37
+ t.equal(lost.movesLeft, 0, "no moves left");
38
+ t.equal(lost.best, won.best, "the best score is kept");
39
+ },
40
+ });
@@ -0,0 +1,49 @@
1
+ // Three frames at 1280x720 compared with references/<tier>/: the dealt
2
+ // board (the nine-sliced panel, the checker tilemap, the gems' idle clips),
3
+ // the swap's line mid-clear (the burst clip, the squash to nothing, the
4
+ // floating +30) and the cascade mid-clear (the gold crit +60 and the
5
+ // camera shake). Every motion is a tween or a flipbook clip on scaled time
6
+ // and the shake is seeded by the tick, so the same script draws the same
7
+ // frames. Record a tier's references with `OG_FX_CAPTURE=1 og test .`.
8
+ import { playtest } from "@origingame/engine/playtest";
9
+ import { readFileSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import { checkFrames, decodePng, outDir } from "./frames.ts";
12
+ import { CASCADE_BOARD, PHASE, matchOf, tap } from "./lib.ts";
13
+
14
+ const FILES = ["dealt.png", "clearing.png", "cascade.png"];
15
+
16
+ export default playtest({
17
+ name: "the dealt board, a clear and a cascade match their reference frames",
18
+ size: "1280x720",
19
+ commands: [
20
+ tap(0.5),
21
+ `0.8:deal:${CASCADE_BOARD}`,
22
+ "1.2:screenshot:dealt.png",
23
+ "1.3:swap:2,0:2,1",
24
+ "1.53:screenshot:clearing.png",
25
+ "1.53:dump_entity:board:clearing.json",
26
+ "2.14:screenshot:cascade.png",
27
+ "2.14:dump_entity:board:cascade.json",
28
+ "2.2:quit",
29
+ ].join(";"),
30
+ check(run, t) {
31
+ t.equal(run.warnings.join("; "), "", "the script ran without warnings");
32
+ t.ok(run.quit, "the script reached quit");
33
+ const clearing = matchOf(run, "clearing.json");
34
+ t.equal([clearing.phase, clearing.chain], [PHASE.clear, 1], "the first frame of play is the swap's own line clearing");
35
+ const cascade = matchOf(run, "cascade.json");
36
+ t.equal([cascade.phase, cascade.chain], [PHASE.clear, 2], "the last is the cascade clearing");
37
+ t.frame(FILES, () => {
38
+ for (const f of FILES) t.equal(run.png(f).width, 1280, `${f} is 1280 wide`);
39
+ });
40
+ // The calm ground: away from the board the frame is the brand ink #14161b.
41
+ t.frame("dealt.png", () => {
42
+ const dealt = decodePng(readFileSync(join(outDir(), "dealt.png")));
43
+ const at = (700 * dealt.width + 20) * 4;
44
+ const ink = [dealt.rgba[at]!, dealt.rgba[at + 1]!, dealt.rgba[at + 2]!];
45
+ t.ok(Math.abs(ink[0]! - 0x14) <= 3 && Math.abs(ink[1]! - 0x16) <= 3 && Math.abs(ink[2]! - 0x1b) <= 3, `the corner is the brand ink (${ink.join(", ")})`);
46
+ });
47
+ checkFrames(run, t, FILES, "");
48
+ },
49
+ });
@@ -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,41 @@
1
+ // A high-DPR screen: the play surface is 640×360 CSS px at 2× over the
2
+ // same 1280×720 render target. The pointer arrives in CSS pixels, so the
3
+ // mapping Pick works out is too (half the 1× numbers), and a tap on a
4
+ // cell's CSS pixel selects that cell.
5
+ import { playtest } from "@origingame/engine/playtest";
6
+ import { cellCentre } from "../src/layout.ts";
7
+ import { CASCADE_BOARD, MAPPING, PHASE, matchOf } from "./lib.ts";
8
+
9
+ /** The 2× surface's mapping: the same view over half the CSS pixels. */
10
+ const HALF = { pxPerM: MAPPING.pxPerM / 2, screenX0: MAPPING.screenX0 / 2, screenY0: MAPPING.screenY0 / 2 } as const;
11
+
12
+ function tapCell(t: number, col: number, row: number): string {
13
+ const c = cellCentre(col, row);
14
+ const at = `${Math.round(HALF.screenX0 + c.x * HALF.pxPerM)},${Math.round(HALF.screenY0 - c.y * HALF.pxPerM)}`;
15
+ return `${t}:touch_move:${at};${t}:touch_start;${(t + 0.05).toFixed(2)}:touch_end`;
16
+ }
17
+
18
+ export default playtest({
19
+ name: "on a 2× screen the pointer maps in CSS pixels and a tap selects the cell under it",
20
+ commands: [
21
+ "0.05:viewport:2x",
22
+ "0.5:touch_move:320,340;0.5:touch_start;0.55:touch_end",
23
+ `0.8:deal:${CASCADE_BOARD}`,
24
+ "0.9:dump_entity:board:dealt.json",
25
+ tapCell(1.0, 5, 0),
26
+ "1.1:dump_entity:board:selected.json",
27
+ tapCell(1.3, 1, 4),
28
+ "1.4:dump_entity:board:moved.json",
29
+ "1.5:quit",
30
+ ].join(";"),
31
+ check(run, t) {
32
+ t.equal(run.warnings.join("; "), "", "the script ran without warnings");
33
+ const dealt = matchOf(run, "dealt.json");
34
+ t.equal(dealt.phase, PHASE.idle, "a tap on the title dealt a round");
35
+ t.equal(dealt.pxPerM, HALF.pxPerM, "30 CSS px per metre on the 360 px tall surface");
36
+ t.equal(dealt.screenX0, HALF.screenX0, "the world origin is at CSS x 320");
37
+ t.equal(dealt.screenY0, HALF.screenY0, "and at CSS y 195");
38
+ t.equal(matchOf(run, "selected.json").selected, 5, "a tap on (5,0) selected cell 5");
39
+ t.equal(matchOf(run, "moved.json").selected, 4 * 7 + 1, "a tap on (1,4), not a neighbour, selected cell 29 instead");
40
+ },
41
+ });
@@ -0,0 +1,64 @@
1
+ // Shared helpers for the match3 playtests: the board's Match as dump_entity
2
+ // writes it, and pointer pixels for cells at the playtests' 1280x720 surface.
3
+ import type { PlaytestRun } from "@origingame/engine/playtest";
4
+ import { cellCentre } from "../src/layout.ts";
5
+
6
+ export interface MatchDump {
7
+ phase: number;
8
+ score: number;
9
+ best: number;
10
+ movesLeft: number;
11
+ chain: number;
12
+ maxChain: number;
13
+ cleared: number;
14
+ lastCleared: number;
15
+ rounds: number;
16
+ selected: number;
17
+ pxPerM: number;
18
+ screenX0: number;
19
+ screenY0: number;
20
+ }
21
+
22
+ export const PHASE = { title: 0, idle: 1, swap: 2, swapBack: 3, clear: 4, fall: 5, won: 6, lost: 7 } as const;
23
+
24
+ export function matchOf(run: PlaytestRun, file: string): MatchDump {
25
+ const dump = run.json<{ components: Record<string, unknown> }>(file);
26
+ const m = dump.components["Match"];
27
+ if (!m) throw new Error(`${file} has no Match`);
28
+ return m as MatchDump;
29
+ }
30
+
31
+ /**
32
+ * The pointer mapping these scripts assume at 1280x720: the view is 12 m
33
+ * tall (60 px per metre) and centred 0.9 m above the board's centre, so the
34
+ * world origin is at (640, 390). Each playtest checks the dumped mapping
35
+ * against it, so a layout change fails loudly instead of tapping elsewhere.
36
+ */
37
+ export const MAPPING = { pxPerM: 60, screenX0: 640, screenY0: 390 } as const;
38
+
39
+ /** The CSS pixel at the centre of a cell (col 0 is the left, row 0 the bottom), offset by (dx, dy) metres. */
40
+ export function px(col: number, row: number, dx = 0, dy = 0): string {
41
+ const c = cellCentre(col, row);
42
+ return `${Math.round(MAPPING.screenX0 + (c.x + dx) * MAPPING.pxPerM)},${Math.round(MAPPING.screenY0 - (c.y + dy) * MAPPING.pxPerM)}`;
43
+ }
44
+
45
+ /** A tap on a cell at t seconds. */
46
+ export const tapCell = (t: number, col: number, row: number) =>
47
+ `${t}:touch_move:${px(col, row)};${t}:touch_start;${(t + 0.05).toFixed(2)}:touch_end`;
48
+
49
+ /** A tap anywhere (starts a round from the title or an end card). */
50
+ export const tap = (t: number) => `${t}:touch_move:640,700;${t}:touch_start;${(t + 0.05).toFixed(2)}:touch_end`;
51
+
52
+ /**
53
+ * A board with no line (top row first): swapping (2,0) with (2,1) makes a
54
+ * line of 3s along the bottom row, and when it clears, the tiles that fall
55
+ * make a line of 0s at (1..3, 0): a cascade. Swapping (5,0) with (6,0)
56
+ * makes nothing.
57
+ */
58
+ export const CASCADE_BOARD = "2340123/0123401/3401234/1234012/4022340/2031123/3300401";
59
+
60
+ export function checkMapping(m: MatchDump, t: { equal<T>(a: T, b: T, what: string): void }): void {
61
+ t.equal(m.pxPerM, MAPPING.pxPerM, "the pointer mapping is 60 px per metre at 1280x720");
62
+ t.equal(m.screenX0, MAPPING.screenX0, "the world origin is at x 640");
63
+ t.equal(m.screenY0, MAPPING.screenY0, "the world origin is at y 390");
64
+ }
@@ -0,0 +1,28 @@
1
+ // The same script twice, in fresh processes, ends in the same world: a
2
+ // refused swap (a yoyo tween), a swap whose line clears and cascades (tween
3
+ // groups, flipbook clips, floating scores, camera shake and the refill's
4
+ // dice), stopped mid-fall so running tweens, clips and floating text are in
5
+ // the hash too.
6
+ import { playtest } from "@origingame/engine/playtest";
7
+ import { CASCADE_BOARD, PHASE, matchOf, tap } from "./lib.ts";
8
+
9
+ export default playtest({
10
+ name: "a replay of swaps, clears and a cascade lands on the same world hash",
11
+ runs: 2,
12
+ commands: [
13
+ tap(0.5),
14
+ `0.8:deal:${CASCADE_BOARD}`,
15
+ "1.0:swap:5,0:6,0",
16
+ "1.5:swap:2,0:2,1",
17
+ "2.75:dump_entity:board:falling.json",
18
+ "2.75:quit",
19
+ ].join(";"),
20
+ check(run, t) {
21
+ t.equal(run.warnings.join("; "), "", "the script ran without warnings");
22
+ t.ok(run.quit, "the script reached quit");
23
+ const m = matchOf(run, "falling.json");
24
+ t.equal([m.phase, m.chain, m.cleared], [PHASE.fall, 2, 6], "it stops while the cascade's gems fall");
25
+ t.equal(run.hashes.length, 2, "the script ran twice");
26
+ t.equal(run.hashes[1], run.hashes[0], "both runs end in the same world");
27
+ },
28
+ });
@@ -0,0 +1,50 @@
1
+ // Sounds: a selection, a refused swap, a swap, its clear and the cascade,
2
+ // the win and (in a second round of one move) the loss each start their own
3
+ // voice once (Sfx.plays on the sfx-<bit> entity); the mix is heard.
4
+ import { playtest } from "@origingame/engine/playtest";
5
+ import { CASCADE_BOARD, tap, tapCell } from "./lib.ts";
6
+
7
+ interface EntityDump {
8
+ components: Record<string, Record<string, unknown>>;
9
+ }
10
+
11
+ const VOICES = [
12
+ [1, "a selection", 2],
13
+ [2, "a swap", 1],
14
+ [4, "a refused swap", 1],
15
+ [8, "a clear", 1],
16
+ [16, "a cascade", 1],
17
+ [32, "the win", 1],
18
+ [64, "the loss", 1],
19
+ ] as const;
20
+
21
+ export default playtest({
22
+ name: "selections, swaps, clears, a cascade, the win and the loss each play their sound",
23
+ commands: [
24
+ "0.1:set:board.Tuning.target=30",
25
+ "0.45:listen:title.wav:0.3",
26
+ tap(0.5),
27
+ `0.8:deal:${CASCADE_BOARD}`,
28
+ tapCell(1.0, 5, 0),
29
+ tapCell(1.2, 6, 0),
30
+ tapCell(2.0, 2, 0),
31
+ tapCell(2.2, 2, 1),
32
+ "2.5:listen:clear.wav:0.3",
33
+ "4.3:set:board.Tuning.target=10000",
34
+ "4.3:set:board.Tuning.moves=1",
35
+ tap(4.5),
36
+ `4.8:deal:${CASCADE_BOARD}`,
37
+ "5.0:swap:2,0:2,1",
38
+ ...VOICES.map(([bit]) => `7.2:dump_entity:sfx-${bit}:sfx-${bit}.json`),
39
+ "7.3:quit",
40
+ ].join(";"),
41
+ check(run, t) {
42
+ t.equal(run.warnings.join("; "), "", "the script ran without warnings");
43
+ t.ok(run.wav("title.wav").peak < 0.001, `the title is silent (peak ${run.wav("title.wav").peak})`);
44
+ t.ok(run.wav("clear.wav").peak > 0.05, `the swap and its clear are heard (peak ${run.wav("clear.wav").peak})`);
45
+ for (const [bit, what, least] of VOICES) {
46
+ const plays = Number(run.json<EntityDump>(`sfx-${bit}.json`).components["Sfx"]?.["plays"] ?? 0);
47
+ t.ok(plays >= least, `${what} played its sound (${plays} time(s), at least ${least})`);
48
+ }
49
+ },
50
+ });
@@ -0,0 +1,67 @@
1
+ // Swapping by two taps on an authored board: the swap that makes a line of
2
+ // three clears it, scores, spends one move, and the tiles that fall make a
3
+ // second line (a cascade worth double). A swap that makes nothing goes back
4
+ // and costs nothing.
5
+ import { playtest } from "@origingame/engine/playtest";
6
+ import { SIZE } from "../src/layout.ts";
7
+ import { CASCADE_BOARD, PHASE, checkMapping, matchOf, tap, tapCell } from "./lib.ts";
8
+
9
+ export default playtest({
10
+ name: "a swap that makes a line clears, scores and cascades; one that makes nothing is free",
11
+ commands: [
12
+ "0.3:screenshot:title.png",
13
+ tap(0.5),
14
+ `0.8:deal:${CASCADE_BOARD}`,
15
+ "0.9:dump_entity:board:dealt.json",
16
+ "0.9:screenshot:play.png",
17
+ // The refused swap first: (5,0) then (6,0).
18
+ tapCell(1.0, 5, 0),
19
+ "1.1:dump_entity:board:selected.json",
20
+ tapCell(1.2, 6, 0),
21
+ "1.42:dump_entity:board:swapping-back.json",
22
+ "1.8:dump_entity:board:refused.json",
23
+ // The line: (2,0) then (2,1).
24
+ tapCell(2.0, 2, 0),
25
+ tapCell(2.2, 2, 1),
26
+ "2.45:screenshot:clearing.png",
27
+ "2.45:dump_entity:board:clearing.json",
28
+ "4.0:dump_entity:board:settled.json",
29
+ "4.0:screenshot:settled.png",
30
+ "4.0:dump_world:world.json",
31
+ "4.1:quit",
32
+ ].join(";"),
33
+ check(run, t) {
34
+ t.equal(run.warnings.join("; "), "", "the script ran without warnings");
35
+ t.ok(run.quit, "the script reached quit");
36
+ const dealt = matchOf(run, "dealt.json");
37
+ checkMapping(dealt, t);
38
+ t.equal(dealt.phase, PHASE.idle, "a tap on the title deals a round");
39
+ t.equal(dealt.movesLeft, 20, "the round starts with Tuning.moves");
40
+
41
+ t.equal(matchOf(run, "selected.json").selected, 5, "the first tap selects cell (5,0)");
42
+ t.equal(matchOf(run, "swapping-back.json").phase, PHASE.swapBack, "the swap that makes nothing goes back");
43
+ const refused = matchOf(run, "refused.json");
44
+ t.equal(refused.phase, PHASE.idle, "back to idle");
45
+ t.equal(refused.movesLeft, 20, "the refused swap cost no move");
46
+ t.equal(refused.score, 0, "and scored nothing");
47
+
48
+ const clearing = matchOf(run, "clearing.json");
49
+ t.equal(clearing.phase, PHASE.clear, "the line is clearing");
50
+ t.equal(clearing.movesLeft, 19, "the swap that made a line spent a move");
51
+
52
+ const settled = matchOf(run, "settled.json");
53
+ t.equal(settled.phase, PHASE.idle, "the board settled");
54
+ t.ok(settled.maxChain >= 2, `the fall made a second line: a cascade (chain ${settled.maxChain})`);
55
+ t.ok(settled.cleared >= 6, `both lines cleared (${settled.cleared} tiles)`);
56
+ t.ok(settled.score >= 3 * 10 + 3 * 10 * 2, `three tiles at 10, then three at 10 x 2 (score ${settled.score})`);
57
+
58
+ const world = run.json<{ systems: Array<{ name: string; matched: number | null }> }>("world.json");
59
+ // The backdrop, the board, the 49 gems and the selection frame; the checker tilemap is no row.
60
+ const rows = SIZE * SIZE + 3;
61
+ for (const s of ["Flow", "Pick", "Resolve", "Animate"]) {
62
+ const sys = world.systems.find((x) => x.name === s);
63
+ t.equal(sys?.matched, rows, `${s} sees every row`);
64
+ }
65
+ t.equal(world.systems.find((x) => x.name === "Hud")?.matched, 1, "Hud sees the board row only");
66
+ },
67
+ });