@origingame/engine 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/bin/og.mjs +25 -0
- package/dist/web/origin-engine.d.ts +1233 -0
- package/dist/web/origin-engine.js +8628 -0
- package/dist/web/origin-engine_bg.wasm +0 -0
- package/dist/web/origin-engine_bg.wasm.d.ts +366 -0
- package/dist/web-publish/origin-engine-lanes.js +8509 -0
- package/dist/web-publish/origin-engine-lanes_bg.wasm +0 -0
- package/dist/web-publish/origin-engine.d.ts +1176 -0
- package/dist/web-publish/origin-engine.js +8313 -0
- package/dist/web-publish/origin-engine_bg.wasm +0 -0
- package/dist/web-publish/origin-engine_bg.wasm.d.ts +347 -0
- package/dist/web-publish/snippets/wasm-bindgen-rayon-38edf6e439f6d70d/src/workerHelpers.no-bundler.js +77 -0
- package/dist/web-publish-2d/origin-engine-lanes.js +8509 -0
- package/dist/web-publish-2d/origin-engine-lanes_bg.wasm +0 -0
- package/dist/web-publish-2d/origin-engine.d.ts +1176 -0
- package/dist/web-publish-2d/origin-engine.js +8313 -0
- package/dist/web-publish-2d/origin-engine_bg.wasm +0 -0
- package/dist/web-publish-2d/origin-engine_bg.wasm.d.ts +347 -0
- package/dist/web-publish-2d/snippets/wasm-bindgen-rayon-38edf6e439f6d70d/src/workerHelpers.no-bundler.js +77 -0
- package/knowledge/README.md +31 -0
- package/knowledge/knowledge.json +13 -0
- package/knowledge/looks/catalogue.json +1871 -0
- package/knowledge/package.json +33 -0
- package/knowledge/rules/platform.json +329 -0
- package/knowledge/scaffold/README.md +27 -0
- package/knowledge/scaffold/distribution.json +48 -0
- package/knowledge/scaffold/origin-engine.json +19 -0
- package/knowledge/scaffold/template/.origin/setup +14 -0
- package/knowledge/scaffold/template/LICENSES.md +23 -0
- package/knowledge/scaffold/template/README.md +37 -0
- package/knowledge/scaffold/template/assets/ai/guard.ogt +48 -0
- package/knowledge/scaffold/template/assets/audio/click.ogt +9 -0
- package/knowledge/scaffold/template/assets/fonts/NotoSansSC-Regular-subset.ttf.ogt +12 -0
- package/knowledge/scaffold/template/assets/fonts/OFL.txt +93 -0
- package/knowledge/scaffold/template/assets/looks/origin-game.ogt +12 -0
- package/knowledge/scaffold/template/assets/nav/yard.ogt +27 -0
- package/knowledge/scaffold/template/assets/scenes/main.ogt +34 -0
- package/knowledge/scaffold/template/assets/strings/en.ogt +14 -0
- package/knowledge/scaffold/template/assets/textures/stone_n.png.ogt +7 -0
- package/knowledge/scaffold/template/gitattributes +22 -0
- package/knowledge/scaffold/template/package.json +18 -0
- package/knowledge/scaffold/template/project.ogt +25 -0
- package/knowledge/scaffold/template/src/main.ts +246 -0
- package/knowledge/scaffold/template/tests/guard.og.ts +67 -0
- package/knowledge/scaffold/template/tests/smoke.og.ts +43 -0
- package/knowledge/scaffold/template/tsconfig.json +17 -0
- package/knowledge/scripts/lib.ts +203 -0
- package/knowledge/scripts/mount.ts +210 -0
- package/knowledge/scripts/pack.ts +24 -0
- package/knowledge/scripts/scaffold.ts +72 -0
- package/knowledge/scripts/validate.ts +749 -0
- package/knowledge/skills/og-ai/SKILL.md +386 -0
- package/knowledge/skills/og-audio/SKILL.md +191 -0
- package/knowledge/skills/og-build/SKILL.md +330 -0
- package/knowledge/skills/og-core/SKILL.md +285 -0
- package/knowledge/skills/og-feel/SKILL.md +385 -0
- package/knowledge/skills/og-game/SKILL.md +1479 -0
- package/knowledge/skills/og-i18n/SKILL.md +220 -0
- package/knowledge/skills/og-platform-sdk/SKILL.md +199 -0
- package/knowledge/skills/og-taste/SKILL.md +537 -0
- package/knowledge/skills/og-ui/SKILL.md +444 -0
- package/knowledge/wiki/ai.md +627 -0
- package/knowledge/wiki/anim.md +1187 -0
- package/knowledge/wiki/atmosphere.md +636 -0
- package/knowledge/wiki/audio.md +732 -0
- package/knowledge/wiki/camera.md +711 -0
- package/knowledge/wiki/commands.md +357 -0
- package/knowledge/wiki/game.md +220 -0
- package/knowledge/wiki/i18n.md +213 -0
- package/knowledge/wiki/index.md +1027 -0
- package/knowledge/wiki/input.md +1714 -0
- package/knowledge/wiki/load.md +1280 -0
- package/knowledge/wiki/math.md +166 -0
- package/knowledge/wiki/nav.md +1154 -0
- package/knowledge/wiki/physics.md +3142 -0
- package/knowledge/wiki/platform.md +857 -0
- package/knowledge/wiki/probe.md +1692 -0
- package/knowledge/wiki/reflect.md +285 -0
- package/knowledge/wiki/render.md +3511 -0
- package/knowledge/wiki/rng.md +393 -0
- package/knowledge/wiki/stream.md +376 -0
- package/knowledge/wiki/system.md +197 -0
- package/knowledge/wiki/systems.md +43 -0
- package/knowledge/wiki/terrain.md +727 -0
- package/knowledge/wiki/tilemap.md +422 -0
- package/knowledge/wiki/time.md +150 -0
- package/knowledge/wiki/transform.md +230 -0
- package/knowledge/wiki/tween.md +580 -0
- package/knowledge/wiki/ui.md +8000 -0
- package/knowledge/wiki/world.md +909 -0
- package/package.json +68 -4
- package/schema/api.schema.json +29675 -0
- package/shell/README.md +22 -0
- package/shell/brand/README.md +23 -0
- package/shell/brand/icon-192.png +0 -0
- package/shell/brand/icon-512.png +0 -0
- package/shell/brand/mark-on-paper.svg +6 -0
- package/shell/brand/origin-mark.svg +5 -0
- package/shell/brand/origin-motion.css +251 -0
- package/shell/brand/tokens.css +20 -0
- package/shell/brand.ts +132 -0
- package/shell/page.ts +715 -0
- package/shell/platform.ts +203 -0
- package/shell/player.ts +238 -0
- package/shell/shell.css +185 -0
- package/shell/sw.ts +177 -0
- package/shell/transport.ts +515 -0
- package/shell/wire.ts +369 -0
- package/src/e6.ts +69 -0
- package/src/env.d.ts +100 -0
- package/src/generated/abi.ts +980 -0
- package/src/index.ts +193 -0
- package/src/node/audio.ts +86 -0
- package/src/node/build.ts +109 -0
- package/src/node/device.ts +981 -0
- package/src/node/doctor.ts +60 -0
- package/src/node/index.ts +102 -0
- package/src/node/platform.ts +24 -0
- package/src/node/playtest-run.ts +249 -0
- package/src/node/png.ts +130 -0
- package/src/node/polish.ts +976 -0
- package/src/node/run.ts +373 -0
- package/src/node/web-build.ts +808 -0
- package/src/p2.ts +35 -0
- package/src/platform-local.ts +211 -0
- package/src/playtest.ts +131 -0
- package/src/runtime.ts +1509 -0
- package/src/systems/cameras.ts +247 -0
- package/src/ui-kit/bind.ts +141 -0
- package/src/ui-kit/huds.ts +303 -0
- package/src/ui-kit/index.ts +36 -0
- package/src/ui-kit/menus.ts +187 -0
- package/src/ui-kit/settings.ts +131 -0
- package/src/ui-kit/store.ts +222 -0
- package/src/ui-kit/story.ts +89 -0
- package/src/ui-kit/util.ts +106 -0
- package/src/web/a11y.ts +429 -0
- package/src/web/audio-worklet.ts +193 -0
- package/src/web/audio.ts +307 -0
- package/src/web/basis.ts +239 -0
- package/src/web/gamepad.ts +123 -0
- package/src/web/host.ts +166 -0
- package/src/web/input.ts +237 -0
- package/src/web/lanes.ts +21 -0
- package/src/web/play.ts +20 -0
- package/src/web/rpc-ring.ts +110 -0
- package/src/web/settle.ts +122 -0
- package/src/web/shell.ts +643 -0
- package/src/web/text-input.ts +248 -0
- package/src/web/worker.ts +1002 -0
- package/types/bindings.json +13654 -0
- package/types/index.d.ts +20090 -0
- package/vendor/basis/LICENSE +201 -0
- package/vendor/basis/NOTICE +15 -0
- package/vendor/basis/README.md +18 -0
- package/vendor/basis/basis_transcoder.js +19 -0
- package/vendor/basis/basis_transcoder.wasm +0 -0
- package/README.md +0 -3
package/shell/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# The player shell
|
|
2
|
+
|
|
3
|
+
Everything in an exported game page that is not the game (docs/web-export.md
|
|
4
|
+
is the full contract). `og build` inlines the boot markup, `brand.ts`'s
|
|
5
|
+
marks and the page configuration into `index.html` and bundles these
|
|
6
|
+
modules next to the engine Worker.
|
|
7
|
+
|
|
8
|
+
| File | Role |
|
|
9
|
+
|---|---|
|
|
10
|
+
| `page.ts` | the main thread (`play.js`): boot splash and real progress, the play gate (audio unlock, fullscreen, wake lock), letterbox and safe area, the engine Worker, the audio worklet, the Player link and the platform bridge |
|
|
11
|
+
| `player.ts` | Player protocol 2, game side: `hello`, `loading`, `ready` on the first drawn frame, `fail`, `crash`, `sample`, `event`; hears `hello`, `start`, `gesture`, `pause`, `resume`; `request(method, args)` sends a `call` and resolves with its `result` (matched by id) |
|
|
12
|
+
| `platform.ts` | `og.platform` on the web: the Worker's platform calls answered through the Player (`save.get`/`save.set` hold the store, `leaderboard.submit`/`top`, telemetry as `event`s), held until the Player's `hello` when framed, and the browser's storage (`src/platform-local.ts`) where there is no Player or no such method; what protocol 2 lacks is `docs/platform-asks.md` |
|
|
13
|
+
| `wire.ts` | the platform's `og-probe` wire (probe protocol 5) over the engine's in-page probe, origin-checked |
|
|
14
|
+
| `sw.ts` | the publish build's service worker: the whole build precached under its content hash |
|
|
15
|
+
| `brand.ts`, `brand/`, `shell.css` | the brand marks, motion and tokens (vendored from the platform's `packages/brand`) |
|
|
16
|
+
|
|
17
|
+
Rules: post only to `window.parent` with this page's own origin, hear only
|
|
18
|
+
`window.parent` at that origin; game code never reaches the DOM, a cookie
|
|
19
|
+
or another origin (it asks the engine, and the shell answers). Tests:
|
|
20
|
+
`tests/web/player.ts` (protocol 2 and the platform bridge in headless
|
|
21
|
+
Chrome), `tests/web/wire.ts`, `tests/web/export.ts`, and
|
|
22
|
+
`tests/platform.test.ts` for the local platform.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Vendored brand (do not edit)
|
|
2
|
+
|
|
3
|
+
The platform's brand package (`origingame/origingame`, `packages/brand`) is
|
|
4
|
+
the source of every file here; the engine introduces no second mark, palette
|
|
5
|
+
or motion (design §9, AGENTS.md "Brand is one ink"). Copied at platform
|
|
6
|
+
revision `ef1555b83`:
|
|
7
|
+
|
|
8
|
+
| File | Source |
|
|
9
|
+
|---|---|
|
|
10
|
+
| `origin-mark.svg` | `packages/brand/motion/origin-mark.svg` (generated there from `source/origin-ai.svg`) |
|
|
11
|
+
| `origin-motion.css` | `packages/brand/motion/origin-motion.css` |
|
|
12
|
+
| `mark-on-paper.svg` | `packages/brand/app-icon/mark-on-paper.svg` (the app-icon tile) |
|
|
13
|
+
| `icon-192.png`, `icon-512.png` | `mark-on-paper.svg` rendered with `rsvg-convert -w N -h N` (the PWA's fallback icons) |
|
|
14
|
+
| `tokens.css` | the dark-theme values the shell reads, from `packages/tokens/dist/{tokens,theme-dark}.css` |
|
|
15
|
+
|
|
16
|
+
To refresh after a brand change, copy the files again from the platform
|
|
17
|
+
checkout, re-render the two PNGs, update the revision above and rebuild.
|
|
18
|
+
`og build` derives the shell's marks from `origin-mark.svg` (`genesis` for
|
|
19
|
+
the boot splash, `progress` with the real load value, `drift` for the error
|
|
20
|
+
card; the `engine` product mark for "Made with Origin Engine" is the
|
|
21
|
+
symbol's inner arms and core ring, as the platform's `product-marks.ts`
|
|
22
|
+
composes it) and inlines only the rules of those motions
|
|
23
|
+
(`shell/brand.ts`).
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-label="Origin AI">
|
|
3
|
+
<!-- Generated by packages/brand/generate-icons.sh from source/origin-ai.svg. Do not edit. -->
|
|
4
|
+
<rect width="1024" height="1024" rx="224" fill="#ffffff"/>
|
|
5
|
+
<g fill="#14161b" transform="translate(293.10 298.80) scale(1.14011)"><g><g id="galaxy-arms" transform="translate(0 370) scale(.1 -.1)"><path id="arm-1" d="M1818 3603 c-86 -5 -89 -23 -4 -23 281 0 650 -159 895 -386 575 -535 625 -1345 114 -1871 -337 -347 -808 -454 -1160 -264 -77 42 -75 36 15 -37 632 -510 1601 -50 1778 845 24 120 25 394 1 513 -131 662 -677 1149 -1366 1219 -100 11 -130 11 -273 4z"/><path id="arm-2" d="M1445 3334 c-804 -124 -1392 -855 -1325 -1646 12 -145 20 -176 20 -78 0 262 122 610 294 837 465 613 1290 781 1884 383 422 -283 614 -819 432 -1208 -59 -126 52 22 123 164 130 259 129 570 -3 842 -253 523 -814 801 -1425 706z"/><path id="arm-3" d="M1370 2805 c-721 -136 -1161 -918 -929 -1652 202 -642 832 -1099 1504 -1092 l90 1 -109 19 c-848 148 -1428 1006 -1184 1754 210 645 958 1022 1467 739 55 -31 37 -8 -43 53 -205 155 -531 228 -796 178z"/><path id="arm-4" d="M1103 2033 c-559 -736 133 -1785 1147 -1740 879 39 1545 813 1465 1703 l-8 89 -13 -115 c-110 -1000 -1083 -1633 -1941 -1262 -553 239 -851 879 -622 1335 37 73 34 72 -28 -10z"/></g><path id="origin-core" fill-rule="evenodd" d="M249 189 A55 55 0 1 1 139 189 A55 55 0 1 1 249 189 Z M209 189 A15 15 0 1 1 179 189 A15 15 0 1 1 209 189 Z"/></g></g>
|
|
6
|
+
</svg>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="og-mark" viewBox="-184 -184 364 364" fill="currentColor" role="img" aria-label="Origin AI">
|
|
3
|
+
<!-- Generated by packages/brand/generate-icons.sh from source/origin-ai.svg. Do not edit. -->
|
|
4
|
+
<g data-part="arms"><g data-part="outer"><g transform="translate(-194 181) scale(.1 -.1)"><path d="M1445 3334 c-804 -124 -1392 -855 -1325 -1646 12 -145 20 -176 20 -78 0 262 122 610 294 837 465 613 1290 781 1884 383 422 -283 614 -819 432 -1208 -59 -126 52 22 123 164 130 259 129 570 -3 842 -253 523 -814 801 -1425 706z"/><path d="M1103 2033 c-559 -736 133 -1785 1147 -1740 879 39 1545 813 1465 1703 l-8 89 -13 -115 c-110 -1000 -1083 -1633 -1941 -1262 -553 239 -851 879 -622 1335 37 73 34 72 -28 -10z"/></g></g><g data-part="inner"><g transform="translate(-194 181) scale(.1 -.1)"><path d="M1818 3603 c-86 -5 -89 -23 -4 -23 281 0 650 -159 895 -386 575 -535 625 -1345 114 -1871 -337 -347 -808 -454 -1160 -264 -77 42 -75 36 15 -37 632 -510 1601 -50 1778 845 24 120 25 394 1 513 -131 662 -677 1149 -1366 1219 -100 11 -130 11 -273 4z"/><path d="M1370 2805 c-721 -136 -1161 -918 -929 -1652 202 -642 832 -1099 1504 -1092 l90 1 -109 19 c-848 148 -1428 1006 -1184 1754 210 645 958 1022 1467 739 55 -31 37 -8 -43 53 -205 155 -531 228 -796 178z"/></g></g></g><g data-part="hub"><path fill-rule="evenodd" transform="translate(-194 -189)" d="M249 189 A55 55 0 1 1 139 189 A55 55 0 1 1 249 189 Z M209 189 A15 15 0 1 1 179 189 A15 15 0 1 1 209 189 Z"/></g>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/* Origin AI brand motion: the one source for animating the symbol on every
|
|
2
|
+
surface (Portal and Ops through @origingame/ui, Kit's GUI and any static
|
|
3
|
+
page through motion/origin-mark.svg). Framework-free and monochrome.
|
|
4
|
+
|
|
5
|
+
Markup contract (motion/origin-mark.svg and @origingame/ui OriginMark):
|
|
6
|
+
|
|
7
|
+
<svg class="og-mark" data-motion="orbit" viewBox="-184 -184 364 364">
|
|
8
|
+
<g data-part="arms">
|
|
9
|
+
<g data-part="outer">…two outer arms…</g>
|
|
10
|
+
<g data-part="inner">…two inner arms…</g>
|
|
11
|
+
</g>
|
|
12
|
+
<g data-part="hub">…core ring…</g>
|
|
13
|
+
</svg>
|
|
14
|
+
|
|
15
|
+
The core ring's centre is the user-space origin, so every part rotates and
|
|
16
|
+
scales about it with the default `transform-origin: 0 0`. Animated parts
|
|
17
|
+
never carry a transform attribute of their own (CSS would replace it);
|
|
18
|
+
geometry offsets live on their children. The arms are not
|
|
19
|
+
2-fold symmetric (opposite arms differ by ~2% of the mark), so a loop only
|
|
20
|
+
joins at a full turn: nothing here ever rests at 180°.
|
|
21
|
+
|
|
22
|
+
Loop periods are motion parameters; durations and easings of one-off moves
|
|
23
|
+
come from @origingame/tokens, with fallbacks for pages without tokens. */
|
|
24
|
+
|
|
25
|
+
.og-mark,
|
|
26
|
+
.og-logo {
|
|
27
|
+
--og-out: var(--ease-out, cubic-bezier(0.23, 1, 0.32, 1));
|
|
28
|
+
--og-spring: var(--ease-spring, cubic-bezier(0.34, 1.4, 0.64, 1));
|
|
29
|
+
--og-bounce: var(--ease-bounce, cubic-bezier(0.34, 1.7, 0.64, 1));
|
|
30
|
+
--og-dur-spring: var(--dur-spring, 520ms);
|
|
31
|
+
--og-dur-bounce: var(--dur-bounce, 640ms);
|
|
32
|
+
}
|
|
33
|
+
.og-mark {
|
|
34
|
+
display: block;
|
|
35
|
+
overflow: visible;
|
|
36
|
+
fill: currentColor;
|
|
37
|
+
}
|
|
38
|
+
.og-mark [data-part="ring"] {
|
|
39
|
+
fill: none;
|
|
40
|
+
stroke: currentColor;
|
|
41
|
+
opacity: 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* spin: the whole symbol turns at a constant rate. The only motion for 12–20 px
|
|
45
|
+
(buttons, rows, cells); it moves the <svg> itself, so many can run at once. */
|
|
46
|
+
.og-mark[data-motion="spin"] {
|
|
47
|
+
animation: og-spin 0.9s linear infinite;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* orbit: indeterminate loading from 24 px. A full turn per cycle in two eased
|
|
51
|
+
half-turns; the inner arms lead and tuck in, the outer arms ease out, so
|
|
52
|
+
the blades never touch. */
|
|
53
|
+
.og-mark[data-motion="orbit"] [data-part="arms"] {
|
|
54
|
+
animation: og-turn 3s cubic-bezier(0.62, 0, 0.32, 1) infinite;
|
|
55
|
+
}
|
|
56
|
+
.og-mark[data-motion="orbit"] [data-part="inner"] {
|
|
57
|
+
animation: og-lead 3s ease-in-out infinite;
|
|
58
|
+
}
|
|
59
|
+
.og-mark[data-motion="orbit"] [data-part="outer"] {
|
|
60
|
+
animation: og-lag 3s ease-in-out infinite;
|
|
61
|
+
}
|
|
62
|
+
.og-mark[data-motion="orbit"] [data-part="hub"] {
|
|
63
|
+
animation: og-tick 3s ease-in-out infinite;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* genesis: plays once. The ring pops, the inner then the outer arms unfurl
|
|
67
|
+
from the centre and spring into place (launch, splash, first screen). */
|
|
68
|
+
.og-mark[data-motion="genesis"] [data-part="hub"] {
|
|
69
|
+
animation: og-pop var(--og-dur-bounce) var(--og-bounce) both;
|
|
70
|
+
}
|
|
71
|
+
.og-mark[data-motion="genesis"] [data-part="inner"] {
|
|
72
|
+
animation: og-unfurl-inner 760ms var(--og-spring) 120ms both;
|
|
73
|
+
}
|
|
74
|
+
.og-mark[data-motion="genesis"] [data-part="outer"] {
|
|
75
|
+
animation: og-unfurl-outer 900ms var(--og-spring) 200ms both;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* breathe: running (Sophon thinking, an Agent run). A very slow turn with the
|
|
79
|
+
three layers contracting out of phase. */
|
|
80
|
+
.og-mark[data-motion="breathe"] [data-part="arms"] {
|
|
81
|
+
animation: og-spin 18s linear infinite;
|
|
82
|
+
}
|
|
83
|
+
.og-mark[data-motion="breathe"] [data-part="outer"] {
|
|
84
|
+
animation: og-breathe-outer 2.6s ease-in-out infinite;
|
|
85
|
+
}
|
|
86
|
+
.og-mark[data-motion="breathe"] [data-part="inner"] {
|
|
87
|
+
animation: og-breathe-inner 2.6s ease-in-out -0.25s infinite;
|
|
88
|
+
}
|
|
89
|
+
.og-mark[data-motion="breathe"] [data-part="hub"] {
|
|
90
|
+
animation: og-breathe-hub 2.6s ease-in-out -0.4s infinite;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* beacon: needs you. The ring beats and ripples leave from the outer edge;
|
|
94
|
+
the arms nudge once a cycle. */
|
|
95
|
+
.og-mark[data-motion="beacon"] [data-part="ring"] {
|
|
96
|
+
animation: og-ripple 2.2s var(--og-out) infinite;
|
|
97
|
+
}
|
|
98
|
+
.og-mark[data-motion="beacon"] [data-part="ring"] + [data-part="ring"] {
|
|
99
|
+
animation-delay: 1.1s;
|
|
100
|
+
}
|
|
101
|
+
.og-mark[data-motion="beacon"] [data-part="hub"] {
|
|
102
|
+
animation: og-beat 2.2s ease-out infinite;
|
|
103
|
+
}
|
|
104
|
+
.og-mark[data-motion="beacon"] [data-part="arms"] {
|
|
105
|
+
animation: og-nudge 2.2s ease-in-out infinite;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* sweep: plays once. A feathered wedge follows the arms round and inks the
|
|
109
|
+
symbol in (empty states, section entrances). */
|
|
110
|
+
.og-mark[data-motion="sweep"] [data-part="hub"] {
|
|
111
|
+
animation: og-pop 400ms var(--og-bounce) both;
|
|
112
|
+
}
|
|
113
|
+
.og-mark[data-motion="sweep"] [data-part="wipe"] {
|
|
114
|
+
animation: og-sweep 950ms cubic-bezier(0.45, 0, 0.2, 1) 120ms both;
|
|
115
|
+
}
|
|
116
|
+
.og-mark[data-motion="sweep"] [data-part="arms"] {
|
|
117
|
+
animation: og-settle 1.2s var(--og-out) 120ms both;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* progress: determinate. The same wedge; set the wipe's stroke-dashoffset to
|
|
121
|
+
100 − percent. The remaining arms stay as a faint ghost; add data-complete
|
|
122
|
+
at 100 and the ring pops. */
|
|
123
|
+
.og-mark[data-motion="progress"] [data-part="ghost"] {
|
|
124
|
+
opacity: 0.14;
|
|
125
|
+
}
|
|
126
|
+
.og-mark[data-motion="progress"] [data-part="wipe"] {
|
|
127
|
+
transition: stroke-dashoffset 240ms var(--og-out);
|
|
128
|
+
}
|
|
129
|
+
.og-mark[data-motion="progress"][data-complete] [data-part="hub"] {
|
|
130
|
+
animation: og-done var(--og-dur-bounce) var(--og-bounce);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* complete: plays once when something really happened (published). The arms
|
|
134
|
+
whirl into a tight knot, the ring swells with a burst, then all springs back. */
|
|
135
|
+
.og-mark[data-motion="complete"] [data-part="arms"] {
|
|
136
|
+
animation: og-collapse 1.5s cubic-bezier(0.5, 0, 0.75, 0) both;
|
|
137
|
+
}
|
|
138
|
+
.og-mark[data-motion="complete"] [data-part="hub"] {
|
|
139
|
+
animation: og-swell 1.5s ease-out both;
|
|
140
|
+
}
|
|
141
|
+
.og-mark[data-motion="complete"] [data-part="ring"] {
|
|
142
|
+
animation: og-burst 1.5s var(--og-out) both;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* drift: offline or unavailable. Dimmed; the layers sway apart and stall. */
|
|
146
|
+
.og-mark[data-motion="drift"] {
|
|
147
|
+
opacity: 0.45;
|
|
148
|
+
}
|
|
149
|
+
.og-mark[data-motion="drift"] [data-part="outer"] {
|
|
150
|
+
animation: og-drift-outer 3.4s ease-in-out infinite;
|
|
151
|
+
}
|
|
152
|
+
.og-mark[data-motion="drift"] [data-part="inner"] {
|
|
153
|
+
animation: og-drift-inner 3.4s ease-in-out infinite;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* answer: a brand link's hover. One full clockwise turn on enter and nothing
|
|
157
|
+
on leave, so it never rewinds; the ring tightens on press. */
|
|
158
|
+
:is(a, button, [data-og-mark-host]):focus-visible .og-mark[data-motion="answer"] [data-part="arms"] {
|
|
159
|
+
animation: og-turn-once 900ms var(--og-spring);
|
|
160
|
+
}
|
|
161
|
+
@media (hover: hover) and (pointer: fine) {
|
|
162
|
+
:is(a, button, [data-og-mark-host]):hover .og-mark[data-motion="answer"] [data-part="arms"],
|
|
163
|
+
.og-mark[data-motion="answer"]:hover [data-part="arms"] {
|
|
164
|
+
animation: og-turn-once 900ms var(--og-spring);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
.og-mark[data-motion="answer"] [data-part="hub"] {
|
|
168
|
+
transition: transform 180ms var(--og-out);
|
|
169
|
+
}
|
|
170
|
+
:is(a, button, [data-og-mark-host]):active .og-mark[data-motion="answer"] [data-part="hub"] {
|
|
171
|
+
transform: scale(0.8);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* unfold: the lockup plays once. The symbol runs genesis, then the letters
|
|
175
|
+
rise from the baseline 45 ms apart (sign-in, first screen, title cards). */
|
|
176
|
+
.og-logo[data-motion="unfold"] [data-part="letter"] {
|
|
177
|
+
animation: og-rise 620ms var(--og-out) calc(420ms + var(--og-letter, 0) * 45ms) both;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
@keyframes og-spin { to { transform: rotate(360deg); } }
|
|
181
|
+
@keyframes og-turn-once { from { transform: rotate(0); } to { transform: rotate(360deg); } }
|
|
182
|
+
@keyframes og-turn {
|
|
183
|
+
0% { transform: rotate(0); }
|
|
184
|
+
50% { transform: rotate(180deg); }
|
|
185
|
+
100% { transform: rotate(360deg); }
|
|
186
|
+
}
|
|
187
|
+
@keyframes og-lead { 0%, 50%, 100% { transform: none; } 25%, 75% { transform: rotate(12deg) scale(0.95); } }
|
|
188
|
+
@keyframes og-lag { 0%, 50%, 100% { transform: none; } 25%, 75% { transform: scale(1.02); } }
|
|
189
|
+
@keyframes og-tick { 0%, 50%, 100% { transform: scale(1); } 25%, 75% { transform: scale(0.88); } }
|
|
190
|
+
@keyframes og-pop { from { transform: scale(0); } to { transform: scale(1); } }
|
|
191
|
+
@keyframes og-unfurl-inner {
|
|
192
|
+
0% { transform: rotate(-140deg) scale(0.3); opacity: 0; }
|
|
193
|
+
25% { opacity: 1; }
|
|
194
|
+
100% { transform: none; opacity: 1; }
|
|
195
|
+
}
|
|
196
|
+
@keyframes og-unfurl-outer {
|
|
197
|
+
0% { transform: rotate(-150deg) scale(0.5); opacity: 0; }
|
|
198
|
+
25% { opacity: 1; }
|
|
199
|
+
100% { transform: none; opacity: 1; }
|
|
200
|
+
}
|
|
201
|
+
@keyframes og-breathe-outer { 0%, 100% { transform: scale(1); } 50% { transform: scale(0.93); } }
|
|
202
|
+
@keyframes og-breathe-inner { 0%, 100% { transform: scale(1); } 50% { transform: scale(0.9) rotate(14deg); } }
|
|
203
|
+
@keyframes og-breathe-hub { 0%, 100% { transform: scale(1); } 50% { transform: scale(0.82); } }
|
|
204
|
+
@keyframes og-ripple {
|
|
205
|
+
0% { transform: scale(0.92); opacity: 0; stroke-width: 16; }
|
|
206
|
+
12% { opacity: 0.9; }
|
|
207
|
+
100% { transform: scale(1.42); opacity: 0; stroke-width: 1.5; }
|
|
208
|
+
}
|
|
209
|
+
@keyframes og-beat { 0%, 100% { transform: scale(1); } 14% { transform: scale(1.24); } 30% { transform: scale(0.95); } 44% { transform: scale(1); } }
|
|
210
|
+
@keyframes og-nudge { 0%, 70%, 100% { transform: rotate(0); } 78% { transform: rotate(-14deg); } 88% { transform: rotate(6deg); } }
|
|
211
|
+
@keyframes og-sweep { from { stroke-dashoffset: 100; } to { stroke-dashoffset: 0; } }
|
|
212
|
+
@keyframes og-settle { from { transform: rotate(-40deg); } to { transform: none; } }
|
|
213
|
+
@keyframes og-done { 0% { transform: scale(1); } 40% { transform: scale(1.3); } 100% { transform: scale(1); } }
|
|
214
|
+
@keyframes og-collapse {
|
|
215
|
+
0% { transform: none; }
|
|
216
|
+
38% { transform: rotate(360deg) scale(0.52); animation-timing-function: var(--og-spring); }
|
|
217
|
+
100% { transform: rotate(720deg) scale(1); }
|
|
218
|
+
}
|
|
219
|
+
@keyframes og-swell { 0%, 30% { transform: scale(1); } 44% { transform: scale(1.4); } 70%, 100% { transform: scale(1); } }
|
|
220
|
+
@keyframes og-burst {
|
|
221
|
+
0%, 36% { transform: scale(0.5); opacity: 0; stroke-width: 22; }
|
|
222
|
+
40% { opacity: 1; }
|
|
223
|
+
100% { transform: scale(1.25); opacity: 0; stroke-width: 1.5; }
|
|
224
|
+
}
|
|
225
|
+
@keyframes og-drift-outer {
|
|
226
|
+
0%, 100% { transform: rotate(0) scale(1); }
|
|
227
|
+
35% { transform: rotate(26deg) scale(1.05); }
|
|
228
|
+
55% { transform: rotate(20deg) scale(1.05); }
|
|
229
|
+
}
|
|
230
|
+
@keyframes og-drift-inner {
|
|
231
|
+
0%, 100% { transform: rotate(0) scale(1); }
|
|
232
|
+
35% { transform: rotate(-18deg) scale(0.95); }
|
|
233
|
+
55% { transform: rotate(-12deg) scale(0.95); }
|
|
234
|
+
}
|
|
235
|
+
@keyframes og-rise { from { transform: translateY(120px); opacity: 0; } to { transform: none; opacity: 1; } }
|
|
236
|
+
|
|
237
|
+
/* Reduced motion keeps the state and drops the movement: every part rests in
|
|
238
|
+
its final pose, a beacon keeps one still ring, progress still fills. */
|
|
239
|
+
@media (prefers-reduced-motion: reduce) {
|
|
240
|
+
.og-mark,
|
|
241
|
+
.og-mark *,
|
|
242
|
+
.og-logo [data-part="letter"] {
|
|
243
|
+
animation: none !important;
|
|
244
|
+
transition: none !important;
|
|
245
|
+
}
|
|
246
|
+
.og-mark[data-motion="beacon"] [data-part="ring"]:first-of-type {
|
|
247
|
+
opacity: 0.6;
|
|
248
|
+
stroke-width: 8;
|
|
249
|
+
transform: scale(1.12);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/* From @origingame/tokens (packages/tokens/dist/tokens.css, theme-dark.css);
|
|
2
|
+
only the values the player shell reads. Brand ink is packages/brand's #14161b. */
|
|
3
|
+
:root {
|
|
4
|
+
--og-ink: #14161b;
|
|
5
|
+
--og-paper: #ffffff;
|
|
6
|
+
--color-fg: #f5f5f7;
|
|
7
|
+
--color-fg-muted: #aeaeb2;
|
|
8
|
+
--color-surface: #1c1c1e;
|
|
9
|
+
--color-surface-raised: #2c2c2e;
|
|
10
|
+
--color-border: #38383a;
|
|
11
|
+
--color-danger: #ff5a4f;
|
|
12
|
+
--color-scrim: rgb(0 0 0 / 0.42);
|
|
13
|
+
--font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI Variable Text", "Segoe UI", system-ui, Roboto, "PingFang SC", "HarmonyOS Sans SC", "MiSans", "Noto Sans SC", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif;
|
|
14
|
+
--font-mono: "Geist Mono Variable", "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
|
|
15
|
+
--radius-card: 14px;
|
|
16
|
+
--radius-button: 9999px;
|
|
17
|
+
--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
|
|
18
|
+
--dur-spring: 520ms;
|
|
19
|
+
--dur-bounce: 640ms;
|
|
20
|
+
}
|
package/shell/brand.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// Build-time brand for the player shell: the marks and the motion rules
|
|
2
|
+
// `og build` inlines into index.html, derived from the vendored brand files
|
|
3
|
+
// (shell/brand/, copied from the platform's packages/brand; README there).
|
|
4
|
+
// Nothing here is drawn: every path comes from origin-mark.svg.
|
|
5
|
+
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
7
|
+
import { dirname, join } from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
|
|
10
|
+
export const BRAND_DIR = join(dirname(fileURLToPath(import.meta.url)), "brand");
|
|
11
|
+
|
|
12
|
+
/** The motions the shell uses: boot, real progress, error. */
|
|
13
|
+
export const SHELL_MOTIONS = ["genesis", "progress", "drift"] as const;
|
|
14
|
+
|
|
15
|
+
function brandFile(name: string): string {
|
|
16
|
+
return readFileSync(join(BRAND_DIR, name), "utf8");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface Parts {
|
|
20
|
+
viewBox: string;
|
|
21
|
+
outer: { transform: string; paths: string };
|
|
22
|
+
inner: { transform: string; paths: string };
|
|
23
|
+
hub: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function parts(svg: string): Parts {
|
|
27
|
+
const arm = (name: string) => {
|
|
28
|
+
const m = new RegExp(`<g data-part="${name}"><g transform="([^"]+)">([\\s\\S]*?)</g></g>`).exec(svg);
|
|
29
|
+
if (!m) throw new Error(`origin-mark.svg lacks the ${name} arms`);
|
|
30
|
+
return { transform: m[1]!, paths: m[2]! };
|
|
31
|
+
};
|
|
32
|
+
const hub = /<g data-part="hub">([\s\S]*?)<\/g>/.exec(svg)?.[1];
|
|
33
|
+
const viewBox = /viewBox="([^"]+)"/.exec(svg)?.[1];
|
|
34
|
+
if (!hub || !viewBox) throw new Error("origin-mark.svg lacks the hub or the viewBox");
|
|
35
|
+
return { viewBox, outer: arm("outer"), inner: arm("inner"), hub };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The boot mark: one symbol that shows `genesis` (the layered arms and the
|
|
40
|
+
* hub), `progress` (a faint ghost and a wedge-masked copy whose wipe the
|
|
41
|
+
* shell moves to 100 − percent) and `drift`, by switching `data-motion`.
|
|
42
|
+
*/
|
|
43
|
+
export function bootMark(svg = brandFile("origin-mark.svg")): string {
|
|
44
|
+
const p = parts(svg);
|
|
45
|
+
const both = '<use href="#og-mo"/><use href="#og-mi"/>';
|
|
46
|
+
return `<svg class="og-mark og-boot-mark" data-motion="genesis" viewBox="${p.viewBox}" aria-hidden="true" focusable="false">`
|
|
47
|
+
+ `<defs><g id="og-mo" transform="${p.outer.transform}">${p.outer.paths}</g><g id="og-mi" transform="${p.inner.transform}">${p.inner.paths}</g>`
|
|
48
|
+
+ '<mask id="og-wipe" maskUnits="userSpaceOnUse" x="-270" y="-270" width="540" height="540">'
|
|
49
|
+
+ '<circle data-part="wipe" r="100" fill="none" stroke="#fff" stroke-width="204" pathLength="100" stroke-dasharray="100 100" stroke-dashoffset="100" transform="rotate(-100)"/></mask></defs>'
|
|
50
|
+
+ '<g data-part="arms"><g data-part="outer"><use href="#og-mo"/></g><g data-part="inner"><use href="#og-mi"/></g></g>'
|
|
51
|
+
+ `<g data-part="ghost">${both}</g><g data-part="fill" mask="url(#og-wipe)">${both}</g>`
|
|
52
|
+
+ `<g data-part="hub">${p.hub}</g></svg>`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The `engine` product mark (the symbol's inner arms and core ring), still, for "Made with Origin Engine". */
|
|
56
|
+
export function engineMark(svg = brandFile("origin-mark.svg")): string {
|
|
57
|
+
const p = parts(svg);
|
|
58
|
+
return `<svg class="og-engine-mark" viewBox="${p.viewBox}" fill="currentColor" aria-hidden="true" focusable="false">`
|
|
59
|
+
+ `<g transform="${p.inner.transform}">${p.inner.paths}</g>${p.hub}</svg>`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function splitTopLevel(text: string): string[] {
|
|
63
|
+
const out: string[] = [];
|
|
64
|
+
let depth = 0;
|
|
65
|
+
let start = 0;
|
|
66
|
+
for (let i = 0; i < text.length; i++) {
|
|
67
|
+
const c = text[i];
|
|
68
|
+
if (c === "(" || c === "[") depth++;
|
|
69
|
+
else if (c === ")" || c === "]") depth--;
|
|
70
|
+
else if (c === "," && depth === 0) {
|
|
71
|
+
out.push(text.slice(start, i));
|
|
72
|
+
start = i + 1;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
out.push(text.slice(start));
|
|
76
|
+
return out.map((s) => s.trim()).filter(Boolean);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function blocks(css: string): Array<{ prelude: string; body: string }> {
|
|
80
|
+
const out: Array<{ prelude: string; body: string }> = [];
|
|
81
|
+
let depth = 0;
|
|
82
|
+
let start = 0;
|
|
83
|
+
let open = -1;
|
|
84
|
+
for (let i = 0; i < css.length; i++) {
|
|
85
|
+
if (css[i] === "{") {
|
|
86
|
+
if (depth === 0) open = i;
|
|
87
|
+
depth++;
|
|
88
|
+
} else if (css[i] === "}") {
|
|
89
|
+
depth--;
|
|
90
|
+
if (depth === 0) {
|
|
91
|
+
out.push({ prelude: css.slice(start, open).trim(), body: css.slice(open + 1, i) });
|
|
92
|
+
start = i + 1;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The rules of origin-motion.css the given motions need: the base
|
|
101
|
+
* `.og-mark` rules, every rule naming only those motions, their keyframes
|
|
102
|
+
* and the reduced-motion block for the same selectors.
|
|
103
|
+
*/
|
|
104
|
+
export function motionSubset(motions: readonly string[], source = brandFile("origin-motion.css")): string {
|
|
105
|
+
const css = source.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
106
|
+
const keep = (selector: string) => {
|
|
107
|
+
if (!selector.includes(".og-mark")) return false;
|
|
108
|
+
const named = [...selector.matchAll(/data-motion="([a-z]+)"/g)].map((m) => m[1]!);
|
|
109
|
+
return named.length ? named.every((n) => motions.includes(n)) : !/data-part="ring"/.test(selector);
|
|
110
|
+
};
|
|
111
|
+
const rules = (text: string): string[] =>
|
|
112
|
+
blocks(text).flatMap(({ prelude, body }) => {
|
|
113
|
+
if (prelude.startsWith("@keyframes")) return [];
|
|
114
|
+
if (prelude.startsWith("@media")) {
|
|
115
|
+
const inner = rules(body);
|
|
116
|
+
return inner.length ? [`${prelude}{${inner.join("")}}`] : [];
|
|
117
|
+
}
|
|
118
|
+
const selectors = splitTopLevel(prelude).filter(keep);
|
|
119
|
+
return selectors.length ? [`${selectors.join(",")}{${body.trim().replace(/\s+/g, " ")}}`] : [];
|
|
120
|
+
});
|
|
121
|
+
const kept = rules(css);
|
|
122
|
+
const used = new Set(kept.join("").match(/og-[a-z-]+(?=[\s;}])/g) ?? []);
|
|
123
|
+
const frames = blocks(css)
|
|
124
|
+
.filter(({ prelude }) => prelude.startsWith("@keyframes") && used.has(prelude.split(/\s+/)[1]!))
|
|
125
|
+
.map(({ prelude, body }) => `${prelude}{${body.trim().replace(/\s+/g, " ")}}`);
|
|
126
|
+
return [...kept, ...frames].join("\n");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** The shell's token values (brand/tokens.css). */
|
|
130
|
+
export function shellTokens(): string {
|
|
131
|
+
return brandFile("tokens.css").replace(/\/\*[\s\S]*?\*\//g, "").trim();
|
|
132
|
+
}
|