@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,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
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Prepares this Game's cloud sandbox snapshot: Origin runs it once per
|
|
3
|
+
# snapshot build, in the checkout, without a terminal, for at most 20 minutes,
|
|
4
|
+
# and saves the result for new sandboxes of the Game. Keep it idempotent; it
|
|
5
|
+
# never receives credentials. Only ignored output is kept: node_modules/ and
|
|
6
|
+
# cooked/ (og cook is incremental, so a new sandbox starts cooked).
|
|
7
|
+
set -eu
|
|
8
|
+
if [ -f bun.lock ]; then bun install --frozen-lockfile; else bun install; fi
|
|
9
|
+
og="${ORIGIN_OG_BIN:-og}"
|
|
10
|
+
if ! command -v "$og" >/dev/null 2>&1; then
|
|
11
|
+
echo "no og on this executor class; the first og run cooks instead"
|
|
12
|
+
exit 0
|
|
13
|
+
fi
|
|
14
|
+
"$og" cook .
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Asset licences
|
|
2
|
+
|
|
3
|
+
| File | Licence | Origin |
|
|
4
|
+
|---|---|---|
|
|
5
|
+
| `assets/tiles/dungeon.png`, `assets/tiles/*.ogt` | CC0 1.0 | self-made: the dungeon tileset (the wall terrain's 47 blob shapes, floor, moss, torch, door) drawn pixel by pixel by `tools/make-assets.py` (standard library only, deterministic), with its tileset and import settings |
|
|
6
|
+
| `assets/maps/tomb.tmj` | CC0 1.0 | self-made: the two-room Tiled map, same script |
|
|
7
|
+
| `assets/sprites/*.png`, `assets/sprites/*.ogt` | CC0 1.0 | self-made: the hero, slime, coin and key flipbook sheets, same script |
|
|
8
|
+
| `assets/audio/*.wav` | CC0 1.0 | self-made: synthesised tones and noise, same script |
|
|
9
|
+
| `assets/fonts/ui.ttf` | SIL Open Font License 1.1 (`assets/fonts/OFL.txt`) | Noto Sans SC Regular, subset to Latin-1 by the same script from the project template's `assets/fonts/NotoSansSC-Regular-subset.ttf` |
|
|
10
|
+
| `references/*/*.png` | CC0 1.0 | frames of this game recorded by `tests/frames.og.ts` |
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# Tomb Room — the topdown archetype (2D)
|
|
2
|
+
|
|
3
|
+
A small Zelda-like. Two rooms of a tomb: a slime guards the west room and
|
|
4
|
+
the key in it, coins lie in both, and a locked door stands between them.
|
|
5
|
+
Walk with WASD or the arrows, swing the sword with Space, dash with Shift.
|
|
6
|
+
Take the key to the door and the east room opens.
|
|
7
|
+
|
|
8
|
+
古墓房间:一个小型俯视角冒险。西边的房间里有史莱姆守着钥匙,两个房间都散落着金币,中间是一扇锁着的门。WASD 或方向键移动,空格挥剑,Shift 冲刺;拿到钥匙走到门前,东边的房间就打开了。
|
|
9
|
+
|
|
10
|
+
This is a complete Origin Engine project: `og run .` plays it headless,
|
|
11
|
+
`og build .` makes the play page, `og test .` runs its playtests. It shows
|
|
12
|
+
the 2D modules working together: a Tiled map with an auto-tiled, colliding
|
|
13
|
+
wall terrain; flipbook sprites with frame events; y-sorted sprites with
|
|
14
|
+
blob shadows; tweens, hit stop, camera trauma and kick, floating damage
|
|
15
|
+
numbers and afterimages; a 2D follow camera held inside each room by a
|
|
16
|
+
camera zone; and an enemy whose mind is data: a behaviour tree
|
|
17
|
+
(`assets/ai/slime.ogt`, og.ai) walking a grid made from the map's wall
|
|
18
|
+
collision (og.nav).
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
og cook . # assets/ into cooked/ (og run and og build cook too)
|
|
22
|
+
og run . --headless --commands "0.2:key_down:KeyD;1.2:key_up:KeyD;1.3:screenshot:play.png;1.4:quit"
|
|
23
|
+
og test . # tests/*.og.ts through og run --headless --commands
|
|
24
|
+
og build . # dist/: the play page for the browser
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## What it demonstrates, and the skills it exercises
|
|
28
|
+
|
|
29
|
+
The 2D modules together: a Tiled map with an auto-tiled, colliding wall
|
|
30
|
+
terrain the door opens through `og.tilemap.setTile` and reads back with
|
|
31
|
+
`getTile` (og-game §8), a four-direction flipbook hero on
|
|
32
|
+
og.CharacterController in its top-down mode whose walk steps are heard
|
|
33
|
+
(og-game §13; og-feel §9, §12; og-audio §1), a slime that patrols, chases,
|
|
34
|
+
bites and flees as og.ai data on an og.nav grid from the map (og-ai §1–§5),
|
|
35
|
+
hits felt five ways (hit stop, flash and squash, knockback, shake and kick,
|
|
36
|
+
floating damage and a crit) both ways (og-feel §3–§7), a dash with
|
|
37
|
+
afterimages, sorting by feet (og-feel §10), and a camera that follows with a
|
|
38
|
+
dead zone and scrolls between the rooms by og.CameraZone's scrollTime
|
|
39
|
+
(og-feel §8). `og polish .` (2026-10-19): feel 97 (taste n/a: the HUD is
|
|
40
|
+
screen-space text).
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## Rules
|
|
44
|
+
|
|
45
|
+
- **Moving.** WASD or the arrows move the hero at `Tuning.speed` on the
|
|
46
|
+
tick the keys are read; a diagonal is normalised. The hero faces the
|
|
47
|
+
stronger axis (a diagonal keeps the old facing when it is one of its keys).
|
|
48
|
+
The hero's body is `og.CharacterController` in its top-down mode
|
|
49
|
+
(`gravityScale` 0): a kinematic box the engine slides along the walls,
|
|
50
|
+
moved by `move` each tick (walk, dash and knockback summed). The slime
|
|
51
|
+
and the hero pass through each other.
|
|
52
|
+
- **The sword.** Space (or J) swings in the facing direction. The hero is
|
|
53
|
+
rooted for the swing's 15 ticks. Damage lands on the attack clip's strike
|
|
54
|
+
frame (its `"hit"` event), within `Tuning.reach` of a point one reach
|
|
55
|
+
ahead of the chest. Hits deal 12, 12, then a 26 crit: the slime (50 hp)
|
|
56
|
+
dies on the third.
|
|
57
|
+
- **The dash.** Shift (or K) dashes `Tuning.dashTicks` ticks at
|
|
58
|
+
`Tuning.dashSpeed` along the keys (or the facing), leaving afterimages;
|
|
59
|
+
then `Tuning.dashCooldown` ticks before the next.
|
|
60
|
+
- **The slime.** Its behaviour tree, `assets/ai/slime.ogt`, is re-checked
|
|
61
|
+
every tick, first branch first: under 30 hp, and having seen the hero in
|
|
62
|
+
the last 3 s, it **flees** to 5 m away; seeing the hero within 0.8 m it
|
|
63
|
+
**bites** (the game's `bite` command, every 0.5 s at most); seeing the
|
|
64
|
+
hero it **chases** at `Tuning.slimeSpeed`; having lost the hero in the
|
|
65
|
+
last 2 s it **searches** where it last saw it; otherwise it **patrols**
|
|
66
|
+
its beat (four points round the key's corner, 0.6 s at each). It sees all
|
|
67
|
+
round within `Tuning.slimeSight`, but not through walls (the pillar hides
|
|
68
|
+
the hero). og.nav walks it on the map's grid around the walls, writing its
|
|
69
|
+
velocity. A bite costs a heart, knocks the hero away and makes it blink
|
|
70
|
+
for `Tuning.iframes` ticks; the slime bounces back off the bite.
|
|
71
|
+
- **Pickups.** Coins and the key are taken when the hero's chest touches
|
|
72
|
+
them. The key is spent opening the door.
|
|
73
|
+
- **The door.** Without the key, walking up to it says "locked". With it,
|
|
74
|
+
the doorway's four wall cells clear (`og.tilemap.setTile` re-tiles their
|
|
75
|
+
neighbours and drops their colliders) and the hero walks into room 2.
|
|
76
|
+
- **Falling.** At 0 hearts the hero lies still for 90 ticks, then stands up
|
|
77
|
+
at the spawn with full hearts. Coins, the key and the door stay as they
|
|
78
|
+
are.
|
|
79
|
+
|
|
80
|
+
Everything is deterministic from the project seed: the clock is the fixed
|
|
81
|
+
tick, og.ai and og.nav are deterministic and in snapshots, the hovering is a triangle
|
|
82
|
+
wave, and `tests/replay.og.ts` replays a whole visit to the same hash.
|
|
83
|
+
|
|
84
|
+
## How a hit feels
|
|
85
|
+
|
|
86
|
+
Each sword hit does five things at once, all in `hitSlime`
|
|
87
|
+
(`src/systems.ts`):
|
|
88
|
+
|
|
89
|
+
| Effect | Call | Default |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| flash and squash | `og.tween.flash`, `og.tween.squash` (their impact holds through the hit stop) | white for 0.14 s; 1.45 × 0.6 recovering in 0.28 s |
|
|
92
|
+
| knockback | the slime's `knockX/knockY`, decaying ×0.82 a tick | `Tuning.knockback` (9 m/s), 16 ticks stunned |
|
|
93
|
+
| hit stop | `og.time.hitStop` | `Tuning.hitStop` (4 ticks), 7 on the crit |
|
|
94
|
+
| camera | `og.camera.shake`, `og.camera.kick` | trauma `Tuning.shake` (0.25, ×1.8 on the crit); a 0.12 m kick away from the hero |
|
|
95
|
+
| damage number | `og.render.floatingText` | white, the crit gold and bigger |
|
|
96
|
+
|
|
97
|
+
The death is two more tweens: the scale to (1.7, 0) with `backIn` and the
|
|
98
|
+
sprite's opacity to 0; the slime hides 30 ticks later. Pickups pop up,
|
|
99
|
+
stretch and fade, and squash the hero a little.
|
|
100
|
+
|
|
101
|
+
## The map
|
|
102
|
+
|
|
103
|
+
`assets/maps/tomb.tmj` is a Tiled map (orthogonal, 40 × 12 cells of
|
|
104
|
+
16 px, uncompressed layers) over the tileset `assets/tiles/dungeon.ogt`
|
|
105
|
+
(Tiled's `../tiles/dungeon.tsx` reference resolves to it). One cell is one
|
|
106
|
+
world unit; the map's bottom-left corner is the world origin, y up.
|
|
107
|
+
|
|
108
|
+
| Layer | Kind | Holds |
|
|
109
|
+
|---|---|---|
|
|
110
|
+
| `floor` | tiles | three slab variants and moss |
|
|
111
|
+
| `walls` | terrain `wall` (`autotile`), `collision` | the outer walls, the dividing wall at x 19–20, three pillars; blob47 picks each cell's shape and the solid tiles collide |
|
|
112
|
+
| `door` | tiles | the door over the doorway's wall cells |
|
|
113
|
+
| `decor` | tiles, `z` = 15 | the animated torches on the north wall, drawn above the actors |
|
|
114
|
+
| `objects` | objects | `spawn`, `enemy` (the slime), `coin` ×6, `key`, `door` (the doorway's rectangle) and `room` ×2 (each room's rectangle) |
|
|
115
|
+
|
|
116
|
+
`start` (`src/main.ts`) reads the objects: actors spawn at their points,
|
|
117
|
+
the door's rectangle gives the cells to clear, and each room's rectangle
|
|
118
|
+
becomes an `og.CameraZone`. The hero carries `og.Follow2D` (a small dead
|
|
119
|
+
zone, `smoothTime` 0.12, `lookAhead` 0.15), and the orthographic view is
|
|
120
|
+
12 cells tall: a whole room, as in the old top-down adventures, and the
|
|
121
|
+
view scrolls across to room 2 over the zones' `scrollTime` (0.45 s) when
|
|
122
|
+
the hero crosses the doorway (`tests/door.og.ts` samples og.Camera's zone
|
|
123
|
+
fields through the crossing with `get:og.Camera`).
|
|
124
|
+
|
|
125
|
+
## State
|
|
126
|
+
|
|
127
|
+
| Type | Kind | What it holds |
|
|
128
|
+
|---|---|---|
|
|
129
|
+
| `Game` | resource | hearts, coins, the key, the door, the room, the slime's hp and hits, the hero's position and facing for the other systems, the strike of this tick, footsteps, invulnerability, knockback, the spawn, the door's cells, sound requests, the font |
|
|
130
|
+
| `Tuning` | resource | the knobs below |
|
|
131
|
+
| `Hero` | the hero | the swing and dash timers, the dash direction, dashes so far |
|
|
132
|
+
| `Slime` | the slime | hp, knockback, stun, bites, death, its clip indices (it also has `og.NavAgent`, `og.AiAgent` and `og.Perception`; the hero has `og.Perceivable`) |
|
|
133
|
+
| `Pickup` | coins and the key | kind, taken, the resting height |
|
|
134
|
+
| `Sfx` | one entity per sound | its `Sound.*` bit (the entity also has `og.AudioSource`) |
|
|
135
|
+
| `Report` | the hero | the run's facts, copied from `Game` each tick so `dump_entity:hero` shows them |
|
|
136
|
+
|
|
137
|
+
## Systems (in tick order)
|
|
138
|
+
|
|
139
|
+
| System | Reads | Writes | Job |
|
|
140
|
+
|---|---|---|---|
|
|
141
|
+
| `Control` | `og.Input`, `Tuning`, `og.FlipbookEvent` | `Game`, `Hero`, `og.Transform`, `og.CharacterController`, `og.Flipbook`, `og.Sprite`, `og.Afterimage` | footsteps and strikes from the hero's frame events; walk, swing, dash, knockback, blinking, falling; the clip and flip |
|
|
142
|
+
| `Slimes` | `Tuning` | `Game`, `Slime`, `og.Transform`, `og.Velocity`, `og.Flipbook`, `og.Sprite`, `og.NavAgent`, `og.AiAgent`, `og.Perception` | take strikes (all five effects), knockback and stun (`NavAgent.drive` off meanwhile), death (the AI stops), sight from `Tuning.slimeSight` |
|
|
143
|
+
| `Pickups` | — | `Game`, `Pickup`, `og.Transform`, `og.Sprite` | hover, take by overlap, pop and fade |
|
|
144
|
+
| `Door` | — | `Game` | the room, "locked", opening the doorway |
|
|
145
|
+
| `Voices` | `Sfx` | `Game`, `og.AudioSource` | plays each requested sound once |
|
|
146
|
+
| `Hud` | `Game` | `Report` | screen text: hearts, coins, the key, the room, the controls for 5 s; the hero's `Report` |
|
|
147
|
+
|
|
148
|
+
og.ai runs before these systems (the slime's decision for the tick) and
|
|
149
|
+
og.nav after them (moving it). During a hit stop the systems still tick but
|
|
150
|
+
hold their timers, as physics, flipbooks and tweens do.
|
|
151
|
+
|
|
152
|
+
## Tunables (`Tuning`)
|
|
153
|
+
|
|
154
|
+
| Field | Default | Range | Unit | What it changes |
|
|
155
|
+
|---|---|---|---|---|
|
|
156
|
+
| `speed` | 4.5 | 1–10 | m/s | walking pace |
|
|
157
|
+
| `dashSpeed` | 13 | 4–24 | m/s | the dash |
|
|
158
|
+
| `dashTicks` | 9 | 2–30 | ticks | the dash's length (dashSpeed × dashTicks / 60 m) |
|
|
159
|
+
| `dashCooldown` | 36 | 0–120 | ticks | ticks between dashes |
|
|
160
|
+
| `reach` | 0.75 | 0.2–2 | m | the sword's reach |
|
|
161
|
+
| `hitStop` | 4 | 0–12 | ticks | the freeze per hit (the crit adds 3) |
|
|
162
|
+
| `shake` | 0.25 | 0–1 | trauma | camera shake per hit |
|
|
163
|
+
| `slimeSpeed` | 1.6 | 0–6 | m/s | the slime's chase and search (the behaviour reads it; it patrols at 0.8) |
|
|
164
|
+
| `slimeSight` | 5 | 0–12 | m | how far the slime sees |
|
|
165
|
+
| `knockback` | 9 | 0–20 | m/s | both ways: the struck slime and the bitten hero |
|
|
166
|
+
| `iframes` | 50 | 0–180 | ticks | the hero's blinking after a bite |
|
|
167
|
+
|
|
168
|
+
They are the field defaults in `src/components.ts`.
|
|
169
|
+
|
|
170
|
+
## What a Maker changes first
|
|
171
|
+
|
|
172
|
+
1. **Feel**: `speed`, `dashSpeed`, `hitStop`, `shake`, `knockback` in
|
|
173
|
+
`Tuning`, and the damage list `DAMAGE` in `src/systems.ts`.
|
|
174
|
+
2. **The level**: open `assets/maps/tomb.tmj` in Tiled. Paint the `walls`
|
|
175
|
+
layer with any tile (the terrain picks the shape), move the objects, add
|
|
176
|
+
coins (`type` coin) or rooms (`type` room). Keep the door's rectangle on
|
|
177
|
+
wall cells. Or change `map_layout` in `tools/make-assets.py`.
|
|
178
|
+
3. **Enemies**: another `enemy` object needs its own name (the `slime`
|
|
179
|
+
command addresses `slime`); `Slimes` already loops over every slime.
|
|
180
|
+
What a slime does is `assets/ai/slime.ogt`: reorder the branches, change
|
|
181
|
+
the numbers, add a `wander` or a state machine (the nodes are listed in
|
|
182
|
+
the og-ai skill). `dump_ai:slime` shows the running node, the path to it,
|
|
183
|
+
the blackboard and what it sees; `dump_nav:slime` its path.
|
|
184
|
+
4. **Look**: the tiles and sprites are drawn in `tools/make-assets.py`;
|
|
185
|
+
run `python3 tools/make-assets.py && og cook .`. The hero's clip order is
|
|
186
|
+
fixed (`src/art.ts` checks it).
|
|
187
|
+
5. **Words**: the HUD strings in `Hud`. The font holds Latin-1 only; add
|
|
188
|
+
characters to `subset_font`'s text and `chars` in `ui.ttf.ogt` first.
|
|
189
|
+
|
|
190
|
+
## Commands (for scripts and agents)
|
|
191
|
+
|
|
192
|
+
| Action | Does |
|
|
193
|
+
|---|---|
|
|
194
|
+
| `place:<x>:<y>` | puts the hero's feet at a world point, standing still |
|
|
195
|
+
| `slime:<x>:<y>` | moves the slime |
|
|
196
|
+
| `give:<key\|coin\|door>` | the key, a coin, or the doorway opened |
|
|
197
|
+
| `face:<down\|up\|left\|right>` | turns the hero |
|
|
198
|
+
| `bite:<slime>` | the behaviour's attack (og.ai queues it with the slime's handle): hurts a hero within 0.85 m |
|
|
199
|
+
| `dump_ai:slime[:file]`, `dump_nav:slime[:file]` | the slime's behaviour, running node, blackboard and perception; its path |
|
|
200
|
+
| core actions | `key_down:KeyD`/`key_up:KeyD` walk, `Space` swings, `ShiftLeft` dashes; `dump_entity:hero` shows `Report` |
|
|
201
|
+
|
|
202
|
+
`facts` (the probe's `state()`) reports `room`, `hp`, `coins`, `hasKey`,
|
|
203
|
+
`enemyHp`, `doorOpen`, `doorWall`, the hero's and slime's positions,
|
|
204
|
+
`facing`, `steps`, `swings`, `hits`, `hurts` and `dashes`.
|
|
205
|
+
|
|
206
|
+
## Playtests (`og test .`)
|
|
207
|
+
|
|
208
|
+
| File | Proves |
|
|
209
|
+
|---|---|
|
|
210
|
+
| `tests/walk.og.ts` | one second of D walks 4.5 m; footsteps come from the walk clip's events; up and left stop at the wall terrain's colliders; facing, the side clip and its flip |
|
|
211
|
+
| `tests/ai.og.ts` | out of sight the slime patrols its beat on og.nav's path; the hero 3 m off is seen three ticks later and the patrol gives way to the chase at about 1.6 m/s; the bite lands through the `bite` command |
|
|
212
|
+
| `tests/flee.og.ts` | the pillar hides the hero 4.5 m away; below the pillar it is seen and chased; under 30 hp the slime flees and keeps over 3.5 m away |
|
|
213
|
+
| `tests/combat.og.ts` | a bite costs a heart and knocks the hero back; the hit stop holds the slime still, white and squashed; 12, 12, 26 kill it; it ends hidden and squashed to nothing |
|
|
214
|
+
| `tests/door.og.ts` | the doorway blocks without the key; walking over the key takes it; the door opens (the wall cell cleared), the key is spent, the hero reaches room 2 |
|
|
215
|
+
| `tests/frames.og.ts` | four frames match `references/<tier>/`: the west room at rest, the crit's freeze frame, a dash, the east room through the open doorway (`OG_FX_CAPTURE=1 og test .` records them) |
|
|
216
|
+
| `tests/replay.og.ts` | a whole visit, run twice in fresh processes, ends on the same world hash |
|
|
217
|
+
|
|
218
|
+
Each one fails when the behaviour it names is broken (checked by halving
|
|
219
|
+
the walk speed, dropping the hit's flash, leaving the doorway's walls in
|
|
220
|
+
place, drawing the map over the actors, and wandering by `Math.random`;
|
|
221
|
+
the AI ones by sight through walls, a bite that never lands and a flee
|
|
222
|
+
threshold of 0; `tests/evidence/E6/break-and-restore.md`).
|
|
223
|
+
|
|
224
|
+
## Assets
|
|
225
|
+
|
|
226
|
+
`tools/make-assets.py` regenerates every asset byte for byte: the tileset
|
|
227
|
+
and its blob47 rules, the Tiled map, the flipbook sheets, the sounds and the
|
|
228
|
+
font subset. `LICENSES.md` lists them.
|
|
229
|
+
|
|
230
|
+
## Known limits (engine gaps, not design)
|
|
231
|
+
|
|
232
|
+
- The playtests read most of the run through the hero's `Report` copy,
|
|
233
|
+
which predates `get:` on resources (E7); `set:Tuning.<field>=<value>`
|
|
234
|
+
now tunes the resource from the command channel.
|
|
235
|
+
- The blob shadows (0.35) barely show on the dark floor.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
type behaviour
|
|
2
|
+
version 1
|
|
3
|
+
---
|
|
4
|
+
# The slime (crates/og-ai/README.md has every node). og.ai runs it before
|
|
5
|
+
# the game's systems; the first branch that does not fail decides, and the
|
|
6
|
+
# tree is re-checked every tick, so seeing the hero halts the patrol at once.
|
|
7
|
+
name "slime"
|
|
8
|
+
route beat { # the west room's middle, round the key's corner
|
|
9
|
+
point 13.5 6.5 0
|
|
10
|
+
point 17.5 6.5 0
|
|
11
|
+
point 17.5 3.5 0
|
|
12
|
+
point 13.5 3.5 0
|
|
13
|
+
}
|
|
14
|
+
tree {
|
|
15
|
+
selector {
|
|
16
|
+
sequence flee { # hurt and recently threatened: keep away
|
|
17
|
+
check Slime.hp below 30
|
|
18
|
+
check sinceSeen below 3
|
|
19
|
+
flee {
|
|
20
|
+
speed 2.6
|
|
21
|
+
distance 5
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
sequence attack { # in reach: bite (the game's `bite` command)
|
|
25
|
+
check sees
|
|
26
|
+
check targetDistance below 0.8
|
|
27
|
+
command bite {
|
|
28
|
+
cooldown 0.5
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
sequence chase { # in sight (Perception.sight is Tuning.slimeSight)
|
|
32
|
+
check sees
|
|
33
|
+
chase {
|
|
34
|
+
speed Tuning.slimeSpeed
|
|
35
|
+
reach 0.6
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
sequence search { # lost it: go where it was last seen
|
|
39
|
+
check sinceSeen below 2
|
|
40
|
+
move_to lastSeen {
|
|
41
|
+
speed Tuning.slimeSpeed
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
patrol beat {
|
|
45
|
+
wait 0.6
|
|
46
|
+
speed 0.8
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|