@origingame/engine 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/bin/og.mjs +1 -1
  2. package/dist/web/origin-engine.d.ts +219 -5
  3. package/dist/web/origin-engine.js +1344 -125
  4. package/dist/web/origin-engine_bg.wasm +0 -0
  5. package/dist/web/origin-engine_bg.wasm.d.ts +68 -3
  6. package/dist/web-publish/origin-engine-lanes.js +1007 -22
  7. package/dist/web-publish/origin-engine-lanes_bg.wasm +0 -0
  8. package/dist/web-publish/origin-engine.d.ts +162 -5
  9. package/dist/web-publish/origin-engine.js +1004 -19
  10. package/dist/web-publish/origin-engine_bg.wasm +0 -0
  11. package/dist/web-publish/origin-engine_bg.wasm.d.ts +52 -3
  12. package/dist/web-publish-2d/origin-engine-lanes.js +1007 -22
  13. package/dist/web-publish-2d/origin-engine-lanes_bg.wasm +0 -0
  14. package/dist/web-publish-2d/origin-engine.d.ts +162 -5
  15. package/dist/web-publish-2d/origin-engine.js +1004 -19
  16. package/dist/web-publish-2d/origin-engine_bg.wasm +0 -0
  17. package/dist/web-publish-2d/origin-engine_bg.wasm.d.ts +52 -3
  18. package/knowledge/looks/catalogue.json +40 -40
  19. package/knowledge/package.json +1 -1
  20. package/knowledge/scaffold/distribution.json +14 -0
  21. package/knowledge/scaffold/origin-engine.json +2 -2
  22. package/knowledge/scaffold/template/README.md +2 -1
  23. package/knowledge/scaffold/template/assets/audio/sting.ogt +10 -0
  24. package/knowledge/scaffold/template/assets/timelines/intro.ogt +101 -0
  25. package/knowledge/scaffold/template/assets/timelines/narration.srt +11 -0
  26. package/knowledge/scaffold/template/host.d.ts +46 -0
  27. package/knowledge/scaffold/template/package.json +1 -1
  28. package/knowledge/scaffold/template/project.ogt +1 -1
  29. package/knowledge/scaffold/template/src/main.ts +1 -1
  30. package/knowledge/scaffold/template/tests/frames.ts +134 -0
  31. package/knowledge/scaffold/template/tests/intro.og.ts +65 -0
  32. package/knowledge/scaffold/template/tests/smoke.og.ts +30 -36
  33. package/knowledge/scaffold/template/tsconfig.json +1 -1
  34. package/knowledge/scripts/mount.ts +2 -1
  35. package/knowledge/scripts/validate.ts +19 -10
  36. package/knowledge/skills/og-build/SKILL.md +8 -5
  37. package/knowledge/skills/og-core/SKILL.md +11 -0
  38. package/knowledge/skills/og-game/SKILL.md +877 -8
  39. package/knowledge/skills/og-platform-sdk/SKILL.md +1 -0
  40. package/knowledge/skills/og-taste/SKILL.md +82 -73
  41. package/knowledge/skills/og-ui/SKILL.md +65 -50
  42. package/knowledge/wiki/ai.md +20 -20
  43. package/knowledge/wiki/anim.md +384 -41
  44. package/knowledge/wiki/atmosphere.md +36 -13
  45. package/knowledge/wiki/audio.md +18 -18
  46. package/knowledge/wiki/camera.md +19 -13
  47. package/knowledge/wiki/commands.md +19 -13
  48. package/knowledge/wiki/debug.md +414 -0
  49. package/knowledge/wiki/game.md +8 -8
  50. package/knowledge/wiki/i18n.md +7 -7
  51. package/knowledge/wiki/index.md +262 -7
  52. package/knowledge/wiki/input.md +47 -47
  53. package/knowledge/wiki/load.md +236 -68
  54. package/knowledge/wiki/math.md +2607 -0
  55. package/knowledge/wiki/nav.md +34 -34
  56. package/knowledge/wiki/net.md +340 -0
  57. package/knowledge/wiki/path.md +402 -0
  58. package/knowledge/wiki/physics.md +95 -95
  59. package/knowledge/wiki/platform.md +463 -29
  60. package/knowledge/wiki/probe.md +681 -67
  61. package/knowledge/wiki/reflect.md +15 -15
  62. package/knowledge/wiki/render.md +3893 -1896
  63. package/knowledge/wiki/rng.md +14 -14
  64. package/knowledge/wiki/stream.md +15 -15
  65. package/knowledge/wiki/system.md +8 -8
  66. package/knowledge/wiki/systems.md +2 -2
  67. package/knowledge/wiki/terrain.md +19 -19
  68. package/knowledge/wiki/tilemap.md +13 -13
  69. package/knowledge/wiki/time.md +4 -4
  70. package/knowledge/wiki/timeline.md +396 -0
  71. package/knowledge/wiki/transform.md +8 -8
  72. package/knowledge/wiki/tween.md +17 -17
  73. package/knowledge/wiki/ui.md +559 -629
  74. package/knowledge/wiki/video.md +157 -0
  75. package/knowledge/wiki/world.md +36 -36
  76. package/package.json +4 -3
  77. package/schema/api.schema.json +21707 -14725
  78. package/shell/page.ts +26 -2
  79. package/shell/platform.ts +46 -0
  80. package/shell/player.ts +7 -0
  81. package/src/depth.ts +71 -0
  82. package/src/e6.ts +32 -1
  83. package/src/env.d.ts +5 -1
  84. package/src/generated/abi.ts +189 -3
  85. package/src/index.ts +25 -4
  86. package/src/light.ts +18 -0
  87. package/src/math.ts +622 -0
  88. package/src/node/index.ts +17 -0
  89. package/src/node/playtest-run.ts +114 -18
  90. package/src/node/polish.ts +13 -3
  91. package/src/node/render.ts +291 -0
  92. package/src/node/run.ts +86 -5
  93. package/src/p2.ts +19 -1
  94. package/src/playtest.ts +57 -4
  95. package/src/runtime.ts +13 -3
  96. package/src/targets.ts +28 -0
  97. package/src/trace.ts +169 -0
  98. package/src/ui-kit/huds.ts +34 -34
  99. package/src/ui-kit/menus.ts +12 -12
  100. package/src/ui-kit/settings.ts +2 -2
  101. package/src/ui-kit/store.ts +18 -18
  102. package/src/ui-kit/story.ts +6 -4
  103. package/src/ui-kit/util.ts +8 -8
  104. package/src/web/shell.ts +58 -0
  105. package/src/web/worker.ts +103 -2
  106. package/types/bindings.json +9101 -6647
  107. package/types/index.d.ts +15503 -11564
