@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,47 @@
1
+ type strings
2
+ version 1
3
+ ---
4
+ # 小径守卫的简体中文字符串(og.i18n);每个键都与 en.ogt 一一对应。
5
+ locale "zh-CN"
6
+ fonts "fonts/NotoSansSC-subset.ttf"
7
+ string "title.name" "小径守卫"
8
+ string "title.tagline" "守住花园小径,直到黑夜过去"
9
+ string "title.play" "开始"
10
+ string "title.settings" "设置"
11
+ string "title.footer" "草地上种花,小径上来客"
12
+ string "hud.wave" "第 {n}/{total} 波"
13
+ string "hud.next" "下一波"
14
+ string "hud.pause" "暂停"
15
+ string "hud.hint" "先选一株植物,再点草地上的格子"
16
+ string "tower.thorn" "荆棘"
17
+ string "tower.frost" "霜百合"
18
+ string "tower.hedge" "树篱"
19
+ string "float.gold" "+{n}"
20
+ string "float.leak" "-{n}"
21
+ string "float.blocked" "这会堵死小径"
22
+ string "float.poor" "还差 {n}"
23
+ string "float.taken" "已有植物"
24
+ string "float.wave" "第 {n} 波"
25
+ string "float.clear" "守住了 +{n}"
26
+ string "pause.title" "已暂停"
27
+ string "pause.resume" "继续"
28
+ string "pause.settings" "设置"
29
+ string "pause.quit" "返回标题"
30
+ string "settings.title" "设置"
31
+ string "settings.master" "总音量"
32
+ string "settings.music" "音乐"
33
+ string "settings.effects" "音效"
34
+ string "settings.language" "语言"
35
+ string "settings.motion" "减少动态效果"
36
+ string "settings.done" "完成"
37
+ string "over.won" "花园守住了"
38
+ string "over.lost" "灯笼熄灭了"
39
+ string "over.best" "最佳 {n}"
40
+ string "over.newBest" "新纪录"
41
+ string "over.waves" "守住的波次"
42
+ string "over.kills" "劝退的来客"
43
+ string "over.lives" "剩下的灯光"
44
+ string "over.retry" "再玩一次"
45
+ string "over.menu" "标题"
46
+ string "lang.en" "English"
47
+ string "lang.zh" "简体中文"
@@ -0,0 +1,10 @@
1
+ type tileset
2
+ version 1
3
+ ---
4
+ # Written by tools/make-assets.py: 0-1 grass, 2 hedge, 3 stone, 4 the gate,
5
+ # 5 the doorstep, 6 a thorn's pad, 7 night flowers, 8 a frost lily's pad,
6
+ # 9 a hedge's pad (the pads layer says which plant stands on a cell).
7
+ texture "tiles/garden.png"
8
+ grid 32 32 10 1
9
+ filter linear
10
+ solid 2 3
@@ -0,0 +1,7 @@
1
+ type import
2
+ version 1
3
+ ---
4
+ # Drawn art: lossless, sampled linear.
5
+ texture {
6
+ kind color
7
+ }
@@ -0,0 +1,22 @@
1
+ # Media to Git LFS, as the platform's other scaffolds route them; text stays
2
+ # diffable (.ogt scenes and settings are line-based text by design).
3
+ *.glb filter=lfs diff=lfs merge=lfs -text
4
+ *.gltf filter=lfs diff=lfs merge=lfs -text
5
+ *.bin filter=lfs diff=lfs merge=lfs -text
6
+ *.png filter=lfs diff=lfs merge=lfs -text
7
+ *.jpg filter=lfs diff=lfs merge=lfs -text
8
+ *.jpeg filter=lfs diff=lfs merge=lfs -text
9
+ *.webp filter=lfs diff=lfs merge=lfs -text
10
+ *.ktx2 filter=lfs diff=lfs merge=lfs -text
11
+ *.hdr filter=lfs diff=lfs merge=lfs -text
12
+ *.exr filter=lfs diff=lfs merge=lfs -text
13
+ *.wav filter=lfs diff=lfs merge=lfs -text
14
+ *.mp3 filter=lfs diff=lfs merge=lfs -text
15
+ *.ogg filter=lfs diff=lfs merge=lfs -text
16
+ *.flac filter=lfs diff=lfs merge=lfs -text
17
+ *.mp4 filter=lfs diff=lfs merge=lfs -text
18
+ *.ttf filter=lfs diff=lfs merge=lfs -text
19
+ *.otf filter=lfs diff=lfs merge=lfs -text
20
+ *.woff2 filter=lfs diff=lfs merge=lfs -text
21
+ *.ogt text eol=lf
22
+ *.ts text eol=lf
@@ -0,0 +1,46 @@
1
+ // The host this project's playtest helpers (tests/*.ts) touch under bun:
2
+ // `node:` modules, `process`, `Buffer`, `TextEncoder`, `import.meta.url`.
3
+ // Declared here because the project type-checks with `types: []` (no
4
+ // @types/node, no bun-types): `og check` reads them through tsconfig.json's
5
+ // include. Game code (src/) uses none of them.
6
+
7
+ declare module "node:fs" {
8
+ export function existsSync(path: string): boolean;
9
+ export function readFileSync(path: string): Uint8Array;
10
+ export function readFileSync(path: string, encoding: string): string;
11
+ export function writeFileSync(path: string, data: string | Uint8Array): void;
12
+ export function mkdirSync(path: string, options?: { recursive?: boolean }): void;
13
+ export function copyFileSync(from: string, to: string): void;
14
+ }
15
+
16
+ declare module "node:path" {
17
+ export function dirname(path: string): string;
18
+ export function join(...parts: string[]): string;
19
+ export function resolve(...parts: string[]): string;
20
+ }
21
+
22
+ declare module "node:url" {
23
+ export function fileURLToPath(url: string | URL): string;
24
+ }
25
+
26
+ declare module "node:zlib" {
27
+ export function inflateSync(data: Uint8Array): Uint8Array;
28
+ export function deflateSync(data: Uint8Array, options?: { level?: number }): Uint8Array;
29
+ }
30
+
31
+ declare const process: {
32
+ readonly env: Record<string, string | undefined>;
33
+ cwd(): string;
34
+ readonly stdout: { write(text: string): boolean };
35
+ };
36
+ declare const Buffer: { concat(chunks: readonly Uint8Array[]): Uint8Array };
37
+ declare class TextEncoder {
38
+ encode(text: string): Uint8Array;
39
+ }
40
+ declare class URL {
41
+ constructor(url: string, base?: string | URL);
42
+ readonly href: string;
43
+ }
44
+ interface ImportMeta {
45
+ readonly url: string;
46
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "origin-tower-defense",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "og dev .",
8
+ "start": "og run .",
9
+ "build": "og build .",
10
+ "publish-build": "og build . --publish",
11
+ "cook": "og cook .",
12
+ "check": "og check .",
13
+ "test": "og test ."
14
+ },
15
+ "dependencies": {
16
+ "@origingame/engine": "0.1.0"
17
+ }
18
+ }
@@ -0,0 +1,14 @@
1
+ type project
2
+ version 1
3
+ ---
4
+ name "Path Guard"
5
+ id "tower-defense"
6
+ engine "0.1.0"
7
+ entry "src/main.ts"
8
+ profile "2d"
9
+ tick 60
10
+ seed 1
11
+ publish {
12
+ probe player
13
+ out "dist"
14
+ }
@@ -0,0 +1,24 @@
1
+ // The sprite sheet's clips (assets/sprites/garden.ogt, written by
2
+ // tools/make-assets.py), by name. Systems pick clips by index every tick,
3
+ // so the indices are fixed here and checked against the sheet in `start`.
4
+
5
+ import { og } from "@origingame/engine";
6
+ import type { FlipbookInfo } from "@origingame/engine";
7
+
8
+ /** The sheet's clips in the order the .ogt declares them. */
9
+ export const CLIPS = ["beetle", "moth", "snail", "lantern", "thorn", "thorn_shoot", "frost", "frost_shoot", "hedge", "seed", "flake", "cursor", "refused"] as const;
10
+ export type ClipName = (typeof CLIPS)[number];
11
+
12
+ /** A clip's index by name. */
13
+ export function clipOf(name: string): number {
14
+ const i = (CLIPS as readonly string[]).indexOf(name);
15
+ if (i < 0) throw new Error(`the sheet has no clip '${name}'`);
16
+ return i;
17
+ }
18
+
19
+ /** Checks the loaded sheet against CLIPS once, in `start`. */
20
+ export function checkClips(info: FlipbookInfo): void {
21
+ CLIPS.forEach((name, i) => {
22
+ if (og.anim.clip(info, name) !== i) throw new Error(`assets/sprites/garden.ogt: clip '${name}' must be clip ${i} (src/art.ts reads clips by position)`);
23
+ });
24
+ }
@@ -0,0 +1,156 @@
1
+ // Every piece of Path Guard's state lives in these reflected types: dumps,
2
+ // the probe, Tune and the command channel's `get` and `set` read and write
3
+ // them by name.
4
+ //
5
+ // The round (Game), the knobs (Tuning) and the player's settings (Prefs)
6
+ // are resources. The actors are pooled rows spawned in `start`: one Plant
7
+ // per buildable cell (placing a plant switches its row on), the Visitors
8
+ // (the creeps) and the Shots, plus the Cursor and one Sfx row per sound.
9
+
10
+ import { bool, defineComponent, defineResource, f32, f64, i32, u32 } from "@origingame/engine";
11
+
12
+ /** The screen the game is on. */
13
+ export const Phase = { Title: 0, Play: 1, Over: 2 } as const;
14
+
15
+ /** What a plant is: the tray's order (keys 1, 2, 3). */
16
+ export const Plant = { Thorn: 0, Frost: 1, Hedge: 2 } as const;
17
+ export const PLANTS = ["thorn", "frost", "hedge"] as const;
18
+
19
+ /** What a visitor is. */
20
+ export const Kind = { Beetle: 0, Moth: 1, Snail: 2 } as const;
21
+
22
+ /** Sound requests: bits of Game.sfx that the Voices system plays once each. */
23
+ export const Sound = { Plant: 1, Thorn: 2, Frost: 4, Pop: 8, Leak: 16, Wave: 32, Clear: 64, Won: 128, Lost: 256, Move: 512 } as const;
24
+
25
+ /** Why a placement was refused (Game.refused counts them; the HUD shakes and says why). */
26
+ export const Refusal = { None: 0, Poor: 1, Blocked: 2, Taken: 3 } as const;
27
+
28
+ /** The round: the lantern's light, the purse, the waves and the effects' bookkeeping. */
29
+ export const Game = defineResource("Game", {
30
+ phase: u32(Phase.Title, { doc: "Phase.* of src/components.ts." }),
31
+ leftTitle: bool(false, { doc: "The player has left the title the game opened on: from then on the title enters with the skin's entrance (the opening one is built settled, og.ui.mainMenu's instant: the web draws it behind the play gate)." }),
32
+ paused: bool(false, { doc: "The pause menu is open (the garden holds)." }),
33
+ settingsOpen: bool(false, { doc: "The settings dialog is open." }),
34
+ won: bool(false, { doc: "The last round held every wave." }),
35
+ rounds: u32(0, { doc: "Rounds started." }),
36
+ lives: u32(0, { doc: "The lantern's light: a visitor that reaches the doorstep takes its share (Tuning.startLives at Play)." }),
37
+ gold: u32(0, { doc: "Coins to plant with." }),
38
+ wave: u32(0, { doc: "The wave under way or last spent (1-based; 0 before the first)." }),
39
+ inWave: bool(false, { doc: "Visitors of the wave are still coming or walking." }),
40
+ toSpawn: u32(0, { doc: "Visitors of this wave not yet through the gate." }),
41
+ spawnTicks: u32(0, { unit: "ticks", doc: "Ticks to the next visitor." }),
42
+ spawned: u32(0, { doc: "Visitors of this wave through the gate so far." }),
43
+ breakTicks: u32(0, { unit: "ticks", doc: "Ticks before the next wave starts by itself." }),
44
+ callNext: bool(false, { doc: "The player asked for the next wave now (an early call pays its bonus)." }),
45
+ walking: u32(0, { doc: "Visitors on the field now." }),
46
+ kills: u32(0, { doc: "Visitors turned away." }),
47
+ leaks: u32(0, { doc: "Visitors that reached the doorstep." }),
48
+ placed: u32(0, { doc: "Plants set this round (each stays to the round's end)." }),
49
+ refused: u32(0, { doc: "Placements refused (too poor, the path closed, the cell taken)." }),
50
+ refusal: u32(0, { doc: "Refusal.* of the last refused placement." }),
51
+ refusedTick: i32(-1, { unit: "ticks", doc: "The tick of the last refusal (the HUD shakes the gold on it)." }),
52
+ shots: u32(0, { doc: "Shots fired." }),
53
+ selected: u32(Plant.Thorn, { doc: "The plant the next placement sets (Plant.*)." }),
54
+ cursorX: i32(4, { doc: "The cursor's cell, a map column." }),
55
+ cursorY: i32(9, { doc: "The cursor's cell, a map row (0 the top)." }),
56
+ score: u32(0, { doc: "The last round's score: kills × 10 + light × 50 + coins left." }),
57
+ best: u32(0, { doc: "The best score, from the store." }),
58
+ newBest: bool(false),
59
+ bestRequest: u32(0, { doc: "The store request reading `best` (og.platform)." }),
60
+ bestRevision: u32(0, { doc: "The revision `best` was read or written at." }),
61
+ bestWrite: u32(0, { doc: "The store request writing `best`." }),
62
+ overTick: u32(0, { unit: "ticks", doc: "The tick the round ended." }),
63
+ lastStop: i32(-100, { unit: "ticks", doc: "The tick of the last kill's hit stop (at most one per 10 ticks, og-feel §3)." }),
64
+ occ0: u32(0, { doc: "Cells holding a visitor (bit per cell, row-major, 4 words): no plant goes there." }),
65
+ occ1: u32(0),
66
+ occ2: u32(0),
67
+ occ3: u32(0),
68
+ pendingPlants: u32(0, { doc: "Plants set on the pads layer that the Plants system has yet to switch on (the pads tile says which)." }),
69
+ reset: bool(false, { doc: "A new round asked the Plants and Visitors systems to clear their rows." }),
70
+ sfx: u32(0, { doc: "Sound.* bits waiting to play." }),
71
+ held: bool(false, { doc: "The time scale is held at 0 (paused or in the settings)." }),
72
+ viewW: f32(0, { unit: "cssPx", doc: "The surface the camera was last framed for." }),
73
+ viewH: f32(0, { unit: "cssPx" }),
74
+ viewTop: f32(0, { unit: "cssPx" }),
75
+ language: u32(0, { doc: "Prefs.language, mirrored for the facts." }),
76
+ grid: f64(0, { doc: "The og.nav grid of the field." }),
77
+ field: f64(0, { doc: "The og.nav flow field to the doorstep." }),
78
+ map: f64(0, { doc: "The field's og.Tilemap entity." }),
79
+ sheet: f64(0, { doc: "The sprite sheet's texture (cursor and range ring are drawn from it)." }),
80
+ ring: f64(0, { doc: "The range ring's texture." }),
81
+ });
82
+
83
+ /** The knobs. Tune reads the tunable fields; `set:Tuning.<field>=<value>` writes them. */
84
+ export const Tuning = defineResource("Tuning", {
85
+ startGold: u32(150, { min: 0, max: 1000, step: 10, tunable: true, label: "Starting coins" }),
86
+ startLives: u32(10, { min: 1, max: 50, step: 1, tunable: true, label: "Lantern light" }),
87
+ waveBreak: f32(8, { min: 0, max: 30, step: 0.5, unit: "s", tunable: true, label: "Break between waves" }),
88
+ spawnGap: f32(1, { min: 0.2, max: 4, step: 0.1, unit: "s", tunable: true, label: "Gap between visitors" }),
89
+ hpGrowth: f32(0.2, { min: 0, max: 1, step: 0.02, tunable: true, label: "Visitor toughness per wave", doc: "Each wave's visitors have 1 + growth × (wave − 1) times their base health." }),
90
+ thornCost: u32(50, { min: 0, max: 500, step: 5, tunable: true, label: "Thorn cost" }),
91
+ thornDamage: f32(14, { min: 1, max: 100, step: 1, tunable: true, label: "Thorn damage" }),
92
+ thornReload: u32(30, { min: 6, max: 240, step: 2, unit: "ticks", tunable: true, label: "Thorn reload" }),
93
+ thornRange: f32(2.8, { min: 1, max: 6, step: 0.1, unit: "m", tunable: true, label: "Thorn range" }),
94
+ frostCost: u32(70, { min: 0, max: 500, step: 5, tunable: true, label: "Frost lily cost" }),
95
+ frostDamage: f32(5, { min: 0, max: 100, step: 1, tunable: true, label: "Frost damage" }),
96
+ frostReload: u32(60, { min: 6, max: 240, step: 2, unit: "ticks", tunable: true, label: "Frost reload" }),
97
+ frostRange: f32(2.2, { min: 1, max: 6, step: 0.1, unit: "m", tunable: true, label: "Frost range" }),
98
+ frostSlow: f32(0.5, { min: 0.1, max: 1, step: 0.05, tunable: true, label: "Frost slow", doc: "A frosted visitor walks at this fraction of its speed." }),
99
+ frostTicks: u32(120, { min: 0, max: 600, step: 10, unit: "ticks", tunable: true, label: "Frost length" }),
100
+ hedgeCost: u32(10, { min: 0, max: 200, step: 5, tunable: true, label: "Hedge cost" }),
101
+ shotSpeed: f32(11, { min: 2, max: 40, step: 0.5, unit: "m/s", tunable: true, label: "Shot speed" }),
102
+ killStop: u32(2, { min: 0, max: 8, step: 1, unit: "ticks", tunable: true, label: "Kill hit stop" }),
103
+ leakStop: u32(6, { min: 0, max: 16, step: 1, unit: "ticks", tunable: true, label: "Leak hit stop" }),
104
+ leakShake: f32(0.4, { min: 0, max: 1, step: 0.05, tunable: true, label: "Leak shake" }),
105
+ });
106
+
107
+ /** The player's settings: kept in og.platform's settings, restored on the first ticks. */
108
+ export const Prefs = defineResource("Prefs", {
109
+ master: f32(1, { min: 0, max: 1, doc: "The master volume." }),
110
+ music: f32(0.7, { min: 0, max: 1, doc: "The music bus." }),
111
+ effects: f32(1, { min: 0, max: 1, doc: "The effects and UI buses." }),
112
+ language: u32(0, { doc: "0 English, 1 简体中文 (LANGUAGES in src/ui.ts)." }),
113
+ reducedMotion: bool(false, { doc: "Screens fade instead of irising; shakes are a third." }),
114
+ loadRequest: u32(0, { doc: "The og.platform.loadSettings request." }),
115
+ loaded: bool(false, { doc: "The saved settings were applied (or there were none)." }),
116
+ saves: u32(0, { doc: "Times the settings were saved." }),
117
+ });
118
+
119
+ /** One cell's plant: every buildable cell has a row, off until a plant is set there. */
120
+ export const Bed = defineComponent("Bed", {
121
+ cellX: i32(0, { doc: "The map column." }),
122
+ cellY: i32(0, { doc: "The map row (0 the top)." }),
123
+ planted: bool(false, { doc: "A plant stands here." }),
124
+ kind: u32(Plant.Thorn, { doc: "Plant.* of src/components.ts." }),
125
+ cool: u32(0, { unit: "ticks", doc: "Ticks to the next shot; the behaviour fires at 0 (assets/ai/*.ogt reads Bed.cool)." }),
126
+ anim: u32(0, { unit: "ticks", doc: "Ticks the shooting clip has left." }),
127
+ shots: u32(0, { doc: "Shots this plant fired." }),
128
+ });
129
+
130
+ /** A visitor (a creep): health, its walk and what turning it away pays. */
131
+ export const Visitor = defineComponent("Visitor", {
132
+ slot: u32(0, { doc: "Its pool index." }),
133
+ active: bool(false, { doc: "Walking the field." }),
134
+ dying: u32(0, { unit: "ticks", doc: "Ticks left of its pop before the row is parked." }),
135
+ kind: u32(Kind.Beetle, { doc: "Kind.* of src/components.ts." }),
136
+ hp: f32(0),
137
+ maxHp: f32(0),
138
+ speed: f32(0, { unit: "m/s", doc: "Its walking speed before frost." }),
139
+ slow: u32(0, { unit: "ticks", doc: "Ticks of frost left." }),
140
+ });
141
+
142
+ /** A shot in flight: a tween carries it, its tag says whom it hits. */
143
+ export const Shot = defineComponent("Shot", {
144
+ tween: f64(0, { doc: "Its flight's og.tween handle; the shot is free once that tween is done." }),
145
+ });
146
+
147
+ /** The cell cursor the keys and the pad move. */
148
+ export const Cursor = defineComponent("Cursor", {
149
+ moves: u32(0, { doc: "Times it moved." }),
150
+ });
151
+
152
+ /** One voice: plays its sound once when Game.sfx asks for its bit. */
153
+ export const Sfx = defineComponent("Sfx", {
154
+ bit: u32(0, { doc: "Sound.* of src/components.ts." }),
155
+ plays: u32(0, { doc: "Times it started." }),
156
+ });
@@ -0,0 +1,253 @@
1
+ // Path Guard, the tower-defense archetype (2D profile): a night garden,
2
+ // visitors coming through the gate in the hedge and walking to the
3
+ // lantern on the doorstep, and the plants a player sets on the lawn to
4
+ // turn them away. Thorns shoot, frost lilies slow, hedges only block, and
5
+ // every plant re-routes the visitors: they walk og.nav's flow field to the
6
+ // doorstep, which goes round whatever stands on the lawn. Five waves; the
7
+ // lantern's light runs out after ten visitors get through.
8
+ //
9
+ // `start` loads the field (assets/maps/garden.ogt: a tilemap whose hedges
10
+ // and stones block), makes the walking grid and the flow field from it,
11
+ // and spawns the pools: one Bed row per lawn cell (off until a plant is
12
+ // set), the visitors, the shots, the cursor, the lantern and one voice per
13
+ // sound. The systems are in src/systems.ts, the screens in src/ui.ts, the
14
+ // state in src/components.ts.
15
+
16
+ import { og } from "@origingame/engine";
17
+ import type { Entity, World } from "@origingame/engine";
18
+ import { checkClips, clipOf } from "./art.ts";
19
+ import { Bed, Cursor, Game, Phase, Prefs, Sfx, Shot, Sound, Tuning, Visitor } from "./components.ts";
20
+ import { buildable, cellCenter, COLS, Control, DOOR, endRound, fire, place, Plants, ROWS, SHOTS, Visitors, VISITORS, Voices, WAVES } from "./systems.ts";
21
+ import { LANGUAGES, startRound, Ui } from "./ui.ts";
22
+
23
+ /** The night behind the field, linear. */
24
+ const NIGHT = { r: 0.0047, g: 0.0137, b: 0.0091 };
25
+
26
+ function must<T>(r: { ok: true; value: T } | { ok: false; reason: string }): T;
27
+ function must(r: { ok: true } | { ok: false; reason: string }): void;
28
+ function must<T>(r: { ok: true; value?: T } | { ok: false; reason: string }): T | void {
29
+ if (!r.ok) throw new Error(r.reason);
30
+ return r.value;
31
+ }
32
+
33
+ function start(world: World): void {
34
+ for (const l of LANGUAGES) must(og.i18n.load(world, `strings/${l.code}.ogt`));
35
+ const skin = must(og.load.skin(world, "skins/pathguard/skin.ogt"));
36
+ const sound = (path: string) => must(og.load.sound(world, path));
37
+ must(
38
+ og.ui.configure(world, {
39
+ skin,
40
+ sounds: { press: sound("audio/ui-press.ogt"), open: sound("audio/ui-open.ogt"), close: sound("audio/ui-close.ogt"), error: sound("audio/ui-error.ogt"), success: sound("audio/ui-success.ogt") },
41
+ }),
42
+ );
43
+
44
+ const settings = must(world.resource(og.RenderSettings));
45
+ settings.environmentIntensity = 0;
46
+ settings.toneMapped = false;
47
+ const camera = must(world.resource(og.Camera));
48
+ camera.orthographic = true;
49
+ camera.height = 14;
50
+ camera.position = { x: COLS / 2, y: ROWS / 2, z: 50 };
51
+ camera.background = NIGHT;
52
+
53
+ const sheet = must(og.load.flipbook(world, "sprites/garden.ogt"));
54
+ checkClips(sheet);
55
+ const tilemap = must(og.load.tilemap(world, "maps/garden.ogt"));
56
+ const map = must(world.spawn({ name: "garden", position: { x: 0, y: tilemap.height * tilemap.tileHeight, z: 0 } }));
57
+ must(world.insert(map, og.Tilemap, { map: tilemap.map }));
58
+ // The walking grid from the hedges and stones, and the flow field every visitor follows to the doorstep.
59
+ const grid = must(og.nav.gridFromTilemap(world, { entity: map, layers: ["walls"] }));
60
+ const goal = cellCenter(DOOR.x, DOOR.y);
61
+ const field = must(og.nav.flowField(world, { grid, targets: [{ x: goal.x, y: goal.y, z: 0 }] }));
62
+ const mind = must(og.ai.load(world, "ai/plant.ogt"));
63
+ const pop = must(og.load.emitter(world, "fx/pop.ogt"));
64
+
65
+ const game = must(world.resource(Game));
66
+ game.grid = grid as unknown as number;
67
+ game.field = field as unknown as number;
68
+ game.map = map as unknown as number;
69
+ game.sheet = sheet.texture as unknown as number;
70
+ game.ring = must(og.load.texture(world, "sprites/ring.png")) as unknown as number;
71
+
72
+ const sprite = (e: Entity, clip: string, layer: number, visible: boolean, size = 1) => {
73
+ must(world.insert(e, og.Sprite, { texture: sheet.texture, width: size, height: size, layer, visible, sortY: true }));
74
+ must(world.insert(e, og.Flipbook, { sheet: sheet.sheet, clip: clipOf(clip) }));
75
+ };
76
+
77
+ // One bed per lawn cell: a plant there is this row switched on.
78
+ for (let y = 1; y < ROWS - 1; y++) {
79
+ for (let x = 1; x < COLS - 1; x++) {
80
+ const c = cellCenter(x, y);
81
+ const bed = must(world.spawn({ name: `bed-${x}-${y}`, position: { x: c.x, y: c.y, z: 0 } }));
82
+ sprite(bed, "thorn", 1, false);
83
+ must(world.insert(bed, Bed, { cellX: x, cellY: y }));
84
+ must(world.insert(bed, og.AiAgent, { behaviour: mind.behaviour, active: false }));
85
+ must(world.insert(bed, og.Perception, { sight: 0, fov: 360, occlude: false, team: 1, memory: 0 }));
86
+ }
87
+ }
88
+ for (let i = 0; i < VISITORS; i++) {
89
+ const v = must(world.spawn({ name: `visitor-${i}`, position: { x: -40, y: -40, z: 0 } }));
90
+ sprite(v, "beetle", 2, false, 0.9);
91
+ must(world.insert(v, Visitor, { slot: i }));
92
+ must(world.insert(v, og.NavAgent, { nav: field, target: { x: goal.x, y: goal.y, z: 0 }, seeking: false, speed: 1, acceleration: 30, radius: 0.3, stopDistance: 0.2, slowRadius: 0, faceMovement: true, turnSpeed: 14 }));
93
+ must(world.insert(v, og.Perceivable, { team: 2 }));
94
+ must(world.insert(v, og.ParticleSystem, { emitter: pop, seed: i + 1, visible: false }));
95
+ }
96
+ for (let i = 0; i < SHOTS; i++) {
97
+ const s = must(world.spawn({ name: `shot-${i}`, position: { x: -40, y: -40, z: 0 } }));
98
+ sprite(s, "seed", 5, true, 0.6);
99
+ must(world.set(s, og.Sprite, { opacity: 0, sortY: false }));
100
+ must(world.insert(s, Shot));
101
+ }
102
+ const lantern = must(world.spawn({ name: "lantern", position: { x: goal.x, y: goal.y + 0.1, z: 0 } }));
103
+ sprite(lantern, "lantern", 3, true, 1.1);
104
+ // The night: a pale glow behind the field and fireflies over it.
105
+ const glow = must(world.spawn({ name: "moonlight", position: { x: COLS / 2, y: ROWS / 2, z: 0 } }));
106
+ must(world.insert(glow, og.Sprite, { texture: must(og.load.texture(world, "sprites/glow.png")), width: 24, height: 24, color: { r: 0.45, g: 0.75, b: 0.55 }, opacity: 0.22, layer: -40 }));
107
+ const flies = must(world.spawn({ name: "fireflies", position: { x: COLS / 2, y: ROWS / 2, z: 0 } }));
108
+ must(world.insert(flies, og.ParticleSystem, { emitter: must(og.load.emitter(world, "fx/fireflies.ogt")), seed: 7 }));
109
+ const cursor = must(world.spawn({ name: "cursor", position: { x: -40, y: -40, z: 0 } }));
110
+ must(world.insert(cursor, Cursor));
111
+
112
+ // The voices: one per sound, played by Voices; the music loops on its bus.
113
+ const voices: [string, number, "sfx" | "ui", number][] = [
114
+ ["audio/plant.wav", Sound.Plant, "ui", 0.7],
115
+ ["audio/thorn.wav", Sound.Thorn, "sfx", 0.35],
116
+ ["audio/frost.wav", Sound.Frost, "sfx", 0.35],
117
+ ["audio/pop.wav", Sound.Pop, "sfx", 0.6],
118
+ ["audio/leak.wav", Sound.Leak, "sfx", 0.8],
119
+ ["audio/wave.wav", Sound.Wave, "sfx", 0.6],
120
+ ["audio/clear.wav", Sound.Clear, "sfx", 0.6],
121
+ ["audio/won.wav", Sound.Won, "sfx", 0.7],
122
+ ["audio/lost.wav", Sound.Lost, "sfx", 0.7],
123
+ ["audio/ui-press.ogt", Sound.Move, "ui", 0.25],
124
+ ];
125
+ for (const [path, bit, bus, volume] of voices) {
126
+ const v = must(world.spawn({ name: `sfx-${bit}` }));
127
+ must(world.insert(v, og.AudioSource, { sound: sound(path), playing: false, bus, volume, priority: 160 }));
128
+ must(world.insert(v, Sfx, { bit }));
129
+ }
130
+ const music = must(world.spawn({ name: "music" }));
131
+ must(world.insert(music, og.AudioSource, { sound: sound("audio/night.wav"), bus: "music", loop: true, volume: 0.55, priority: 220 }));
132
+ // The music ducks under the hits and the waves (og-audio §4).
133
+ const fx = must(world.spawn({ name: "music-bus" }));
134
+ must(world.insert(fx, og.AudioBusFx, { bus: "music", duck: true, duckBus: "sfx", duckDepth: 6, duckRelease: 0.5 }));
135
+ }
136
+
137
+ function gameOf(world: World) {
138
+ return must(world.resource(Game));
139
+ }
140
+
141
+ function num(s: string | undefined, what: string): number {
142
+ const n = Number(s);
143
+ if (s === undefined || !Number.isFinite(n)) throw new Error(`${what} is a number`);
144
+ return n;
145
+ }
146
+
147
+ /** `begin`: start a round now, as Play on the title does. */
148
+ function begin(world: World): void {
149
+ startRound(world, gameOf(world), must(world.resource(Tuning)));
150
+ }
151
+
152
+ /** `plant:<thorn|frost|hedge>:<x>:<y>`: set a plant on a map cell (column, row from the top) as a placement does, refusals included. */
153
+ function plantCmd(world: World, args: readonly string[]): void {
154
+ const kind = ["thorn", "frost", "hedge"].indexOf(args[0] ?? "");
155
+ if (kind < 0) throw new Error("write plant:<thorn|frost|hedge>:<x>:<y>");
156
+ const g = gameOf(world);
157
+ g.selected = kind;
158
+ place(world, g, must(world.resource(Tuning)), num(args[1], "x"), num(args[2], "y"));
159
+ }
160
+
161
+ /** `gold:<n>`: set the purse. */
162
+ function gold(world: World, args: readonly string[]): void {
163
+ gameOf(world).gold = num(args[0], "n");
164
+ }
165
+
166
+ /** `wave:<n>`: skip to the break before wave n (the field and the purse stay). */
167
+ function wave(world: World, args: readonly string[]): void {
168
+ const g = gameOf(world);
169
+ const n = Math.max(1, Math.min(WAVES.length, num(args[0], "n")));
170
+ g.wave = n - 1;
171
+ g.inWave = false;
172
+ g.toSpawn = 0;
173
+ g.breakTicks = Math.max(1, Math.round(must(world.resource(Tuning)).waveBreak * 60));
174
+ }
175
+
176
+ /** `lose`: put the lantern out (the round ends lost on the next tick). */
177
+ function lose(world: World): void {
178
+ const g = gameOf(world);
179
+ if (g.phase === Phase.Play) g.lives = 0;
180
+ }
181
+
182
+ /** `win`: end the round as won now. */
183
+ function win(world: World): void {
184
+ const g = gameOf(world);
185
+ if (g.phase === Phase.Play) endRound(world, g, true);
186
+ }
187
+
188
+ /** `viewport:<desktop|phone>`: lay the UI out for that surface (og.ui.previewViewport). */
189
+ function viewport(world: World, args: readonly string[]): void {
190
+ const phone = { width: 390, height: 844, dpr: 3, safeArea: { left: 0, top: 47, right: 0, bottom: 34 }, coarsePointer: true, breakpoint: "phonePortrait" } as const;
191
+ og.ui.previewViewport(world, args[0] === "phone" ? phone : null);
192
+ }
193
+
194
+ /** `language:<en|zh-CN>`: switch the language as the settings do. */
195
+ function language(world: World, args: readonly string[]): void {
196
+ const p = must(world.resource(Prefs));
197
+ const at = LANGUAGES.findIndex((l) => l.code === args[0]);
198
+ if (at < 0) throw new Error(`write language:<${LANGUAGES.map((l) => l.code).join("|")}>`);
199
+ p.language = at;
200
+ og.i18n.setLocale(world, LANGUAGES[at]!.code);
201
+ }
202
+
203
+ export default og.game({
204
+ tick: 60,
205
+ seed: 1,
206
+ resources: [Game, Tuning, Prefs],
207
+ systems: [Control, Plants, Visitors, Ui, Voices],
208
+ render: { antialias: "off" },
209
+ start,
210
+ commands: { fire, begin, plant: plantCmd, gold, wave, lose, win, viewport, language },
211
+ commandHelp: {
212
+ fire: { usage: "<plant>", doc: "The plants' behaviour command (og.ai queues it with the plant's handle): a shot at what it sees." },
213
+ begin: { usage: "", doc: "Start a round now, as Play on the title does." },
214
+ plant: { usage: "<thorn|frost|hedge>:<x>:<y>", doc: "Set a plant on a map cell (column, row from the top) as a placement does, refusals included." },
215
+ gold: { usage: "<n>", doc: "Set the purse." },
216
+ wave: { usage: "<n>", doc: "Skip to the break before wave n (the field and the purse stay)." },
217
+ lose: { usage: "", doc: "Put the lantern out: the round ends lost." },
218
+ win: { usage: "", doc: "End the round as won now." },
219
+ viewport: { usage: "<desktop|phone>", doc: "Lay the UI out for a phone in portrait (390×844 at 3×) or back for the surface." },
220
+ language: { usage: "<en|zh-CN>", doc: "Switch the language as the settings do." },
221
+ },
222
+ facts: (world) => {
223
+ const game = world.resource(Game);
224
+ if (!game.ok) return null;
225
+ const g = game.value;
226
+ return {
227
+ phase: ["title", "play", "over"][g.phase],
228
+ paused: g.paused,
229
+ settings: g.settingsOpen,
230
+ wave: g.wave,
231
+ waves: WAVES.length,
232
+ inWave: g.inWave,
233
+ lives: g.lives,
234
+ gold: g.gold,
235
+ kills: g.kills,
236
+ leaks: g.leaks,
237
+ placed: g.placed,
238
+ refused: g.refused,
239
+ refusal: ["none", "poor", "blocked", "taken"][g.refusal],
240
+ shots: g.shots,
241
+ walking: g.walking,
242
+ selected: ["thorn", "frost", "hedge"][g.selected],
243
+ cursor: { x: g.cursorX, y: g.cursorY },
244
+ cursorBuildable: buildable(world, g, g.cursorX, g.cursorY),
245
+ won: g.won,
246
+ score: g.score,
247
+ best: g.best,
248
+ newBest: g.newBest,
249
+ rounds: g.rounds,
250
+ language: LANGUAGES[g.language]!.code,
251
+ };
252
+ },
253
+ });