@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,603 @@
1
+ #!/usr/bin/env python3
2
+ """Regenerates the runner archetype's self-made assets (CC0), byte for byte:
3
+
4
+ python3 tools/make-assets.py # from the archetype directory
5
+
6
+ - assets/sprites/*.png: pixel-art sheets (`kind pixel`: lossless, no mips,
7
+ sampled nearest) with their flipbook sheets (`.ogt`, `type flipbook`):
8
+ the courier (idle, an 8-frame run with footfall events, jump, fall and a
9
+ crash), the props (crate, fence, moon, cloud) and the spinning coin,
10
+ - assets/sprites/sky.png: the night sky's colour bands,
11
+ - assets/tiles/city.png + city.ogt: the city tileset (roofs, near and far
12
+ buildings, lit and dark windows, a blinking window and antenna light),
13
+ - assets/maps/{far,near,roofs}.ogt: the three parallax strips as tilemaps,
14
+ - assets/audio/*.wav: jump, coin, crash and start sounds,
15
+ - assets/fonts/ui.ttf: Noto Sans SC subset to Latin-1 and the game's
16
+ strings (needs fontTools and the project template's font; skipped with a
17
+ note when either is missing, since the committed subset is already there).
18
+
19
+ Standard library only except the font subset; deterministic. Pixel art is
20
+ drawn texel by texel (`Pix`), so nearest sampling shows clean pixels.
21
+ """
22
+ import math, os, sys
23
+
24
+ here = os.path.dirname(os.path.abspath(__file__))
25
+ sys.path.insert(0, here)
26
+ from oglite import hex_rgb, png_bytes, write_wav, tone, noise, mix, concat, subset_font # noqa: E402
27
+
28
+ root = os.path.dirname(here)
29
+ assets = os.path.join(root, "assets")
30
+
31
+ INK = "#14161b"
32
+ PIXEL_IMPORT = """type import
33
+ version 1
34
+ ---
35
+ # Written by tools/make-assets.py: pixel art, lossless, sampled nearest.
36
+ texture {
37
+ kind pixel
38
+ }
39
+ """
40
+
41
+
42
+ class Pix:
43
+ """A pixel grid: every texel opaque or transparent, no antialiasing."""
44
+
45
+ def __init__(self, w, h):
46
+ self.w, self.h = w, h
47
+ self.px = [None] * (w * h)
48
+
49
+ def set(self, x, y, col):
50
+ x, y = int(math.floor(x)), int(math.floor(y))
51
+ if 0 <= x < self.w and 0 <= y < self.h:
52
+ self.px[y * self.w + x] = col
53
+
54
+ def get(self, x, y):
55
+ return self.px[y * self.w + x] if 0 <= x < self.w and 0 <= y < self.h else None
56
+
57
+ def rect(self, x, y, w, h, col):
58
+ for yy in range(int(round(y)), int(round(y + h))):
59
+ for xx in range(int(round(x)), int(round(x + w))):
60
+ self.set(xx, yy, col)
61
+
62
+ def disc(self, cx, cy, r, col, ry=None):
63
+ ry = r if ry is None else ry
64
+ for yy in range(int(cy - ry - 1), int(cy + ry + 2)):
65
+ for xx in range(int(cx - r - 1), int(cx + r + 2)):
66
+ dx = (xx + 0.5 - cx) / max(r, 0.01)
67
+ dy = (yy + 0.5 - cy) / max(ry, 0.01)
68
+ if dx * dx + dy * dy <= 1.0:
69
+ self.set(xx, yy, col)
70
+
71
+ def line(self, x0, y0, x1, y1, col, width=1):
72
+ n = int(max(abs(x1 - x0), abs(y1 - y0)) * 2) + 1
73
+ for i in range(n + 1):
74
+ t = i / n
75
+ x = x0 + (x1 - x0) * t
76
+ y = y0 + (y1 - y0) * t
77
+ self.rect(round(x - width / 2), round(y - width / 2), width, width, col)
78
+
79
+ def blit(self, other, ox, oy):
80
+ for y in range(other.h):
81
+ for x in range(other.w):
82
+ c = other.px[y * other.w + x]
83
+ if c is not None:
84
+ self.set(ox + x, oy + y, c)
85
+
86
+ def outline(self, col):
87
+ """A one-texel outline around every opaque region (readable silhouettes on a dark sky)."""
88
+ add = []
89
+ for y in range(self.h):
90
+ for x in range(self.w):
91
+ if self.px[y * self.w + x] is not None:
92
+ continue
93
+ for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)):
94
+ c = self.get(x + dx, y + dy)
95
+ if c is not None and c != col:
96
+ add.append((x, y))
97
+ break
98
+ for x, y in add:
99
+ self.set(x, y, col)
100
+
101
+ def png(self):
102
+ rows = []
103
+ for y in range(self.h):
104
+ row = bytearray()
105
+ for x in range(self.w):
106
+ c = self.px[y * self.w + x]
107
+ row += (bytes(hex_rgb(c)) + b"\xff") if c is not None else b"\x00\x00\x00\x00"
108
+ rows.append(bytes(row))
109
+ return png_bytes(self.w, self.h, rows)
110
+
111
+
112
+ SPACING = 2 # texels between frames, so a frame never samples its neighbour
113
+
114
+
115
+ def sheet(frames, fw, fh, cols):
116
+ rows = (len(frames) + cols - 1) // cols
117
+ s = Pix(fw * cols + SPACING * (cols - 1), fh * rows + SPACING * (rows - 1))
118
+ for i, f in enumerate(frames):
119
+ s.blit(f, (i % cols) * (fw + SPACING), (i // cols) * (fh + SPACING))
120
+ return s
121
+
122
+
123
+ # ── the courier: 32 x 40 frames, feet at the bottom row ────────────────────
124
+
125
+ FW, FH = 32, 40
126
+ SKIN, JACKET, DARK, SCARF, HELMET, SHOE, BAG, STRAP, PANTS, PANTS_DARK = (
127
+ "#f0c39a", "#2fa59a", "#1d6f68", "#ff7a3d", "#f4f4f4", "#2a2433", "#c98b3f", "#8a5a24", "#3b3550", "#2c2740")
128
+ SLEEVE = "#5fd8c9"
129
+
130
+
131
+ def limb(p, hip, a1, a2, l1, l2, col, w=3):
132
+ """A two-segment limb from `hip`: angles from straight down, positive forward (+x)."""
133
+ k = (hip[0] + l1 * math.sin(a1), hip[1] + l1 * math.cos(a1))
134
+ f = (k[0] + l2 * math.sin(a2), k[1] + l2 * math.cos(a2))
135
+ p.line(hip[0], hip[1], k[0], k[1], col, w)
136
+ p.line(k[0], k[1], f[0], f[1], col, w)
137
+ return f
138
+
139
+
140
+ def courier(legs, arms, bob=0, crash=False, lean=0):
141
+ """legs/arms: ((upper, lower) back, (upper, lower) front), radians from straight down."""
142
+ p = Pix(FW, FH)
143
+ hip = (14 + lean * 0.3, 25 + bob)
144
+ sh = (15 + lean, 15 + bob)
145
+ f = limb(p, hip, legs[0][0], legs[0][1], 7, 7, PANTS_DARK)
146
+ p.rect(f[0] - 1, f[1] - 1, 4, 2, SHOE)
147
+ limb(p, (sh[0], sh[1] + 1), arms[0][0], arms[0][1], 4, 5, DARK, 2)
148
+ # scarf trailing back, the mail bag on its strap
149
+ p.rect(sh[0] - 9, sh[1] - 1, 8, 2, SCARF)
150
+ p.rect(sh[0] - 12, sh[1] + 1, 4, 2, SCARF)
151
+ p.rect(sh[0] - 7, sh[1] + 4, 6, 6, BAG)
152
+ p.rect(sh[0] - 7, sh[1] + 4, 6, 1, "#e0a85a")
153
+ # torso
154
+ p.rect(sh[0] - 3, sh[1] - 1, 8, 11, JACKET)
155
+ p.rect(sh[0] - 3, sh[1] - 1, 2, 11, DARK)
156
+ p.line(sh[0] - 3, sh[1] + 5, sh[0] + 4, sh[1] - 1, STRAP)
157
+ p.rect(sh[0] - 3, sh[1] + 9, 8, 2, PANTS)
158
+ f = limb(p, hip, legs[1][0], legs[1][1], 7, 7, PANTS)
159
+ p.rect(f[0] - 1, f[1] - 1, 4, 2, SHOE)
160
+ h = limb(p, (sh[0] + 2, sh[1] + 1), arms[1][0], arms[1][1], 4, 5, SLEEVE, 2)
161
+ p.rect(h[0] - 1, h[1] - 1, 2, 2, SKIN)
162
+ # head: face, helmet, visor, scarf at the neck
163
+ hx, hy = sh[0] + 2, sh[1] - 6
164
+ p.disc(hx, hy, 4.5, SKIN)
165
+ p.rect(hx - 5, hy - 5, 9, 3, HELMET)
166
+ p.rect(hx - 4, hy - 6, 7, 1, HELMET)
167
+ p.rect(hx + 1, hy - 3, 4, 1, "#c8d4e0")
168
+ p.rect(hx + 2, hy - 1, 1, 1, SHOE)
169
+ p.rect(sh[0] - 3, sh[1] - 2, 8, 2, SCARF)
170
+ if crash:
171
+ for sx, sy in ((hx - 7, hy - 10), (hx + 3, hy - 12), (hx + 8, hy - 7)):
172
+ p.rect(sx, sy, 1, 3, "#ffd23f")
173
+ p.rect(sx - 1, sy + 1, 3, 1, "#ffd23f")
174
+ p.outline(INK)
175
+ return p
176
+
177
+
178
+ def run_frame(k):
179
+ """Frame k of 8: legs and arms swing in opposite phases; contact on frames 2 and 6."""
180
+ ph = 2 * math.pi * k / 8
181
+
182
+ def leg(off):
183
+ s = math.sin(ph + off)
184
+ thigh = 0.8 * s
185
+ bend = 0.2 + 1.2 * max(0.0, -math.cos(ph + off))
186
+ return (thigh, thigh - bend)
187
+
188
+ def arm(off):
189
+ s = math.sin(ph + off)
190
+ return (-0.8 * s, -0.8 * s - 1.2)
191
+
192
+ bob = 0 if k % 4 == 2 else -1
193
+ return courier((leg(math.pi), leg(0)), (arm(0), arm(math.pi)), bob=bob, lean=1)
194
+
195
+
196
+ FRAMES = (
197
+ [run_frame(k) for k in range(8)] # 0-7 run
198
+ + [courier(((-0.7, -1.7), (1.3, 0.1)), ((-1.0, -2.4), (2.2, 2.8)), bob=-2, lean=1)] # 8 jump (rising)
199
+ + [courier(((-0.2, -0.9), (0.7, 0.0)), ((-2.2, -2.6), (2.4, 2.8)), bob=-1)] # 9 fall
200
+ + [courier(((-1.3, -1.7), (1.2, 1.5)), ((-1.6, -1.1), (1.7, 1.1)), crash=True, lean=l, bob=6) # 10-13 crash
201
+ for l in (-3, -2, -1, -1)]
202
+ + [courier(((0.08, 0.0), (-0.08, 0.0)), ((0.15, -0.1), (-0.15, 0.1)), bob=b) for b in (0, -1)] # 14-15 idle
203
+ )
204
+
205
+ COURIER_OGT = """type flipbook
206
+ version 1
207
+ ---
208
+ # The courier (tools/make-assets.py): 32 x 40 frames standing on their feet
209
+ # (the pivot: the entity's position is the feet, so a squash keeps them down).
210
+ texture "courier.png"
211
+ pivot 0.5 0
212
+ grid 32 40 8 2
213
+ spacing 2
214
+ clip idle {
215
+ frames 14 15
216
+ fps 2
217
+ }
218
+ clip run {
219
+ frames 0 1 2 3 4 5 6 7
220
+ fps 14
221
+ event 2 "step"
222
+ event 6 "step"
223
+ }
224
+ clip jump {
225
+ frames 8
226
+ fps 1
227
+ }
228
+ clip fall {
229
+ frames 9
230
+ fps 1
231
+ }
232
+ clip crash {
233
+ frames 10 11 12 13
234
+ fps 12
235
+ mode once
236
+ }
237
+ """
238
+
239
+
240
+ # ── props: crate, fence, moon, cloud (one sheet) and the coin ──────────────
241
+
242
+ def crate():
243
+ p = Pix(16, 16)
244
+ p.rect(0, 0, 16, 16, "#7a4a2a")
245
+ p.rect(2, 2, 12, 12, "#b8743f")
246
+ p.line(2.5, 2.5, 13, 13, "#7a4a2a", 2)
247
+ p.line(13, 2.5, 2.5, 13, "#7a4a2a", 2)
248
+ p.rect(2, 2, 12, 1, "#d08f55")
249
+ p.rect(0, 15, 16, 1, "#5a341c")
250
+ return p
251
+
252
+
253
+ def fence():
254
+ p = Pix(16, 32)
255
+ p.rect(2, 2, 2, 30, "#6b6f80")
256
+ p.rect(12, 2, 2, 30, "#6b6f80")
257
+ for i, y in enumerate((4, 14, 24)):
258
+ p.rect(0, y, 16, 5, "#f2f2f2")
259
+ for x in range(16):
260
+ if ((x + (y - y) + i * 2) // 3) % 2 == 0:
261
+ for d in range(5):
262
+ p.set((x + d) % 16, y + 4 - d, "#e8453c")
263
+ p.rect(6, 0, 4, 2, "#ffb020")
264
+ p.outline(INK)
265
+ return p
266
+
267
+
268
+ def moon():
269
+ p = Pix(32, 32)
270
+ p.disc(16, 16, 14, "#fff3c4")
271
+ p.disc(11, 12, 2.5, "#ead9a2")
272
+ p.disc(20, 20, 3.5, "#ead9a2")
273
+ p.disc(21, 10, 1.5, "#ead9a2")
274
+ return p
275
+
276
+
277
+ def cloud():
278
+ p = Pix(64, 24)
279
+ for x, y, r in ((14, 15, 7), (25, 11, 9), (38, 12, 8), (49, 15, 6), (32, 17, 7)):
280
+ p.disc(x, y, r, "#2e2a55")
281
+ p.rect(8, 15, 48, 7, "#2e2a55")
282
+ for x, y, r in ((25, 10, 7), (38, 11, 6)):
283
+ p.disc(x, y - 1, r, "#3b3566")
284
+ return p
285
+
286
+
287
+ def props_sheet():
288
+ s = Pix(72, 60)
289
+ s.blit(crate(), 0, 0)
290
+ s.blit(fence(), 18, 0)
291
+ s.blit(moon(), 36, 0)
292
+ s.blit(cloud(), 0, 36)
293
+ return s
294
+
295
+
296
+ PROPS_OGT = """type flipbook
297
+ version 1
298
+ ---
299
+ # Props (tools/make-assets.py): one frame each, 2 texels apart.
300
+ texture "props.png"
301
+ frame crate 0 0 16 16
302
+ frame fence 18 0 16 32
303
+ frame moon 36 0 32 32
304
+ frame cloud 0 36 64 24
305
+ clip crate {
306
+ frames crate
307
+ }
308
+ clip fence {
309
+ frames fence
310
+ }
311
+ clip moon {
312
+ frames moon
313
+ }
314
+ clip cloud {
315
+ frames cloud
316
+ }
317
+ """
318
+
319
+
320
+ def coin(k):
321
+ """Spin frame k of 6: the coin's width follows |cos|."""
322
+ p = Pix(16, 16)
323
+ w = max(1.0, 6.5 * abs(math.cos(math.pi * k / 6)))
324
+ if w > 2:
325
+ p.disc(8, 8, w, "#b8860b", ry=6.5)
326
+ p.disc(8, 8, w - 1.5, "#ffd23f", ry=5)
327
+ p.rect(8 - max(1, int(w / 3)), 5, max(1, int(w / 3)), 6, "#fff3b0")
328
+ else:
329
+ p.rect(7, 2, 2, 12, "#ffd23f")
330
+ p.rect(7, 2, 1, 12, "#b8860b")
331
+ p.outline(INK)
332
+ return p
333
+
334
+
335
+ COIN_OGT = """type flipbook
336
+ version 1
337
+ ---
338
+ # The coin (tools/make-assets.py): six frames of a turn.
339
+ texture "coin.png"
340
+ grid 16 16 6 1
341
+ spacing 2
342
+ clip spin {
343
+ frames 0 1 2 3 4 5
344
+ fps 10
345
+ }
346
+ """
347
+
348
+
349
+ def sky():
350
+ """Flat bands from night to dusk, one texel wide."""
351
+ bands = ["#0b0e24", "#0f122b", "#141634", "#1a1a3e", "#211d47", "#2a2151", "#34255a", "#3f2a5f"]
352
+ p = Pix(1, len(bands))
353
+ for k, col in enumerate(bands):
354
+ p.set(0, k, col)
355
+ return p
356
+
357
+
358
+ # ── the city tileset: 16 x 16 tiles, 4 x 4 ────────────────────────────────
359
+
360
+ ROOF, ROOF_LIP, BRICK, MORTAR = "#5ec4b6", "#3a8f86", "#2b2540", "#352e4f"
361
+ NEAR, NEAR_EDGE = "#191432", "#272048"
362
+ FAR, FAR_EDGE = "#29234f", "#332c5e"
363
+ LIT, LIT2, COOL, DARKWIN = "#f7c76b", "#ffd98a", "#7fd1e8", "#100d22"
364
+
365
+
366
+ def bricks(p, rows):
367
+ for y, off in rows:
368
+ for x in range(-8 + off, 16, 8):
369
+ p.rect(x + 1, y, 6, 3, MORTAR)
370
+
371
+
372
+ def tile(kind):
373
+ p = Pix(16, 16)
374
+ if kind == "roof": # 0: the roof's teal cap over bricks
375
+ p.rect(0, 0, 16, 16, BRICK)
376
+ p.rect(0, 0, 16, 3, ROOF)
377
+ p.rect(0, 3, 16, 1, ROOF_LIP)
378
+ p.rect(3, 1, 2, 1, "#8fe0d4")
379
+ bricks(p, ((6, 0), (10, 4), (14, 0)))
380
+ elif kind in ("brick", "brick_lit", "brick_dark"): # 1-3
381
+ p.rect(0, 0, 16, 16, BRICK)
382
+ bricks(p, ((1, 0), (5, 4), (9, 0), (13, 4)))
383
+ if kind != "brick":
384
+ p.rect(4, 3, 8, 9, INK)
385
+ p.rect(5, 4, 6, 7, LIT if kind == "brick_lit" else DARKWIN)
386
+ p.rect(8, 4, 1, 7, INK)
387
+ elif kind == "far": # 4
388
+ p.rect(0, 0, 16, 16, FAR)
389
+ elif kind == "far_win": # 5: lit specks
390
+ p.rect(0, 0, 16, 16, FAR)
391
+ p.rect(3, 4, 2, 2, LIT)
392
+ p.rect(10, 9, 2, 2, COOL)
393
+ p.rect(11, 2, 2, 2, LIT2)
394
+ elif kind == "far_top": # 6: a stepped top
395
+ p.rect(0, 6, 16, 10, FAR)
396
+ p.rect(4, 2, 8, 4, FAR)
397
+ p.rect(0, 6, 4, 1, FAR_EDGE)
398
+ p.rect(12, 6, 4, 1, FAR_EDGE)
399
+ p.rect(4, 2, 8, 1, FAR_EDGE)
400
+ elif kind in ("antenna", "antenna_on"): # 7, 15: light off and on
401
+ p.rect(7, 3, 2, 13, FAR)
402
+ p.rect(4, 12, 8, 4, FAR)
403
+ p.rect(6, 0, 4, 3, "#ff4a3d" if kind == "antenna_on" else "#4a1d2a")
404
+ elif kind == "near": # 8
405
+ p.rect(0, 0, 16, 16, NEAR)
406
+ elif kind in ("near_lit", "near_dark", "near_lit2"): # 9, 10, 14
407
+ p.rect(0, 0, 16, 16, NEAR)
408
+ a = {"near_lit": (LIT, DARKWIN, DARKWIN, LIT2), "near_dark": (DARKWIN, DARKWIN, DARKWIN, DARKWIN),
409
+ "near_lit2": (LIT2, LIT, DARKWIN, LIT)}[kind]
410
+ for (x, y), col in zip(((3, 3), (9, 3), (3, 10), (9, 10)), a):
411
+ p.rect(x, y, 4, 4, col)
412
+ elif kind == "near_top": # 11: cornice
413
+ p.rect(0, 4, 16, 12, NEAR)
414
+ p.rect(0, 3, 16, 2, NEAR_EDGE)
415
+ elif kind == "tank": # 12: a water tank on legs
416
+ p.rect(3, 2, 10, 8, NEAR_EDGE)
417
+ p.rect(2, 1, 12, 2, NEAR_EDGE)
418
+ p.rect(7, 0, 2, 1, NEAR_EDGE)
419
+ p.rect(4, 10, 1, 6, NEAR_EDGE)
420
+ p.rect(11, 10, 1, 6, NEAR_EDGE)
421
+ p.line(4, 15, 11, 10, NEAR_EDGE)
422
+ elif kind == "sign": # 13: a neon sign
423
+ p.rect(0, 0, 16, 16, NEAR)
424
+ p.rect(2, 4, 12, 8, "#ff4fa3")
425
+ p.rect(3, 5, 10, 6, "#3a1033")
426
+ p.rect(5, 7, 6, 2, "#ff8fd0")
427
+ return p
428
+
429
+
430
+ TILES = ["roof", "brick", "brick_lit", "brick_dark", "far", "far_win", "far_top", "antenna",
431
+ "near", "near_lit", "near_dark", "near_top", "tank", "sign", "near_lit2", "antenna_on"]
432
+
433
+ CITY_OGT = """type tileset
434
+ version 1
435
+ ---
436
+ # The city (tools/make-assets.py): 0 roof, 1-3 bricks, 4-7 far skyline,
437
+ # 8-14 near buildings, 15 the antenna's light on.
438
+ texture "tiles/city.png"
439
+ grid 16 16 4 4
440
+ spacing 2
441
+ filter nearest
442
+ # A window that goes on and off, and an antenna light that blinks.
443
+ tile 9 {
444
+ frames 9 14 9 10
445
+ durations 2.5 0.4 1.8 0.6
446
+ }
447
+ tile 7 {
448
+ frames 7 15
449
+ durations 0.9 0.5
450
+ }
451
+ """
452
+
453
+
454
+ def city_sheet():
455
+ """4 x 4 tiles, SPACING apart, each edge extruded into the gap (a tile never samples its neighbour)."""
456
+ step = 16 + SPACING
457
+ s = Pix(4 * step - SPACING, 4 * step - SPACING)
458
+ for i, k in enumerate(TILES):
459
+ t = tile(k)
460
+ ox, oy = (i % 4) * step, (i // 4) * step
461
+ for y in range(-1, 17):
462
+ for x in range(-1, 17):
463
+ c = t.get(min(15, max(0, x)), min(15, max(0, y)))
464
+ if c is not None:
465
+ s.set(ox + x, oy + y, c)
466
+ return s
467
+
468
+
469
+ # ── the parallax strips (tilemaps) ─────────────────────────────────────────
470
+
471
+ def far_rows():
472
+ w, h = 40, 9
473
+ heights = [3, 5, 5, 4, 7, 7, 7, 3, 4, 6, 6, 2, 5, 8, 8, 5, 4, 4, 6, 3,
474
+ 3, 7, 7, 7, 5, 4, 6, 6, 3, 5, 5, 8, 4, 4, 6, 6, 3, 2, 5, 5]
475
+ rows = []
476
+ for y in range(h):
477
+ level = h - 1 - y
478
+ row = ""
479
+ for x in range(w):
480
+ top = heights[x]
481
+ if level < top - 1:
482
+ row += "w" if (x * 7 + level * 3) % 5 == 0 else "#"
483
+ elif level == top - 1:
484
+ row += "^"
485
+ elif level == top and top >= 7 and x % 3 == 1:
486
+ row += "a"
487
+ else:
488
+ row += "."
489
+ rows.append(row)
490
+ return w, h, rows
491
+
492
+
493
+ def near_rows():
494
+ w, h = 24, 6
495
+ heights = [3, 3, 3, 0, 5, 5, 5, 5, 0, 0, 4, 4, 4, 4, 4, 0, 6, 6, 6, 0, 3, 3, 3, 3]
496
+ rows = []
497
+ for y in range(h):
498
+ level = h - 1 - y
499
+ row = ""
500
+ for x in range(w):
501
+ top = heights[x]
502
+ if top == 0:
503
+ row += "t" if level == 0 and x == 9 else "."
504
+ elif level < top - 1:
505
+ k = (x * 5 + level * 7) % 6
506
+ row += "s" if (x == 12 and level == 2) else ("L" if k == 0 else "l" if k in (1, 4) else "d" if k == 2 else "n")
507
+ elif level == top - 1:
508
+ row += "c"
509
+ else:
510
+ row += "."
511
+ rows.append(row)
512
+ return w, h, rows
513
+
514
+
515
+ def roof_rows():
516
+ w = 16
517
+ windows = "".join("L" if x % 5 == 1 else "D" if x % 5 == 3 else "b" for x in range(w))
518
+ return w, 3, ["=" * w, windows, "b" * w]
519
+
520
+
521
+ def map_ogt(name, unit, size, rows, legend, comment):
522
+ w, h = size
523
+ out = ["type tilemap", "version 1", "---", f"# {comment} (tools/make-assets.py).", f"size {w} {h}", "tile 16 16",
524
+ f"unit {unit}", 'tileset "tiles/city.ogt"', f"layer {name} {{", " z 0", " repeat x"]
525
+ for ch, t in legend:
526
+ out.append(f' legend "{ch}" {t}')
527
+ for r in rows:
528
+ out.append(f' row "{r}"')
529
+ out.append("}")
530
+ return "\n".join(out) + "\n"
531
+
532
+
533
+ def main():
534
+ def write(rel, data):
535
+ path = os.path.join(assets, rel)
536
+ os.makedirs(os.path.dirname(path), exist_ok=True)
537
+ with open(path, "wb" if isinstance(data, bytes) else "w") as f:
538
+ f.write(data)
539
+
540
+ def png(rel, pix):
541
+ write(rel, pix.png())
542
+ write(rel + ".ogt", PIXEL_IMPORT)
543
+
544
+ png("sprites/courier.png", sheet(FRAMES, FW, FH, 8))
545
+ png("sprites/props.png", props_sheet())
546
+ png("sprites/coin.png", sheet([coin(k) for k in range(6)], 16, 16, 6))
547
+ png("sprites/sky.png", sky())
548
+ png("tiles/city.png", city_sheet())
549
+ write("sprites/courier.ogt", COURIER_OGT)
550
+ write("sprites/props.ogt", PROPS_OGT)
551
+ write("sprites/coin.ogt", COIN_OGT)
552
+ write("tiles/city.ogt", CITY_OGT)
553
+ w, h, rows = far_rows()
554
+ write("maps/far.ogt", map_ogt("far", 0.5, (w, h), rows, [("#", 4), ("w", 5), ("^", 6), ("a", 7)],
555
+ "The far skyline, 0.5 units a tile"))
556
+ w, h, rows = near_rows()
557
+ write("maps/near.ogt", map_ogt("near", 0.65, (w, h), rows,
558
+ [("n", 8), ("L", 9), ("l", 14), ("d", 10), ("c", 11), ("t", 12), ("s", 13)],
559
+ "The near buildings, 0.65 units a tile"))
560
+ w, h, rows = roof_rows()
561
+ write("maps/roofs.ogt", map_ogt("roofs", 0.65, (w, h), rows, [("=", 0), ("b", 1), ("L", 2), ("D", 3)],
562
+ "The roof the courier runs on, 0.65 units a tile"))
563
+
564
+ a = os.path.join(assets, "audio")
565
+ os.makedirs(a, exist_ok=True)
566
+ write_wav(os.path.join(a, "jump.wav"), tone(0.18, 320, 760, shape="square", decay=0.08, gain=0.35))
567
+ write_wav(os.path.join(a, "coin.wav"), concat(tone(0.06, 988, shape="square", decay=0.05, gain=0.3),
568
+ tone(0.22, 1319, shape="square", decay=0.08, gain=0.3)))
569
+ write_wav(os.path.join(a, "crash.wav"), mix(noise(0.45, decay=0.12, gain=0.6, seed=7),
570
+ tone(0.45, 180, 60, shape="tri", decay=0.15, gain=0.5)))
571
+ write_wav(os.path.join(a, "start.wav"), concat(tone(0.09, 523, shape="tri", gain=0.4, decay=0.06),
572
+ tone(0.09, 659, shape="tri", gain=0.4, decay=0.06),
573
+ tone(0.2, 784, shape="tri", gain=0.4, decay=0.1)))
574
+
575
+ fonts = os.path.join(assets, "fonts")
576
+ os.makedirs(fonts, exist_ok=True)
577
+ font_src = os.path.join(root, "..", "..", "game", "assets", "fonts", "NotoSansSC-Regular-subset.ttf")
578
+ try:
579
+ subset_font(font_src, os.path.join(fonts, "ui.ttf"), CHARS)
580
+ except Exception as e: # noqa: BLE001
581
+ print(f"font subset skipped ({e}); the committed assets/fonts/ui.ttf stays")
582
+ with open(os.path.join(fonts, "ui.ttf.ogt"), "w") as f:
583
+ f.write(FONT_IMPORT.format(chars=CHARS))
584
+
585
+
586
+ # Every CJK character the HUD shows (src/systems.ts); Latin-1 comes from the charset.
587
+ CHARS = "屋顶快跑点击或按空格开始小跳住高再来一次"
588
+
589
+ FONT_IMPORT = """type import
590
+ version 1
591
+ ---
592
+ # Written by tools/make-assets.py: the atlas holds Latin-1 and the HUD's own
593
+ # CJK characters (CHARS there); add a character to CHARS before showing it.
594
+ font {{
595
+ charset latin
596
+ chars "{chars}"
597
+ em_px 48
598
+ px_range 6
599
+ }}
600
+ """
601
+
602
+ if __name__ == "__main__":
603
+ main()