@@ -86,6 +86,7 @@ if (answer.ok && typeof answer.data.value === "number") run.best[i] = Math.max(r
86
86
  | Analytics | `telemetry(world, "round.over", { score, coins })` | one-way; a name of 1–64 letters, digits, `.` and `_` (the Player files it as `game.custom.<name>`), at most 16 scalar attributes; never personal data |
87
87
  | Origin Point | `point(world, { reason, amount })` | the platform decides what it grants (today nothing under a Player: `{ granted: 0, balance: null }` until an approved point policy; show nothing that depends on it) |
88
88
  | AI-generated content shown | `aiMark(world, { kind, label })` | disclosure, not permission |
89
+ | Play with others in one room | `roomCreate(world, { name })`, `roomJoin(world, room)`, `roomLeave`, `roomSend(world, data, to?)`, `roomPeers`, `roomInfo`; the answers as `og.RoomJoined`, `og.RoomPeerJoined`, `og.RoomPeerLeft`, `og.RoomMessage` (read one with `roomMessage`) | natively `og relay` with `og run --room ws://127.0.0.1:P/<room>`; on the web the Player's rooms, when its hello lists `room.join` (`roomInfo(world).host` is `none` otherwise and joins are refused). A message is this peer's host input only: for a game both peers simulate, use og.net's lockstep (`og.Lockstep`, `og.PlayerInput`, og-game §20) and send nothing yourself |
89
90
 
90
91
  `og.platform.info(world)` says what answers here (`player`, `local` or
91
92
  `none`) and the methods it answers (`capabilities`): check it rather than
@@ -1,13 +1,14 @@
1
1
  ---
2
2
  name: og-taste
3
- description: Art direction for a game on Origin Engine — choosing a style from the premise (genre, mood, era, audience) out of a 37-style catalogue and binding it as a look (post stack, stylisation, material defaults, skin) with a UI skin, a palette and a type ramp; composition and hierarchy on the play surface, colour, typography, motion, feedback and consistency; atmosphere (fog, sky, time of day, weather, water); the anti-patterns that make a game look like a web form; and the checklist to run (with og run captures and dump_ui) before calling a game done. Use when starting a game's look, building or reviewing any screen or HUD, or when a game "works but looks generic".
3
+ description: Art direction for a game on Origin Engine — choosing a style from the premise (genre, mood, era, audience) out of a 37-style catalogue and binding it as a look (post stack, stylisation, material defaults, the UI style) with a palette and a type ramp; composition and hierarchy on the play surface, colour, typography, motion, feedback and consistency; atmosphere (fog, sky, time of day, weather, water); the anti-patterns that make a game look like a web form; and the checklist to run (with og run captures and dump_ui) before calling a game done. Use when starting a game's look, building or reviewing any screen or HUD, or when a game "works but looks generic".
4
4
  ---
5
5
 
6
6
  A game is judged in its first second, before anyone reads a word. The
7
- engine's UI is correct by default and characterless by default: the platform
8
- tokens are grey glass that suits a settings page. This skill is how to give a
9
- game a look of its own and keep it consistent. The mechanics are og-ui's
10
- (skins, motion and sounds are og-ui §6) and the amounts of game feel
7
+ engine's UI is correct by default and characterless by default: its
8
+ placeholder style, `prototype`, is greys and square corners meant to be
9
+ replaced, and the game's UI never borrows the Origin platform's look. This
10
+ skill is how to give a game a look of its own and keep it consistent. The
11
+ mechanics are og-ui's (styles, motion and sounds are og-ui §6) and the amounts of game feel
11
12
  og-feel's; this is the taste.
12
13
 
13
14
  ## What works
@@ -57,11 +58,16 @@ for a new game and writes the result (`--look <slug>` names it instead):
57
58
  `assets/looks/<game>.ogt` extending the preset, named by `GameDesc.look`,
58
59
  with a README section on changing it. For a game that exists, write the
59
60
  same file by hand (§12) and add `look: "looks/<game>.ogt"` to `og.game`.
60
- The entry also names the palette rules, the asset specs and the skin pack
61
- that matches it. Then pick **one** starting skin. The six packs in the engine's
62
- `templates/ui-kit/assets/skins/` are starting points, not the only looks:
63
-
64
- | Pack | Premises it fits | Signature | Start look (§12) |
61
+ The entry also names the palette rules, the asset specs and the UI style
62
+ its preset carries (the look's `ui` line, §12), so a game that extends a
63
+ preset is dressed by it from the first frame. The game's UI follows the
64
+ game's art, never the Origin platform's: without a style it draws in the
65
+ engine's placeholder `prototype` (greys, near-square corners, a cyan focus
66
+ ring), and `og polish` warns when a game ships in it. The six built-in
67
+ styles in the engine's `templates/styles/` are starting points, not the
68
+ only looks:
69
+
70
+ | Style | Premises it fits | Signature | Start look (§12) |
65
71
  |---|---|---|---|
66
72
  | `pixel` | platformers, roguelikes, retro shooters, jam games | square frames, 2 px bevels, hard shadows, a bitmap face | `16bit-pixel` |
67
73
  | `cartoon` | casual, match-3, party, kids, idle | thick outlines, cream panels, squash on press, pops | `bold-comic-illustration` |
@@ -70,88 +76,90 @@ that matches it. Then pick **one** starting skin. The six packs in the engine's
70
76
  | `paper` | narrative, puzzle, cosy, mystery, word games | off-white sheets, ink outlines, soft shadows, one red | `pencil-hatching` |
71
77
  | `neon` | arcade, rhythm, racing, synthwave | dark glass, glowing tubes, italic display | `neon-vector` |
72
78
 
73
- ```ts
74
- const skin = og.load.skin(world, "skins/fantasy/skin.ogt");
75
- if (skin.ok) og.ui.configure(world, { skin: skin.value });
79
+ ```text
80
+ ui "fantasy" # in the game's look: a built-in style by slug
81
+ ui "styles/harbour/style.ogt" # or the game's own
76
82
  ```
77
83
 
78
- Make it the game's own by changing the skin file, never by passing colours
79
- to individual calls: copy the pack directory into the game's
80
- `assets/skins/<game>/` (`cp -R .../skins/fantasy assets/skins/<game>`) and
81
- load `skins/<game>/skin.ogt`. Nothing in it needs rewriting: the ids in a
82
- skin file are read relative to the file first (`atlas "atlas.png"`, `body
83
- "Newsreader-Medium.ttf"`), then from `assets/` (`skins/fantasy/atlas.png`
84
- still works). The one asset outside the pack is its CJK `fallback`,
85
- `fonts/NotoSansSC-Regular-subset.ttf` (2.5 MB, shared by every pack, so
86
- not copied six times): a project made by `npm create @origingame/game`
87
- has it at that id; otherwise point the line at the game's own CJK font,
88
- or delete it when the game shows no Chinese, Japanese or Korean (the cook
89
- names the line it cannot find). Then change `palette` (start with
90
- `accent`, `bg`, `surface`, `fg`), `ramp` and `fonts` (any OFL font beside
91
- the skin file), and redraw the atlas if the frames should change (`og skin
92
- pack`, og-ui §6). The art in the art direction of the
93
- world (sprites, backgrounds) and the skin must agree: a pixel-art world in a
84
+ Make it the game's own by changing the style file, never by passing colours
85
+ to individual calls: copy the built-in's directory into the game's
86
+ `assets/styles/<game>/` (`cp -R .../templates/styles/fantasy
87
+ assets/styles/<game>`) and name it in the look (`ui
88
+ "styles/<game>/style.ogt"`). Nothing in it needs rewriting: the ids in a
89
+ style file are read relative to the file first (`atlas "atlas.png"`, `body
90
+ "Newsreader-Medium.ttf"`), then from `assets/`. A style is complete: it
91
+ names every palette role and the four radii (`small`, `control`, `bar`,
92
+ `panel`), so nothing of it comes from anywhere else. Then change `palette`
93
+ (start with `primary`, `backdrop`, `panel`, `ink`), `radius`, `ramp` and
94
+ `fonts` (any OFL font beside the style file), and redraw the atlas if the
95
+ frames should change (`og style pack`, og-ui §6). A game that shows Chinese,
96
+ Japanese or Korean adds a `fallback` font to its style (or its string
97
+ table's `fonts` line, og-i18n). For a quick re-colour without a copy,
98
+ `og.ui.configure(world, { primaryColor })` derives the primary's hover,
99
+ pressed and soft tones and the ink on it. The art direction of the world
100
+ (sprites, backgrounds) and the style must agree: a pixel-art world in a
94
101
  neon UI reads as two games.
95
102
 
96
103
  ## 2. Composition and hierarchy
97
104
 
98
105
  - **What the eye reads first**: decide the one thing per screen (the score on
99
106
  results, Play on the title, the health on a HUD) and make it the biggest,
100
- the brightest or the only accent-coloured thing. If two things compete,
107
+ the brightest or the only thing in the primary colour. If two things compete,
101
108
  shrink one.
102
109
  - **The play surface is the hero**: a HUD sits in the corners and edges
103
110
  (anchors `topLeft`, `topRight`, `bottom`), never across the middle. Keep
104
111
  the centre third free during play.
105
- - **HUD weight**: at most three HUD groups visible at once; each is a capsule
106
- with an icon and a number, not a panel with labels. Move rarely needed
112
+ - **HUD weight**: at most three HUD groups visible at once; each is a HUD box
113
+ (`frame: "hud"`) with an icon and a number, not a panel with labels. Move rarely needed
107
114
  information behind pause.
108
115
  - **Safe area**: everything interactive inside `og.ui.frameViewport().safeArea`
109
116
  plus the density gutter; anchors do this for you, absolute positions do not.
110
117
  - **Phone vs desktop**: on `phonePortrait` the thumb zone is the bottom third:
111
118
  primary actions go there, touch controls at least 44 CSS px apart; titles
112
- shrink one step (`displayMd` instead of `displayLg`). On desktop, menus sit
119
+ shrink one step (`xl2` instead of `xl3`). On desktop, menus sit
113
120
  centred or left, never full-width.
114
121
  - **Grouping**: spacing does the grouping. Related things are `space.sm`
115
122
  apart, groups `space.lg` or more. Do not draw a box around everything.
116
123
 
117
124
  ## 3. Colour
118
125
 
119
- - **Limited palette**: a ground (`bg`), one or two surfaces, one ink (`fg`)
120
- with a muted ink, **one accent**, and the status colours. That is the whole
121
- palette; a skin declares exactly these.
122
- - **One accent**, used for the one primary action per screen, earned stars,
123
- the player's own row and focus. If everything is accent, nothing is.
126
+ - **Limited palette**: a ground (`backdrop`), one or two surfaces (`panel`,
127
+ `panelRaised`), one ink (`ink`) with a muted ink (`inkMuted`), **one
128
+ primary**, and the status colours. That is the whole palette; a style
129
+ declares these and the roles derived from them.
130
+ - **One primary**, used for the one primary action per screen, earned stars,
131
+ the player's own row and focus. If everything is primary, nothing is.
124
132
  - **Value before hue**: check the screen in greyscale. Text, the primary
125
133
  button and the player character must separate from their ground by value
126
134
  alone (body text at least 4.5:1; the packs' test enforces it for theirs:
127
135
  every label ink, every button label in every state, a text field's
128
- placeholder, which draws in `fgSubtle` on the field's frame, and an
129
- accent badge, `accentInk` on `accentSoft` over a panel (the results
136
+ placeholder, which draws in `inkFaint` on the field's frame, and a
137
+ primary badge, `highlight` on `primarySoft` over a panel (the results
130
138
  screen's "New best"), and a status badge in each of `success`, `warn`,
131
139
  `danger` and `info` (the tone's own ink on its 16 % tint), so a copied
132
- pack whose `fgSubtle`, `fill`, `accentInk`, `accentSoft` or a status
140
+ style whose `inkFaint`, `control`, `highlight`, `primarySoft` or a status
133
141
  colour moves must keep those pairs at 4.5:1 too. The status colours are
134
142
  also the meters', toasts' and status dots' colours: on a cream panel they
135
- are deep (the cartoon pack's warn is `#7b5102`, not a bright amber that
136
- read 1.63:1), on a dark one light (a red under a dark pack's panel is a
143
+ are deep (the cartoon style's warn is `#7b5102`, not a bright amber that
144
+ read 1.63:1), on a dark one light (a red under a dark style's panel is a
137
145
  salmon such as `#ff8080`). Make them for the panels; a badge placed on
138
- the other side of the scale (a light pack's badge straight on its dark
146
+ the other side of the scale (a light style's badge straight on its dark
139
147
  stage) moves its own ink towards white or black until it reads there,
140
148
  and the neutral badge does the same on its `fill`, so neither needs a
141
- second palette. A game's copied skin is held to the same bars: the
149
+ second palette. A game's own style is held to the same bars: the
142
150
  engine's `pack_contrast.rs` measures the archetypes' and examples'
143
- skins too, and their fixes were value, not hue (pathguard's lantern
151
+ styles too, and their fixes were value, not hue (pathguard's lantern
144
152
  orange deepened under a cream label, sparkorb's gold to a burnished
145
153
  amber under its cream ink, lampdive's plum fill darkened under its
146
154
  placeholder, Lantern Vale's status colours taken back to the fantasy
147
- pack's deep ones);
155
+ style's deep ones);
148
156
  it also reads the primary and secondary labels from a rendered frame,
149
157
  where thin strokes may never reach their colour: a pixel font's one-pixel
150
- strokes fall between pixels and took half their ink, so the pixel pack
158
+ strokes fall between pixels and took half their ink, so the pixel style
151
159
  thickens them with `textWeight body 0.5` and cooks DotGothic16 at
152
- `px_range 8`; a copy of the pack made before that needs both lines).
160
+ `px_range 8`; a copy of the style made before that needs both lines).
153
161
  - Saturated colour on large areas tires the eye: keep saturation for small
154
- things (the accent, icons, effects) and keep grounds calmer.
162
+ things (the primary, icons, effects) and keep grounds calmer.
155
163
  - Status colours mean status: never use `danger` red as decoration.
156
164
 
157
165
  ## 4. Typography
@@ -163,22 +171,22 @@ neon UI reads as two games.
163
171
  - **Numerals**: scores, timers, prices and stats in the numeric role
164
172
  (`font: "mono"`), so digits do not jitter as they count; big scores in the
165
173
  display face. Count them with `og.ui.counter`, never by setting text.
166
- - **A ramp, not sizes**: use the skin's steps (`displayLg`, `xl`, `body`,
174
+ - **A ramp, not sizes**: use the style's steps (`xl3`, `xl`, `body`,
167
175
  `meta`); three or four steps per screen at most. Each step carries its
168
- tracking (display sizes tighten, as the tokens do; a skin's `ramp` line
176
+ tracking (display sizes tighten; a style's `ramp` line
169
177
  takes it as its fourth number after the CJK leading), and it is applied
170
178
  to Latin text only.
171
- - Headings get `heading: true` so the skin's flourish frames them.
179
+ - Headings get `heading: true` so the style's flourish frames them.
172
180
 
173
181
  ## 5. Motion
174
182
 
175
183
  - Every change of state moves; nothing pops in or out without a transition
176
184
  unless reduced motion is on.
177
- - **Durations 120 to 300 ms** for UI (the skin's `durations`), up to 700 ms
185
+ - **Durations 120 to 300 ms** for UI (the style's `durations`), up to 700 ms
178
186
  for a screen transition, longer only for rewards. **Never linear**: ease
179
187
  out on arrival, ease in on exit.
180
188
  - **Anticipation and follow-through**: a press squashes before the action
181
- (`press` in the skin), a reward overshoots and settles (`pop`, `motion.pop`).
189
+ (`press` in the style), a reward overshoots and settles (`pop`, `motion.pop`).
182
190
  - **Stagger**: a panel's controls arrive one after another (`motion.stagger`
183
191
  30–60 ms), not all at once.
184
192
  - **Screens change through a transition** (`og.ui.screenTransition`): fade
@@ -190,22 +198,22 @@ neon UI reads as two games.
190
198
  ## 6. Feedback
191
199
 
192
200
  Every input is answered **on the same tick** (og-feel §1) by a visual and a
193
- sound: hover (the skin's lift or highlight), press (squash, the `press`
201
+ sound: hover (the style's lift or highlight), press (squash, the `press`
194
202
  sound), success (a toast, a burst, `success`), refusal (a disabled button
195
203
  shakes and plays `error` by itself; a wrong answer or a locked door shakes
196
204
  its element with `og.ui.shake` and the `error` sound; never silently ignore
197
205
  a press, and never shake the camera for it). Hand the UI its sounds with
198
206
  `og.ui.configure(world, { sounds: { hover, press, open, close, success, error } })`
199
- or the skin's `sound` lines. Results land with a count-up, stars with a
207
+ or the style's `sound` lines. Results land with a count-up, stars with a
200
208
  `og.ui.burst`, rewards with a pop.
201
209
 
202
210
  ## 7. Consistency
203
211
 
204
- - **One skin per game.** Every screen, dialog and HUD uses it.
205
- - **One radius scale**: the skin's `radius`; never pass radii by hand.
206
- - **One shadow direction**: the skin's shadows all fall the same way (down);
212
+ - **One style per game.** Every screen, dialog and HUD uses it.
213
+ - **One radius scale**: the style's `radius`; never pass radii by hand.
214
+ - **One shadow direction**: the style's shadows all fall the same way (down);
207
215
  in-world shadows should agree.
208
- - **One icon set**: the skin's `icons`; do not mix icon styles.
216
+ - **One icon set**: the style's `icons`; do not mix icon styles.
209
217
  - **One transition kind** for screen changes, one entrance for dialogs.
210
218
 
211
219
  ## 8. Atmosphere
@@ -255,15 +263,16 @@ Each of these is a defect, not a style choice:
255
263
  - the HUD as a toolbar: a full-width bar of labelled boxes across the top;
256
264
  - labels instead of icons on the HUD ("Health: 72/100" instead of a heart
257
265
  and a bar);
258
- - every colour saturated, or every button in the accent;
266
+ - every colour saturated, or every button in the primary colour;
259
267
  - a settings-page layout (left-aligned form rows) for a title screen.
260
268
 
261
269
  ## 10. The checklist before a game is done
262
270
 
263
271
  Run each item; each is checkable from a scripted run.
264
272
 
265
- 1. **A skin is configured**: `og.ui.theme(world).skin` is not empty (log it
266
- from a command, or read it in a playtest through a `facts` field).
273
+ 1. **A style is configured**: `og.ui.theme(world).style` is not empty and
274
+ not `prototype` (log it from a command, or read it in a playtest through
275
+ a `facts` field).
267
276
  2. **Title, pause and results screens captured**: `og run --headless --commands
268
277
  "0.5:show:title;1.5:screenshot:title.png;…"` (with the game's own commands);
269
278
  look at each capture in greyscale: the one thing per screen reads first.
@@ -277,7 +286,7 @@ Run each item; each is checkable from a scripted run.
277
286
  `og run --size 1170x2532`) and check in `dump_ui` that every control is at
278
287
  least 44 × 44 CSS px and nothing leaves the safe area.
279
288
  6. **Text legible**: body text at 13 px or more in `dump_ui` heights (a line
280
- of at least 16 px); the skin passes 4.5:1 (the packs' test, or the same
289
+ of at least 16 px); the style passes 4.5:1 (the built-ins' test, or the same
281
290
  check over your atlas).
282
291
  7. **Motion present**: captures 100 ms apart after `show:` differ (an
283
292
  entrance), and a score `dump_ui` text read twice during the results
@@ -290,7 +299,7 @@ Run each item; each is checkable from a scripted run.
290
299
  10. **Reduced motion**: with `reducedMotion: true` the captures show fades
291
300
  only, and nothing is missing.
292
301
  11. **CJK**: switch the language to Chinese in settings and capture: no
293
- missing-glyph boxes (the skin's fallback font covers it).
302
+ missing-glyph boxes (the style's or the string table's fallback font covers it).
294
303
  12. **None of section 9** is visible in any capture.
295
304
  13. **`og polish .` scores taste 90 or more** where it applies, and every
296
305
  finding left is one you read in the frames and can name a reason for
@@ -303,7 +312,7 @@ occupancy, screen transitions, UI sounds, locale keys) from the frames, the
303
312
  UI trees and the run's audio, with og-feel's measures beside it; og-build §5
304
313
  is the one list of its measures, thresholds and fixes. The numbers cannot
305
314
  judge a style: read the frames before changing anything, and fix the cause
306
- in the skin or the template, never per call.
315
+ in the style or the template, never per call.
307
316
 
308
317
  ## 12. Style catalogue: looks
309
318
 
@@ -311,7 +320,7 @@ A style is chosen once per game and then held by data, not by habit: the
311
320
  **look** (`assets/looks/<game>.ogt`, an `.ogt` of `type look`) binds the
312
321
  frame's whole treatment (tone mapping and exposure, the post stack, the
313
322
  stylisation primitives, material defaults such as toon shading and
314
- outlines, fog and sky, the UI skin and the antialiasing) in one diffable
323
+ outlines, fog and sky, the UI style and the antialiasing) in one diffable
315
324
  file. The engine ships the primitives and the presets; the taste is in
316
325
  which one you pick and how you tune it.
317
326
 
@@ -320,7 +329,7 @@ describes 37 styles in four families (pixel and retro, paint and line,
320
329
  shape and material, 3D rendering). For each: a one-line descriptor, three
321
330
  checks a critic can judge on a captured frame, the premises it fits
322
331
  (genre, mood, era, audience, as §1 asks them), the preset it maps to, the
323
- skin pack, palette constraints, asset specs (logical resolution, sprite
332
+ UI style (`ui`), palette constraints, asset specs (logical resolution, sprite
324
333
  scale, face budget, outline width, texture density) and an art-bible
325
334
  direction. Match the premise's four answers against `premise`, shortlist
326
335
  two, and choose by the checks you can actually hold for the whole game.
@@ -344,7 +353,7 @@ version 1
344
353
  ---
345
354
  name "Harbour at dusk"
346
355
  extends "anime-cel-3d" # the preset's slug (templates/looks/<slug>.ogt)
347
- skin "skins/harbour/skin.ogt" # the game's own copy of the style's skin pack
356
+ ui "styles/harbour/style.ogt" # the game's own UI style (the preset names a built-in)
348
357
  RenderSettings {
349
358
  toneMapping agx
350
359
  exposure 1.1
@@ -455,8 +464,8 @@ middle ground.
455
464
 
456
465
  Name it in the game (`og.game({ look: "looks/harbour.ogt", … })`) and it
457
466
  is applied after the first scene spawns and before `start` runs (so
458
- `start` may still change any field, and reads the look's skin from
459
- `og.render.look(world)?.skin`); switch looks at a tick with
467
+ `start` may still change any field, and reads the look's UI style from
468
+ `og.render.look(world)?.style`); switch looks at a tick with
460
469
  `og.load.look` and `og.render.applyLook` (a flashback, a photo mode, an
461
470
  area's mood). A look's `MaterialStyle` reaches every lit material that
462
471
  leaves the field at its default; a material that must stay as made (a
@@ -492,7 +501,7 @@ in its style step (`docs/platform-asks.md` in the engine repository): the
492
501
  catalogue entry chosen from the premise, the look written, and the
493
502
  entry's three checks handed to the critic that reads the frames.
494
503
 
495
- | Slug | Family | Style | Support | Skin |
504
+ | Slug | Family | Style | Support | UI |
496
505
  |---|---|---|---|---|
497
506
  | `expressionist-painting` | Paint and line | Expressionist paint | content | `paper` |
498
507
  | `bold-comic-illustration` | Paint and line | Bold comic illustration | full | `fantasy` |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: og-ui
3
- description: Game screens and HUDs on Origin Engine with og.ui — which screen template to pick (title, pause, settings, five HUDs, cutscene, shop, inventory, level select, results, leaderboard, tutorial, loading, dialogs, toasts), how to feed it data and read what it answers, the widget set beneath, focus for keyboard and gamepad, accessibility (the screen-reader mirror), phones, touch gestures and the safe area, text (fields, text areas, selection and the clipboard, input methods, ellipsis, kerning, shaping, font fallback), and the look: skins (style packs) and how their frames tile, count-ups, bursts, shakes, screen transitions and UI sounds. Use when building or changing menus, HUDs or any UI in a project with a project.ogt.
3
+ description: Game screens and HUDs on Origin Engine with og.ui — which screen template to pick (title, pause, settings, five HUDs, cutscene, shop, inventory, level select, results, leaderboard, tutorial, loading, dialogs, toasts), how to feed it data and read what it answers, the widget set beneath, focus for keyboard and gamepad, accessibility (the screen-reader mirror), phones, touch gestures and the safe area, text (fields, text areas, selection and the clipboard, input methods, ellipsis, kerning, shaping, font fallback), and the look: UI styles (a look's `ui` line, the six built-ins, the game's own) and how their frames tile, count-ups, bursts, shakes, screen transitions and UI sounds. Use when building or changing menus, HUDs or any UI in a project with a project.ogt.
4
4
  ---
5
5
 
6
6
  `og.ui` is the engine's immediate-mode UI: a system describes the tree every
@@ -14,7 +14,7 @@ Two games written from this package alone (`examples/lantern-vale`,
14
14
  `examples/hollowbell`) used the main menu, pause, cutscene, shop and
15
15
  results templates (the results with a localised Best and New best) and a
16
16
  text field with input-method composition, and none needed wiring beyond
17
- its desc; a skin pack copied into the game restyled every screen, and
17
+ its desc; a style copied into the game restyled every screen, and
18
18
  `og polish` measured taste 99 for both. The Kestrel Stair
19
19
  (`examples/kestrel-stair`) used the cutscene, tutorial overlay,
20
20
  inventory with drag and drop, level select, leaderboard with its honest
@@ -50,14 +50,14 @@ if (r.closed) state.screen = "map";
50
50
  Screenshots of every template at desktop (1280×720) and on a phone in portrait
51
51
  (390×844 at 3×) are in the engine repository under `templates/ui-kit/screens/`;
52
52
  the full-size reference frames are `crates/og-ui/references/metal/kit-*.png`,
53
- and every style pack's screens on a phone `kit-skin-<pack>-phone.png`.
53
+ and every style pack's screens on a phone `kit-style-<pack>-phone.png`.
54
54
 
55
55
  | The game needs | Call | It answers |
56
56
  |---|---|---|
57
57
  | A title screen | `og.ui.mainMenu(id, { title, subtitle, items, footer, instant })` (`instant` while it is the screen the game opens on: §6) | `selected`: the item id |
58
58
  | Pause | `og.ui.pauseMenu(id, { open, items })` (Resume, Settings, Quit by default) | `selected`; Back answers `resume` |
59
59
  | Settings | `og.ui.settingsScreen(id, { open, settings, languages, onChange, onSave })` | the new `GameSettings`, `changed`, `closed` |
60
- | A platformer HUD | `og.ui.platformerHud(id, { lives, maxLives, coins, score, level, timeLeft, touch })` (what it is not given it does not draw: `maxLives: 0` no hearts, no `coins` or `score` no capsule for them; the words `pauseLabel`, `jumpLabel` from the string table, og-i18n §2) | `paused`, `moveX`, `action: "jump"` |
60
+ | A platformer HUD | `og.ui.platformerHud(id, { lives, maxLives, coins, score, level, timeLeft, touch })` (what it is not given it does not draw: `maxLives: 0` no hearts, no `coins` or `score` no box for them; the words `pauseLabel`, `jumpLabel` from the string table, og-i18n §2) | `paused`, `moveX`, `action: "jump"` |
61
61
  | A shooter HUD | `og.ui.shooterHud(id, { health, armor, ammo, reserve, abilities, map, touch })` (the words `killsLabel`, `fireLabel`, `pauseLabel`) | `paused`, `action` (ability id, `fire`), stick |
62
62
  | A puzzle HUD | `og.ui.puzzleHud(id, { level, score, target, stars, moves, timeLeft })` (the words `movesLabel`, `targetLabel` with `{target}`, `pauseLabel`) | `paused` |
63
63
  | An idle or incremental HUD | `og.ui.idleHud(id, { currencies, generators })` (the words `tapLabel`, `levelLabel` with `{level}`, `rateLabel` with `{rate}`, `pauseLabel`) | `action: "tap"` or `"buy:<id>"` |
@@ -143,9 +143,9 @@ its own names `coin`, `gear`, `fire`…, and every kit screen draws them) loads
143
143
  { font } })`; `og.ui.icon(id, "heart", { size, tint })` and `[icon=heart]`
144
144
  then draw crisp distance-field glyphs at any size in their ink. A texture
145
145
  grid (`icons: { texture, columns, rows, names }`) still works for painted
146
- icons. Icons sit in `fgMuted` or `fgSubtle` (design language: icons are
147
- neutral). Sets name their icons differently (iconoir's `coins` and
148
- `fire-flame`, the skin grids' `coin` and `fire`): an unknown name draws an
146
+ icons. Icons sit in `inkMuted` or `inkFaint` (icons stay neutral; colour
147
+ is for state). Sets name their icons differently (iconoir's `coins` and
148
+ `fire-flame`, the style grids' `coin` and `fire`): an unknown name draws an
149
149
  empty slot and warns, so a template that may meet either asks
150
150
  `og.ui.hasIcon(name)` first and draws its fallback, or names an icon twice
151
151
  in its set (`icon "coin" "coins"`). A path's `stroke-linejoin` is kept
@@ -284,46 +284,60 @@ fingers with `touch_start` for the first and `touch:1:down:<x,y>` /
284
284
  `touch:1:move:<x,y>` / `touch:1:up` for the second
285
285
  (`templates/ui-kit/tests/gestures.og.ts`).
286
286
 
287
- ## 6. Theming: skins, motion, transitions, sounds
288
-
289
- The default look is the platform's tokens; a game should have its own. A
290
- **skin** (`og.load.skin`, an `.ogt` of `type skin`) replaces the palette,
291
- radii, type ramp, durations, fonts and icons, and dresses every element
292
- class in nine-slice frames from its atlas (panel, card, button with hover,
293
- pressed and disabled, field, slot, tooltip, HUD capsule, progress track and
294
- fill), with ornaments (panel corners, the divider, a heading flourish), a
295
- panel texture, shadows and glows, press squash and hover lift, entrances and
296
- sounds. Choosing one is og-taste §1. Six packs to start from are in
297
- the engine repository under `templates/ui-kit/assets/skins/` (`pixel`,
298
- `cartoon`, `fantasy`, `scifi`, `paper`, `neon`; copy the directory into the
299
- game's `assets/skins/` and rename it at will: a skin's ids are read
300
- relative to its file first, then from `assets/`; og-taste §1 has the one
301
- line to check, the CJK `fallback`):
287
+ ## 6. Styles: the look of the game's UI, motion, transitions, sounds
288
+
289
+ The game's UI follows the game's art, never the Origin platform's look:
290
+ the engine has no house style to fall back on. Without a style a UI draws
291
+ in **prototype**, the engine's placeholder: greys, near-square corners and
292
+ a cyan focus ring, meant to be replaced. `og polish` warns when a game
293
+ ships in it. A **style** (`og.load.style`, an `.ogt` of `type style`) is
294
+ complete: its scheme, every palette role (`backdrop`, `panel`, `ink`,
295
+ `inkMuted`, `primary`, `focus`, `hud`, `danger`, ...), the four radii
296
+ (`small`, `control`, `bar`, `panel`), the type ramp, durations, fonts and
297
+ icons. It dresses every element class in nine-slice frames from its atlas
298
+ (panel, card, button with hover, pressed and disabled, field, slot,
299
+ tooltip, HUD box, progress track and fill), with ornaments (panel corners,
300
+ the divider, a heading flourish), a panel texture, shadows and glows,
301
+ press squash and hover lift, entrances and sounds. Choosing one is
302
+ og-taste §1.
303
+
304
+ The usual way in is the game's look (og-taste §12): its `ui` line names
305
+ the style, and every catalogue preset already names one. `ui "pixel"` is a
306
+ built-in style by slug (`pixel`, `cartoon`, `fantasy`, `scifi`, `paper`,
307
+ `neon`; `og cook` bundles the one a look names). `ui
308
+ "styles/<name>/style.ogt"` is the game's own, and a game's
309
+ `assets/styles/<slug>/style.ogt` wins over the built-in of that slug.
310
+ To make one from a built-in, copy `templates/styles/<slug>/` of the
311
+ engine repository into the game's `assets/styles/<name>/` and change it
312
+ at will: a style's ids are read relative to its file first, then from
313
+ `assets/`. A style file must name every palette role and all four radii;
314
+ `og cook` lists the ones missing. Loading one by hand:
302
315
 
303
316
  ```ts
304
- const skin = og.load.skin(world, "skins/fantasy/skin.ogt"); // atlas, fonts, icons load with it
317
+ const style = og.load.style(world, "styles/fantasy/style.ogt"); // atlas, fonts, icons load with it
305
318
  og.ui.configure(world, {
306
- skin: skin.ok ? skin.value : null,
307
- contrast: "high", // still applies over a skin
319
+ style: style.ok ? style.value : null,
320
+ contrast: "high", // still applies over a style
308
321
  reducedMotion: settings.reducedMotion, // every motion becomes a fade
309
322
  sounds: { hover, press, open, close, success, error }, // og.load.sound handles, played on the ui bus
310
323
  });
311
- og.ui.configure(world, { reducedMotion: true }); // later, from the settings: the skin, fonts and sounds stay
324
+ og.ui.configure(world, { reducedMotion: true }); // later, from the settings: the style, fonts and sounds stay
325
+ og.ui.configure(world, { primaryColor: "#e0553b" }); // re-colour the style's primary; hover, pressed and the ink on it follow
312
326
  ```
313
327
 
314
328
  `configure` takes a partial UiDesc: a field left out keeps the value the
315
- last call gave it (or the skin a look applied, `GameDesc.look` and
316
- `og.render.applyLook`), and `null` clears one (`skin: null` is the
317
- platform tokens again). A UI left with no font at all warns that it draws
329
+ last call gave it (or the style a look applied, `GameDesc.look` and
330
+ `og.render.applyLook`), and `null` clears one (`style: null` is the
331
+ prototype style again). A UI left with no font at all warns that it draws
318
332
  no text.
319
333
 
320
- A skin's `slice` line says how a frame's edges and centre fill: `stretch`
334
+ A style's `slice` line says how a frame's edges and centre fill: `stretch`
321
335
  (the default), `repeat` (tiled at the art's size from the corner, the last
322
336
  tile cut) or `round` (a whole number of tiles resized to fit, as CSS
323
337
  `round`): tile patterned borders (a rope, a rivet row, a pixel bevel),
324
338
  stretch plain ones. `glass <part> <blur> <fill> [tint]` makes a part glass:
325
339
  the game behind it blurred (`blur` CSS px), tinted by the part's fill, under
326
- its frame drawn at `fill` opacity (the scifi pack's dialogs:
340
+ its frame drawn at `fill` opacity (the scifi style's dialogs:
327
341
  `glass panel 16 0.85 0.15`). Glass blurs the scene and the UI drawn under
328
342
  it (a HUD under a glass dialog); it costs a frame copy and a blur pyramid
329
343
  when any is on screen (about 0.8 ms at 1280×720 on an M4 Pro), and one more
@@ -335,43 +349,44 @@ translucent fills (secondary buttons, chips, tracks) flat. Your own
335
349
  see-through overlays should follow: read `og.ui.frameTheme().reducedTransparency`
336
350
  (and `reducedMotion`) and draw them opaque.
337
351
 
338
- A skin of the game's own art: draw each part as a nine-patch PNG
352
+ A style of the game's own art: draw each part as a nine-patch PNG
339
353
  (`panel.9.png`, `button.9.png`, `buttonHover.9.png`, ..., `soft.9.png`: the
340
354
  art inside a one-pixel border whose top row and left column mark in opaque
341
355
  black what stretches) and any ornament as a plain PNG (`corner.png`,
342
- `divider.png`, `flourish.png`, `tile.png`) in the skin's directory, then
343
- `og skin pack assets/skins/<name>`: it packs them into `atlas.png` and
344
- writes the skin file's `atlas`, `slice` and `soft` lines and the ornaments'
345
- regions, keeping every other line (palette, fonts, a slice's `tint` or
356
+ `divider.png`, `flourish.png`, `tile.png`) in the style's directory, then
357
+ `og style pack assets/styles/<name>`: it packs them into `atlas.png` and
358
+ writes the style file's `atlas`, `slice` and `soft` lines and the ornaments'
359
+ regions, keeping every other line (palette, radii, fonts, a slice's `tint` or
346
360
  `repeat`). Re-run it after redrawing a part.
347
361
 
348
- Never pass hex colours for chrome: use `{ token: "accent" }`, `"fgMuted"`,
349
- `"surface"` and the type steps (`displayLg`, `xl`, `meta`); the skin's
350
- palette answers them. A container that should look like a HUD capsule or a
351
- card takes `frame: "capsule"` (or `"card"`, `"slot"`…) in its desc; titles
352
- take `heading: true`. Without a skin both are plain.
362
+ Never pass hex colours for chrome: use `{ token: "primary" }`, `"inkMuted"`,
363
+ `"panel"` and the type steps (`xl3`, `xl`, `meta`); the style's
364
+ palette answers them. A container that should look like a HUD box or a
365
+ card takes `frame: "hud"` (or `"card"`, `"slot"`…) in its desc; titles
366
+ take `heading: true`. Without a frame atlas both are plain fills at the
367
+ style's radii.
353
368
 
354
369
  Motion and juice, all by the tick and in the UI state:
355
370
 
356
371
  | Want | Call |
357
372
  |---|---|
358
- | A score, coins or a reward counting up | `og.ui.counter(id, value, { from: 0, font: "display", step: "displayLg" })` |
373
+ | A score, coins or a reward counting up | `og.ui.counter(id, value, { from: 0, font: "display", step: "xl3" })` |
359
374
  | Sparks when a star or reward lands | `og.ui.burst({ count: 10 })` right after the element |
360
375
  | A panel or item popping in | `og.ui.transition(id, show, { kind: "pop" })` (also `fade`, `slideUp`…, `scale`) |
361
376
  | A change of game screen | `const s = og.ui.screenTransition("Screens", state.screen, { kind: "iris" }).shown;` then build `s` (`fade`, `wipe`, `iris`, `dissolve`, `slide`; a colour or a texture) |
362
- | A wrong answer, a locked door, a refused purchase | `og.ui.shake({ sound: true })` right after the element, once, on the tick it happens: it swings three times and comes to rest by itself (the skin's `motion.shake`, else 6 px), with the `error` sound |
377
+ | A wrong answer, a locked door, a refused purchase | `og.ui.shake({ sound: true })` right after the element, once, on the tick it happens: it swings three times and comes to rest by itself (the style's `motion.shake`, else 6 px), with the `error` sound |
363
378
 
364
379
  `screenTransition` keeps the previous screen in `shown` for the closing
365
380
  half of its duration, switches at full cover, then reveals the new one.
366
381
  Build `shown`, not the requested screen; `dump_ui` therefore lists the old
367
382
  screen until the midpoint. The cover itself does not block input. A new
368
- panel's own skin entrance starts when that panel is first built, and
383
+ panel's own style entrance starts when that panel is first built, and
369
384
  ignores presses until it arrives (§4): the closing half of a wipe plus
370
385
  that entrance explains why a press just after the switch can be lost.
371
386
 
372
- Skins add the rest themselves: dialog and panel entrances with staggered
387
+ Styles add the rest themselves: dialog and panel entrances with staggered
373
388
  controls, hover lift and press squash on springs (the `hoverSpring 1 350` and `pressSpring 0.55 120` lines of a
374
- skin's `motion` block: damping ratio and response in ms; below 1 bounces), eased progress bars, toasts flying in,
389
+ style's `motion` block: damping ratio and response in ms; below 1 bounces), eased progress bars, toasts flying in,
375
390
  a shake and the error sound on a press of a disabled button. Reduced motion
376
391
  keeps every shake still and turns the rest into fades. The screen the game
377
392
  opens on is built settled (`instant: true` on its `mainMenu`, `anchor` or
@@ -388,7 +403,7 @@ scripts are shaped (Arabic joins, Devanagari conjuncts) and every font's
388
403
  kerning pairs apply. What a game sets:
389
404
 
390
405
  ```ts
391
- og.ui.label("Quest", quest.text, { width: { kind: "grow" }, maxLines: 2, ellipsis: true, color: { token: "fgMuted" } }); // cut with … on the last line
406
+ og.ui.label("Quest", quest.text, { width: { kind: "grow" }, maxLines: 2, ellipsis: true, color: { token: "inkMuted" } }); // cut with … on the last line
392
407
  og.ui.label("Gold", gold, { font: "rounded", step: "xl", nowrap: true }); // body, display, mono or rounded
393
408
  ```
394
409
 
@@ -430,7 +445,7 @@ script text with `type:`, not `key_down`.
430
445
 
431
446
  Fonts (the one rule for every label, field and button):
432
447
 
433
- 1. The role's font (body, display, mono, rounded; the skin's or
448
+ 1. The role's font (body, display, mono, rounded; the style's or
434
449
  `og.ui.configure`'s) when it has every character: the whole label in it.
435
450
  2. Else the first of the locale's fonts (its string table's `fonts` line)
436
451
  that has every character: the whole label in that face, its Latin and
@@ -439,9 +454,9 @@ Fonts (the one rule for every label, field and button):
439
454
  3. Else run by run: each run of the label from the first font that has it,
440
455
  the role's font first, then the locale's fonts, `UiDesc.fallbackFonts`
441
456
  (a Hebrew or Devanagari face, a CJK face cooked with `runtime true`) and
442
- the skin's `fallback`; a letter and its accents stay in one face. A
457
+ the style's `fallback`; a letter and its accents stay in one face. A
443
458
  CJK-only subset (lantern-vale's `tools/subset-font.py`) gives this: the
444
- Latin in the skin's face, the Chinese in Noto, on one line.
459
+ Latin in the style's face, the Chinese in Noto, on one line.
445
460
 
446
461
  So choose by the subset: keep Latin in the CJK subset for one face per
447
462
  label, leave it out to keep the game's Latin face beside CJK. Tracking