@origingame/create-game 0.0.0-stage → 0.1.1
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/gitignore +8 -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/gitignore +8 -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/gitignore +8 -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/gitignore +8 -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/gitignore +8 -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/gitignore +8 -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/gitignore +8 -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/gitignore +8 -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 +503 -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/gitignore +8 -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,134 @@
|
|
|
1
|
+
// The reference-frame check: decode the PNGs a run wrote and compare them
|
|
2
|
+
// with references/<tier>/ under og-tests' tolerance (a channel off by more
|
|
3
|
+
// than 32/255 is an outlier; fail above 0.5 % outliers or an RGB RMSE above
|
|
4
|
+
// 0.01). A tier without its own references compares with `metal` and says
|
|
5
|
+
// so. `OG_FX_CAPTURE=1 og test .` records this tier's references instead.
|
|
6
|
+
// (Copied from templates/fx-showcase/tests/frames.ts so the archetype stands alone.)
|
|
7
|
+
|
|
8
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
9
|
+
import { dirname, join, resolve } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { inflateSync } from "node:zlib";
|
|
12
|
+
import type { PlaytestChecks, PlaytestRun } from "@origingame/engine/playtest";
|
|
13
|
+
|
|
14
|
+
export interface Image {
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
/** RGBA8 rows. */
|
|
18
|
+
rgba: Uint8Array;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** An 8-bit RGB or RGBA, non-interlaced PNG, unfiltered. */
|
|
22
|
+
export function decodePng(bytes: Uint8Array): Image {
|
|
23
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
24
|
+
let at = 8;
|
|
25
|
+
let width = 0;
|
|
26
|
+
let height = 0;
|
|
27
|
+
let channels = 4;
|
|
28
|
+
const idat: Uint8Array[] = [];
|
|
29
|
+
while (at < bytes.length) {
|
|
30
|
+
const len = view.getUint32(at);
|
|
31
|
+
const type = String.fromCharCode(...bytes.subarray(at + 4, at + 8));
|
|
32
|
+
const data = bytes.subarray(at + 8, at + 8 + len);
|
|
33
|
+
if (type === "IHDR") {
|
|
34
|
+
width = view.getUint32(at + 8);
|
|
35
|
+
height = view.getUint32(at + 12);
|
|
36
|
+
const depth = data[8]!;
|
|
37
|
+
const color = data[9]!;
|
|
38
|
+
if (depth !== 8 || (color !== 6 && color !== 2) || data[12] !== 0) throw new Error(`unsupported PNG (depth ${depth}, colour ${color}, interlace ${data[12]})`);
|
|
39
|
+
channels = color === 6 ? 4 : 3;
|
|
40
|
+
} else if (type === "IDAT") idat.push(data);
|
|
41
|
+
else if (type === "IEND") break;
|
|
42
|
+
at += 12 + len;
|
|
43
|
+
}
|
|
44
|
+
const raw = inflateSync(Buffer.concat(idat));
|
|
45
|
+
const stride = width * channels;
|
|
46
|
+
const out = new Uint8Array(width * height * 4);
|
|
47
|
+
const prev = new Uint8Array(stride);
|
|
48
|
+
const cur = new Uint8Array(stride);
|
|
49
|
+
for (let y = 0; y < height; y++) {
|
|
50
|
+
const filter = raw[y * (stride + 1)]!;
|
|
51
|
+
const line = raw.subarray(y * (stride + 1) + 1, (y + 1) * (stride + 1));
|
|
52
|
+
for (let i = 0; i < stride; i++) {
|
|
53
|
+
const a = i >= channels ? cur[i - channels]! : 0;
|
|
54
|
+
const b = prev[i]!;
|
|
55
|
+
const c = i >= channels ? prev[i - channels]! : 0;
|
|
56
|
+
let v = line[i]!;
|
|
57
|
+
if (filter === 1) v += a;
|
|
58
|
+
else if (filter === 2) v += b;
|
|
59
|
+
else if (filter === 3) v += (a + b) >> 1;
|
|
60
|
+
else if (filter === 4) {
|
|
61
|
+
const p = a + b - c;
|
|
62
|
+
const pa = Math.abs(p - a), pb = Math.abs(p - b), pc = Math.abs(p - c);
|
|
63
|
+
v += pa <= pb && pa <= pc ? a : pb <= pc ? b : c;
|
|
64
|
+
}
|
|
65
|
+
cur[i] = v & 255;
|
|
66
|
+
}
|
|
67
|
+
for (let x = 0; x < width; x++) {
|
|
68
|
+
for (let k = 0; k < 4; k++) out[(y * width + x) * 4 + k] = k < channels ? cur[x * channels + k]! : 255;
|
|
69
|
+
}
|
|
70
|
+
prev.set(cur);
|
|
71
|
+
}
|
|
72
|
+
return { width, height, rgba: out };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** og-tests' rule: outlier fraction and RGB RMSE between two equal-size frames. */
|
|
76
|
+
export function difference(a: Image, b: Image): { outliers: number; rmse: number } {
|
|
77
|
+
if (a.width !== b.width || a.height !== b.height) return { outliers: 1, rmse: 1 };
|
|
78
|
+
let outliers = 0;
|
|
79
|
+
let sum = 0;
|
|
80
|
+
const n = a.width * a.height;
|
|
81
|
+
for (let p = 0; p < n; p++) {
|
|
82
|
+
let out = false;
|
|
83
|
+
for (let k = 0; k < 3; k++) {
|
|
84
|
+
const d = Math.abs(a.rgba[p * 4 + k]! - b.rgba[p * 4 + k]!);
|
|
85
|
+
if (d > 32) out = true;
|
|
86
|
+
const f = d / 255;
|
|
87
|
+
sum += f * f;
|
|
88
|
+
}
|
|
89
|
+
if (out) outliers++;
|
|
90
|
+
}
|
|
91
|
+
return { outliers: outliers / n, rmse: Math.sqrt(sum / (n * 3)) };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** The playtest's output directory (the bundled playtest runs from it). */
|
|
95
|
+
export function outDir(): string {
|
|
96
|
+
return dirname(fileURLToPath(import.meta.url));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** The project's references/, from its og-out/test/<name>/. */
|
|
100
|
+
export function referencesDir(): string {
|
|
101
|
+
return resolve(outDir(), "..", "..", "..", "references");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Compares each screenshot with its reference (`<prefix><file>`), or records it. */
|
|
105
|
+
export function checkFrames(run: PlaytestRun, t: PlaytestChecks, files: readonly string[], prefix: string): void {
|
|
106
|
+
// Read only where there is a renderer: without one (tier `none`) each frame is
|
|
107
|
+
// reported "no renderer, frame unchecked" and every other check still runs.
|
|
108
|
+
t.frame(files, () => {
|
|
109
|
+
const refs = referencesDir();
|
|
110
|
+
t.ok(existsSync(refs) || process.env.OG_FX_CAPTURE !== undefined, `the references directory exists at ${refs}`);
|
|
111
|
+
const own = join(refs, run.tier);
|
|
112
|
+
const capture = process.env.OG_FX_CAPTURE !== undefined;
|
|
113
|
+
for (const f of files) {
|
|
114
|
+
const actual = join(outDir(), f);
|
|
115
|
+
// Each file on its own: this tier's reference where it has one, metal's otherwise.
|
|
116
|
+
const mine = join(own, prefix + f);
|
|
117
|
+
const ref = capture || existsSync(mine) ? mine : join(refs, "metal", prefix + f);
|
|
118
|
+
if (ref !== mine) process.stdout.write(` note: no ${run.tier} reference for ${prefix}${f}; comparing with metal\n`);
|
|
119
|
+
if (capture) {
|
|
120
|
+
mkdirSync(own, { recursive: true });
|
|
121
|
+
copyFileSync(actual, ref);
|
|
122
|
+
t.ok(true, `recorded ${ref}`);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (!existsSync(ref)) {
|
|
126
|
+
t.ok(false, `${ref} is missing: record it with OG_FX_CAPTURE=1 og test .`);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const d = difference(decodePng(readFileSync(actual)), decodePng(readFileSync(ref)));
|
|
130
|
+
process.stdout.write(` ${f}: ${(d.outliers * 100).toFixed(3)} % outliers, RMSE ${d.rmse.toFixed(5)} against ${ref}\n`);
|
|
131
|
+
t.ok(d.outliers <= 0.005 && d.rmse <= 0.01, `${f} matches ${prefix}${f}: ${(d.outliers * 100).toFixed(3)} % outliers (≤ 0.5 %), RMSE ${d.rmse.toFixed(5)} (≤ 0.01)`);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Shared readers for Spark Orb's playtests. `get:Game:<file>` writes the
|
|
2
|
+
// run's resource, the state the game's `facts` (src/main.ts) report to the
|
|
3
|
+
// probe; `dump_ui` the screen's elements and their text; `dump_platform`
|
|
4
|
+
// the calls to og.platform.
|
|
5
|
+
import type { PlaytestRun } from "@origingame/engine/playtest";
|
|
6
|
+
|
|
7
|
+
export interface Game {
|
|
8
|
+
phase: number;
|
|
9
|
+
paused: boolean;
|
|
10
|
+
held: boolean;
|
|
11
|
+
confirmNova: boolean;
|
|
12
|
+
welcome: boolean;
|
|
13
|
+
sparks: number;
|
|
14
|
+
runEarned: number;
|
|
15
|
+
lifetime: number;
|
|
16
|
+
owned0: number;
|
|
17
|
+
owned1: number;
|
|
18
|
+
owned2: number;
|
|
19
|
+
owned3: number;
|
|
20
|
+
dust: number;
|
|
21
|
+
novas: number;
|
|
22
|
+
lastGain: number;
|
|
23
|
+
rate: number;
|
|
24
|
+
tapValue: number;
|
|
25
|
+
taps: number;
|
|
26
|
+
buys: number;
|
|
27
|
+
refused: number;
|
|
28
|
+
selected: number;
|
|
29
|
+
away: number;
|
|
30
|
+
offline: number;
|
|
31
|
+
saves: number;
|
|
32
|
+
saveConflicts: number;
|
|
33
|
+
loaded: boolean;
|
|
34
|
+
language: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Phase.* of src/components.ts. */
|
|
38
|
+
export const TITLE = 0;
|
|
39
|
+
export const PLAY = 1;
|
|
40
|
+
export const NOVA = 2;
|
|
41
|
+
|
|
42
|
+
/** The Game resource from a `get:Game:<file>` step. */
|
|
43
|
+
export function game(run: PlaytestRun, file: string): Game {
|
|
44
|
+
return run.json<{ value: Game }>(file).value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface UiElement { id: string; parent: string; kind: string; text: string; x: number; y: number; width: number; height: number }
|
|
48
|
+
|
|
49
|
+
/** The UI's elements from a `dump_ui:<file>` step. */
|
|
50
|
+
export function ui(run: PlaytestRun, file: string): { surface: { width: number; height: number }; elements: UiElement[] } {
|
|
51
|
+
return run.json(file);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The text of the first element with this id whose parent is `parent` (any parent when omitted). */
|
|
55
|
+
export function text(run: PlaytestRun, file: string, id: string, parent?: string): string | undefined {
|
|
56
|
+
return ui(run, file).elements.find((e) => e.id === id && (parent === undefined || e.parent === parent))?.text;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface PlatformDump {
|
|
60
|
+
host: string;
|
|
61
|
+
calls: Array<{ method: string; args: Record<string, unknown>; tick: number; state: string; result?: { ok: boolean; data?: Record<string, unknown>; error?: { code: string } } }>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** A key pressed at t (down, then up two ticks later). */
|
|
65
|
+
export const press = (key: string, t: number) => `${t}:key_down:${key};${(t + 0.034).toFixed(3)}:key_up:${key}`;
|
|
66
|
+
|
|
67
|
+
/** A tap on a UI element by its id path (pressed, released a tick later). */
|
|
68
|
+
export const tap = (id: string, t: number) => `${t}:touch_move_to:${id};${(t + 0.017).toFixed(3)}:touch_start;${(t + 0.05).toFixed(3)}:touch_end`;
|
|
69
|
+
|
|
70
|
+
/** A tap at a CSS pixel. */
|
|
71
|
+
export const tapAt = (x: number, y: number, t: number) => `${t}:touch_move:${x},${y};${(t + 0.017).toFixed(3)}:touch_start;${(t + 0.05).toFixed(3)}:touch_end`;
|
|
72
|
+
|
|
73
|
+
/** The orb's centre on the 1280×720 desktop surface (src/ui.ts: 10 m tall, the orb at y 0.35 m). */
|
|
74
|
+
export const ORB = { x: 640, y: 360 - 0.35 * 72 } as const;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Milestones and big numbers: the tenth Storm doubles what storms make
|
|
2
|
+
// (a burst from its card, a fanfare, "Storm ×2!"); the wallet shows 1.23M
|
|
3
|
+
// as "1.23M" and the rate in the same words; a supernova's stardust makes
|
|
4
|
+
// everything 10 % stronger each.
|
|
5
|
+
import { playtest } from "@origingame/engine/playtest";
|
|
6
|
+
import { game, press, text } from "./lib.ts";
|
|
7
|
+
|
|
8
|
+
export default playtest({
|
|
9
|
+
name: "the tenth of a kind doubles it; big numbers read 1.23M",
|
|
10
|
+
commands: [
|
|
11
|
+
"0.3:play",
|
|
12
|
+
"0.9:own:1:9",
|
|
13
|
+
"1.0:get:Game:nine.json",
|
|
14
|
+
"1.1:sparks:1234567",
|
|
15
|
+
press("Digit2", 1.2),
|
|
16
|
+
"1.35:get:Game:ten.json",
|
|
17
|
+
"1.4:dump_ui:ui.json",
|
|
18
|
+
"1.45:screenshot:milestone.png",
|
|
19
|
+
"1.5:quit",
|
|
20
|
+
].join(";"),
|
|
21
|
+
check(run, t) {
|
|
22
|
+
t.equal(run.warnings.join("; "), "", "the script ran without warnings");
|
|
23
|
+
const nine = game(run, "nine.json");
|
|
24
|
+
t.equal(nine.rate, 36, "nine storms make 36 a second (4 each)");
|
|
25
|
+
const ten = game(run, "ten.json");
|
|
26
|
+
t.equal(ten.owned1, 10, "2 bought the tenth");
|
|
27
|
+
t.equal(ten.rate, 80, "ten storms make twice 40: 80 a second");
|
|
28
|
+
t.equal(text(run, "ui.json", "Amount", "Sparks"), `${(ten.sparks / 1e6).toFixed(2)}M`, `the wallet reads ${text(run, "ui.json", "Amount", "Sparks")}`);
|
|
29
|
+
t.equal(text(run, "ui.json", "Rate"), "+80 a second", "the rate reads +80 a second");
|
|
30
|
+
t.equal(text(run, "ui.json", "Owned", "Head"), "×0", "the first card counts its embers");
|
|
31
|
+
},
|
|
32
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// The supernova and its card (the run's end): with 300,000 sparks made
|
|
2
|
+
// this run, N asks; Not yet keeps everything; the HUD's Supernova and
|
|
3
|
+
// Collapse burst the orb: sparks and generators gone, one stardust, the
|
|
4
|
+
// card with the run's sparks, time and the stardust; Ignite again starts
|
|
5
|
+
// the next run, every rate 10 % stronger.
|
|
6
|
+
import { playtest } from "@origingame/engine/playtest";
|
|
7
|
+
import { game, NOVA, PLAY, press, tap, text } from "./lib.ts";
|
|
8
|
+
|
|
9
|
+
export default playtest({
|
|
10
|
+
name: "a supernova: asked, declined, collapsed, the run's card, the next run stronger",
|
|
11
|
+
commands: [
|
|
12
|
+
"0.3:play",
|
|
13
|
+
"0.9:own:0:5",
|
|
14
|
+
"1.0:sparks:300000",
|
|
15
|
+
press("KeyN", 1.1),
|
|
16
|
+
"1.4:get:Game:asked.json",
|
|
17
|
+
tap("Later", 1.5),
|
|
18
|
+
"1.8:get:Game:declined.json",
|
|
19
|
+
tap("Nova/Supernova", 1.9),
|
|
20
|
+
"2.3:dump_ui:confirm.json",
|
|
21
|
+
tap("Collapse", 2.4),
|
|
22
|
+
"2.55:get:Game:collapsed.json",
|
|
23
|
+
"3.3:dump_ui:card.json",
|
|
24
|
+
"3.35:screenshot:card.png",
|
|
25
|
+
tap("Again", 3.5),
|
|
26
|
+
"4.2:sparks:15",
|
|
27
|
+
press("Digit1", 4.3),
|
|
28
|
+
"4.5:get:Game:next.json",
|
|
29
|
+
"4.6:dump_platform:platform.json",
|
|
30
|
+
"4.7:quit",
|
|
31
|
+
].join(";"),
|
|
32
|
+
check(run, t) {
|
|
33
|
+
t.equal(run.warnings.join("; "), "", "the script ran without warnings");
|
|
34
|
+
t.equal(game(run, "asked.json").confirmNova, true, "N asked");
|
|
35
|
+
const declined = game(run, "declined.json");
|
|
36
|
+
t.ok(!declined.confirmNova && declined.owned0 === 5 && declined.dust === 0, "Not yet kept everything");
|
|
37
|
+
t.ok(text(run, "confirm.json", "Body")?.includes("1 stardust") ?? false, "the question says what it gives");
|
|
38
|
+
const c = game(run, "collapsed.json");
|
|
39
|
+
t.equal(c.phase, NOVA, "Collapse ended the run");
|
|
40
|
+
t.equal(c.dust, 1, "one stardust (the square root of 3)");
|
|
41
|
+
t.ok(c.sparks === 0 && c.owned0 === 0 && c.runEarned === 0, "sparks and generators gone");
|
|
42
|
+
t.equal(text(run, "card.json", "Value", "Made"), "300K", "the card shows the run's sparks");
|
|
43
|
+
t.equal(text(run, "card.json", "Value", "Gain"), "+1", "and the stardust gained");
|
|
44
|
+
t.equal(text(run, "card.json", "Value", "Bonus"), "+10%", "and what it is worth");
|
|
45
|
+
const next = game(run, "next.json");
|
|
46
|
+
t.equal(next.phase, PLAY, "Ignite again started the next run");
|
|
47
|
+
t.ok(Math.abs(next.rate - 0.55) < 1e-6, `an ember now makes 0.55 a second (${next.rate})`);
|
|
48
|
+
const p = run.json<{ calls: Array<{ method: string; args: Record<string, unknown> }> }>("platform.json");
|
|
49
|
+
t.ok(p.calls.some((x) => x.method === "telemetry" && x.args["name"] === "orb.nova"), "the supernova was reported");
|
|
50
|
+
t.ok(p.calls.some((x) => x.method === "store.set" && (x.args["value"] as { dust?: number } | undefined)?.dust === 1), "and saved at once");
|
|
51
|
+
},
|
|
52
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// The save and the time away: Escape saves through og.platform's store;
|
|
2
|
+
// the game closes and opens two hours later (`relaunch:7200`, the host's
|
|
3
|
+
// time away); the title offers Continue; the welcome back says how long
|
|
4
|
+
// and how much (half the rate for the time away) and Collect closes it
|
|
5
|
+
// with the sparks added. Another device then writes a save with more
|
|
6
|
+
// progress: this one's next write is refused as stale, read again, and
|
|
7
|
+
// the other's progress kept and written back.
|
|
8
|
+
import { playtest } from "@origingame/engine/playtest";
|
|
9
|
+
import { game, type PlatformDump, press, tap, text, TITLE } from "./lib.ts";
|
|
10
|
+
|
|
11
|
+
export default playtest({
|
|
12
|
+
name: "the save, two hours away credited at half the rate, a stale write merged",
|
|
13
|
+
commands: [
|
|
14
|
+
"0.3:play",
|
|
15
|
+
"0.9:own:0:10",
|
|
16
|
+
"0.9:own:1:2",
|
|
17
|
+
"1.0:sparks:500",
|
|
18
|
+
press("Escape", 1.2),
|
|
19
|
+
"1.5:get:Game:saved.json",
|
|
20
|
+
"1.6:relaunch:7200",
|
|
21
|
+
"1.8:get:Game:closed.json",
|
|
22
|
+
"2.2:dump_ui:title.json",
|
|
23
|
+
tap("play", 2.3),
|
|
24
|
+
"3.2:get:Game:welcome.json",
|
|
25
|
+
"3.2:dump_ui:welcome-ui.json",
|
|
26
|
+
"3.25:screenshot:welcome.png",
|
|
27
|
+
tap("Collect", 3.3),
|
|
28
|
+
"3.6:get:Game:collected.json",
|
|
29
|
+
"3.7:stale_save",
|
|
30
|
+
press("Escape", 3.8),
|
|
31
|
+
"4.3:get:Game:merged.json",
|
|
32
|
+
"4.3:dump_platform:platform.json",
|
|
33
|
+
"4.4:quit",
|
|
34
|
+
].join(";"),
|
|
35
|
+
check(run, t) {
|
|
36
|
+
t.equal(run.warnings.join("; "), "", "the script ran without warnings");
|
|
37
|
+
const saved = game(run, "saved.json");
|
|
38
|
+
t.equal(saved.saves, 1, "Escape saved");
|
|
39
|
+
t.equal(saved.rate, 10 * 0.5 * 2 + 2 * 4, "ten embers (doubled) and two storms: 18 a second");
|
|
40
|
+
const closed = game(run, "closed.json");
|
|
41
|
+
t.equal(closed.phase, TITLE, "the relaunch opens on the title");
|
|
42
|
+
t.ok(text(run, "title.json", "play")?.startsWith("Continue") ?? false, "which offers Continue");
|
|
43
|
+
const w = game(run, "welcome.json");
|
|
44
|
+
t.equal(w.owned0, 10, "the save came back");
|
|
45
|
+
t.ok(Math.abs(w.offline - 18 * 0.5 * 7200) < 1e-6, `two hours away at half of 18 a second: 64,800 (${w.offline})`);
|
|
46
|
+
t.equal(w.welcome, true, "the welcome back is up");
|
|
47
|
+
t.equal(text(run, "welcome-ui.json", "Body"), "While you were away (2 h 0 min) the orb made 64.8K sparks.", "and says how long and how much");
|
|
48
|
+
const c = game(run, "collected.json");
|
|
49
|
+
t.equal(c.welcome, false, "Collect closed it");
|
|
50
|
+
t.ok(c.sparks >= saved.sparks + 64800, `the sparks were added (${c.sparks.toFixed(0)})`);
|
|
51
|
+
const m = game(run, "merged.json");
|
|
52
|
+
t.equal(m.saveConflicts, 1, "the write after the other device's was refused as stale");
|
|
53
|
+
t.ok(m.dust === 3 && m.owned0 === 20 && m.lifetime === 1e9, "the save with more progress (the other device's) was kept");
|
|
54
|
+
const p = run.json<PlatformDump>("platform.json");
|
|
55
|
+
const writes = p.calls.filter((x) => x.method === "store.set" && x.args["key"] === "orb");
|
|
56
|
+
const last = writes[writes.length - 1];
|
|
57
|
+
t.equal((last?.args["value"] as { lifetime?: number } | undefined)?.lifetime, 1e9, "and written back");
|
|
58
|
+
t.equal(last?.result?.ok, true, "at the revision read again");
|
|
59
|
+
},
|
|
60
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// A gamepad alone: A on the title plays, A taps the orb, the d-pad's right
|
|
2
|
+
// chooses the next generator, X buys the chosen one (refused when too
|
|
3
|
+
// dear), Y asks for the supernova, Start pauses and B resumes.
|
|
4
|
+
import { playtest } from "@origingame/engine/playtest";
|
|
5
|
+
import { game, PLAY } from "./lib.ts";
|
|
6
|
+
|
|
7
|
+
/** A pad button pressed at t and let go 0.05 s later (the pad set aside, so the release is no input of its own). */
|
|
8
|
+
const pad = (button: string, t: number) => `${t}:gamepad:${button};${(t + 0.05).toFixed(2)}:gamepad:off`;
|
|
9
|
+
|
|
10
|
+
export default playtest({
|
|
11
|
+
name: "A, the d-pad, X, Y, Start and B play the orb",
|
|
12
|
+
commands: [
|
|
13
|
+
// The title's first item holds the focus when the menu opens (og-ui §4), so A plays at once; no d-pad press first.
|
|
14
|
+
pad("a", 1.1),
|
|
15
|
+
"1.9:get:Game:started.json",
|
|
16
|
+
pad("a", 2.0),
|
|
17
|
+
pad("a", 2.1),
|
|
18
|
+
pad("a", 2.2),
|
|
19
|
+
"2.4:get:Game:tapped.json",
|
|
20
|
+
"2.5:sparks:200",
|
|
21
|
+
pad("right", 2.6),
|
|
22
|
+
pad("x", 2.7),
|
|
23
|
+
"2.9:get:Game:bought.json",
|
|
24
|
+
pad("right", 3.0),
|
|
25
|
+
pad("right", 3.1),
|
|
26
|
+
pad("x", 3.2),
|
|
27
|
+
"3.4:get:Game:refused.json",
|
|
28
|
+
"3.5:sparks:300000",
|
|
29
|
+
pad("y", 3.6),
|
|
30
|
+
"3.8:get:Game:asked.json",
|
|
31
|
+
pad("b", 3.9),
|
|
32
|
+
"4.1:get:Game:declined.json",
|
|
33
|
+
pad("start", 4.2),
|
|
34
|
+
"4.4:get:Game:paused.json",
|
|
35
|
+
pad("b", 4.5),
|
|
36
|
+
"4.8:get:Game:resumed.json",
|
|
37
|
+
"4.9:quit",
|
|
38
|
+
].join(";"),
|
|
39
|
+
check(run, t) {
|
|
40
|
+
t.equal(run.warnings.join("; "), "", "the script ran without warnings");
|
|
41
|
+
t.equal(game(run, "started.json").phase, PLAY, "the pad played from the title");
|
|
42
|
+
t.equal(game(run, "tapped.json").taps, 3, "A tapped the orb three times");
|
|
43
|
+
const bought = game(run, "bought.json");
|
|
44
|
+
t.equal(bought.selected, 1, "right chose the Storm");
|
|
45
|
+
t.equal(bought.owned1, 1, "X bought it");
|
|
46
|
+
t.equal(bought.sparks < 200 - 109, true, "for 110");
|
|
47
|
+
const refused = game(run, "refused.json");
|
|
48
|
+
t.equal(refused.selected, 3, "right twice more chose the Star");
|
|
49
|
+
t.ok(refused.refused === 1 && refused.owned3 === 0, "X was refused: a Star costs 13,000");
|
|
50
|
+
t.equal(game(run, "asked.json").confirmNova, true, "Y asked for the supernova");
|
|
51
|
+
t.equal(game(run, "declined.json").confirmNova, false, "B declined it");
|
|
52
|
+
t.equal(game(run, "paused.json").paused, true, "Start paused");
|
|
53
|
+
t.equal(game(run, "resumed.json").paused, false, "B resumed");
|
|
54
|
+
},
|
|
55
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Pause and settings: Escape holds the orb (time scale 0, the embers make
|
|
2
|
+
// nothing); Settings switch to 简体中文 and move the music; Done saves the
|
|
3
|
+
// settings through og.platform; Resume lets the orb make sparks again; the
|
|
4
|
+
// numbers now read in the Chinese units (万, 亿); Escape pauses again and
|
|
5
|
+
// Quit goes back to the title, writing the save once more there.
|
|
6
|
+
import { playtest } from "@origingame/engine/playtest";
|
|
7
|
+
import { game, type PlatformDump, press, tap, text, TITLE } from "./lib.ts";
|
|
8
|
+
|
|
9
|
+
export default playtest({
|
|
10
|
+
name: "Escape pauses, settings switch to 简体中文 and save, numbers read in 万 and 亿",
|
|
11
|
+
commands: [
|
|
12
|
+
"0.3:play",
|
|
13
|
+
"0.9:own:0:4",
|
|
14
|
+
"1.0:sparks:123456789",
|
|
15
|
+
press("Escape", 1.2),
|
|
16
|
+
"1.4:get:Game:paused.json",
|
|
17
|
+
"1.4:get:og.TimeScale.scale:scale.json",
|
|
18
|
+
"1.9:get:Game:held.json",
|
|
19
|
+
"2.0:screenshot:pause.png",
|
|
20
|
+
tap("settings", 2.1),
|
|
21
|
+
"2.7:move_to_text:简体中文",
|
|
22
|
+
"2.72:touch_start",
|
|
23
|
+
"2.75:touch_end",
|
|
24
|
+
"3.0:touch_move_to:Music/Slider",
|
|
25
|
+
"3.02:touch_start",
|
|
26
|
+
"3.05:touch_end",
|
|
27
|
+
"3.4:screenshot:settings-zh.png",
|
|
28
|
+
tap("Done", 3.6),
|
|
29
|
+
"4.0:get:Prefs:prefs.json",
|
|
30
|
+
"4.0:dump_platform:platform.json",
|
|
31
|
+
tap("resume", 4.1),
|
|
32
|
+
"4.8:dump_ui:play-zh.json",
|
|
33
|
+
"4.8:get:Game:resumed.json",
|
|
34
|
+
"4.85:screenshot:play-zh.png",
|
|
35
|
+
"5.4:get:Game:running.json",
|
|
36
|
+
press("Escape", 5.5),
|
|
37
|
+
"6.0:get:Game:repaused.json",
|
|
38
|
+
tap("quit", 6.2),
|
|
39
|
+
"6.8:get:Game:quit.json",
|
|
40
|
+
"6.9:quit",
|
|
41
|
+
].join(";"),
|
|
42
|
+
check(run, t) {
|
|
43
|
+
t.equal(run.warnings.join("; "), "", "the script ran without warnings");
|
|
44
|
+
const paused = game(run, "paused.json");
|
|
45
|
+
t.equal(paused.paused, true, "Escape paused");
|
|
46
|
+
t.equal(run.json<{ value: number }>("scale.json").value, 0, "the orb holds: time scale 0");
|
|
47
|
+
t.equal(game(run, "held.json").sparks, paused.sparks, "the embers made nothing while paused");
|
|
48
|
+
const prefs = run.json<{ value: { language: number; music: number; saves: number } }>("prefs.json").value;
|
|
49
|
+
t.equal(prefs.language, 1, "简体中文 was chosen");
|
|
50
|
+
t.ok(prefs.music < 0.7, `the music slider moved (${prefs.music.toFixed(2)})`);
|
|
51
|
+
t.equal(prefs.saves, 1, "Done saved once");
|
|
52
|
+
const p = run.json<PlatformDump>("platform.json");
|
|
53
|
+
const saved = p.calls.find((c) => c.method === "store.set" && c.args["key"] === "og.settings");
|
|
54
|
+
t.equal((saved?.args["value"] as { language?: string } | undefined)?.language, "zh-CN", "the settings went to og.platform with the language");
|
|
55
|
+
t.equal(text(run, "play-zh.json", "Amount", "Sparks"), "1.23亿", "123,456,789 reads 1.23亿");
|
|
56
|
+
t.equal(text(run, "play-zh.json", "Rate"), "每秒 +2", "the rate reads 每秒 +2");
|
|
57
|
+
t.equal(text(run, "play-zh.json", "Name", "Head"), "余烬", "the first card is 余烬");
|
|
58
|
+
t.equal(game(run, "resumed.json").paused, false, "Resume closed the pause menu");
|
|
59
|
+
t.ok(game(run, "running.json").sparks > game(run, "resumed.json").sparks, "and the embers make sparks again");
|
|
60
|
+
const repaused = game(run, "repaused.json");
|
|
61
|
+
const quit = game(run, "quit.json");
|
|
62
|
+
t.equal(quit.phase, TITLE, "Quit went back to the title");
|
|
63
|
+
t.equal(quit.saves, repaused.saves + 1, "and wrote the save there, after the pause's");
|
|
64
|
+
t.frame(["pause.png", "settings-zh.png", "play-zh.png"], () => {
|
|
65
|
+
for (const f of ["pause.png", "settings-zh.png", "play-zh.png"]) t.equal(run.png(f).width, 1280, `${f} captured`);
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// The phone layout (390×844 at 3×, a notch and a home bar): the title's
|
|
2
|
+
// menu, then the wallet and Pause below the notch, the four generators
|
|
3
|
+
// along the bottom above the home bar with touch-sized buttons, the orb
|
|
4
|
+
// above them taking a finger's tap, and a card's button buying.
|
|
5
|
+
import { playtest } from "@origingame/engine/playtest";
|
|
6
|
+
import { game, PLAY, tap, tapAt, type UiElement, ui } from "./lib.ts";
|
|
7
|
+
|
|
8
|
+
/** The orb's centre on the phone (src/ui.ts: 7.4 m across, the orb at y 1.4 m). */
|
|
9
|
+
const ORB = { x: 195, y: 422 - 1.4 * (390 / 7.4) };
|
|
10
|
+
|
|
11
|
+
export default playtest({
|
|
12
|
+
name: "the phone layout: a HUD inside the safe area, the orb tapped, a card bought",
|
|
13
|
+
size: "1170x2532",
|
|
14
|
+
commands: [
|
|
15
|
+
"0.1:viewport:phone",
|
|
16
|
+
"0.9:dump_ui:title.json",
|
|
17
|
+
tap("play", 1.0),
|
|
18
|
+
"1.1:touch_move:10,400",
|
|
19
|
+
"1.7:get:Game:started.json",
|
|
20
|
+
tapAt(ORB.x, ORB.y, 1.8),
|
|
21
|
+
tapAt(ORB.x + 20, ORB.y - 10, 1.9),
|
|
22
|
+
"2.1:get:Game:tapped.json",
|
|
23
|
+
"2.2:sparks:100",
|
|
24
|
+
tap("Shop/Gen#0/Buy", 2.3),
|
|
25
|
+
"2.4:touch_move:10,400",
|
|
26
|
+
"2.6:get:Game:bought.json",
|
|
27
|
+
"2.7:dump_ui:hud.json",
|
|
28
|
+
"2.75:screenshot:play.png",
|
|
29
|
+
"2.8:quit",
|
|
30
|
+
].join(";"),
|
|
31
|
+
check(run, t) {
|
|
32
|
+
t.equal(run.warnings.join("; "), "", "the script ran without warnings");
|
|
33
|
+
const title = ui(run, "title.json").elements;
|
|
34
|
+
t.ok(title.some((e) => e.id === "play") && title.every((e) => e.y >= 47 && e.y + e.height <= 844 - 34), "the title's menu sits inside the safe area");
|
|
35
|
+
t.equal(game(run, "started.json").phase, PLAY, "a tap on Play showed the orb");
|
|
36
|
+
t.equal(game(run, "tapped.json").taps, 2, "two taps on the orb");
|
|
37
|
+
t.equal(game(run, "bought.json").owned0, 1, "the Ember card's button bought one");
|
|
38
|
+
const hud = ui(run, "hud.json");
|
|
39
|
+
const el = (id: string, parent?: string): UiElement | undefined => hud.elements.find((e) => e.id === id && (parent === undefined || e.parent === parent));
|
|
40
|
+
t.equal(hud.surface.width, 390, "laid out for a 390 px wide phone");
|
|
41
|
+
for (const id of ["Wallet", "Corner"]) t.ok((el(id)?.y ?? 0) >= 47, `${id} sits below the notch`);
|
|
42
|
+
const shop = el("Shop");
|
|
43
|
+
t.ok(shop !== undefined && shop.y + shop.height <= 844 - 34 && shop.x >= 0 && shop.x + shop.width <= 390, "the generators sit above the home bar, inside the width");
|
|
44
|
+
const buys = hud.elements.filter((e) => e.id === "Buy");
|
|
45
|
+
t.equal(buys.length, 4, "four generator buttons");
|
|
46
|
+
t.ok(buys.every((e) => e.width >= 44 && e.height >= 44), "each at least 44 px square");
|
|
47
|
+
t.ok(shop !== undefined && ORB.y < shop.y - 80, "the orb sits well above them");
|
|
48
|
+
t.frame("play.png", () => t.equal(run.png("play.png").width, 1170, "play.png captured at the phone's device pixels"));
|
|
49
|
+
},
|
|
50
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Determinism: a run with taps, purchases, a milestone and the motes
|
|
2
|
+
// circling, run twice in fresh processes, ends on the same world hash.
|
|
3
|
+
import { playtest } from "@origingame/engine/playtest";
|
|
4
|
+
import { game, ORB, press, tap, tapAt } from "./lib.ts";
|
|
5
|
+
|
|
6
|
+
export default playtest({
|
|
7
|
+
name: "a run with taps, purchases and a milestone replays to the same hash",
|
|
8
|
+
runs: 2,
|
|
9
|
+
commands: [
|
|
10
|
+
tap("play", 0.3),
|
|
11
|
+
...Array.from({ length: 8 }, (_, i) => tapAt(ORB.x, ORB.y, +(0.9 + i * 0.1).toFixed(2))),
|
|
12
|
+
"1.8:sparks:400",
|
|
13
|
+
press("Digit1", 1.9),
|
|
14
|
+
press("Digit1", 2.0),
|
|
15
|
+
press("Digit2", 2.1),
|
|
16
|
+
"2.2:own:0:9",
|
|
17
|
+
press("Digit1", 2.3),
|
|
18
|
+
"3.5:get:Game:end.json",
|
|
19
|
+
"3.6:quit",
|
|
20
|
+
].join(";"),
|
|
21
|
+
check(run, t) {
|
|
22
|
+
t.equal(run.warnings.join("; "), "", "the script ran without warnings");
|
|
23
|
+
t.equal(run.hashes.length, 2, "two runs");
|
|
24
|
+
t.equal(run.hashes[1], run.hashes[0], `both runs end on hash ${run.hashes[0]}`);
|
|
25
|
+
const end = game(run, "end.json");
|
|
26
|
+
t.ok(end.taps === 8 && end.owned0 === 10, `the run had its taps (${end.taps}) and a milestone (${end.owned0} embers)`);
|
|
27
|
+
},
|
|
28
|
+
});
|