@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.
- package/README.md +57 -2
- package/archetypes/README.md +99 -0
- package/archetypes/idle/.origin/setup +14 -0
- package/archetypes/idle/LICENSES.md +12 -0
- package/archetypes/idle/README.md +234 -0
- package/archetypes/idle/assets/audio/buy.wav +0 -0
- package/archetypes/idle/assets/audio/milestone.wav +0 -0
- package/archetypes/idle/assets/audio/nova.wav +0 -0
- package/archetypes/idle/assets/audio/orbit.wav +0 -0
- package/archetypes/idle/assets/audio/tap.wav +0 -0
- package/archetypes/idle/assets/audio/ui-close.ogt +9 -0
- package/archetypes/idle/assets/audio/ui-error.ogt +9 -0
- package/archetypes/idle/assets/audio/ui-open.ogt +9 -0
- package/archetypes/idle/assets/audio/ui-press.ogt +9 -0
- package/archetypes/idle/assets/audio/ui-success.ogt +9 -0
- package/archetypes/idle/assets/fonts/NotoSansSC-subset.ttf +0 -0
- package/archetypes/idle/assets/fonts/NotoSansSC-subset.ttf.ogt +10 -0
- package/archetypes/idle/assets/fonts/OFL.txt +93 -0
- package/archetypes/idle/assets/fx/drift.ogt +21 -0
- package/archetypes/idle/assets/fx/milestone.ogt +21 -0
- package/archetypes/idle/assets/fx/nova.ogt +22 -0
- package/archetypes/idle/assets/fx/stars.ogt +20 -0
- package/archetypes/idle/assets/fx/tap.ogt +23 -0
- package/archetypes/idle/assets/looks/idle.ogt +17 -0
- package/archetypes/idle/assets/skins/sparkorb/ChakraPetch-BoldItalic.ttf +0 -0
- package/archetypes/idle/assets/skins/sparkorb/ChakraPetch-BoldItalic.ttf.ogt +9 -0
- package/archetypes/idle/assets/skins/sparkorb/LICENSES.md +19 -0
- package/archetypes/idle/assets/skins/sparkorb/OFL.txt +95 -0
- package/archetypes/idle/assets/skins/sparkorb/Orbitron-Medium.ttf +0 -0
- package/archetypes/idle/assets/skins/sparkorb/Orbitron-Medium.ttf.ogt +9 -0
- package/archetypes/idle/assets/skins/sparkorb/Rajdhani-Bold.ttf +0 -0
- package/archetypes/idle/assets/skins/sparkorb/Rajdhani-Bold.ttf.ogt +9 -0
- package/archetypes/idle/assets/skins/sparkorb/atlas.png +0 -0
- package/archetypes/idle/assets/skins/sparkorb/atlas.png.ogt +7 -0
- package/archetypes/idle/assets/skins/sparkorb/icons.png +0 -0
- package/archetypes/idle/assets/skins/sparkorb/icons.png.ogt +7 -0
- package/archetypes/idle/assets/skins/sparkorb/skin.ogt +113 -0
- package/archetypes/idle/assets/sprites/glow.png +0 -0
- package/archetypes/idle/assets/sprites/glow.png.ogt +8 -0
- package/archetypes/idle/assets/sprites/mote-crystal.png +0 -0
- package/archetypes/idle/assets/sprites/mote-crystal.png.ogt +7 -0
- package/archetypes/idle/assets/sprites/mote-ember.png +0 -0
- package/archetypes/idle/assets/sprites/mote-ember.png.ogt +7 -0
- package/archetypes/idle/assets/sprites/mote-star.png +0 -0
- package/archetypes/idle/assets/sprites/mote-star.png.ogt +7 -0
- package/archetypes/idle/assets/sprites/mote-storm.png +0 -0
- package/archetypes/idle/assets/sprites/mote-storm.png.ogt +7 -0
- package/archetypes/idle/assets/sprites/orb.png +0 -0
- package/archetypes/idle/assets/sprites/orb.png.ogt +7 -0
- package/archetypes/idle/assets/sprites/spark.png +0 -0
- package/archetypes/idle/assets/sprites/spark.png.ogt +7 -0
- package/archetypes/idle/assets/strings/en.ogt +62 -0
- package/archetypes/idle/assets/strings/zh-CN.ogt +62 -0
- package/archetypes/idle/gitattributes +22 -0
- package/archetypes/idle/host.d.ts +46 -0
- package/archetypes/idle/package.json +18 -0
- package/archetypes/idle/project.ogt +14 -0
- package/archetypes/idle/references/metal/card.png +0 -0
- package/archetypes/idle/references/metal/orbit.png +0 -0
- package/archetypes/idle/references/metal/title.png +0 -0
- package/archetypes/idle/src/components.ts +139 -0
- package/archetypes/idle/src/main.ts +247 -0
- package/archetypes/idle/src/numbers.ts +126 -0
- package/archetypes/idle/src/systems.ts +340 -0
- package/archetypes/idle/src/ui.ts +412 -0
- package/archetypes/idle/tests/away.og.ts +42 -0
- package/archetypes/idle/tests/flow.og.ts +57 -0
- package/archetypes/idle/tests/frames.og.ts +33 -0
- package/archetypes/idle/tests/frames.ts +134 -0
- package/archetypes/idle/tests/lib.ts +74 -0
- package/archetypes/idle/tests/milestone.og.ts +32 -0
- package/archetypes/idle/tests/nova.og.ts +52 -0
- package/archetypes/idle/tests/offline.og.ts +60 -0
- package/archetypes/idle/tests/pad.og.ts +55 -0
- package/archetypes/idle/tests/pause.og.ts +68 -0
- package/archetypes/idle/tests/phone.og.ts +50 -0
- package/archetypes/idle/tests/replay.og.ts +28 -0
- package/archetypes/idle/tools/make-assets.py +448 -0
- package/archetypes/idle/tools/oglite.py +285 -0
- package/archetypes/idle/tsconfig.json +17 -0
- package/archetypes/match3/.origin/setup +14 -0
- package/archetypes/match3/LICENSES.md +7 -0
- package/archetypes/match3/README.md +205 -0
- package/archetypes/match3/assets/audio/cascade.wav +0 -0
- package/archetypes/match3/assets/audio/clear.wav +0 -0
- package/archetypes/match3/assets/audio/lost.wav +0 -0
- package/archetypes/match3/assets/audio/refused.wav +0 -0
- package/archetypes/match3/assets/audio/select.wav +0 -0
- package/archetypes/match3/assets/audio/swap.wav +0 -0
- package/archetypes/match3/assets/audio/won.wav +0 -0
- package/archetypes/match3/assets/fonts/OFL.txt +93 -0
- package/archetypes/match3/assets/fonts/ui.ttf +0 -0
- package/archetypes/match3/assets/fonts/ui.ttf.ogt +11 -0
- package/archetypes/match3/assets/scenes/main.ogt +24 -0
- package/archetypes/match3/assets/sprites/backdrop.png +0 -0
- package/archetypes/match3/assets/sprites/board.ogt +21 -0
- package/archetypes/match3/assets/sprites/cells.ogt +7 -0
- package/archetypes/match3/assets/sprites/cells.png +0 -0
- package/archetypes/match3/assets/sprites/cells.png.ogt +7 -0
- package/archetypes/match3/assets/sprites/gems.ogt +75 -0
- package/archetypes/match3/assets/sprites/gems.png +0 -0
- package/archetypes/match3/assets/sprites/panel.png +0 -0
- package/archetypes/match3/assets/sprites/ring.png +0 -0
- package/archetypes/match3/gitattributes +22 -0
- package/archetypes/match3/host.d.ts +46 -0
- package/archetypes/match3/package.json +18 -0
- package/archetypes/match3/project.ogt +18 -0
- package/archetypes/match3/references/metal/cascade.png +0 -0
- package/archetypes/match3/references/metal/clearing.png +0 -0
- package/archetypes/match3/references/metal/dealt.png +0 -0
- package/archetypes/match3/src/art.ts +54 -0
- package/archetypes/match3/src/board.ts +112 -0
- package/archetypes/match3/src/components.ts +95 -0
- package/archetypes/match3/src/layout.ts +25 -0
- package/archetypes/match3/src/main.ts +199 -0
- package/archetypes/match3/src/systems.ts +631 -0
- package/archetypes/match3/tests/drag.og.ts +31 -0
- package/archetypes/match3/tests/endings.og.ts +40 -0
- package/archetypes/match3/tests/frames.og.ts +49 -0
- package/archetypes/match3/tests/frames.ts +134 -0
- package/archetypes/match3/tests/hidpi.og.ts +41 -0
- package/archetypes/match3/tests/lib.ts +64 -0
- package/archetypes/match3/tests/replay.og.ts +28 -0
- package/archetypes/match3/tests/sounds.og.ts +50 -0
- package/archetypes/match3/tests/swap.og.ts +67 -0
- package/archetypes/match3/tools/make-assets.py +404 -0
- package/archetypes/match3/tools/oglite.py +285 -0
- package/archetypes/match3/tsconfig.json +17 -0
- package/archetypes/rhythm/.origin/setup +14 -0
- package/archetypes/rhythm/LICENSES.md +13 -0
- package/archetypes/rhythm/README.md +235 -0
- package/archetypes/rhythm/assets/audio/bass0.wav +0 -0
- package/archetypes/rhythm/assets/audio/bass1.wav +0 -0
- package/archetypes/rhythm/assets/audio/bass2.wav +0 -0
- package/archetypes/rhythm/assets/audio/bass3.wav +0 -0
- package/archetypes/rhythm/assets/audio/chords0.wav +0 -0
- package/archetypes/rhythm/assets/audio/chords1.wav +0 -0
- package/archetypes/rhythm/assets/audio/chords2.wav +0 -0
- package/archetypes/rhythm/assets/audio/chords3.wav +0 -0
- package/archetypes/rhythm/assets/audio/clear.wav +0 -0
- package/archetypes/rhythm/assets/audio/click.wav +0 -0
- package/archetypes/rhythm/assets/audio/drums.wav +0 -0
- package/archetypes/rhythm/assets/audio/fail.wav +0 -0
- package/archetypes/rhythm/assets/audio/leadA.wav +0 -0
- package/archetypes/rhythm/assets/audio/leadB.wav +0 -0
- package/archetypes/rhythm/assets/audio/miss.wav +0 -0
- package/archetypes/rhythm/assets/audio/tap0.wav +0 -0
- package/archetypes/rhythm/assets/audio/tap1.wav +0 -0
- package/archetypes/rhythm/assets/audio/tap2.wav +0 -0
- package/archetypes/rhythm/assets/audio/ui-close.ogt +9 -0
- package/archetypes/rhythm/assets/audio/ui-error.ogt +9 -0
- package/archetypes/rhythm/assets/audio/ui-open.ogt +9 -0
- package/archetypes/rhythm/assets/audio/ui-press.ogt +9 -0
- package/archetypes/rhythm/assets/audio/ui-success.ogt +9 -0
- package/archetypes/rhythm/assets/charts/neon-pulse.ogt +919 -0
- package/archetypes/rhythm/assets/fonts/NotoSansSC-subset.ttf +0 -0
- package/archetypes/rhythm/assets/fonts/NotoSansSC-subset.ttf.ogt +10 -0
- package/archetypes/rhythm/assets/fonts/OFL.txt +93 -0
- package/archetypes/rhythm/assets/fx/hit.ogt +22 -0
- package/archetypes/rhythm/assets/skins/lanebeat/ChakraPetch-Medium.ttf +0 -0
- package/archetypes/rhythm/assets/skins/lanebeat/ChakraPetch-Medium.ttf.ogt +9 -0
- package/archetypes/rhythm/assets/skins/lanebeat/JetBrainsMono-Regular.ttf +0 -0
- package/archetypes/rhythm/assets/skins/lanebeat/JetBrainsMono-Regular.ttf.ogt +9 -0
- package/archetypes/rhythm/assets/skins/lanebeat/LICENSES.md +17 -0
- package/archetypes/rhythm/assets/skins/lanebeat/OFL.txt +95 -0
- package/archetypes/rhythm/assets/skins/lanebeat/Orbitron-Bold.ttf +0 -0
- package/archetypes/rhythm/assets/skins/lanebeat/Orbitron-Bold.ttf.ogt +9 -0
- package/archetypes/rhythm/assets/skins/lanebeat/atlas.png +0 -0
- package/archetypes/rhythm/assets/skins/lanebeat/atlas.png.ogt +7 -0
- package/archetypes/rhythm/assets/skins/lanebeat/icons.png +0 -0
- package/archetypes/rhythm/assets/skins/lanebeat/icons.png.ogt +7 -0
- package/archetypes/rhythm/assets/skins/lanebeat/skin.ogt +110 -0
- package/archetypes/rhythm/assets/sprites/lane.png +0 -0
- package/archetypes/rhythm/assets/sprites/lane.png.ogt +7 -0
- package/archetypes/rhythm/assets/sprites/note.png +0 -0
- package/archetypes/rhythm/assets/sprites/note.png.ogt +7 -0
- package/archetypes/rhythm/assets/sprites/ring.png +0 -0
- package/archetypes/rhythm/assets/sprites/ring.png.ogt +7 -0
- package/archetypes/rhythm/assets/sprites/spark.png +0 -0
- package/archetypes/rhythm/assets/sprites/spark.png.ogt +7 -0
- package/archetypes/rhythm/assets/strings/en.ogt +61 -0
- package/archetypes/rhythm/assets/strings/zh-CN.ogt +61 -0
- package/archetypes/rhythm/gitattributes +22 -0
- package/archetypes/rhythm/host.d.ts +46 -0
- package/archetypes/rhythm/package.json +18 -0
- package/archetypes/rhythm/project.ogt +14 -0
- package/archetypes/rhythm/references/metal/highway.png +0 -0
- package/archetypes/rhythm/references/metal/results.png +0 -0
- package/archetypes/rhythm/references/metal/title.png +0 -0
- package/archetypes/rhythm/src/components.ts +147 -0
- package/archetypes/rhythm/src/main.ts +202 -0
- package/archetypes/rhythm/src/systems.ts +490 -0
- package/archetypes/rhythm/src/ui.ts +421 -0
- package/archetypes/rhythm/tests/band.og.ts +45 -0
- package/archetypes/rhythm/tests/calibrate.og.ts +49 -0
- package/archetypes/rhythm/tests/clear.og.ts +51 -0
- package/archetypes/rhythm/tests/fail.og.ts +40 -0
- package/archetypes/rhythm/tests/flow.og.ts +54 -0
- package/archetypes/rhythm/tests/frames.og.ts +35 -0
- package/archetypes/rhythm/tests/frames.ts +134 -0
- package/archetypes/rhythm/tests/lib.ts +123 -0
- package/archetypes/rhythm/tests/pad.og.ts +41 -0
- package/archetypes/rhythm/tests/pause.og.ts +77 -0
- package/archetypes/rhythm/tests/phone.og.ts +44 -0
- package/archetypes/rhythm/tests/replay.og.ts +27 -0
- package/archetypes/rhythm/tests/timing.og.ts +37 -0
- package/archetypes/rhythm/tools/make-assets.py +383 -0
- package/archetypes/rhythm/tools/oglite.py +285 -0
- package/archetypes/rhythm/tsconfig.json +17 -0
- package/archetypes/roguelike/.origin/setup +14 -0
- package/archetypes/roguelike/LICENSES.md +14 -0
- package/archetypes/roguelike/README.md +256 -0
- package/archetypes/roguelike/assets/ai/foe.ogt +28 -0
- package/archetypes/roguelike/assets/ai/guardian.ogt +26 -0
- package/archetypes/roguelike/assets/audio/bump.wav +0 -0
- package/archetypes/roguelike/assets/audio/drink.wav +0 -0
- package/archetypes/roguelike/assets/audio/fall.wav +0 -0
- package/archetypes/roguelike/assets/audio/hit.wav +0 -0
- package/archetypes/roguelike/assets/audio/hurt.wav +0 -0
- package/archetypes/roguelike/assets/audio/item.wav +0 -0
- package/archetypes/roguelike/assets/audio/kill.wav +0 -0
- package/archetypes/roguelike/assets/audio/pickup.wav +0 -0
- package/archetypes/roguelike/assets/audio/roar.wav +0 -0
- package/archetypes/roguelike/assets/audio/stairs.wav +0 -0
- package/archetypes/roguelike/assets/audio/step.ogt +9 -0
- package/archetypes/roguelike/assets/audio/telegraph.wav +0 -0
- package/archetypes/roguelike/assets/audio/tomb.wav +0 -0
- package/archetypes/roguelike/assets/audio/ui-close.ogt +9 -0
- package/archetypes/roguelike/assets/audio/ui-error.ogt +9 -0
- package/archetypes/roguelike/assets/audio/ui-open.ogt +9 -0
- package/archetypes/roguelike/assets/audio/ui-press.ogt +9 -0
- package/archetypes/roguelike/assets/audio/ui-success.ogt +9 -0
- package/archetypes/roguelike/assets/audio/won.wav +0 -0
- package/archetypes/roguelike/assets/fonts/NotoSansSC-subset.ttf +0 -0
- package/archetypes/roguelike/assets/fonts/NotoSansSC-subset.ttf.ogt +10 -0
- package/archetypes/roguelike/assets/fonts/OFL.txt +93 -0
- package/archetypes/roguelike/assets/maps/dive.ogt +106 -0
- package/archetypes/roguelike/assets/skins/lampdive/DotGothic16-Regular.ttf +0 -0
- package/archetypes/roguelike/assets/skins/lampdive/DotGothic16-Regular.ttf.ogt +9 -0
- package/archetypes/roguelike/assets/skins/lampdive/LICENSES.md +16 -0
- package/archetypes/roguelike/assets/skins/lampdive/OFL.txt +93 -0
- package/archetypes/roguelike/assets/skins/lampdive/atlas.png +0 -0
- package/archetypes/roguelike/assets/skins/lampdive/atlas.png.ogt +7 -0
- package/archetypes/roguelike/assets/skins/lampdive/icons.png +0 -0
- package/archetypes/roguelike/assets/skins/lampdive/icons.png.ogt +7 -0
- package/archetypes/roguelike/assets/skins/lampdive/skin.ogt +115 -0
- package/archetypes/roguelike/assets/sprites/dive.ogt +50 -0
- package/archetypes/roguelike/assets/sprites/dive.png +0 -0
- package/archetypes/roguelike/assets/sprites/dive.png.ogt +7 -0
- package/archetypes/roguelike/assets/sprites/glow.png +0 -0
- package/archetypes/roguelike/assets/sprites/glow.png.ogt +7 -0
- package/archetypes/roguelike/assets/strings/en.ogt +57 -0
- package/archetypes/roguelike/assets/strings/zh-CN.ogt +57 -0
- package/archetypes/roguelike/assets/tiles/tomb.ogt +31 -0
- package/archetypes/roguelike/assets/tiles/tomb.png +0 -0
- package/archetypes/roguelike/assets/tiles/tomb.png.ogt +7 -0
- package/archetypes/roguelike/gitattributes +22 -0
- package/archetypes/roguelike/host.d.ts +46 -0
- package/archetypes/roguelike/package.json +18 -0
- package/archetypes/roguelike/project.ogt +14 -0
- package/archetypes/roguelike/references/metal/dive.png +0 -0
- package/archetypes/roguelike/references/metal/over.png +0 -0
- package/archetypes/roguelike/references/metal/title.png +0 -0
- package/archetypes/roguelike/src/art.ts +23 -0
- package/archetypes/roguelike/src/components.ts +169 -0
- package/archetypes/roguelike/src/dive.ts +144 -0
- package/archetypes/roguelike/src/foes.ts +174 -0
- package/archetypes/roguelike/src/main.ts +258 -0
- package/archetypes/roguelike/src/systems.ts +620 -0
- package/archetypes/roguelike/src/ui.ts +350 -0
- package/archetypes/roguelike/tests/flow.og.ts +54 -0
- package/archetypes/roguelike/tests/foes.og.ts +45 -0
- package/archetypes/roguelike/tests/frames.og.ts +32 -0
- package/archetypes/roguelike/tests/frames.ts +134 -0
- package/archetypes/roguelike/tests/lib.ts +83 -0
- package/archetypes/roguelike/tests/lose.og.ts +44 -0
- package/archetypes/roguelike/tests/pad.og.ts +54 -0
- package/archetypes/roguelike/tests/pause.og.ts +59 -0
- package/archetypes/roguelike/tests/phone.og.ts +54 -0
- package/archetypes/roguelike/tests/replay.og.ts +24 -0
- package/archetypes/roguelike/tests/win.og.ts +60 -0
- package/archetypes/roguelike/tools/make-assets.py +518 -0
- package/archetypes/roguelike/tools/oglite.py +285 -0
- package/archetypes/roguelike/tsconfig.json +17 -0
- package/archetypes/runner/.origin/setup +14 -0
- package/archetypes/runner/LICENSES.md +10 -0
- package/archetypes/runner/README.md +225 -0
- package/archetypes/runner/assets/audio/coin.wav +0 -0
- package/archetypes/runner/assets/audio/crash.wav +0 -0
- package/archetypes/runner/assets/audio/hold.ogt +9 -0
- package/archetypes/runner/assets/audio/jump.wav +0 -0
- package/archetypes/runner/assets/audio/start.wav +0 -0
- package/archetypes/runner/assets/fonts/OFL.txt +93 -0
- package/archetypes/runner/assets/fonts/ui.ttf +0 -0
- package/archetypes/runner/assets/fonts/ui.ttf.ogt +11 -0
- package/archetypes/runner/assets/maps/far.ogt +25 -0
- package/archetypes/runner/assets/maps/near.ogt +25 -0
- package/archetypes/runner/assets/maps/roofs.ogt +19 -0
- package/archetypes/runner/assets/scenes/main.ogt +35 -0
- package/archetypes/runner/assets/sprites/coin.ogt +11 -0
- package/archetypes/runner/assets/sprites/coin.png +0 -0
- package/archetypes/runner/assets/sprites/coin.png.ogt +7 -0
- package/archetypes/runner/assets/sprites/courier.ogt +32 -0
- package/archetypes/runner/assets/sprites/courier.png +0 -0
- package/archetypes/runner/assets/sprites/courier.png.ogt +7 -0
- package/archetypes/runner/assets/sprites/props.ogt +21 -0
- package/archetypes/runner/assets/sprites/props.png +0 -0
- package/archetypes/runner/assets/sprites/props.png.ogt +7 -0
- package/archetypes/runner/assets/sprites/sky.png +0 -0
- package/archetypes/runner/assets/sprites/sky.png.ogt +7 -0
- package/archetypes/runner/assets/tiles/city.ogt +18 -0
- package/archetypes/runner/assets/tiles/city.png +0 -0
- package/archetypes/runner/assets/tiles/city.png.ogt +7 -0
- package/archetypes/runner/gitattributes +22 -0
- package/archetypes/runner/host.d.ts +46 -0
- package/archetypes/runner/package.json +18 -0
- package/archetypes/runner/project.ogt +18 -0
- package/archetypes/runner/references/metal/frames-coin.png +0 -0
- package/archetypes/runner/references/metal/frames-crash.png +0 -0
- package/archetypes/runner/references/metal/frames-title.png +0 -0
- package/archetypes/runner/src/art.ts +57 -0
- package/archetypes/runner/src/components.ts +94 -0
- package/archetypes/runner/src/main.ts +241 -0
- package/archetypes/runner/src/systems.ts +553 -0
- package/archetypes/runner/tests/coins.og.ts +30 -0
- package/archetypes/runner/tests/flow.og.ts +72 -0
- package/archetypes/runner/tests/frames.og.ts +37 -0
- package/archetypes/runner/tests/frames.ts +134 -0
- package/archetypes/runner/tests/jump.og.ts +44 -0
- package/archetypes/runner/tests/lib.ts +42 -0
- package/archetypes/runner/tests/records.og.ts +43 -0
- package/archetypes/runner/tests/replay.og.ts +36 -0
- package/archetypes/runner/tests/sounds.og.ts +43 -0
- package/archetypes/runner/tests/tune.og.ts +36 -0
- package/archetypes/runner/tools/make-assets.py +603 -0
- package/archetypes/runner/tools/oglite.py +285 -0
- package/archetypes/runner/tsconfig.json +17 -0
- package/archetypes/third-person/.origin/setup +14 -0
- package/archetypes/third-person/LICENSES.md +23 -0
- package/archetypes/third-person/README.md +249 -0
- package/archetypes/third-person/assets/ai/sentinel-east.ogt +69 -0
- package/archetypes/third-person/assets/ai/sentinel-north.ogt +69 -0
- package/archetypes/third-person/assets/ai/sentinel-west.ogt +69 -0
- package/archetypes/third-person/assets/anims/humanoid-clips.glb +0 -0
- package/archetypes/third-person/assets/audio/alarm.wav +0 -0
- package/archetypes/third-person/assets/audio/dawn.wav +0 -0
- package/archetypes/third-person/assets/audio/grab.wav +0 -0
- package/archetypes/third-person/assets/audio/ignite.wav +0 -0
- package/archetypes/third-person/assets/audio/jump.wav +0 -0
- package/archetypes/third-person/assets/audio/night.wav +0 -0
- package/archetypes/third-person/assets/audio/splash.wav +0 -0
- package/archetypes/third-person/assets/audio/step.ogt +9 -0
- package/archetypes/third-person/assets/audio/ui-close.ogt +9 -0
- package/archetypes/third-person/assets/audio/ui-error.ogt +9 -0
- package/archetypes/third-person/assets/audio/ui-open.ogt +9 -0
- package/archetypes/third-person/assets/audio/ui-press.ogt +9 -0
- package/archetypes/third-person/assets/audio/ui-success.ogt +9 -0
- package/archetypes/third-person/assets/audio/vale.wav +0 -0
- package/archetypes/third-person/assets/fonts/NotoSansSC-subset.ttf +0 -0
- package/archetypes/third-person/assets/fonts/NotoSansSC-subset.ttf.ogt +10 -0
- package/archetypes/third-person/assets/fonts/OFL.txt +93 -0
- package/archetypes/third-person/assets/fx/burst.ogt +25 -0
- package/archetypes/third-person/assets/fx/fire.ogt +24 -0
- package/archetypes/third-person/assets/fx/smoke.ogt +24 -0
- package/archetypes/third-person/assets/fx/splash.ogt +24 -0
- package/archetypes/third-person/assets/luts/teal-orange.cube +4917 -0
- package/archetypes/third-person/assets/models/stout.glb +0 -0
- package/archetypes/third-person/assets/models/strider.glb +0 -0
- package/archetypes/third-person/assets/models/strider.glb.ogt +9 -0
- package/archetypes/third-person/assets/nav/vale.ogt +43 -0
- package/archetypes/third-person/assets/rigs/strider.ogt +6 -0
- package/archetypes/third-person/assets/skins/ember/Cinzel-Bold.ttf +0 -0
- package/archetypes/third-person/assets/skins/ember/Cinzel-Bold.ttf.ogt +9 -0
- package/archetypes/third-person/assets/skins/ember/LICENSES.md +13 -0
- package/archetypes/third-person/assets/skins/ember/Newsreader-Medium.ttf +0 -0
- package/archetypes/third-person/assets/skins/ember/Newsreader-Medium.ttf.ogt +9 -0
- package/archetypes/third-person/assets/skins/ember/OFL.txt +94 -0
- package/archetypes/third-person/assets/skins/ember/atlas.png +0 -0
- package/archetypes/third-person/assets/skins/ember/atlas.png.ogt +7 -0
- package/archetypes/third-person/assets/skins/ember/icons.png +0 -0
- package/archetypes/third-person/assets/skins/ember/icons.png.ogt +7 -0
- package/archetypes/third-person/assets/skins/ember/skin.ogt +109 -0
- package/archetypes/third-person/assets/strings/en.ogt +47 -0
- package/archetypes/third-person/assets/strings/zh-CN.ogt +47 -0
- package/archetypes/third-person/assets/terrain/vale-control.png +0 -0
- package/archetypes/third-person/assets/terrain/vale.ogt +42 -0
- package/archetypes/third-person/assets/terrain/vale.png +0 -0
- package/archetypes/third-person/assets/textures/decals.png +0 -0
- package/archetypes/third-person/assets/textures/flame.png +0 -0
- package/archetypes/third-person/assets/textures/smoke.png +0 -0
- package/archetypes/third-person/assets/textures/spark.png +0 -0
- package/archetypes/third-person/assets/textures/terrain-grass.png +0 -0
- package/archetypes/third-person/assets/textures/terrain-path.png +0 -0
- package/archetypes/third-person/assets/textures/terrain-rock.png +0 -0
- package/archetypes/third-person/assets/textures/terrain-sand.png +0 -0
- package/archetypes/third-person/gitattributes +22 -0
- package/archetypes/third-person/host.d.ts +46 -0
- package/archetypes/third-person/package.json +18 -0
- package/archetypes/third-person/project.ogt +14 -0
- package/archetypes/third-person/references/metal/beacon.png +0 -0
- package/archetypes/third-person/references/metal/dawn.png +0 -0
- package/archetypes/third-person/references/metal/title.png +0 -0
- package/archetypes/third-person/references/webgl2/beacon.png +0 -0
- package/archetypes/third-person/src/anim.ts +87 -0
- package/archetypes/third-person/src/beacons.ts +80 -0
- package/archetypes/third-person/src/components.ts +148 -0
- package/archetypes/third-person/src/hero.ts +293 -0
- package/archetypes/third-person/src/main.ts +164 -0
- package/archetypes/third-person/src/sentinels.ts +159 -0
- package/archetypes/third-person/src/sound.ts +66 -0
- package/archetypes/third-person/src/ui.ts +451 -0
- package/archetypes/third-person/src/world.ts +212 -0
- package/archetypes/third-person/tests/flow.og.ts +70 -0
- package/archetypes/third-person/tests/frames.og.ts +35 -0
- package/archetypes/third-person/tests/frames.ts +134 -0
- package/archetypes/third-person/tests/hitstop.og.ts +26 -0
- package/archetypes/third-person/tests/lib.ts +87 -0
- package/archetypes/third-person/tests/lose.og.ts +44 -0
- package/archetypes/third-person/tests/pause.og.ts +64 -0
- package/archetypes/third-person/tests/phone.og.ts +65 -0
- package/archetypes/third-person/tests/replay.og.ts +32 -0
- package/archetypes/third-person/tests/restart.og.ts +28 -0
- package/archetypes/third-person/tests/sentinels.og.ts +48 -0
- package/archetypes/third-person/tests/win.og.ts +62 -0
- package/archetypes/third-person/tools/make-assets.py +353 -0
- package/archetypes/third-person/tools/oglite.py +285 -0
- package/archetypes/third-person/tsconfig.json +17 -0
- package/archetypes/topdown/.origin/setup +14 -0
- package/archetypes/topdown/LICENSES.md +10 -0
- package/archetypes/topdown/README.md +235 -0
- package/archetypes/topdown/assets/ai/slime.ogt +49 -0
- package/archetypes/topdown/assets/audio/coin.wav +0 -0
- package/archetypes/topdown/assets/audio/door.wav +0 -0
- package/archetypes/topdown/assets/audio/hit.wav +0 -0
- package/archetypes/topdown/assets/audio/hurt.wav +0 -0
- package/archetypes/topdown/assets/audio/key.wav +0 -0
- package/archetypes/topdown/assets/audio/step.ogt +9 -0
- package/archetypes/topdown/assets/fonts/OFL.txt +93 -0
- package/archetypes/topdown/assets/fonts/ui.ttf +0 -0
- package/archetypes/topdown/assets/fonts/ui.ttf.ogt +9 -0
- package/archetypes/topdown/assets/maps/tomb.tmj +2183 -0
- package/archetypes/topdown/assets/sprites/hero.ogt +58 -0
- package/archetypes/topdown/assets/sprites/hero.png +0 -0
- package/archetypes/topdown/assets/sprites/hero.png.ogt +7 -0
- package/archetypes/topdown/assets/sprites/items.ogt +15 -0
- package/archetypes/topdown/assets/sprites/items.png +0 -0
- package/archetypes/topdown/assets/sprites/items.png.ogt +7 -0
- package/archetypes/topdown/assets/sprites/slime.ogt +17 -0
- package/archetypes/topdown/assets/sprites/slime.png +0 -0
- package/archetypes/topdown/assets/sprites/slime.png.ogt +7 -0
- package/archetypes/topdown/assets/tiles/dungeon.ogt +65 -0
- package/archetypes/topdown/assets/tiles/dungeon.png +0 -0
- package/archetypes/topdown/assets/tiles/dungeon.png.ogt +7 -0
- package/archetypes/topdown/gitattributes +22 -0
- package/archetypes/topdown/host.d.ts +46 -0
- package/archetypes/topdown/package.json +18 -0
- package/archetypes/topdown/project.ogt +14 -0
- package/archetypes/topdown/references/metal/crit.png +0 -0
- package/archetypes/topdown/references/metal/dash.png +0 -0
- package/archetypes/topdown/references/metal/east.png +0 -0
- package/archetypes/topdown/references/metal/start.png +0 -0
- package/archetypes/topdown/src/art.ts +48 -0
- package/archetypes/topdown/src/components.ts +128 -0
- package/archetypes/topdown/src/main.ts +275 -0
- package/archetypes/topdown/src/systems.ts +503 -0
- package/archetypes/topdown/tests/ai.og.ts +64 -0
- package/archetypes/topdown/tests/combat.og.ts +72 -0
- package/archetypes/topdown/tests/door.og.ts +62 -0
- package/archetypes/topdown/tests/flee.og.ts +49 -0
- package/archetypes/topdown/tests/frames.og.ts +45 -0
- package/archetypes/topdown/tests/frames.ts +134 -0
- package/archetypes/topdown/tests/lib.ts +71 -0
- package/archetypes/topdown/tests/replay.og.ts +40 -0
- package/archetypes/topdown/tests/walk.og.ts +46 -0
- package/archetypes/topdown/tools/make-assets.py +623 -0
- package/archetypes/topdown/tools/oglite.py +285 -0
- package/archetypes/topdown/tsconfig.json +17 -0
- package/archetypes/tower-defense/.origin/setup +14 -0
- package/archetypes/tower-defense/LICENSES.md +15 -0
- package/archetypes/tower-defense/README.md +240 -0
- package/archetypes/tower-defense/assets/ai/plant.ogt +16 -0
- package/archetypes/tower-defense/assets/audio/clear.wav +0 -0
- package/archetypes/tower-defense/assets/audio/frost.wav +0 -0
- package/archetypes/tower-defense/assets/audio/leak.wav +0 -0
- package/archetypes/tower-defense/assets/audio/lost.wav +0 -0
- package/archetypes/tower-defense/assets/audio/night.wav +0 -0
- package/archetypes/tower-defense/assets/audio/plant.wav +0 -0
- package/archetypes/tower-defense/assets/audio/pop.wav +0 -0
- package/archetypes/tower-defense/assets/audio/thorn.wav +0 -0
- package/archetypes/tower-defense/assets/audio/ui-close.ogt +9 -0
- package/archetypes/tower-defense/assets/audio/ui-error.ogt +9 -0
- package/archetypes/tower-defense/assets/audio/ui-open.ogt +9 -0
- package/archetypes/tower-defense/assets/audio/ui-press.ogt +9 -0
- package/archetypes/tower-defense/assets/audio/ui-success.ogt +9 -0
- package/archetypes/tower-defense/assets/audio/wave.wav +0 -0
- package/archetypes/tower-defense/assets/audio/won.wav +0 -0
- package/archetypes/tower-defense/assets/fonts/NotoSansSC-subset.ttf +0 -0
- package/archetypes/tower-defense/assets/fonts/NotoSansSC-subset.ttf.ogt +10 -0
- package/archetypes/tower-defense/assets/fonts/OFL.txt +93 -0
- package/archetypes/tower-defense/assets/fx/fireflies.ogt +21 -0
- package/archetypes/tower-defense/assets/fx/pop.ogt +21 -0
- package/archetypes/tower-defense/assets/maps/garden.ogt +91 -0
- package/archetypes/tower-defense/assets/skins/pathguard/Fredoka-SemiBold.ttf +0 -0
- package/archetypes/tower-defense/assets/skins/pathguard/Fredoka-SemiBold.ttf.ogt +9 -0
- package/archetypes/tower-defense/assets/skins/pathguard/LICENSES.md +13 -0
- package/archetypes/tower-defense/assets/skins/pathguard/LilitaOne-Regular.ttf +0 -0
- package/archetypes/tower-defense/assets/skins/pathguard/LilitaOne-Regular.ttf.ogt +9 -0
- package/archetypes/tower-defense/assets/skins/pathguard/OFL.txt +94 -0
- package/archetypes/tower-defense/assets/skins/pathguard/atlas.png +0 -0
- package/archetypes/tower-defense/assets/skins/pathguard/atlas.png.ogt +7 -0
- package/archetypes/tower-defense/assets/skins/pathguard/icons.png +0 -0
- package/archetypes/tower-defense/assets/skins/pathguard/icons.png.ogt +7 -0
- package/archetypes/tower-defense/assets/skins/pathguard/skin.ogt +111 -0
- package/archetypes/tower-defense/assets/sprites/garden.ogt +66 -0
- package/archetypes/tower-defense/assets/sprites/garden.png +0 -0
- package/archetypes/tower-defense/assets/sprites/garden.png.ogt +7 -0
- package/archetypes/tower-defense/assets/sprites/glow.png +0 -0
- package/archetypes/tower-defense/assets/sprites/glow.png.ogt +7 -0
- package/archetypes/tower-defense/assets/sprites/ring.png +0 -0
- package/archetypes/tower-defense/assets/sprites/ring.png.ogt +7 -0
- package/archetypes/tower-defense/assets/sprites/spark.png +0 -0
- package/archetypes/tower-defense/assets/sprites/spark.png.ogt +7 -0
- package/archetypes/tower-defense/assets/strings/en.ogt +47 -0
- package/archetypes/tower-defense/assets/strings/zh-CN.ogt +47 -0
- package/archetypes/tower-defense/assets/tiles/garden.ogt +10 -0
- package/archetypes/tower-defense/assets/tiles/garden.png +0 -0
- package/archetypes/tower-defense/assets/tiles/garden.png.ogt +7 -0
- package/archetypes/tower-defense/gitattributes +22 -0
- package/archetypes/tower-defense/host.d.ts +46 -0
- package/archetypes/tower-defense/package.json +18 -0
- package/archetypes/tower-defense/project.ogt +14 -0
- package/archetypes/tower-defense/references/metal/end.png +0 -0
- package/archetypes/tower-defense/references/metal/title.png +0 -0
- package/archetypes/tower-defense/references/metal/wave.png +0 -0
- package/archetypes/tower-defense/src/art.ts +24 -0
- package/archetypes/tower-defense/src/components.ts +156 -0
- package/archetypes/tower-defense/src/main.ts +253 -0
- package/archetypes/tower-defense/src/systems.ts +658 -0
- package/archetypes/tower-defense/src/ui.ts +382 -0
- package/archetypes/tower-defense/tests/flow.og.ts +56 -0
- package/archetypes/tower-defense/tests/frames.og.ts +31 -0
- package/archetypes/tower-defense/tests/frames.ts +134 -0
- package/archetypes/tower-defense/tests/lib.ts +81 -0
- package/archetypes/tower-defense/tests/lose.og.ts +40 -0
- package/archetypes/tower-defense/tests/maze.og.ts +53 -0
- package/archetypes/tower-defense/tests/pad.og.ts +41 -0
- package/archetypes/tower-defense/tests/pause.og.ts +59 -0
- package/archetypes/tower-defense/tests/phone.og.ts +49 -0
- package/archetypes/tower-defense/tests/replay.og.ts +28 -0
- package/archetypes/tower-defense/tests/win.og.ts +58 -0
- package/archetypes/tower-defense/tools/make-assets.py +640 -0
- package/archetypes/tower-defense/tools/oglite.py +285 -0
- package/archetypes/tower-defense/tsconfig.json +17 -0
- package/bin/create.ts +496 -0
- package/looks/catalogue.json +1871 -0
- package/package.json +31 -4
- package/template/.origin/setup +14 -0
- package/template/LICENSES.md +23 -0
- package/template/README.md +37 -0
- package/template/assets/ai/guard.ogt +48 -0
- package/template/assets/audio/blip.wav +0 -0
- package/template/assets/audio/click.ogt +9 -0
- package/template/assets/fonts/NotoSansSC-Regular-subset.ttf +0 -0
- package/template/assets/fonts/NotoSansSC-Regular-subset.ttf.ogt +12 -0
- package/template/assets/fonts/OFL.txt +93 -0
- package/template/assets/looks/origin-game.ogt +12 -0
- package/template/assets/models/dropship.glb +0 -0
- package/template/assets/models/walker.glb +0 -0
- package/template/assets/nav/yard.ogt +27 -0
- package/template/assets/scenes/main.ogt +34 -0
- package/template/assets/strings/en.ogt +14 -0
- package/template/assets/textures/stone.png +0 -0
- package/template/assets/textures/stone_n.png +0 -0
- package/template/assets/textures/stone_n.png.ogt +7 -0
- package/template/gitattributes +22 -0
- package/template/package.json +18 -0
- package/template/project.ogt +25 -0
- package/template/src/main.ts +246 -0
- package/template/tests/guard.og.ts +67 -0
- package/template/tests/smoke.og.ts +43 -0
- package/template/tsconfig.json +17 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Regenerates Three-Lane Beat's self-made assets (CC0), byte for byte:
|
|
3
|
+
|
|
4
|
+
python3 tools/make-assets.py # from the archetype directory
|
|
5
|
+
|
|
6
|
+
- assets/charts/neon-pulse.ogt: the song's chart, a scene of one `Song`
|
|
7
|
+
and a `Note` per note (lane, beat), written from the same note lists as
|
|
8
|
+
the stems, so every note is on a sound,
|
|
9
|
+
- assets/audio/*.wav: the song "Neon Pulse" (120 BPM, 24 bars) as stems
|
|
10
|
+
the game starts on bar lines through og.Music (drums: one bar; bass,
|
|
11
|
+
chords and two lead phrases: two bars each), the metronome's click, a
|
|
12
|
+
tap per lane, the miss, the clear and the fail; *.ogt: the UI's og sfx
|
|
13
|
+
presets,
|
|
14
|
+
- assets/sprites/*.png: the note, the receptor ring, the lane's glow and
|
|
15
|
+
the spark,
|
|
16
|
+
- assets/fx/*.ogt: the hit's burst and the beat's dust,
|
|
17
|
+
- assets/fonts/NotoSansSC-subset.ttf + .ogt: Noto Sans SC subset to the
|
|
18
|
+
characters of assets/strings/zh-CN.ogt (needs fontTools and the project
|
|
19
|
+
template's font; skipped with a note when either is missing).
|
|
20
|
+
|
|
21
|
+
Standard library only except the font subset; deterministic.
|
|
22
|
+
"""
|
|
23
|
+
import math, os, sys
|
|
24
|
+
|
|
25
|
+
here = os.path.dirname(os.path.abspath(__file__))
|
|
26
|
+
sys.path.insert(0, here)
|
|
27
|
+
from oglite import Canvas, png_bytes, write_wav, tone, noise, mix, concat, subset_font # noqa: E402
|
|
28
|
+
|
|
29
|
+
root = os.path.dirname(here)
|
|
30
|
+
assets = os.path.join(root, "assets")
|
|
31
|
+
RATE = 22050
|
|
32
|
+
BPM = 120
|
|
33
|
+
BEAT = 60 / BPM
|
|
34
|
+
BAR = 4 * BEAT
|
|
35
|
+
BARS = 24
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def write(path, data):
|
|
39
|
+
path = os.path.join(assets, path)
|
|
40
|
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
41
|
+
with open(path, "wb" if isinstance(data, bytes) else "w") as f:
|
|
42
|
+
f.write(data)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def canvas_png(c):
|
|
46
|
+
return png_bytes(c.w, c.h, c.rgba())
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# ── the song: note lists the stems and the chart share ─────────────────────
|
|
50
|
+
|
|
51
|
+
# A minor: A C D E G (MIDI numbers).
|
|
52
|
+
def hz(midi):
|
|
53
|
+
return 440.0 * 2 ** ((midi - 69) / 12)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# Two-bar chords (root, third, fifth) for the bass and the pads: Am, F, C, G.
|
|
57
|
+
CHORDS = [(57, 60, 64), (53, 57, 60), (48, 52, 55), (55, 59, 62)]
|
|
58
|
+
# Two lead phrases of sixteen eighths (None rests), two bars each.
|
|
59
|
+
LEAD_A = [69, None, 72, 74, 76, None, 74, 72, 69, None, 67, 69, 72, None, None, None]
|
|
60
|
+
LEAD_B = [76, 79, 76, 74, 72, None, 74, 76, 81, None, 79, 76, 74, 72, 69, None]
|
|
61
|
+
|
|
62
|
+
def lane_of(midi):
|
|
63
|
+
"""A lead note's lane by pitch: low, middle, high."""
|
|
64
|
+
return 0 if midi < 71 else (1 if midi < 76 else 2)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def chart_notes():
|
|
68
|
+
"""(beat, lane) from 0 = bar 1's first beat. Follows the music: the
|
|
69
|
+
count-in bars' quarters, then kick and snare, then the lead's melody,
|
|
70
|
+
then chords of two lanes, then the last hit on all three."""
|
|
71
|
+
notes = []
|
|
72
|
+
for bar in range(1, BARS + 1):
|
|
73
|
+
b0 = (bar - 1) * 4
|
|
74
|
+
if bar <= 2:
|
|
75
|
+
for k in range(4):
|
|
76
|
+
notes.append((b0 + k, 1 if bar == 1 else (0, 2, 0, 2)[k]))
|
|
77
|
+
elif bar <= 4:
|
|
78
|
+
notes += [(b0, 0), (b0 + 1, 2), (b0 + 2, 0), (b0 + 3, 2)]
|
|
79
|
+
if bar == 4:
|
|
80
|
+
notes.append((b0 + 3.5, 1))
|
|
81
|
+
elif bar <= 8:
|
|
82
|
+
notes += [(b0, 0), (b0 + 1, 2), (b0 + 2, 0), (b0 + 3, 2)]
|
|
83
|
+
notes += [(b0 + 1.5, 1)] if bar % 2 == 0 else [(b0 + 2.5, 1)]
|
|
84
|
+
elif bar <= 20:
|
|
85
|
+
phrase = LEAD_A if ((bar - 9) // 2) % 2 == 0 else LEAD_B
|
|
86
|
+
half = ((bar - 9) % 2) * 8
|
|
87
|
+
for k in range(8):
|
|
88
|
+
m = phrase[half + k]
|
|
89
|
+
if m is not None:
|
|
90
|
+
notes.append((b0 + k * 0.5, lane_of(m)))
|
|
91
|
+
elif bar <= 22:
|
|
92
|
+
notes += [(b0, 0), (b0, 2), (b0 + 1, 1), (b0 + 2, 0), (b0 + 2, 1), (b0 + 3, 2)]
|
|
93
|
+
else:
|
|
94
|
+
notes += [(b0, 0), (b0 + 1, 1), (b0 + 2, 2), (b0 + 3, 1)]
|
|
95
|
+
end = BARS * 4
|
|
96
|
+
notes += [(end, 0), (end, 1), (end, 2)]
|
|
97
|
+
return sorted(notes)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def chart_text():
|
|
101
|
+
notes = chart_notes()
|
|
102
|
+
out = [
|
|
103
|
+
"type scene", "version 1", "---",
|
|
104
|
+
"# Neon Pulse's chart (tools/make-assets.py writes it from the song's note lists).",
|
|
105
|
+
"# A chart is a scene: one entity with a Song (tempo, metre, length in bars) and an",
|
|
106
|
+
"# entity per note, its Note's lane (0 left, 1 middle, 2 right) and beat (from 0 at bar",
|
|
107
|
+
"# 1's first beat; 3.5 is the 'and' of beat 4). Edit it by hand: the game reads it as is.",
|
|
108
|
+
'name "Neon Pulse"',
|
|
109
|
+
'entity "song" {',
|
|
110
|
+
" Song {",
|
|
111
|
+
f" bpm {BPM}",
|
|
112
|
+
" beatsPerBar 4",
|
|
113
|
+
f" bars {BARS}",
|
|
114
|
+
f" notes {len(notes)}",
|
|
115
|
+
" }",
|
|
116
|
+
"}",
|
|
117
|
+
]
|
|
118
|
+
for i, (beat, lane) in enumerate(notes):
|
|
119
|
+
out += [f'entity "note-{i}" {{', " Note {", f" index {i}", f" lane {lane}", f" beat {beat:g}", " }", "}"]
|
|
120
|
+
return "\n".join(out) + "\n"
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# ── synthesis ──────────────────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
def silence(seconds):
|
|
126
|
+
return [0.0] * int(seconds * RATE)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def place(out, sound, at):
|
|
130
|
+
s0 = int(round(at * RATE))
|
|
131
|
+
for i, v in enumerate(sound):
|
|
132
|
+
if s0 + i < len(out):
|
|
133
|
+
out[s0 + i] += v
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def kick():
|
|
137
|
+
n = int(0.25 * RATE)
|
|
138
|
+
out, phase = [], 0.0
|
|
139
|
+
for i in range(n):
|
|
140
|
+
t = i / RATE
|
|
141
|
+
f = 50 + 110 * math.exp(-t / 0.03)
|
|
142
|
+
phase += 2 * math.pi * f / RATE
|
|
143
|
+
out.append(0.9 * math.sin(phase) * math.exp(-t / 0.12))
|
|
144
|
+
return out
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def snare():
|
|
148
|
+
return mix(noise(0.18, decay=0.06, gain=0.45, seed=7), tone(0.12, 190, 160, shape="tri", decay=0.04, gain=0.3))
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def hat(open_=False):
|
|
152
|
+
return noise(0.12 if open_ else 0.05, decay=0.04 if open_ else 0.012, gain=0.16, seed=13)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def saw(seconds, f, gain, decay):
|
|
156
|
+
n = int(seconds * RATE)
|
|
157
|
+
out = []
|
|
158
|
+
for i in range(n):
|
|
159
|
+
t = i / RATE
|
|
160
|
+
ph = (f * t) % 1.0
|
|
161
|
+
env = min(1.0, t / 0.005) * math.exp(-t / decay)
|
|
162
|
+
out.append(gain * env * (2 * ph - 1))
|
|
163
|
+
return out
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def lowpass(x, a=0.2):
|
|
167
|
+
y, out = 0.0, []
|
|
168
|
+
for v in x:
|
|
169
|
+
y += a * (v - y)
|
|
170
|
+
out.append(y)
|
|
171
|
+
return out
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def drums():
|
|
175
|
+
out = silence(BAR)
|
|
176
|
+
for k in range(4):
|
|
177
|
+
if k % 2 == 0:
|
|
178
|
+
place(out, kick(), k * BEAT)
|
|
179
|
+
else:
|
|
180
|
+
place(out, snare(), k * BEAT)
|
|
181
|
+
for k in range(8):
|
|
182
|
+
place(out, hat(k == 7), k * BEAT / 2)
|
|
183
|
+
return out
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def stem_bass(chord):
|
|
187
|
+
out = silence(2 * BAR)
|
|
188
|
+
root = chord[0] - 12
|
|
189
|
+
for k in range(16):
|
|
190
|
+
f = hz(root + (12 if k % 4 == 3 else 0))
|
|
191
|
+
place(out, lowpass(saw(BEAT / 2 * 0.95, f, 0.35, 0.18), 0.12), k * BEAT / 2)
|
|
192
|
+
return out
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def stem_chords(chord):
|
|
196
|
+
out = silence(2 * BAR)
|
|
197
|
+
for k in (0, 1.5, 3, 4, 5.5, 7):
|
|
198
|
+
for m in chord:
|
|
199
|
+
place(out, lowpass(saw(BEAT * 1.2, hz(m), 0.07, 0.35), 0.18), k * BEAT)
|
|
200
|
+
return out
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def stem_lead(phrase):
|
|
204
|
+
out = silence(2 * BAR)
|
|
205
|
+
for k, m in enumerate(phrase):
|
|
206
|
+
if m is None:
|
|
207
|
+
continue
|
|
208
|
+
f = hz(m)
|
|
209
|
+
n = int(BEAT / 2 * 0.9 * RATE)
|
|
210
|
+
note = []
|
|
211
|
+
for i in range(n):
|
|
212
|
+
t = i / RATE
|
|
213
|
+
env = min(1.0, t / 0.004) * math.exp(-t / 0.22)
|
|
214
|
+
note.append(0.22 * env * (math.sin(2 * math.pi * f * t) + 0.45 * math.sin(4 * math.pi * f * t + 0.4) + 0.2 * math.sin(6 * math.pi * f * t)))
|
|
215
|
+
place(out, note, k * BEAT / 2)
|
|
216
|
+
return out
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def song_stems():
|
|
220
|
+
"""Each stem one or two bars long; the game starts each on its bar line.
|
|
221
|
+
The bass and the chords follow the two-bar chords in turn, so each is
|
|
222
|
+
four two-bar stems."""
|
|
223
|
+
stems = {"drums": drums()}
|
|
224
|
+
for i, ch in enumerate(CHORDS):
|
|
225
|
+
stems[f"bass{i}"] = stem_bass(ch)
|
|
226
|
+
stems[f"chords{i}"] = stem_chords(ch)
|
|
227
|
+
stems["leadA"] = stem_lead(LEAD_A)
|
|
228
|
+
stems["leadB"] = stem_lead(LEAD_B)
|
|
229
|
+
return stems
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def sounds():
|
|
233
|
+
a = os.path.join(assets, "audio")
|
|
234
|
+
os.makedirs(a, exist_ok=True)
|
|
235
|
+
w = lambda name, s: write_wav(os.path.join(a, name), s) # noqa: E731
|
|
236
|
+
for name, s in song_stems().items():
|
|
237
|
+
w(f"{name}.wav", s)
|
|
238
|
+
w("click.wav", concat(tone(0.03, 1760, shape="square", decay=0.012, gain=0.3)))
|
|
239
|
+
for lane, f in enumerate((523.25, 659.25, 783.99)):
|
|
240
|
+
w(f"tap{lane}.wav", mix(tone(0.09, f, shape="tri", decay=0.035, gain=0.35), noise(0.02, decay=0.006, gain=0.12, seed=lane + 1)))
|
|
241
|
+
w("miss.wav", mix(tone(0.18, 140, 90, shape="square", decay=0.06, gain=0.22), noise(0.1, decay=0.03, gain=0.12, seed=31)))
|
|
242
|
+
w("clear.wav", concat(*[tone(0.11, hz(m), shape="tri", gain=0.38, decay=0.09) for m in (69, 72, 76, 79)], tone(0.6, hz(81), shape="tri", gain=0.4, decay=0.35)))
|
|
243
|
+
w("fail.wav", concat(*[tone(0.2, hz(m), shape="tri", gain=0.38, decay=0.16) for m in (64, 62, 60)], tone(0.7, hz(57), shape="tri", gain=0.38, decay=0.4)))
|
|
244
|
+
presets = {"ui-press": "click", "ui-open": "select", "ui-close": "back", "ui-error": "error", "ui-success": "levelup"}
|
|
245
|
+
for name, preset in presets.items():
|
|
246
|
+
write(f"audio/{name}.ogt", f'type sfx\nversion 1\n---\n# og sfx\'s "{preset}" preset, synthesised by og cook (the UI\'s {name[3:]} sound).\npreset "{preset}"\nseed 0\npitch 1\nlength 1\nvolume 0.8\n')
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
# ── art ────────────────────────────────────────────────────────────────────
|
|
250
|
+
|
|
251
|
+
def note_png():
|
|
252
|
+
"""A note: a rounded bar, white (the sprite tints it by lane), a bright core."""
|
|
253
|
+
c = Canvas(96, 40)
|
|
254
|
+
c.rect(2, 4, 92, 32, "#ffffff", radius=14, alpha=0.35)
|
|
255
|
+
c.rect(6, 8, 84, 24, "#ffffff", radius=11)
|
|
256
|
+
c.rect(14, 13, 68, 6, "#ffffff", radius=3, alpha=0.6)
|
|
257
|
+
return c
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def ring_rgba():
|
|
261
|
+
"""The receptor: an outline of the note's shape, clear inside."""
|
|
262
|
+
n_w, n_h = 96, 40
|
|
263
|
+
rows = []
|
|
264
|
+
for y in range(n_h):
|
|
265
|
+
row = bytearray()
|
|
266
|
+
for x in range(n_w):
|
|
267
|
+
# Signed distance to a rounded rectangle 88 x 28 at (4, 6), radius 12.
|
|
268
|
+
cx = min(max(x + 0.5, 16), 80)
|
|
269
|
+
cy = min(max(y + 0.5, 18), 22)
|
|
270
|
+
d = math.hypot(x + 0.5 - cx, y + 0.5 - cy) - 12
|
|
271
|
+
a = max(0.0, 1 - abs(d + 1.5) / 1.8)
|
|
272
|
+
glow = max(0.0, 1 - abs(d) / 6) * 0.25
|
|
273
|
+
row += bytes([255, 255, 255, round(min(1.0, a + glow) * 255)])
|
|
274
|
+
rows.append(bytes(row))
|
|
275
|
+
return png_bytes(n_w, n_h, rows)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def lane_rgba():
|
|
279
|
+
"""A lane's glow: bright at the bottom, fading up, soft at the sides."""
|
|
280
|
+
w, h = 32, 128
|
|
281
|
+
rows = []
|
|
282
|
+
for y in range(h):
|
|
283
|
+
row = bytearray()
|
|
284
|
+
for x in range(w):
|
|
285
|
+
side = 1 - abs((x + 0.5) / w * 2 - 1)
|
|
286
|
+
up = (y + 0.5) / h
|
|
287
|
+
a = (side ** 1.5) * (up ** 2.0) * 0.9
|
|
288
|
+
row += bytes([255, 255, 255, round(a * 255)])
|
|
289
|
+
rows.append(bytes(row))
|
|
290
|
+
return png_bytes(w, h, rows)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def spark():
|
|
294
|
+
c = Canvas(16, 16)
|
|
295
|
+
for r, a in ((7.5, 0.15), (5, 0.35), (3, 0.8), (1.6, 1.0)):
|
|
296
|
+
c.circle(8, 8, r, "#ffffff", a)
|
|
297
|
+
return c
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
LINEAR = """type import
|
|
301
|
+
version 1
|
|
302
|
+
---
|
|
303
|
+
# Drawn art: lossless, sampled linear.
|
|
304
|
+
texture {
|
|
305
|
+
kind color
|
|
306
|
+
}
|
|
307
|
+
"""
|
|
308
|
+
|
|
309
|
+
PIXEL = """type import
|
|
310
|
+
version 1
|
|
311
|
+
---
|
|
312
|
+
# A soft gradient: kept exact (RGBA8, no block compression), sampled nearest.
|
|
313
|
+
texture {
|
|
314
|
+
kind pixel
|
|
315
|
+
}
|
|
316
|
+
"""
|
|
317
|
+
|
|
318
|
+
HIT = """type particles
|
|
319
|
+
version 1
|
|
320
|
+
---
|
|
321
|
+
# A hit: sparks thrown up from the receptor in the lane's colour (the sprite tints them).
|
|
322
|
+
maxParticles 40
|
|
323
|
+
shape box 0.5 0.05 0
|
|
324
|
+
spread 60
|
|
325
|
+
burst 0 14
|
|
326
|
+
duration 0.1
|
|
327
|
+
lifetime 0.25 0.45
|
|
328
|
+
speed 3 6
|
|
329
|
+
size 0.08 0.16
|
|
330
|
+
sizeOverLife 0 1 1 0
|
|
331
|
+
colorOverLife {
|
|
332
|
+
key 0 1 1 1 1
|
|
333
|
+
key 1 1 1 1 0
|
|
334
|
+
}
|
|
335
|
+
gravity 0 -8 0
|
|
336
|
+
drag 2
|
|
337
|
+
texture "sprites/spark.png"
|
|
338
|
+
blend additive
|
|
339
|
+
render sprite 20
|
|
340
|
+
"""
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
# ── the font ───────────────────────────────────────────────────────────────
|
|
344
|
+
|
|
345
|
+
def zh_characters():
|
|
346
|
+
text = open(os.path.join(assets, "strings", "zh-CN.ogt"), encoding="utf-8").read()
|
|
347
|
+
# And the punctuation the UI draws itself: the en dash (a grade not yet
|
|
348
|
+
# known), the em dash (the UI kit's number with no value) and the
|
|
349
|
+
# ellipsis (a cut label), which the zh-CN screens showed as missing
|
|
350
|
+
# glyphs before og-ui warned about them.
|
|
351
|
+
chars = sorted({ch for ch in text if ord(ch) > 0x2E7F} | set("–—…"))
|
|
352
|
+
return "".join(chars)
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def font():
|
|
356
|
+
chars = zh_characters()
|
|
357
|
+
src = os.path.join(root, "..", "..", "game", "assets", "fonts", "NotoSansSC-Regular-subset.ttf")
|
|
358
|
+
try:
|
|
359
|
+
subset_font(src, os.path.join(assets, "fonts", "NotoSansSC-subset.ttf"), chars)
|
|
360
|
+
except Exception as e: # noqa: BLE001
|
|
361
|
+
print(f"font subset skipped ({e}); the committed assets/fonts/NotoSansSC-subset.ttf stays")
|
|
362
|
+
write("fonts/NotoSansSC-subset.ttf.ogt", "type import\nversion 1\n---\n"
|
|
363
|
+
"# Latin-1, CJK punctuation and every character of assets/strings/zh-CN.ogt (tools/make-assets.py).\n"
|
|
364
|
+
f'font {{\n charset latin cjk-punct\n chars "{chars}"\n em_px 32\n px_range 4\n}}\n')
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def main():
|
|
368
|
+
write("charts/neon-pulse.ogt", chart_text())
|
|
369
|
+
write("sprites/note.png", canvas_png(note_png()))
|
|
370
|
+
write("sprites/note.png.ogt", LINEAR)
|
|
371
|
+
write("sprites/ring.png", ring_rgba())
|
|
372
|
+
write("sprites/ring.png.ogt", LINEAR)
|
|
373
|
+
write("sprites/lane.png", lane_rgba())
|
|
374
|
+
write("sprites/lane.png.ogt", PIXEL)
|
|
375
|
+
write("sprites/spark.png", canvas_png(spark()))
|
|
376
|
+
write("sprites/spark.png.ogt", LINEAR)
|
|
377
|
+
write("fx/hit.ogt", HIT)
|
|
378
|
+
sounds()
|
|
379
|
+
font()
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
if __name__ == "__main__":
|
|
383
|
+
main()
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""A tiny, standard-library-only sprite kit for Origin Engine archetypes.
|
|
2
|
+
|
|
3
|
+
It draws antialiased vector shapes into RGBA images (4x4 supersampling),
|
|
4
|
+
writes PNGs, packs sprites into one glTF binary (.glb) of unit quads, one
|
|
5
|
+
mesh and one emissive, alpha-masked material per sprite, and writes 16-bit
|
|
6
|
+
WAV sounds. Everything is a pure function of its inputs, so the assets it
|
|
7
|
+
makes are byte-for-byte reproducible.
|
|
8
|
+
|
|
9
|
+
A sprite quad lies in the xy plane facing +z, 1 x 1 units, centred on the
|
|
10
|
+
origin, with uv (0,0) at the image's top left. Its material is black base
|
|
11
|
+
colour (alpha from the image, for the mask), roughness 1, and the image as
|
|
12
|
+
the emissive texture: with no light, no environment and tone mapping off,
|
|
13
|
+
the frame shows the image's colours exactly.
|
|
14
|
+
"""
|
|
15
|
+
import json, math, struct, wave, zlib
|
|
16
|
+
|
|
17
|
+
SS = 4 # supersamples per axis
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def hex_rgb(h):
|
|
21
|
+
h = h.lstrip("#")
|
|
22
|
+
return tuple(int(h[i:i + 2], 16) for i in (0, 2, 4))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Canvas:
|
|
26
|
+
"""An RGBA canvas; shapes are painted in order with coverage antialiasing.
|
|
27
|
+
Coordinates are in pixels, origin top left, y down."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, w, h):
|
|
30
|
+
self.w, self.h = w, h
|
|
31
|
+
self.px = [[0.0, 0.0, 0.0, 0.0] for _ in range(w * h)] # premultiplied
|
|
32
|
+
|
|
33
|
+
def _paint(self, inside, bbox, colour, alpha=1.0):
|
|
34
|
+
r, g, b = (c / 255.0 for c in hex_rgb(colour))
|
|
35
|
+
x0, y0, x1, y1 = bbox
|
|
36
|
+
x0 = max(0, int(math.floor(x0))); y0 = max(0, int(math.floor(y0)))
|
|
37
|
+
x1 = min(self.w, int(math.ceil(x1))); y1 = min(self.h, int(math.ceil(y1)))
|
|
38
|
+
n = SS * SS
|
|
39
|
+
for y in range(y0, y1):
|
|
40
|
+
for x in range(x0, x1):
|
|
41
|
+
hits = 0
|
|
42
|
+
for sy in range(SS):
|
|
43
|
+
py = y + (sy + 0.5) / SS
|
|
44
|
+
for sx in range(SS):
|
|
45
|
+
if inside(x + (sx + 0.5) / SS, py):
|
|
46
|
+
hits += 1
|
|
47
|
+
if hits == 0:
|
|
48
|
+
continue
|
|
49
|
+
a = alpha * hits / n
|
|
50
|
+
p = self.px[y * self.w + x]
|
|
51
|
+
p[0] = r * a + p[0] * (1 - a)
|
|
52
|
+
p[1] = g * a + p[1] * (1 - a)
|
|
53
|
+
p[2] = b * a + p[2] * (1 - a)
|
|
54
|
+
p[3] = a + p[3] * (1 - a)
|
|
55
|
+
|
|
56
|
+
def rect(self, x, y, w, h, colour, radius=0.0, alpha=1.0):
|
|
57
|
+
rad = min(radius, w / 2, h / 2)
|
|
58
|
+
|
|
59
|
+
def inside(px, py):
|
|
60
|
+
if px < x or px > x + w or py < y or py > y + h:
|
|
61
|
+
return False
|
|
62
|
+
if rad <= 0:
|
|
63
|
+
return True
|
|
64
|
+
cx = min(max(px, x + rad), x + w - rad)
|
|
65
|
+
cy = min(max(py, y + rad), y + h - rad)
|
|
66
|
+
return (px - cx) ** 2 + (py - cy) ** 2 <= rad * rad
|
|
67
|
+
|
|
68
|
+
self._paint(inside, (x, y, x + w, y + h), colour, alpha)
|
|
69
|
+
|
|
70
|
+
def circle(self, cx, cy, r, colour, alpha=1.0):
|
|
71
|
+
self.ellipse(cx, cy, r, r, colour, alpha)
|
|
72
|
+
|
|
73
|
+
def ellipse(self, cx, cy, rx, ry, colour, alpha=1.0):
|
|
74
|
+
def inside(px, py):
|
|
75
|
+
return ((px - cx) / rx) ** 2 + ((py - cy) / ry) ** 2 <= 1.0
|
|
76
|
+
|
|
77
|
+
self._paint(inside, (cx - rx, cy - ry, cx + rx, cy + ry), colour, alpha)
|
|
78
|
+
|
|
79
|
+
def ring(self, cx, cy, r, width, colour, alpha=1.0):
|
|
80
|
+
def inside(px, py):
|
|
81
|
+
d = math.hypot(px - cx, py - cy)
|
|
82
|
+
return r - width <= d <= r
|
|
83
|
+
|
|
84
|
+
self._paint(inside, (cx - r, cy - r, cx + r, cy + r), colour, alpha)
|
|
85
|
+
|
|
86
|
+
def poly(self, pts, colour, alpha=1.0):
|
|
87
|
+
xs = [p[0] for p in pts]; ys = [p[1] for p in pts]
|
|
88
|
+
|
|
89
|
+
def inside(px, py):
|
|
90
|
+
c = False
|
|
91
|
+
j = len(pts) - 1
|
|
92
|
+
for i in range(len(pts)):
|
|
93
|
+
xi, yi = pts[i]; xj, yj = pts[j]
|
|
94
|
+
if (yi > py) != (yj > py) and px < (xj - xi) * (py - yi) / (yj - yi) + xi:
|
|
95
|
+
c = not c
|
|
96
|
+
j = i
|
|
97
|
+
return c
|
|
98
|
+
|
|
99
|
+
self._paint(inside, (min(xs), min(ys), max(xs), max(ys)), colour, alpha)
|
|
100
|
+
|
|
101
|
+
def line(self, x0, y0, x1, y1, width, colour, alpha=1.0):
|
|
102
|
+
dx, dy = x1 - x0, y1 - y0
|
|
103
|
+
ln = math.hypot(dx, dy) or 1.0
|
|
104
|
+
nx, ny = -dy / ln * width / 2, dx / ln * width / 2
|
|
105
|
+
self.poly([(x0 + nx, y0 + ny), (x1 + nx, y1 + ny), (x1 - nx, y1 - ny), (x0 - nx, y0 - ny)], colour, alpha)
|
|
106
|
+
self.circle(x0, y0, width / 2, colour, alpha)
|
|
107
|
+
self.circle(x1, y1, width / 2, colour, alpha)
|
|
108
|
+
|
|
109
|
+
def rgba(self):
|
|
110
|
+
"""Straight-alpha RGBA8 rows. Transparent pixels take the colour of
|
|
111
|
+
the nearest painted pixel (a few dilation passes), so filtering and
|
|
112
|
+
mips never pull a dark fringe into the edge."""
|
|
113
|
+
w, h = self.w, self.h
|
|
114
|
+
col = [None] * (w * h)
|
|
115
|
+
for i, p in enumerate(self.px):
|
|
116
|
+
if p[3] > 0:
|
|
117
|
+
col[i] = (p[0] / p[3], p[1] / p[3], p[2] / p[3])
|
|
118
|
+
for _ in range(8):
|
|
119
|
+
nxt = list(col)
|
|
120
|
+
changed = False
|
|
121
|
+
for y in range(h):
|
|
122
|
+
for x in range(w):
|
|
123
|
+
i = y * w + x
|
|
124
|
+
if col[i] is not None:
|
|
125
|
+
continue
|
|
126
|
+
for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)):
|
|
127
|
+
xx, yy = x + dx, y + dy
|
|
128
|
+
if 0 <= xx < w and 0 <= yy < h and col[yy * w + xx] is not None:
|
|
129
|
+
nxt[i] = col[yy * w + xx]
|
|
130
|
+
changed = True
|
|
131
|
+
break
|
|
132
|
+
col = nxt
|
|
133
|
+
if not changed:
|
|
134
|
+
break
|
|
135
|
+
rows = []
|
|
136
|
+
for y in range(h):
|
|
137
|
+
row = bytearray()
|
|
138
|
+
for x in range(w):
|
|
139
|
+
i = y * w + x
|
|
140
|
+
c = col[i] or (0.0, 0.0, 0.0)
|
|
141
|
+
a = self.px[i][3]
|
|
142
|
+
row += bytes([round(min(1, max(0, c[0])) * 255), round(min(1, max(0, c[1])) * 255),
|
|
143
|
+
round(min(1, max(0, c[2])) * 255), round(min(1, max(0, a)) * 255)])
|
|
144
|
+
rows.append(bytes(row))
|
|
145
|
+
return rows
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def png_bytes(w, h, rows):
|
|
149
|
+
raw = b"".join(b"\x00" + r for r in rows)
|
|
150
|
+
|
|
151
|
+
def chunk(t, d):
|
|
152
|
+
return struct.pack(">I", len(d)) + t + d + struct.pack(">I", zlib.crc32(t + d) & 0xFFFFFFFF)
|
|
153
|
+
|
|
154
|
+
return (b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", struct.pack(">IIBBBBB", w, h, 8, 6, 0, 0, 0))
|
|
155
|
+
+ chunk(b"IDAT", zlib.compress(raw, 9)) + chunk(b"IEND", b""))
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def write_glb(path, sprites):
|
|
159
|
+
"""sprites: list of (name, png bytes, masked). One quad mesh per sprite,
|
|
160
|
+
in list order: GltfInfo.geometries[i] and materials[i] are sprite i."""
|
|
161
|
+
bin_ = bytearray()
|
|
162
|
+
|
|
163
|
+
def add(data, align=4):
|
|
164
|
+
while len(bin_) % align:
|
|
165
|
+
bin_.append(0)
|
|
166
|
+
off = len(bin_)
|
|
167
|
+
bin_.extend(data)
|
|
168
|
+
return off, len(data)
|
|
169
|
+
|
|
170
|
+
pos = struct.pack("<12f", -0.5, -0.5, 0, 0.5, -0.5, 0, 0.5, 0.5, 0, -0.5, 0.5, 0)
|
|
171
|
+
nrm = struct.pack("<12f", *([0, 0, 1] * 4))
|
|
172
|
+
uv = struct.pack("<8f", 0, 1, 1, 1, 1, 0, 0, 0)
|
|
173
|
+
idx = struct.pack("<6H", 0, 1, 2, 0, 2, 3)
|
|
174
|
+
views, accessors = [], []
|
|
175
|
+
for data, target in ((pos, 34962), (nrm, 34962), (uv, 34962), (idx, 34963)):
|
|
176
|
+
o, n = add(data)
|
|
177
|
+
views.append({"buffer": 0, "byteOffset": o, "byteLength": n, "target": target})
|
|
178
|
+
accessors = [
|
|
179
|
+
{"bufferView": 0, "componentType": 5126, "count": 4, "type": "VEC3", "min": [-0.5, -0.5, 0], "max": [0.5, 0.5, 0]},
|
|
180
|
+
{"bufferView": 1, "componentType": 5126, "count": 4, "type": "VEC3"},
|
|
181
|
+
{"bufferView": 2, "componentType": 5126, "count": 4, "type": "VEC2"},
|
|
182
|
+
{"bufferView": 3, "componentType": 5123, "count": 6, "type": "SCALAR"},
|
|
183
|
+
]
|
|
184
|
+
images, textures, materials, meshes, nodes = [], [], [], [], []
|
|
185
|
+
for i, (name, data, masked) in enumerate(sprites):
|
|
186
|
+
o, n = add(data)
|
|
187
|
+
views.append({"buffer": 0, "byteOffset": o, "byteLength": n})
|
|
188
|
+
images.append({"bufferView": len(views) - 1, "mimeType": "image/png", "name": name})
|
|
189
|
+
textures.append({"source": i, "sampler": 0})
|
|
190
|
+
m = {
|
|
191
|
+
"name": name,
|
|
192
|
+
"pbrMetallicRoughness": {"baseColorFactor": [0, 0, 0, 1], "baseColorTexture": {"index": i},
|
|
193
|
+
"metallicFactor": 0, "roughnessFactor": 1},
|
|
194
|
+
"emissiveTexture": {"index": i},
|
|
195
|
+
"emissiveFactor": [1, 1, 1],
|
|
196
|
+
}
|
|
197
|
+
if masked:
|
|
198
|
+
m["alphaMode"] = "MASK"
|
|
199
|
+
m["alphaCutoff"] = 0.5
|
|
200
|
+
materials.append(m)
|
|
201
|
+
meshes.append({"name": name, "primitives": [{"attributes": {"POSITION": 0, "NORMAL": 1, "TEXCOORD_0": 2},
|
|
202
|
+
"indices": 3, "material": i}]})
|
|
203
|
+
nodes.append({"name": name, "mesh": i, "translation": [i * 1.2, 0, 0]})
|
|
204
|
+
while len(bin_) % 4:
|
|
205
|
+
bin_.append(0)
|
|
206
|
+
doc = {
|
|
207
|
+
"asset": {"version": "2.0", "generator": "oglite.py"},
|
|
208
|
+
"scene": 0, "scenes": [{"nodes": list(range(len(nodes)))}], "nodes": nodes, "meshes": meshes,
|
|
209
|
+
"materials": materials, "textures": textures, "images": images,
|
|
210
|
+
"samplers": [{"magFilter": 9729, "minFilter": 9987, "wrapS": 33071, "wrapT": 33071}],
|
|
211
|
+
"accessors": accessors, "bufferViews": views, "buffers": [{"byteLength": len(bin_)}],
|
|
212
|
+
}
|
|
213
|
+
js = json.dumps(doc, separators=(",", ":")).encode()
|
|
214
|
+
while len(js) % 4:
|
|
215
|
+
js += b" "
|
|
216
|
+
out = struct.pack("<III", 0x46546C67, 2, 12 + 8 + len(js) + 8 + len(bin_))
|
|
217
|
+
out += struct.pack("<II", len(js), 0x4E4F534A) + js + struct.pack("<II", len(bin_), 0x004E4942) + bytes(bin_)
|
|
218
|
+
with open(path, "wb") as f:
|
|
219
|
+
f.write(out)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def write_wav(path, samples, rate=22050):
|
|
223
|
+
"""samples: floats in [-1, 1], mono."""
|
|
224
|
+
with wave.open(path, "wb") as w:
|
|
225
|
+
w.setnchannels(1)
|
|
226
|
+
w.setsampwidth(2)
|
|
227
|
+
w.setframerate(rate)
|
|
228
|
+
w.writeframes(b"".join(struct.pack("<h", int(max(-1, min(1, s)) * 32000)) for s in samples))
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def tone(seconds, f0, f1=None, rate=22050, shape="sine", attack=0.005, decay=None, gain=0.6):
|
|
232
|
+
"""A sweep from f0 to f1 Hz with a short attack and an exponential decay."""
|
|
233
|
+
f1 = f0 if f1 is None else f1
|
|
234
|
+
n = int(seconds * rate)
|
|
235
|
+
out, phase = [], 0.0
|
|
236
|
+
decay = decay or seconds / 4
|
|
237
|
+
for i in range(n):
|
|
238
|
+
t = i / rate
|
|
239
|
+
f = f0 + (f1 - f0) * (i / max(1, n - 1))
|
|
240
|
+
phase += 2 * math.pi * f / rate
|
|
241
|
+
if shape == "square":
|
|
242
|
+
s = 1.0 if math.sin(phase) >= 0 else -1.0
|
|
243
|
+
s *= 0.5
|
|
244
|
+
elif shape == "tri":
|
|
245
|
+
s = 2 / math.pi * math.asin(math.sin(phase))
|
|
246
|
+
else:
|
|
247
|
+
s = math.sin(phase)
|
|
248
|
+
env = min(1.0, t / attack) * math.exp(-t / decay)
|
|
249
|
+
out.append(s * env * gain)
|
|
250
|
+
return out
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def noise(seconds, rate=22050, decay=0.08, gain=0.5, seed=1):
|
|
254
|
+
x = seed & 0xFFFFFFFF
|
|
255
|
+
out = []
|
|
256
|
+
for i in range(int(seconds * rate)):
|
|
257
|
+
x ^= (x << 13) & 0xFFFFFFFF; x ^= x >> 17; x ^= (x << 5) & 0xFFFFFFFF
|
|
258
|
+
out.append(((x / 0xFFFFFFFF) * 2 - 1) * math.exp(-(i / rate) / decay) * gain)
|
|
259
|
+
return out
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def mix(*parts):
|
|
263
|
+
n = max(len(p) for p in parts)
|
|
264
|
+
return [sum(p[i] for p in parts if i < len(p)) for i in range(n)]
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def concat(*parts):
|
|
268
|
+
out = []
|
|
269
|
+
for p in parts:
|
|
270
|
+
out.extend(p)
|
|
271
|
+
return out
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def subset_font(src, dst, text):
|
|
275
|
+
"""Subset a TTF to Latin-1 plus `text` (needs fontTools)."""
|
|
276
|
+
from fontTools import subset
|
|
277
|
+
opts = subset.Options()
|
|
278
|
+
opts.name_IDs = ["*"]
|
|
279
|
+
opts.notdef_outline = True
|
|
280
|
+
opts.layout_features = []
|
|
281
|
+
font = subset.load_font(src, opts)
|
|
282
|
+
s = subset.Subsetter(opts)
|
|
283
|
+
s.populate(text="".join(chr(c) for c in range(0x20, 0x7F)) + "".join(chr(c) for c in range(0xA0, 0x100)) + text)
|
|
284
|
+
s.subset(font)
|
|
285
|
+
subset.save_font(font, dst, opts)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"types": [],
|
|
8
|
+
"strict": true,
|
|
9
|
+
"exactOptionalPropertyTypes": true,
|
|
10
|
+
"noFallthroughCasesInSwitch": true,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"verbatimModuleSyntax": true,
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
14
|
+
"noEmit": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/**/*.ts", "tests/**/*.ts", "host.d.ts"]
|
|
17
|
+
}
|