@caputchin/game-dino-runner 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Caputchin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # Dino Runner
2
+
3
+ Jump the cactus, duck the birds, and run as far as you can. A Caputchin
4
+ first-party game, recreated from the Chrome offline dino in clean, framework-
5
+ free TypeScript and rendered with inline SVG so every pixel is skinnable.
6
+
7
+ ## How it plays
8
+
9
+ The runner sprints over a scrolling desert. Obstacles arrive from the right:
10
+ ground cacti (which clump up to three wide as the run speeds up) and, once the
11
+ run is fast enough, flying birds. Jump the ground obstacles, duck the low
12
+ fliers, and survive. Speed ramps up the whole time. The light and dark looks
13
+ are separate skins the host picks (the dark skin runs under a moon-and-stars
14
+ night sky); the palette stays fixed for the session.
15
+
16
+ The game is endless, so success is evaluated at crash time: the first run that
17
+ reaches the configured **pass score** reports success (`bridge.pass`), and so
18
+ does every later run that beats it. Best score is tracked in memory for the
19
+ session (the sandboxed iframe has no storage).
20
+
21
+ | Input | Keyboard | Touch |
22
+ |---|---|---|
23
+ | Jump | `Space` / `Up` / `W` | tap the field, or the jump button |
24
+ | Duck | hold `Down` / `S` | hold the duck button |
25
+ | Restart | `Space` / `Up` on the game-over screen | the Restart button |
26
+
27
+ ## Customization
28
+
29
+ Everything is driven by [`caputchin.json`](caputchin.json) and resolved by the
30
+ widget into the game's runtime context:
31
+
32
+ - **Locales** — all on-screen text (including the game-over title the original
33
+ hard-coded) ships in the 11 official languages and is fully overridable.
34
+ - **Skins** — `light` and `dark` are separate presets the host picks (no
35
+ in-game switching); each sets the background, foreground, and button colors.
36
+ All 16 sprites are `currentColor` SVGs decoded and inlined at runtime, so a
37
+ customer skin can recolor them or swap the art wholesale (e.g. a different
38
+ runner) without touching code.
39
+ - **Configurations** — speed, acceleration, gravity, jump strength, obstacle
40
+ spacing, the pass score, whether birds spawn, and sound on/off. Ships with
41
+ `default` (tuned for short sessions), `classic` (the original's exact speed +
42
+ bird timing), `casual`, `hardcore`, and `calm` presets.
43
+
44
+ ## Accessibility
45
+
46
+ Keyboard- and touch-operable, screen-reader labelled, with live-region
47
+ announcements for run start / game over / new best. Honors
48
+ `prefers-reduced-motion` by freezing decorative parallax (clouds, moon, stars)
49
+ while keeping the core run intact. Note the core challenge is a visual reflex
50
+ task by nature.
51
+
52
+ ## Build
53
+
54
+ ```bash
55
+ pnpm --filter @caputchin/game-dino-runner build
56
+ ```
57
+
58
+ Produces a single self-contained IIFE at `dist/dino-runner.js` with every
59
+ sprite and sound inlined as a data URI, per the Caputchin game bundle
60
+ constraint.
61
+
62
+ ## License
63
+
64
+ MIT, see [LICENSE](../../LICENSE).
65
+
66
+ The sprite art and sound effects come from the Chromium open-source "t-rex
67
+ runner" offline game and are used under its BSD-3-Clause license; see
68
+ [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md).
@@ -0,0 +1,55 @@
1
+ # Third-party notices
2
+
3
+ ## Sprite art + sound effects (Dino Runner)
4
+
5
+ Two sets of assets come from the Chromium open-source "t-rex runner" offline
6
+ game:
7
+
8
+ - **Sprites** (`src/assets/sprites/`): the runner, cacti, pterodactyl, and
9
+ cloud, traced from the offline sprite sheet and re-emitted as `currentColor`
10
+ SVGs.
11
+ - **Sound effects** (`src/assets/sounds/`): the jump, score-milestone, and
12
+ crash clips, the original Ogg/Vorbis files used verbatim.
13
+
14
+ Both originate from:
15
+
16
+ > Copyright The Chromium Authors
17
+
18
+ and are distributed under the BSD-3-Clause license below. This notice satisfies
19
+ the license's source- and binary-redistribution attribution requirement; the
20
+ assets are inlined into the built bundle (`dist/dino-runner.js`).
21
+
22
+ The rest of this package (game engine, layout, configuration, localization,
23
+ and all other code) is original work under this repository's MIT license.
24
+
25
+ ### Chromium BSD-3-Clause license
26
+
27
+ ```
28
+ Copyright 2015 The Chromium Authors
29
+
30
+ Redistribution and use in source and binary forms, with or without
31
+ modification, are permitted provided that the following conditions are
32
+ met:
33
+
34
+ * Redistributions of source code must retain the above copyright
35
+ notice, this list of conditions and the following disclaimer.
36
+ * Redistributions in binary form must reproduce the above
37
+ copyright notice, this list of conditions and the following disclaimer
38
+ in the documentation and/or other materials provided with the
39
+ distribution.
40
+ * Neither the name of Google LLC nor the names of its
41
+ contributors may be used to endorse or promote products derived from
42
+ this software without specific prior written permission.
43
+
44
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
45
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
46
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
47
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
48
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
49
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
50
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
51
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
52
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
53
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
54
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
55
+ ```
package/caputchin.json ADDED
@@ -0,0 +1,630 @@
1
+ {
2
+ "marketplace": {
3
+ "name": "Dino Runner",
4
+ "description": "Jump the cactus, duck the birds, and run as far as you can.",
5
+ "preview": "preview.png",
6
+ "categories": ["Arcade", "Reflex"],
7
+ "support": {
8
+ "responsive": true,
9
+ "touch": true,
10
+ "keyboard": true,
11
+ "screenReader": true,
12
+ "audio": "optional"
13
+ }
14
+ },
15
+ "npm": "@caputchin/game-dino-runner",
16
+ "entry": "dist/dino-runner.js",
17
+ "preferred": {
18
+ "width": 600,
19
+ "height": 150
20
+ },
21
+ "locales": {
22
+ "schema": {
23
+ "ariaGame": {
24
+ "name": "Game region label",
25
+ "description": "Screen-reader label for the whole play area."
26
+ },
27
+ "headerScore": {
28
+ "name": "Score label",
29
+ "description": "Label preceding the live distance score in the header."
30
+ },
31
+ "headerBest": {
32
+ "name": "Best score label",
33
+ "description": "Label preceding the session-best score in the header."
34
+ },
35
+ "startTitle": {
36
+ "name": "Start screen title",
37
+ "description": "Headline shown on the start screen before the first run."
38
+ },
39
+ "startBody": {
40
+ "name": "Start screen body",
41
+ "description": "Short instructions shown on the start screen."
42
+ },
43
+ "startButton": {
44
+ "name": "Start button",
45
+ "description": "Label for the button that begins the run."
46
+ },
47
+ "controlsHint": {
48
+ "name": "Controls hint",
49
+ "description": "Small keyboard-controls reminder shown under the start button."
50
+ },
51
+ "gameOverTitle": {
52
+ "name": "Game over title",
53
+ "description": "Headline shown when the runner crashes. This is the text the host fully controls (the original Chrome game hard-coded it)."
54
+ },
55
+ "gameOverScore": {
56
+ "name": "Game over score line",
57
+ "description": "Final score shown on the game-over screen.",
58
+ "tokens": ["score"]
59
+ },
60
+ "gameOverBest": {
61
+ "name": "Game over best line",
62
+ "description": "Session-best score shown on the game-over screen.",
63
+ "tokens": ["score"]
64
+ },
65
+ "restartButton": {
66
+ "name": "Restart button",
67
+ "description": "Label for the button that restarts after a crash."
68
+ },
69
+ "ariaJump": {
70
+ "name": "Jump control label",
71
+ "description": "Screen-reader / touch-button label for the jump action."
72
+ },
73
+ "ariaDuck": {
74
+ "name": "Duck control label",
75
+ "description": "Screen-reader / touch-button label for the duck action."
76
+ },
77
+ "ariaSound": {
78
+ "name": "Sound toggle label",
79
+ "description": "Accessible label for the in-game button that turns sound on / off."
80
+ },
81
+ "announceStart": {
82
+ "name": "Announcer: run started",
83
+ "description": "Live-region message when the run begins."
84
+ },
85
+ "announceGameOver": {
86
+ "name": "Announcer: game over",
87
+ "description": "Live-region message when the runner crashes.",
88
+ "tokens": ["score"]
89
+ },
90
+ "announceNewBest": {
91
+ "name": "Announcer: new best",
92
+ "description": "Live-region message when the run sets a new session best.",
93
+ "tokens": ["score"]
94
+ }
95
+ },
96
+ "presets": {
97
+ "English": {
98
+ "_lang": "en",
99
+ "_default": true,
100
+ "ariaGame": "Endless runner. Jump the cactus, duck the birds, and run as far as you can.",
101
+ "headerScore": "Score",
102
+ "headerBest": "Best",
103
+ "startTitle": "Dino Runner",
104
+ "startBody": "Jump the cactus and duck the birds. Run as far as you can without crashing.",
105
+ "startButton": "Start",
106
+ "controlsHint": "Space or Up to jump, Down to duck",
107
+ "gameOverTitle": "Game over",
108
+ "gameOverScore": "Score {score}",
109
+ "gameOverBest": "Best {score}",
110
+ "restartButton": "Restart",
111
+ "ariaJump": "Jump",
112
+ "ariaDuck": "Duck",
113
+ "ariaSound": "Sound",
114
+ "announceStart": "Go",
115
+ "announceGameOver": "Game over. You scored {score}.",
116
+ "announceNewBest": "New best score: {score}."
117
+ },
118
+ "Chinese (Simplified)": {
119
+ "_lang": "zh-Hans",
120
+ "_default": true,
121
+ "ariaGame": "无尽跑酷。跳过仙人掌,躲开飞鸟,尽量跑得更远。",
122
+ "headerScore": "得分",
123
+ "headerBest": "最佳",
124
+ "startTitle": "小恐龙快跑",
125
+ "startBody": "跳过仙人掌,躲开飞鸟。在不撞上障碍的情况下尽量跑得更远。",
126
+ "startButton": "开始",
127
+ "controlsHint": "空格或上键跳跃,下键下蹲",
128
+ "gameOverTitle": "游戏结束",
129
+ "gameOverScore": "得分 {score}",
130
+ "gameOverBest": "最佳 {score}",
131
+ "restartButton": "重新开始",
132
+ "ariaJump": "跳跃",
133
+ "ariaDuck": "下蹲",
134
+ "ariaSound": "声音",
135
+ "announceStart": "出发",
136
+ "announceGameOver": "游戏结束。你得了 {score} 分。",
137
+ "announceNewBest": "新的最佳成绩:{score}。"
138
+ },
139
+ "Spanish": {
140
+ "_lang": "es",
141
+ "_default": true,
142
+ "ariaGame": "Carrera sin fin. Salta los cactus, esquiva los pájaros y corre lo más lejos que puedas.",
143
+ "headerScore": "Puntos",
144
+ "headerBest": "Mejor",
145
+ "startTitle": "Dino Runner",
146
+ "startBody": "Salta los cactus y esquiva los pájaros. Corre lo más lejos que puedas sin chocar.",
147
+ "startButton": "Empezar",
148
+ "controlsHint": "Espacio o flecha arriba para saltar, abajo para agacharte",
149
+ "gameOverTitle": "Fin del juego",
150
+ "gameOverScore": "Puntos {score}",
151
+ "gameOverBest": "Mejor {score}",
152
+ "restartButton": "Reiniciar",
153
+ "ariaJump": "Saltar",
154
+ "ariaDuck": "Agacharse",
155
+ "ariaSound": "Sonido",
156
+ "announceStart": "¡Ya!",
157
+ "announceGameOver": "Fin del juego. Hiciste {score} puntos.",
158
+ "announceNewBest": "Nueva mejor puntuación: {score}."
159
+ },
160
+ "Arabic": {
161
+ "_lang": "ar",
162
+ "_default": true,
163
+ "ariaGame": "جري بلا نهاية. اقفز فوق الصبار، وتفادَ الطيور، واركض أبعد ما تستطيع.",
164
+ "headerScore": "النقاط",
165
+ "headerBest": "الأفضل",
166
+ "startTitle": "عداء الديناصور",
167
+ "startBody": "اقفز فوق الصبار وتفادَ الطيور. اركض أبعد ما تستطيع دون أن تصطدم.",
168
+ "startButton": "ابدأ",
169
+ "controlsHint": "مسافة أو السهم العلوي للقفز، السهم السفلي للانحناء",
170
+ "gameOverTitle": "انتهت اللعبة",
171
+ "gameOverScore": "النقاط {score}",
172
+ "gameOverBest": "الأفضل {score}",
173
+ "restartButton": "أعد المحاولة",
174
+ "ariaJump": "اقفز",
175
+ "ariaDuck": "انحنِ",
176
+ "ariaSound": "الصوت",
177
+ "announceStart": "انطلق",
178
+ "announceGameOver": "انتهت اللعبة. حصلت على {score} نقطة.",
179
+ "announceNewBest": "أفضل نتيجة جديدة: {score}."
180
+ },
181
+ "Portuguese": {
182
+ "_lang": "pt",
183
+ "_default": true,
184
+ "ariaGame": "Corrida sem fim. Pule os cactos, desvie dos pássaros e corra o mais longe que conseguir.",
185
+ "headerScore": "Pontos",
186
+ "headerBest": "Melhor",
187
+ "startTitle": "Dino Runner",
188
+ "startBody": "Pule os cactos e desvie dos pássaros. Corra o mais longe que conseguir sem bater.",
189
+ "startButton": "Começar",
190
+ "controlsHint": "Espaço ou seta para cima para pular, para baixo para abaixar",
191
+ "gameOverTitle": "Fim de jogo",
192
+ "gameOverScore": "Pontos {score}",
193
+ "gameOverBest": "Melhor {score}",
194
+ "restartButton": "Reiniciar",
195
+ "ariaJump": "Pular",
196
+ "ariaDuck": "Abaixar",
197
+ "ariaSound": "Som",
198
+ "announceStart": "Já!",
199
+ "announceGameOver": "Fim de jogo. Você fez {score} pontos.",
200
+ "announceNewBest": "Novo recorde: {score}."
201
+ },
202
+ "French": {
203
+ "_lang": "fr",
204
+ "_default": true,
205
+ "ariaGame": "Course sans fin. Sautez par-dessus les cactus, esquivez les oiseaux et courez le plus loin possible.",
206
+ "headerScore": "Score",
207
+ "headerBest": "Record",
208
+ "startTitle": "Dino Runner",
209
+ "startBody": "Sautez par-dessus les cactus et esquivez les oiseaux. Courez le plus loin possible sans vous écraser.",
210
+ "startButton": "Commencer",
211
+ "controlsHint": "Espace ou flèche haut pour sauter, bas pour se baisser",
212
+ "gameOverTitle": "Partie terminée",
213
+ "gameOverScore": "Score {score}",
214
+ "gameOverBest": "Record {score}",
215
+ "restartButton": "Recommencer",
216
+ "ariaJump": "Sauter",
217
+ "ariaDuck": "Se baisser",
218
+ "ariaSound": "Son",
219
+ "announceStart": "Partez !",
220
+ "announceGameOver": "Partie terminée. Vous avez fait {score}.",
221
+ "announceNewBest": "Nouveau record : {score}."
222
+ },
223
+ "German": {
224
+ "_lang": "de",
225
+ "_default": true,
226
+ "ariaGame": "Endlos-Lauf. Spring über die Kakteen, duck dich unter den Vögeln und lauf so weit du kannst.",
227
+ "headerScore": "Punkte",
228
+ "headerBest": "Rekord",
229
+ "startTitle": "Dino Runner",
230
+ "startBody": "Spring über die Kakteen und duck dich unter den Vögeln. Lauf so weit du kannst, ohne zu verunglücken.",
231
+ "startButton": "Start",
232
+ "controlsHint": "Leertaste oder Pfeil hoch zum Springen, runter zum Ducken",
233
+ "gameOverTitle": "Spiel vorbei",
234
+ "gameOverScore": "Punkte {score}",
235
+ "gameOverBest": "Rekord {score}",
236
+ "restartButton": "Neustart",
237
+ "ariaJump": "Springen",
238
+ "ariaDuck": "Ducken",
239
+ "ariaSound": "Ton",
240
+ "announceStart": "Los",
241
+ "announceGameOver": "Spiel vorbei. Du hast {score} Punkte erreicht.",
242
+ "announceNewBest": "Neuer Rekord: {score}."
243
+ },
244
+ "Russian": {
245
+ "_lang": "ru",
246
+ "_default": true,
247
+ "ariaGame": "Бесконечный забег. Перепрыгивай кактусы, уворачивайся от птиц и беги как можно дальше.",
248
+ "headerScore": "Очки",
249
+ "headerBest": "Рекорд",
250
+ "startTitle": "Дино Раннер",
251
+ "startBody": "Перепрыгивай кактусы и уворачивайся от птиц. Беги как можно дальше, не врезаясь.",
252
+ "startButton": "Начать",
253
+ "controlsHint": "Пробел или стрелка вверх, чтобы прыгнуть, вниз, чтобы пригнуться",
254
+ "gameOverTitle": "Игра окончена",
255
+ "gameOverScore": "Очки {score}",
256
+ "gameOverBest": "Рекорд {score}",
257
+ "restartButton": "Заново",
258
+ "ariaJump": "Прыжок",
259
+ "ariaDuck": "Пригнуться",
260
+ "ariaSound": "Звук",
261
+ "announceStart": "Вперёд",
262
+ "announceGameOver": "Игра окончена. Ваш счёт: {score}.",
263
+ "announceNewBest": "Новый рекорд: {score}."
264
+ },
265
+ "Japanese": {
266
+ "_lang": "ja",
267
+ "_default": true,
268
+ "ariaGame": "エンドレスラン。サボテンを飛び越え、鳥をかわして、できるだけ遠くまで走ろう。",
269
+ "headerScore": "スコア",
270
+ "headerBest": "ベスト",
271
+ "startTitle": "ダイノランナー",
272
+ "startBody": "サボテンを飛び越え、鳥をかわそう。ぶつからずに、できるだけ遠くまで走ってね。",
273
+ "startButton": "スタート",
274
+ "controlsHint": "スペースか上キーでジャンプ、下キーでしゃがむ",
275
+ "gameOverTitle": "ゲームオーバー",
276
+ "gameOverScore": "スコア {score}",
277
+ "gameOverBest": "ベスト {score}",
278
+ "restartButton": "もう一度",
279
+ "ariaJump": "ジャンプ",
280
+ "ariaDuck": "しゃがむ",
281
+ "ariaSound": "サウンド",
282
+ "announceStart": "スタート",
283
+ "announceGameOver": "ゲームオーバー。スコアは {score} でした。",
284
+ "announceNewBest": "自己ベスト更新:{score}。"
285
+ },
286
+ "Korean": {
287
+ "_lang": "ko",
288
+ "_default": true,
289
+ "ariaGame": "끝없는 달리기. 선인장을 뛰어넘고, 새를 피하며, 최대한 멀리 달리세요.",
290
+ "headerScore": "점수",
291
+ "headerBest": "최고",
292
+ "startTitle": "다이노 러너",
293
+ "startBody": "선인장을 뛰어넘고 새를 피하세요. 부딪히지 않고 최대한 멀리 달리세요.",
294
+ "startButton": "시작",
295
+ "controlsHint": "스페이스나 위쪽 키로 점프, 아래쪽 키로 숙이기",
296
+ "gameOverTitle": "게임 오버",
297
+ "gameOverScore": "점수 {score}",
298
+ "gameOverBest": "최고 {score}",
299
+ "restartButton": "다시 하기",
300
+ "ariaJump": "점프",
301
+ "ariaDuck": "숙이기",
302
+ "ariaSound": "소리",
303
+ "announceStart": "출발",
304
+ "announceGameOver": "게임 오버. 점수는 {score}점이에요.",
305
+ "announceNewBest": "최고 기록 경신: {score}."
306
+ },
307
+ "Indonesian": {
308
+ "_lang": "id",
309
+ "_default": true,
310
+ "ariaGame": "Lari tanpa henti. Lompati kaktus, hindari burung, dan berlarilah sejauh mungkin.",
311
+ "headerScore": "Skor",
312
+ "headerBest": "Terbaik",
313
+ "startTitle": "Dino Runner",
314
+ "startBody": "Lompati kaktus dan hindari burung. Berlarilah sejauh mungkin tanpa menabrak.",
315
+ "startButton": "Mulai",
316
+ "controlsHint": "Spasi atau panah atas untuk lompat, bawah untuk menunduk",
317
+ "gameOverTitle": "Permainan selesai",
318
+ "gameOverScore": "Skor {score}",
319
+ "gameOverBest": "Terbaik {score}",
320
+ "restartButton": "Ulangi",
321
+ "ariaJump": "Lompat",
322
+ "ariaDuck": "Menunduk",
323
+ "ariaSound": "Suara",
324
+ "announceStart": "Lari!",
325
+ "announceGameOver": "Permainan selesai. Skor kamu {score}.",
326
+ "announceNewBest": "Rekor baru: {score}."
327
+ }
328
+ }
329
+ },
330
+ "skins": {
331
+ "schema": {
332
+ "bg": {
333
+ "type": "color",
334
+ "name": "Background",
335
+ "description": "Stage background; full-bleed behind the scene."
336
+ },
337
+ "fg": {
338
+ "type": "color",
339
+ "name": "Foreground",
340
+ "description": "currentColor for every sprite, the ground, and the header text."
341
+ },
342
+ "button_bg": {
343
+ "type": "color",
344
+ "name": "Primary button bg",
345
+ "description": "Background of the Start / Restart primary button."
346
+ },
347
+ "button_text": {
348
+ "type": "color",
349
+ "name": "Primary button text",
350
+ "description": "Text on the primary button."
351
+ },
352
+ "button_hover": {
353
+ "type": "color",
354
+ "name": "Primary button hover bg",
355
+ "description": "Hover background of the primary button."
356
+ },
357
+ "button_secondary_text": {
358
+ "type": "color",
359
+ "name": "Touch control text",
360
+ "description": "Text + border color of the on-screen touch controls (jump / duck)."
361
+ },
362
+ "button_secondary_border": {
363
+ "type": "color",
364
+ "name": "Touch control border",
365
+ "description": "Outline color of the on-screen touch controls."
366
+ },
367
+ "button_secondary_hover_bg": {
368
+ "type": "color",
369
+ "name": "Touch control active bg",
370
+ "description": "Active / pressed background of the on-screen touch controls."
371
+ },
372
+ "focus_ring": {
373
+ "type": "color",
374
+ "name": "Focus ring",
375
+ "description": "Outline color shown on buttons / the stage when keyboard focus enters."
376
+ },
377
+ "sprite_runner_idle": {
378
+ "type": "image",
379
+ "name": "Runner: idle",
380
+ "description": "Standing runner shown on the start screen. currentColor drives the tint."
381
+ },
382
+ "sprite_runner_run_1": {
383
+ "type": "image",
384
+ "name": "Runner: run frame 1",
385
+ "description": "First running frame (legs alternate with frame 2)."
386
+ },
387
+ "sprite_runner_run_2": {
388
+ "type": "image",
389
+ "name": "Runner: run frame 2",
390
+ "description": "Second running frame."
391
+ },
392
+ "sprite_runner_jump": {
393
+ "type": "image",
394
+ "name": "Runner: jump",
395
+ "description": "Airborne runner shown during a jump."
396
+ },
397
+ "sprite_runner_duck_1": {
398
+ "type": "image",
399
+ "name": "Runner: duck frame 1",
400
+ "description": "First ducking frame (legs alternate with frame 2)."
401
+ },
402
+ "sprite_runner_duck_2": {
403
+ "type": "image",
404
+ "name": "Runner: duck frame 2",
405
+ "description": "Second ducking frame."
406
+ },
407
+ "sprite_runner_crash": {
408
+ "type": "image",
409
+ "name": "Runner: crash",
410
+ "description": "Runner shown after a collision (closed eye)."
411
+ },
412
+ "sprite_cactus_small": {
413
+ "type": "image",
414
+ "name": "Obstacle: small cactus",
415
+ "description": "Small ground obstacle. May clump 1-3 wide."
416
+ },
417
+ "sprite_cactus_large": {
418
+ "type": "image",
419
+ "name": "Obstacle: large cactus",
420
+ "description": "Tall ground obstacle. May clump 1-3 wide."
421
+ },
422
+ "sprite_bird_1": {
423
+ "type": "image",
424
+ "name": "Obstacle: bird wings up",
425
+ "description": "Flying obstacle, wings-up frame."
426
+ },
427
+ "sprite_bird_2": {
428
+ "type": "image",
429
+ "name": "Obstacle: bird wings down",
430
+ "description": "Flying obstacle, wings-down frame."
431
+ },
432
+ "sprite_cloud": {
433
+ "type": "image",
434
+ "name": "Scenery: cloud",
435
+ "description": "Decorative parallax cloud (shown in both skins)."
436
+ },
437
+ "sprite_moon": {
438
+ "type": "image",
439
+ "name": "Scenery: moon",
440
+ "description": "Decorative moon (dark skin only)."
441
+ },
442
+ "sprite_star": {
443
+ "type": "image",
444
+ "name": "Scenery: star",
445
+ "description": "Decorative star (dark skin only)."
446
+ },
447
+ "sprite_ground": {
448
+ "type": "image",
449
+ "name": "Scenery: ground",
450
+ "description": "Scrolling ground strip. Tiles seamlessly at 600 logical units wide."
451
+ },
452
+ "sprite_restart": {
453
+ "type": "image",
454
+ "name": "Restart icon",
455
+ "description": "Reload icon shown on the restart button."
456
+ },
457
+ "sprite_sound_on": {
458
+ "type": "image",
459
+ "name": "Sound-on icon",
460
+ "description": "Speaker icon shown on the sound toggle when sound is on."
461
+ },
462
+ "sprite_sound_off": {
463
+ "type": "image",
464
+ "name": "Sound-off icon",
465
+ "description": "Muted-speaker icon shown on the sound toggle when sound is off."
466
+ }
467
+ },
468
+ "presets": {
469
+ "light": {
470
+ "_theme": "light",
471
+ "_default": true,
472
+ "bg": "#F7F7F7",
473
+ "fg": "#535353",
474
+ "button_bg": "#535353",
475
+ "button_text": "#F7F7F7",
476
+ "button_hover": "#333333",
477
+ "button_secondary_text": "#535353",
478
+ "button_secondary_border": "#535353",
479
+ "button_secondary_hover_bg": "#E2E2E2",
480
+ "focus_ring": "#535353"
481
+ },
482
+ "dark": {
483
+ "_theme": "dark",
484
+ "_default": true,
485
+ "bg": "#1B1B1B",
486
+ "fg": "#F7F7F7",
487
+ "button_bg": "#F7F7F7",
488
+ "button_text": "#1B1B1B",
489
+ "button_hover": "#D9D9D9",
490
+ "button_secondary_text": "#F7F7F7",
491
+ "button_secondary_border": "#8A8A8A",
492
+ "button_secondary_hover_bg": "#2E2E2E",
493
+ "focus_ring": "#F7F7F7"
494
+ }
495
+ }
496
+ },
497
+ "configurations": {
498
+ "schema": {
499
+ "start_speed": {
500
+ "type": "range",
501
+ "min": 3,
502
+ "max": 10,
503
+ "step": 0.5,
504
+ "name": "Starting speed",
505
+ "description": "Run speed at the start of a run, in logical units per reference frame."
506
+ },
507
+ "max_speed": {
508
+ "type": "range",
509
+ "min": 6,
510
+ "max": 20,
511
+ "step": 0.5,
512
+ "name": "Max speed",
513
+ "description": "Speed cap the run accelerates toward."
514
+ },
515
+ "acceleration": {
516
+ "type": "range",
517
+ "min": 0.0002,
518
+ "max": 0.004,
519
+ "step": 0.0002,
520
+ "name": "Acceleration",
521
+ "description": "Speed gained per frame until the cap is hit. Higher means the run ramps up faster."
522
+ },
523
+ "gravity": {
524
+ "type": "range",
525
+ "min": 0.3,
526
+ "max": 1,
527
+ "step": 0.05,
528
+ "name": "Gravity",
529
+ "description": "Downward pull on the jump arc. Higher means shorter, snappier jumps."
530
+ },
531
+ "jump_velocity": {
532
+ "type": "range",
533
+ "min": 6,
534
+ "max": 16,
535
+ "step": 0.5,
536
+ "name": "Jump strength",
537
+ "description": "Initial upward velocity of a jump. Higher means the runner jumps higher."
538
+ },
539
+ "gap_coefficient": {
540
+ "type": "range",
541
+ "min": 0.3,
542
+ "max": 1.5,
543
+ "step": 0.1,
544
+ "name": "Obstacle spacing",
545
+ "description": "Multiplier on the gap between obstacles. Higher means more breathing room."
546
+ },
547
+ "pass_score": {
548
+ "type": "range",
549
+ "min": 10,
550
+ "max": 2000,
551
+ "step": 10,
552
+ "name": "Pass score",
553
+ "description": "Score a run must reach to count as a pass. The first qualifying crash (and each new best after) reports success."
554
+ },
555
+ "birds_enabled": {
556
+ "type": "boolean",
557
+ "name": "Flying obstacles",
558
+ "description": "Spawn birds (flying obstacles) once the run is fast enough."
559
+ },
560
+ "bird_min_speed": {
561
+ "type": "range",
562
+ "min": 5,
563
+ "max": 15,
564
+ "step": 0.5,
565
+ "name": "Bird unlock speed",
566
+ "description": "Run speed at which birds start to appear."
567
+ },
568
+ "show_score": {
569
+ "type": "boolean",
570
+ "name": "Show score",
571
+ "description": "Display the live score in the header."
572
+ },
573
+ "show_best": {
574
+ "type": "boolean",
575
+ "name": "Show best score",
576
+ "description": "Display the session-best score in the header."
577
+ },
578
+ "sound": {
579
+ "type": "boolean",
580
+ "name": "Sound effects",
581
+ "description": "Play the jump / milestone / crash sound effects (starts after the first interaction)."
582
+ }
583
+ },
584
+ "presets": {
585
+ "default": {
586
+ "_default": true,
587
+ "start_speed": 6,
588
+ "max_speed": 13,
589
+ "acceleration": 0.0022,
590
+ "gravity": 0.6,
591
+ "jump_velocity": 10,
592
+ "gap_coefficient": 0.6,
593
+ "pass_score": 100,
594
+ "birds_enabled": true,
595
+ "bird_min_speed": 7,
596
+ "show_score": true,
597
+ "show_best": true,
598
+ "sound": true
599
+ },
600
+ "classic": {
601
+ "_extends": "default",
602
+ "acceleration": 0.001,
603
+ "bird_min_speed": 8.5
604
+ },
605
+ "casual": {
606
+ "_extends": "default",
607
+ "start_speed": 5,
608
+ "max_speed": 10,
609
+ "acceleration": 0.0006,
610
+ "gap_coefficient": 0.9,
611
+ "pass_score": 60,
612
+ "bird_min_speed": 10
613
+ },
614
+ "hardcore": {
615
+ "_extends": "default",
616
+ "start_speed": 7,
617
+ "max_speed": 16,
618
+ "acceleration": 0.0016,
619
+ "gap_coefficient": 0.5,
620
+ "pass_score": 250
621
+ },
622
+ "calm": {
623
+ "_extends": "default",
624
+ "birds_enabled": false,
625
+ "sound": false,
626
+ "pass_score": 60
627
+ }
628
+ }
629
+ }
630
+ }
@@ -0,0 +1,247 @@
1
+ (function(){'use strict';var nr=Object.defineProperty;var or=(r,e,i)=>e in r?nr(r,e,{enumerable:true,configurable:true,writable:true,value:i}):r[e]=i;var l=(r,e,i)=>or(r,typeof e!="symbol"?e+"":e,i);var sr="__caputchin_default__";function ar(r){if(typeof r.id=="string"&&r.id.length>0)return r.id;if(typeof document<"u"){let e=document.querySelector("script[data-game-id]"),i=e?e.getAttribute("data-game-id"):null;if(i&&i.length>0)return i}return sr}function $e(r,e){if(!r||typeof r!="object"){console.warn("[caputchin/game-sdk] register() called without a manifest; skipping");return}let i=ar(r),n=globalThis;n.Caputchin||(console.warn("[caputchin/game-sdk] Caputchin global not found; was the SDK loaded outside a Caputchin iframe?"),n.Caputchin={games:{},manifests:{}});let o=n.Caputchin;o.manifests||(o.manifests={}),o.games||(o.games={}),Object.prototype.hasOwnProperty.call(o.games,i)&&console.warn(`[caputchin/game-sdk] duplicate registry key "${i}"; last-write-wins`),o.games[i]=e,o.manifests[i]=r;}var k=16.666666666666668,m=600,U=150,K=10,fe=12,A={width:44,height:47,widthDuck:59,startX:50},v={gravity:.6,dropVelocity:-5,speedDropCoefficient:3,minJumpRise:30,autoCapRise:63,ceilingY:4},Z={initial:6,max:13,acceleration:.001},Qe=.6,Xe=3,xe=2,Ve=.025,X={run:1e3/12,duck:1e3/8,bird:1e3/6},Ke=.2,Ze=.3,ze=.25,qe=6;var M=U-A.height-K,z=class{constructor(e){l(this,"cfg",e);l(this,"x",A.startX);l(this,"y",M);l(this,"status","waiting");l(this,"velocity",0);l(this,"duckHeld",false);l(this,"speedDrop",false);l(this,"reachedMinHeight",false);l(this,"runTimer",0);l(this,"runFrame",0);l(this,"duckTimer",0);l(this,"duckFrame",0);l(this,"jumpCount",0);}get grounded(){return this.y>=M}get ducking(){return this.status==="ducking"}start(){this.status==="waiting"&&(this.status="running");}startJump(e){this.status!=="running"&&this.status!=="ducking"||(this.status="jumping",this.speedDrop=false,this.reachedMinHeight=false,this.velocity=this.cfg.initialJumpVelocity-e/10);}endJump(){this.status==="jumping"&&this.reachedMinHeight&&this.velocity<v.dropVelocity&&(this.velocity=v.dropVelocity);}setDuck(e){this.duckHeld=e,e?this.status==="jumping"?(this.speedDrop=true,this.velocity=1):this.status==="running"&&(this.status="ducking"):(this.speedDrop=false,this.status==="ducking"&&(this.status="running"));}crash(){this.status="crashed",this.velocity=0;}reset(){this.y=M,this.status="waiting",this.velocity=0,this.duckHeld=false,this.speedDrop=false,this.reachedMinHeight=false,this.runTimer=0,this.runFrame=0,this.duckTimer=0,this.duckFrame=0,this.jumpCount=0;}update(e,i){let n=e/k;this.status==="jumping"&&(this.y+=this.velocity*(this.speedDrop?v.speedDropCoefficient:1)*n,this.velocity+=this.cfg.gravity*n,this.y<=M-v.minJumpRise&&(this.reachedMinHeight=true),(this.y<M-v.autoCapRise||this.speedDrop)&&this.endJump(),this.y<v.ceilingY&&(this.y=v.ceilingY,this.velocity<0&&(this.velocity=0)),this.y>=M&&(this.y=M,this.velocity=0,this.jumpCount+=1,this.status=this.duckHeld?"ducking":"running",this.speedDrop=false,this.reachedMinHeight=false)),this.advanceAnimation(e);}advanceAnimation(e){this.status==="running"?(this.runTimer+=e,this.runTimer>=X.run&&(this.runTimer=0,this.runFrame^=1)):this.status==="ducking"&&(this.duckTimer+=e,this.duckTimer>=X.duck&&(this.duckTimer=0,this.duckFrame^=1));}frame(){switch(this.status){case "waiting":return this.upright("runner-idle");case "crashed":return this.upright("runner-crash");case "jumping":return this.upright("runner-jump");case "ducking":return {sprite:this.duckFrame===0?"runner-duck-1":"runner-duck-2",x:this.x,y:this.y,width:A.widthDuck,height:A.height};default:return this.upright(this.runFrame===0?"runner-run-1":"runner-run-2")}}upright(e){return {sprite:e,x:this.x,y:this.y,width:A.width,height:A.height}}};function We(r){return {x:r.x,y:r.y,ducking:r.ducking}}var W={"cactus-small":{id:"cactus-small",width:17,height:35,yPos:105,minGap:120,minSpeed:0,groupable:true,groupSpeed:4,boxes:[{x:0,y:7,width:5,height:27},{x:4,y:0,width:6,height:34},{x:10,y:4,width:7,height:14}]},"cactus-large":{id:"cactus-large",width:25,height:50,yPos:90,minGap:120,minSpeed:0,groupable:true,groupSpeed:7,boxes:[{x:0,y:12,width:7,height:38},{x:8,y:0,width:7,height:49},{x:13,y:10,width:10,height:38}]},bird:{id:"bird",width:46,height:40,yPos:[100,75,50],minGap:150,minSpeed:0,groupable:false,groupSpeed:1/0,boxes:[{x:15,y:15,width:16,height:5},{x:18,y:21,width:24,height:6},{x:2,y:14,width:4,height:3},{x:6,y:10,width:4,height:7},{x:10,y:8,width:6,height:9}]}},et=.8;function tt(r){if(r.typeId==="bird")return [{sprite:r.frame===0?"bird-1":"bird-2",dx:0,width:r.width,height:r.height}];let e=W[r.typeId],i=r.typeId,n=[];for(let o=0;o<r.size;o+=1)n.push({sprite:i,dx:o*e.width,width:e.width,height:r.height});return n}function q(r,e,i){return Math.floor(r()*(i-e+1))+e}var ee=class{constructor(e=Math.random){l(this,"rng",e);l(this,"obstacles",[]);}reset(){this.obstacles.length=0;}update(e,i,n){let o=e/k;for(let d of this.obstacles)d.x-=(i+d.speedOffset)*o,d.typeId==="bird"&&(d.animTimer+=e,d.animTimer>=X.bird&&(d.animTimer=0,d.frame^=1));for(;this.obstacles.length>0&&this.obstacles[0].x+this.obstacles[0].width<0;)this.obstacles.shift();let a=this.obstacles[this.obstacles.length-1];(!a||a.x+a.width+a.gap<=m)&&this.spawn(i,n);}spawn(e,i){let n=this.pickType(e,i);if(!n)return;let o=n.groupable&&e>n.groupSpeed?q(this.rng,1,Xe):1,a=n.width*o,h,d=0;if(n.id==="bird"){let g=n.yPos;h=g[q(this.rng,0,g.length-1)],d=this.rng()>.5?et:-et;}else h=n.yPos;let p=[];for(let g=0;g<o;g+=1)for(let u of n.boxes)p.push({x:u.x+g*n.width,y:u.y,width:u.width,height:u.height});this.obstacles.push({typeId:n.id,x:m,y:h,width:a,height:n.height,size:o,boxes:p,gap:this.computeGap(n,a,e,i.gapCoefficient),speedOffset:d,frame:0,animTimer:0});}pickType(e,i){let n=[W["cactus-small"],W["cactus-large"]];i.birdsEnabled&&e>=i.birdMinSpeed&&n.push(W.bird);let o=n.filter(d=>e>=d.minSpeed),a=o.filter(d=>!this.wouldOverduplicate(d.id)),h=a.length>0?a:o;return h.length===0?null:h[q(this.rng,0,h.length-1)]}wouldOverduplicate(e){return this.obstacles.length<xe?false:this.obstacles.slice(-xe).every(n=>n.typeId===e)}computeGap(e,i,n,o){let a=Math.round(i*n+e.minGap*o),h=Math.round(a*1.5);return q(this.rng,a,h)}};var cr=46,hr=40,dr=9,ur=6,te=class{constructor(e=Math.random){l(this,"rng",e);l(this,"groundX",0);l(this,"clouds",[]);l(this,"stars",[]);l(this,"moon");l(this,"nextCloudGap");this.moon={x:m*.75,y:24};for(let i=0;i<ur;i+=1)this.stars.push({x:this.rng()*m,y:8+this.rng()*40});this.nextCloudGap=this.randomCloudGap();}reset(){this.groundX=0,this.clouds.length=0,this.nextCloudGap=this.randomCloudGap();}update(e,i,n){let o=e/k;for(this.groundX-=i*o;this.groundX<=-m;)this.groundX+=m;n||(this.updateClouds(i*Ke*o),this.drift(this.stars,i*Ze*o,dr),this.driftOne(this.moon,i*ze*o,hr));}updateClouds(e){for(let o of this.clouds)o.x-=e;for(;this.clouds.length>0&&this.clouds[0].x+cr<0;)this.clouds.shift();let i=this.clouds[this.clouds.length-1],n=!i||i.x<m-this.nextCloudGap;this.clouds.length<qe&&n&&(this.clouds.push({x:m,y:8+this.rng()*48}),this.nextCloudGap=this.randomCloudGap());}drift(e,i,n){for(let o of e)this.driftOne(o,i,n);}driftOne(e,i,n){e.x-=i,e.x+n<0&&(e.x=m);}randomCloudGap(){return 100+this.rng()*300}};function lr(r,e){return r.x<e.x+e.width&&r.x+r.width>e.x&&r.y<e.y+e.height&&r.y+r.height>e.y}var rt={running:[{x:22,y:0,width:17,height:16},{x:1,y:18,width:30,height:9},{x:10,y:35,width:14,height:8},{x:1,y:24,width:29,height:5},{x:5,y:30,width:21,height:4},{x:9,y:34,width:15,height:4}],ducking:[{x:1,y:18,width:55,height:25}]};function pr(r){return r?rt.ducking:rt.running}function it(r,e,i){return {x:r.x+e,y:r.y+i,width:r.width,height:r.height}}function nt(r,e){let i=pr(r.ducking);for(let n of i){let o=it(n,r.x,r.y);for(let a of e.boxes){let h=it(a,e.x,e.y);if(lr(o,h))return true}}return false}var re={marketplace:{name:"Dino Runner",description:"Jump the cactus, duck the birds, and run as far as you can.",preview:"preview.png",categories:["Arcade","Reflex"],support:{responsive:true,touch:true,keyboard:true,screenReader:true,audio:"optional"}},npm:"@caputchin/game-dino-runner",entry:"dist/dino-runner.js",preferred:{width:600,height:150},locales:{schema:{ariaGame:{name:"Game region label",description:"Screen-reader label for the whole play area."},headerScore:{name:"Score label",description:"Label preceding the live distance score in the header."},headerBest:{name:"Best score label",description:"Label preceding the session-best score in the header."},startTitle:{name:"Start screen title",description:"Headline shown on the start screen before the first run."},startBody:{name:"Start screen body",description:"Short instructions shown on the start screen."},startButton:{name:"Start button",description:"Label for the button that begins the run."},controlsHint:{name:"Controls hint",description:"Small keyboard-controls reminder shown under the start button."},gameOverTitle:{name:"Game over title",description:"Headline shown when the runner crashes. This is the text the host fully controls (the original Chrome game hard-coded it)."},gameOverScore:{name:"Game over score line",description:"Final score shown on the game-over screen.",tokens:["score"]},gameOverBest:{name:"Game over best line",description:"Session-best score shown on the game-over screen.",tokens:["score"]},restartButton:{name:"Restart button",description:"Label for the button that restarts after a crash."},ariaJump:{name:"Jump control label",description:"Screen-reader / touch-button label for the jump action."},ariaDuck:{name:"Duck control label",description:"Screen-reader / touch-button label for the duck action."},ariaSound:{name:"Sound toggle label",description:"Accessible label for the in-game button that turns sound on / off."},announceStart:{name:"Announcer: run started",description:"Live-region message when the run begins."},announceGameOver:{name:"Announcer: game over",description:"Live-region message when the runner crashes.",tokens:["score"]},announceNewBest:{name:"Announcer: new best",description:"Live-region message when the run sets a new session best.",tokens:["score"]}},presets:{English:{_lang:"en",_default:true,ariaGame:"Endless runner. Jump the cactus, duck the birds, and run as far as you can.",headerScore:"Score",headerBest:"Best",startTitle:"Dino Runner",startBody:"Jump the cactus and duck the birds. Run as far as you can without crashing.",startButton:"Start",controlsHint:"Space or Up to jump, Down to duck",gameOverTitle:"Game over",gameOverScore:"Score {score}",gameOverBest:"Best {score}",restartButton:"Restart",ariaJump:"Jump",ariaDuck:"Duck",ariaSound:"Sound",announceStart:"Go",announceGameOver:"Game over. You scored {score}.",announceNewBest:"New best score: {score}."},"Chinese (Simplified)":{_lang:"zh-Hans",_default:true,ariaGame:"\u65E0\u5C3D\u8DD1\u9177\u3002\u8DF3\u8FC7\u4ED9\u4EBA\u638C\uFF0C\u8EB2\u5F00\u98DE\u9E1F\uFF0C\u5C3D\u91CF\u8DD1\u5F97\u66F4\u8FDC\u3002",headerScore:"\u5F97\u5206",headerBest:"\u6700\u4F73",startTitle:"\u5C0F\u6050\u9F99\u5FEB\u8DD1",startBody:"\u8DF3\u8FC7\u4ED9\u4EBA\u638C\uFF0C\u8EB2\u5F00\u98DE\u9E1F\u3002\u5728\u4E0D\u649E\u4E0A\u969C\u788D\u7684\u60C5\u51B5\u4E0B\u5C3D\u91CF\u8DD1\u5F97\u66F4\u8FDC\u3002",startButton:"\u5F00\u59CB",controlsHint:"\u7A7A\u683C\u6216\u4E0A\u952E\u8DF3\u8DC3\uFF0C\u4E0B\u952E\u4E0B\u8E72",gameOverTitle:"\u6E38\u620F\u7ED3\u675F",gameOverScore:"\u5F97\u5206 {score}",gameOverBest:"\u6700\u4F73 {score}",restartButton:"\u91CD\u65B0\u5F00\u59CB",ariaJump:"\u8DF3\u8DC3",ariaDuck:"\u4E0B\u8E72",ariaSound:"\u58F0\u97F3",announceStart:"\u51FA\u53D1",announceGameOver:"\u6E38\u620F\u7ED3\u675F\u3002\u4F60\u5F97\u4E86 {score} \u5206\u3002",announceNewBest:"\u65B0\u7684\u6700\u4F73\u6210\u7EE9\uFF1A{score}\u3002"},Spanish:{_lang:"es",_default:true,ariaGame:"Carrera sin fin. Salta los cactus, esquiva los p\xE1jaros y corre lo m\xE1s lejos que puedas.",headerScore:"Puntos",headerBest:"Mejor",startTitle:"Dino Runner",startBody:"Salta los cactus y esquiva los p\xE1jaros. Corre lo m\xE1s lejos que puedas sin chocar.",startButton:"Empezar",controlsHint:"Espacio o flecha arriba para saltar, abajo para agacharte",gameOverTitle:"Fin del juego",gameOverScore:"Puntos {score}",gameOverBest:"Mejor {score}",restartButton:"Reiniciar",ariaJump:"Saltar",ariaDuck:"Agacharse",ariaSound:"Sonido",announceStart:"\xA1Ya!",announceGameOver:"Fin del juego. Hiciste {score} puntos.",announceNewBest:"Nueva mejor puntuaci\xF3n: {score}."},Arabic:{_lang:"ar",_default:true,ariaGame:"\u062C\u0631\u064A \u0628\u0644\u0627 \u0646\u0647\u0627\u064A\u0629. \u0627\u0642\u0641\u0632 \u0641\u0648\u0642 \u0627\u0644\u0635\u0628\u0627\u0631\u060C \u0648\u062A\u0641\u0627\u062F\u064E \u0627\u0644\u0637\u064A\u0648\u0631\u060C \u0648\u0627\u0631\u0643\u0636 \u0623\u0628\u0639\u062F \u0645\u0627 \u062A\u0633\u062A\u0637\u064A\u0639.",headerScore:"\u0627\u0644\u0646\u0642\u0627\u0637",headerBest:"\u0627\u0644\u0623\u0641\u0636\u0644",startTitle:"\u0639\u062F\u0627\u0621 \u0627\u0644\u062F\u064A\u0646\u0627\u0635\u0648\u0631",startBody:"\u0627\u0642\u0641\u0632 \u0641\u0648\u0642 \u0627\u0644\u0635\u0628\u0627\u0631 \u0648\u062A\u0641\u0627\u062F\u064E \u0627\u0644\u0637\u064A\u0648\u0631. \u0627\u0631\u0643\u0636 \u0623\u0628\u0639\u062F \u0645\u0627 \u062A\u0633\u062A\u0637\u064A\u0639 \u062F\u0648\u0646 \u0623\u0646 \u062A\u0635\u0637\u062F\u0645.",startButton:"\u0627\u0628\u062F\u0623",controlsHint:"\u0645\u0633\u0627\u0641\u0629 \u0623\u0648 \u0627\u0644\u0633\u0647\u0645 \u0627\u0644\u0639\u0644\u0648\u064A \u0644\u0644\u0642\u0641\u0632\u060C \u0627\u0644\u0633\u0647\u0645 \u0627\u0644\u0633\u0641\u0644\u064A \u0644\u0644\u0627\u0646\u062D\u0646\u0627\u0621",gameOverTitle:"\u0627\u0646\u062A\u0647\u062A \u0627\u0644\u0644\u0639\u0628\u0629",gameOverScore:"\u0627\u0644\u0646\u0642\u0627\u0637 {score}",gameOverBest:"\u0627\u0644\u0623\u0641\u0636\u0644 {score}",restartButton:"\u0623\u0639\u062F \u0627\u0644\u0645\u062D\u0627\u0648\u0644\u0629",ariaJump:"\u0627\u0642\u0641\u0632",ariaDuck:"\u0627\u0646\u062D\u0646\u0650",ariaSound:"\u0627\u0644\u0635\u0648\u062A",announceStart:"\u0627\u0646\u0637\u0644\u0642",announceGameOver:"\u0627\u0646\u062A\u0647\u062A \u0627\u0644\u0644\u0639\u0628\u0629. \u062D\u0635\u0644\u062A \u0639\u0644\u0649 {score} \u0646\u0642\u0637\u0629.",announceNewBest:"\u0623\u0641\u0636\u0644 \u0646\u062A\u064A\u062C\u0629 \u062C\u062F\u064A\u062F\u0629: {score}."},Portuguese:{_lang:"pt",_default:true,ariaGame:"Corrida sem fim. Pule os cactos, desvie dos p\xE1ssaros e corra o mais longe que conseguir.",headerScore:"Pontos",headerBest:"Melhor",startTitle:"Dino Runner",startBody:"Pule os cactos e desvie dos p\xE1ssaros. Corra o mais longe que conseguir sem bater.",startButton:"Come\xE7ar",controlsHint:"Espa\xE7o ou seta para cima para pular, para baixo para abaixar",gameOverTitle:"Fim de jogo",gameOverScore:"Pontos {score}",gameOverBest:"Melhor {score}",restartButton:"Reiniciar",ariaJump:"Pular",ariaDuck:"Abaixar",ariaSound:"Som",announceStart:"J\xE1!",announceGameOver:"Fim de jogo. Voc\xEA fez {score} pontos.",announceNewBest:"Novo recorde: {score}."},French:{_lang:"fr",_default:true,ariaGame:"Course sans fin. Sautez par-dessus les cactus, esquivez les oiseaux et courez le plus loin possible.",headerScore:"Score",headerBest:"Record",startTitle:"Dino Runner",startBody:"Sautez par-dessus les cactus et esquivez les oiseaux. Courez le plus loin possible sans vous \xE9craser.",startButton:"Commencer",controlsHint:"Espace ou fl\xE8che haut pour sauter, bas pour se baisser",gameOverTitle:"Partie termin\xE9e",gameOverScore:"Score {score}",gameOverBest:"Record {score}",restartButton:"Recommencer",ariaJump:"Sauter",ariaDuck:"Se baisser",ariaSound:"Son",announceStart:"Partez !",announceGameOver:"Partie termin\xE9e. Vous avez fait {score}.",announceNewBest:"Nouveau record : {score}."},German:{_lang:"de",_default:true,ariaGame:"Endlos-Lauf. Spring \xFCber die Kakteen, duck dich unter den V\xF6geln und lauf so weit du kannst.",headerScore:"Punkte",headerBest:"Rekord",startTitle:"Dino Runner",startBody:"Spring \xFCber die Kakteen und duck dich unter den V\xF6geln. Lauf so weit du kannst, ohne zu verungl\xFCcken.",startButton:"Start",controlsHint:"Leertaste oder Pfeil hoch zum Springen, runter zum Ducken",gameOverTitle:"Spiel vorbei",gameOverScore:"Punkte {score}",gameOverBest:"Rekord {score}",restartButton:"Neustart",ariaJump:"Springen",ariaDuck:"Ducken",ariaSound:"Ton",announceStart:"Los",announceGameOver:"Spiel vorbei. Du hast {score} Punkte erreicht.",announceNewBest:"Neuer Rekord: {score}."},Russian:{_lang:"ru",_default:true,ariaGame:"\u0411\u0435\u0441\u043A\u043E\u043D\u0435\u0447\u043D\u044B\u0439 \u0437\u0430\u0431\u0435\u0433. \u041F\u0435\u0440\u0435\u043F\u0440\u044B\u0433\u0438\u0432\u0430\u0439 \u043A\u0430\u043A\u0442\u0443\u0441\u044B, \u0443\u0432\u043E\u0440\u0430\u0447\u0438\u0432\u0430\u0439\u0441\u044F \u043E\u0442 \u043F\u0442\u0438\u0446 \u0438 \u0431\u0435\u0433\u0438 \u043A\u0430\u043A \u043C\u043E\u0436\u043D\u043E \u0434\u0430\u043B\u044C\u0448\u0435.",headerScore:"\u041E\u0447\u043A\u0438",headerBest:"\u0420\u0435\u043A\u043E\u0440\u0434",startTitle:"\u0414\u0438\u043D\u043E \u0420\u0430\u043D\u043D\u0435\u0440",startBody:"\u041F\u0435\u0440\u0435\u043F\u0440\u044B\u0433\u0438\u0432\u0430\u0439 \u043A\u0430\u043A\u0442\u0443\u0441\u044B \u0438 \u0443\u0432\u043E\u0440\u0430\u0447\u0438\u0432\u0430\u0439\u0441\u044F \u043E\u0442 \u043F\u0442\u0438\u0446. \u0411\u0435\u0433\u0438 \u043A\u0430\u043A \u043C\u043E\u0436\u043D\u043E \u0434\u0430\u043B\u044C\u0448\u0435, \u043D\u0435 \u0432\u0440\u0435\u0437\u0430\u044F\u0441\u044C.",startButton:"\u041D\u0430\u0447\u0430\u0442\u044C",controlsHint:"\u041F\u0440\u043E\u0431\u0435\u043B \u0438\u043B\u0438 \u0441\u0442\u0440\u0435\u043B\u043A\u0430 \u0432\u0432\u0435\u0440\u0445, \u0447\u0442\u043E\u0431\u044B \u043F\u0440\u044B\u0433\u043D\u0443\u0442\u044C, \u0432\u043D\u0438\u0437, \u0447\u0442\u043E\u0431\u044B \u043F\u0440\u0438\u0433\u043D\u0443\u0442\u044C\u0441\u044F",gameOverTitle:"\u0418\u0433\u0440\u0430 \u043E\u043A\u043E\u043D\u0447\u0435\u043D\u0430",gameOverScore:"\u041E\u0447\u043A\u0438 {score}",gameOverBest:"\u0420\u0435\u043A\u043E\u0440\u0434 {score}",restartButton:"\u0417\u0430\u043D\u043E\u0432\u043E",ariaJump:"\u041F\u0440\u044B\u0436\u043E\u043A",ariaDuck:"\u041F\u0440\u0438\u0433\u043D\u0443\u0442\u044C\u0441\u044F",ariaSound:"\u0417\u0432\u0443\u043A",announceStart:"\u0412\u043F\u0435\u0440\u0451\u0434",announceGameOver:"\u0418\u0433\u0440\u0430 \u043E\u043A\u043E\u043D\u0447\u0435\u043D\u0430. \u0412\u0430\u0448 \u0441\u0447\u0451\u0442: {score}.",announceNewBest:"\u041D\u043E\u0432\u044B\u0439 \u0440\u0435\u043A\u043E\u0440\u0434: {score}."},Japanese:{_lang:"ja",_default:true,ariaGame:"\u30A8\u30F3\u30C9\u30EC\u30B9\u30E9\u30F3\u3002\u30B5\u30DC\u30C6\u30F3\u3092\u98DB\u3073\u8D8A\u3048\u3001\u9CE5\u3092\u304B\u308F\u3057\u3066\u3001\u3067\u304D\u308B\u3060\u3051\u9060\u304F\u307E\u3067\u8D70\u308D\u3046\u3002",headerScore:"\u30B9\u30B3\u30A2",headerBest:"\u30D9\u30B9\u30C8",startTitle:"\u30C0\u30A4\u30CE\u30E9\u30F3\u30CA\u30FC",startBody:"\u30B5\u30DC\u30C6\u30F3\u3092\u98DB\u3073\u8D8A\u3048\u3001\u9CE5\u3092\u304B\u308F\u305D\u3046\u3002\u3076\u3064\u304B\u3089\u305A\u306B\u3001\u3067\u304D\u308B\u3060\u3051\u9060\u304F\u307E\u3067\u8D70\u3063\u3066\u306D\u3002",startButton:"\u30B9\u30BF\u30FC\u30C8",controlsHint:"\u30B9\u30DA\u30FC\u30B9\u304B\u4E0A\u30AD\u30FC\u3067\u30B8\u30E3\u30F3\u30D7\u3001\u4E0B\u30AD\u30FC\u3067\u3057\u3083\u304C\u3080",gameOverTitle:"\u30B2\u30FC\u30E0\u30AA\u30FC\u30D0\u30FC",gameOverScore:"\u30B9\u30B3\u30A2 {score}",gameOverBest:"\u30D9\u30B9\u30C8 {score}",restartButton:"\u3082\u3046\u4E00\u5EA6",ariaJump:"\u30B8\u30E3\u30F3\u30D7",ariaDuck:"\u3057\u3083\u304C\u3080",ariaSound:"\u30B5\u30A6\u30F3\u30C9",announceStart:"\u30B9\u30BF\u30FC\u30C8",announceGameOver:"\u30B2\u30FC\u30E0\u30AA\u30FC\u30D0\u30FC\u3002\u30B9\u30B3\u30A2\u306F {score} \u3067\u3057\u305F\u3002",announceNewBest:"\u81EA\u5DF1\u30D9\u30B9\u30C8\u66F4\u65B0\uFF1A{score}\u3002"},Korean:{_lang:"ko",_default:true,ariaGame:"\uB05D\uC5C6\uB294 \uB2EC\uB9AC\uAE30. \uC120\uC778\uC7A5\uC744 \uB6F0\uC5B4\uB118\uACE0, \uC0C8\uB97C \uD53C\uD558\uBA70, \uCD5C\uB300\uD55C \uBA40\uB9AC \uB2EC\uB9AC\uC138\uC694.",headerScore:"\uC810\uC218",headerBest:"\uCD5C\uACE0",startTitle:"\uB2E4\uC774\uB178 \uB7EC\uB108",startBody:"\uC120\uC778\uC7A5\uC744 \uB6F0\uC5B4\uB118\uACE0 \uC0C8\uB97C \uD53C\uD558\uC138\uC694. \uBD80\uB52A\uD788\uC9C0 \uC54A\uACE0 \uCD5C\uB300\uD55C \uBA40\uB9AC \uB2EC\uB9AC\uC138\uC694.",startButton:"\uC2DC\uC791",controlsHint:"\uC2A4\uD398\uC774\uC2A4\uB098 \uC704\uCABD \uD0A4\uB85C \uC810\uD504, \uC544\uB798\uCABD \uD0A4\uB85C \uC219\uC774\uAE30",gameOverTitle:"\uAC8C\uC784 \uC624\uBC84",gameOverScore:"\uC810\uC218 {score}",gameOverBest:"\uCD5C\uACE0 {score}",restartButton:"\uB2E4\uC2DC \uD558\uAE30",ariaJump:"\uC810\uD504",ariaDuck:"\uC219\uC774\uAE30",ariaSound:"\uC18C\uB9AC",announceStart:"\uCD9C\uBC1C",announceGameOver:"\uAC8C\uC784 \uC624\uBC84. \uC810\uC218\uB294 {score}\uC810\uC774\uC5D0\uC694.",announceNewBest:"\uCD5C\uACE0 \uAE30\uB85D \uACBD\uC2E0: {score}."},Indonesian:{_lang:"id",_default:true,ariaGame:"Lari tanpa henti. Lompati kaktus, hindari burung, dan berlarilah sejauh mungkin.",headerScore:"Skor",headerBest:"Terbaik",startTitle:"Dino Runner",startBody:"Lompati kaktus dan hindari burung. Berlarilah sejauh mungkin tanpa menabrak.",startButton:"Mulai",controlsHint:"Spasi atau panah atas untuk lompat, bawah untuk menunduk",gameOverTitle:"Permainan selesai",gameOverScore:"Skor {score}",gameOverBest:"Terbaik {score}",restartButton:"Ulangi",ariaJump:"Lompat",ariaDuck:"Menunduk",ariaSound:"Suara",announceStart:"Lari!",announceGameOver:"Permainan selesai. Skor kamu {score}.",announceNewBest:"Rekor baru: {score}."}}},skins:{schema:{bg:{type:"color",name:"Background",description:"Stage background; full-bleed behind the scene."},fg:{type:"color",name:"Foreground",description:"currentColor for every sprite, the ground, and the header text."},button_bg:{type:"color",name:"Primary button bg",description:"Background of the Start / Restart primary button."},button_text:{type:"color",name:"Primary button text",description:"Text on the primary button."},button_hover:{type:"color",name:"Primary button hover bg",description:"Hover background of the primary button."},button_secondary_text:{type:"color",name:"Touch control text",description:"Text + border color of the on-screen touch controls (jump / duck)."},button_secondary_border:{type:"color",name:"Touch control border",description:"Outline color of the on-screen touch controls."},button_secondary_hover_bg:{type:"color",name:"Touch control active bg",description:"Active / pressed background of the on-screen touch controls."},focus_ring:{type:"color",name:"Focus ring",description:"Outline color shown on buttons / the stage when keyboard focus enters."},sprite_runner_idle:{type:"image",name:"Runner: idle",description:"Standing runner shown on the start screen. currentColor drives the tint."},sprite_runner_run_1:{type:"image",name:"Runner: run frame 1",description:"First running frame (legs alternate with frame 2)."},sprite_runner_run_2:{type:"image",name:"Runner: run frame 2",description:"Second running frame."},sprite_runner_jump:{type:"image",name:"Runner: jump",description:"Airborne runner shown during a jump."},sprite_runner_duck_1:{type:"image",name:"Runner: duck frame 1",description:"First ducking frame (legs alternate with frame 2)."},sprite_runner_duck_2:{type:"image",name:"Runner: duck frame 2",description:"Second ducking frame."},sprite_runner_crash:{type:"image",name:"Runner: crash",description:"Runner shown after a collision (closed eye)."},sprite_cactus_small:{type:"image",name:"Obstacle: small cactus",description:"Small ground obstacle. May clump 1-3 wide."},sprite_cactus_large:{type:"image",name:"Obstacle: large cactus",description:"Tall ground obstacle. May clump 1-3 wide."},sprite_bird_1:{type:"image",name:"Obstacle: bird wings up",description:"Flying obstacle, wings-up frame."},sprite_bird_2:{type:"image",name:"Obstacle: bird wings down",description:"Flying obstacle, wings-down frame."},sprite_cloud:{type:"image",name:"Scenery: cloud",description:"Decorative parallax cloud (shown in both skins)."},sprite_moon:{type:"image",name:"Scenery: moon",description:"Decorative moon (dark skin only)."},sprite_star:{type:"image",name:"Scenery: star",description:"Decorative star (dark skin only)."},sprite_ground:{type:"image",name:"Scenery: ground",description:"Scrolling ground strip. Tiles seamlessly at 600 logical units wide."},sprite_restart:{type:"image",name:"Restart icon",description:"Reload icon shown on the restart button."},sprite_sound_on:{type:"image",name:"Sound-on icon",description:"Speaker icon shown on the sound toggle when sound is on."},sprite_sound_off:{type:"image",name:"Sound-off icon",description:"Muted-speaker icon shown on the sound toggle when sound is off."}},presets:{light:{_theme:"light",_default:true,bg:"#F7F7F7",fg:"#535353",button_bg:"#535353",button_text:"#F7F7F7",button_hover:"#333333",button_secondary_text:"#535353",button_secondary_border:"#535353",button_secondary_hover_bg:"#E2E2E2",focus_ring:"#535353"},dark:{_theme:"dark",_default:true,bg:"#1B1B1B",fg:"#F7F7F7",button_bg:"#F7F7F7",button_text:"#1B1B1B",button_hover:"#D9D9D9",button_secondary_text:"#F7F7F7",button_secondary_border:"#8A8A8A",button_secondary_hover_bg:"#2E2E2E",focus_ring:"#F7F7F7"}}},configurations:{schema:{start_speed:{type:"range",min:3,max:10,step:.5,name:"Starting speed",description:"Run speed at the start of a run, in logical units per reference frame."},max_speed:{type:"range",min:6,max:20,step:.5,name:"Max speed",description:"Speed cap the run accelerates toward."},acceleration:{type:"range",min:2e-4,max:.004,step:2e-4,name:"Acceleration",description:"Speed gained per frame until the cap is hit. Higher means the run ramps up faster."},gravity:{type:"range",min:.3,max:1,step:.05,name:"Gravity",description:"Downward pull on the jump arc. Higher means shorter, snappier jumps."},jump_velocity:{type:"range",min:6,max:16,step:.5,name:"Jump strength",description:"Initial upward velocity of a jump. Higher means the runner jumps higher."},gap_coefficient:{type:"range",min:.3,max:1.5,step:.1,name:"Obstacle spacing",description:"Multiplier on the gap between obstacles. Higher means more breathing room."},pass_score:{type:"range",min:10,max:2e3,step:10,name:"Pass score",description:"Score a run must reach to count as a pass. The first qualifying crash (and each new best after) reports success."},birds_enabled:{type:"boolean",name:"Flying obstacles",description:"Spawn birds (flying obstacles) once the run is fast enough."},bird_min_speed:{type:"range",min:5,max:15,step:.5,name:"Bird unlock speed",description:"Run speed at which birds start to appear."},show_score:{type:"boolean",name:"Show score",description:"Display the live score in the header."},show_best:{type:"boolean",name:"Show best score",description:"Display the session-best score in the header."},sound:{type:"boolean",name:"Sound effects",description:"Play the jump / milestone / crash sound effects (starts after the first interaction)."}},presets:{default:{_default:true,start_speed:6,max_speed:13,acceleration:.0022,gravity:.6,jump_velocity:10,gap_coefficient:.6,pass_score:100,birds_enabled:true,bird_min_speed:7,show_score:true,show_best:true,sound:true},classic:{_extends:"default",acceleration:.001,bird_min_speed:8.5},casual:{_extends:"default",start_speed:5,max_speed:10,acceleration:6e-4,gap_coefficient:.9,pass_score:60,bird_min_speed:10},hardcore:{_extends:"default",start_speed:7,max_speed:16,acceleration:.0016,gap_coefficient:.5,pass_score:250},calm:{_extends:"default",birds_enabled:false,sound:false,pass_score:60}}}};var ot=re.configurations?.presets?.default??{};function T(r,e){let i=ot[r];return typeof i=="number"&&Number.isFinite(i)?i:e}function ie(r,e){let i=ot[r];return typeof i=="boolean"?i:e}var b={startSpeed:T("start_speed",Z.initial),maxSpeed:T("max_speed",Z.max),acceleration:T("acceleration",Z.acceleration),gravity:T("gravity",v.gravity),jumpMagnitude:T("jump_velocity",10),gapCoefficient:T("gap_coefficient",Qe),passScore:T("pass_score",100),birdsEnabled:ie("birds_enabled",true),birdMinSpeed:T("bird_min_speed",8.5),showScore:ie("show_score",true),showBest:ie("show_best",true),sound:ie("sound",true)};function C(r,e){if(!r)return null;let i=r[e];return typeof i=="number"&&Number.isFinite(i)?i:null}function ne(r,e){if(!r)return null;let i=r[e];return typeof i=="boolean"?i:null}function st(r){let e=r?.config??null,i=C(e,"jump_velocity")??b.jumpMagnitude;return {startSpeed:C(e,"start_speed")??b.startSpeed,maxSpeed:C(e,"max_speed")??b.maxSpeed,acceleration:C(e,"acceleration")??b.acceleration,gravity:C(e,"gravity")??b.gravity,initialJumpVelocity:-Math.abs(i),gapCoefficient:C(e,"gap_coefficient")??b.gapCoefficient,passScore:C(e,"pass_score")??b.passScore,birdsEnabled:ne(e,"birds_enabled")??b.birdsEnabled,birdMinSpeed:C(e,"bird_min_speed")??b.birdMinSpeed,showScore:ne(e,"show_score")??b.showScore,showBest:ne(e,"show_best")??b.showBest,sound:ne(e,"sound")??b.sound}}var at='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44 47" fill="currentColor" shape-rendering="crispEdges">%0A <rect x="24" y="2" width="16" height="11"/>%0A <rect x="22" y="4" width="2" height="41"/>%0A <rect x="40" y="4" width="2" height="9"/>%0A <rect x="24" y="13" width="8" height="4"/>%0A <rect x="32" y="15" width="6" height="2"/>%0A <rect x="2" y="17" width="2" height="12"/>%0A <rect x="20" y="17" width="2" height="22"/>%0A <rect x="24" y="17" width="6" height="13"/>%0A <rect x="17" y="19" width="3" height="18"/>%0A <rect x="4" y="21" width="2" height="10"/>%0A <rect x="14" y="21" width="3" height="18"/>%0A <rect x="30" y="21" width="4" height="2"/>%0A <rect x="6" y="23" width="2" height="10"/>%0A <rect x="12" y="23" width="2" height="22"/>%0A <rect x="32" y="23" width="2" height="2"/>%0A <rect x="8" y="25" width="4" height="10"/>%0A <rect x="24" y="30" width="4" height="3"/>%0A <rect x="24" y="33" width="2" height="2"/>%0A <rect x="10" y="35" width="2" height="2"/>%0A <rect x="17" y="37" width="1" height="2"/>%0A <rect x="14" y="39" width="2" height="2"/>%0A <rect x="14" y="43" width="2" height="2"/>%0A <rect x="24" y="43" width="2" height="2"/>%0A</svg>%0A';var ct='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44 47" fill="currentColor" shape-rendering="crispEdges">%0A <rect x="24" y="2" width="16" height="3"/>%0A <rect x="22" y="4" width="2" height="35"/>%0A <rect x="40" y="4" width="2" height="9"/>%0A <rect x="24" y="5" width="2" height="30"/>%0A <rect x="28" y="5" width="12" height="8"/>%0A <rect x="26" y="7" width="2" height="26"/>%0A <rect x="28" y="13" width="4" height="4"/>%0A <rect x="32" y="15" width="6" height="2"/>%0A <rect x="2" y="17" width="2" height="12"/>%0A <rect x="20" y="17" width="2" height="20"/>%0A <rect x="28" y="17" width="2" height="13"/>%0A <rect x="17" y="19" width="3" height="18"/>%0A <rect x="4" y="21" width="2" height="10"/>%0A <rect x="14" y="21" width="3" height="18"/>%0A <rect x="30" y="21" width="4" height="2"/>%0A <rect x="6" y="23" width="2" height="10"/>%0A <rect x="12" y="23" width="2" height="22"/>%0A <rect x="32" y="23" width="2" height="2"/>%0A <rect x="8" y="25" width="4" height="10"/>%0A <rect x="10" y="35" width="2" height="2"/>%0A <rect x="17" y="37" width="1" height="2"/>%0A <rect x="24" y="37" width="3" height="2"/>%0A <rect x="14" y="39" width="2" height="2"/>%0A <rect x="14" y="43" width="2" height="2"/>%0A</svg>%0A';var ht='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44 47" fill="currentColor" shape-rendering="crispEdges">%0A <rect x="24" y="2" width="16" height="3"/>%0A <rect x="22" y="4" width="2" height="41"/>%0A <rect x="40" y="4" width="2" height="9"/>%0A <rect x="24" y="5" width="2" height="30"/>%0A <rect x="28" y="5" width="12" height="8"/>%0A <rect x="26" y="7" width="2" height="26"/>%0A <rect x="28" y="13" width="4" height="4"/>%0A <rect x="32" y="15" width="6" height="2"/>%0A <rect x="2" y="17" width="2" height="12"/>%0A <rect x="20" y="17" width="2" height="22"/>%0A <rect x="28" y="17" width="2" height="13"/>%0A <rect x="17" y="19" width="3" height="18"/>%0A <rect x="4" y="21" width="2" height="10"/>%0A <rect x="14" y="21" width="3" height="16"/>%0A <rect x="30" y="21" width="4" height="2"/>%0A <rect x="6" y="23" width="2" height="10"/>%0A <rect x="12" y="23" width="2" height="16"/>%0A <rect x="32" y="23" width="2" height="2"/>%0A <rect x="8" y="25" width="4" height="10"/>%0A <rect x="10" y="35" width="2" height="2"/>%0A <rect x="14" y="37" width="2" height="4"/>%0A <rect x="16" y="39" width="2" height="2"/>%0A <rect x="24" y="43" width="2" height="2"/>%0A</svg>%0A';var dt='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44 47" fill="currentColor" shape-rendering="crispEdges">%0A <rect x="24" y="2" width="16" height="3"/>%0A <rect x="22" y="4" width="2" height="41"/>%0A <rect x="40" y="4" width="2" height="9"/>%0A <rect x="24" y="5" width="2" height="30"/>%0A <rect x="28" y="5" width="12" height="8"/>%0A <rect x="26" y="7" width="2" height="26"/>%0A <rect x="28" y="13" width="4" height="4"/>%0A <rect x="32" y="15" width="6" height="2"/>%0A <rect x="2" y="17" width="2" height="12"/>%0A <rect x="20" y="17" width="2" height="22"/>%0A <rect x="28" y="17" width="2" height="13"/>%0A <rect x="17" y="19" width="3" height="18"/>%0A <rect x="4" y="21" width="2" height="10"/>%0A <rect x="14" y="21" width="3" height="18"/>%0A <rect x="30" y="21" width="4" height="2"/>%0A <rect x="6" y="23" width="2" height="10"/>%0A <rect x="12" y="23" width="2" height="22"/>%0A <rect x="32" y="23" width="2" height="2"/>%0A <rect x="8" y="25" width="4" height="10"/>%0A <rect x="10" y="35" width="2" height="2"/>%0A <rect x="17" y="37" width="1" height="2"/>%0A <rect x="14" y="39" width="2" height="2"/>%0A <rect x="14" y="43" width="2" height="2"/>%0A <rect x="24" y="43" width="2" height="2"/>%0A</svg>%0A';var ut='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 59 47" fill="currentColor" shape-rendering="crispEdges">%0A <rect x="2" y="19" width="2" height="6"/>%0A <rect x="39" y="20" width="16" height="3"/>%0A <rect x="4" y="21" width="4" height="6"/>%0A <rect x="16" y="21" width="17" height="15"/>%0A <rect x="37" y="22" width="2" height="11"/>%0A <rect x="55" y="22" width="2" height="9"/>%0A <rect x="8" y="23" width="8" height="8"/>%0A <rect x="33" y="23" width="4" height="10"/>%0A <rect x="39" y="23" width="2" height="12"/>%0A <rect x="43" y="23" width="12" height="8"/>%0A <rect x="41" y="25" width="2" height="10"/>%0A <rect x="6" y="27" width="2" height="2"/>%0A <rect x="10" y="31" width="6" height="2"/>%0A <rect x="43" y="31" width="4" height="4"/>%0A <rect x="12" y="33" width="4" height="2"/>%0A <rect x="33" y="33" width="2" height="3"/>%0A <rect x="47" y="33" width="6" height="2"/>%0A <rect x="14" y="35" width="2" height="2"/>%0A <rect x="16" y="36" width="10" height="1"/>%0A <rect x="31" y="36" width="2" height="4"/>%0A <rect x="13" y="37" width="2" height="4"/>%0A <rect x="19" y="37" width="6" height="2"/>%0A <rect x="33" y="38" width="2" height="2"/>%0A <rect x="15" y="39" width="2" height="2"/>%0A <rect x="19" y="39" width="4" height="2"/>%0A <rect x="19" y="41" width="2" height="4"/>%0A <rect x="21" y="43" width="2" height="2"/>%0A</svg>%0A';var lt='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 59 47" fill="currentColor" shape-rendering="crispEdges">%0A <rect x="2" y="19" width="2" height="6"/>%0A <rect x="39" y="20" width="16" height="3"/>%0A <rect x="4" y="21" width="4" height="6"/>%0A <rect x="16" y="21" width="17" height="15"/>%0A <rect x="37" y="22" width="2" height="11"/>%0A <rect x="55" y="22" width="2" height="9"/>%0A <rect x="8" y="23" width="8" height="8"/>%0A <rect x="33" y="23" width="4" height="10"/>%0A <rect x="39" y="23" width="2" height="12"/>%0A <rect x="43" y="23" width="12" height="8"/>%0A <rect x="41" y="25" width="2" height="10"/>%0A <rect x="6" y="27" width="2" height="2"/>%0A <rect x="10" y="31" width="6" height="2"/>%0A <rect x="43" y="31" width="4" height="4"/>%0A <rect x="12" y="33" width="4" height="2"/>%0A <rect x="33" y="33" width="2" height="3"/>%0A <rect x="47" y="33" width="6" height="2"/>%0A <rect x="14" y="35" width="2" height="6"/>%0A <rect x="16" y="36" width="10" height="1"/>%0A <rect x="31" y="36" width="2" height="4"/>%0A <rect x="13" y="37" width="1" height="8"/>%0A <rect x="16" y="37" width="3" height="2"/>%0A <rect x="23" y="37" width="5" height="2"/>%0A <rect x="33" y="38" width="2" height="2"/>%0A <rect x="16" y="39" width="1" height="2"/>%0A <rect x="14" y="41" width="1" height="4"/>%0A <rect x="15" y="43" width="2" height="2"/>%0A</svg>%0A';var pt='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44 47" fill="currentColor" shape-rendering="crispEdges">%0A <rect x="24" y="2" width="16" height="3"/>%0A <rect x="22" y="4" width="2" height="41"/>%0A <rect x="40" y="4" width="2" height="11"/>%0A <rect x="24" y="5" width="2" height="30"/>%0A <rect x="30" y="5" width="10" height="10"/>%0A <rect x="27" y="6" width="2" height="2"/>%0A <rect x="26" y="9" width="4" height="21"/>%0A <rect x="30" y="15" width="8" height="2"/>%0A <rect x="2" y="17" width="2" height="12"/>%0A <rect x="20" y="17" width="2" height="22"/>%0A <rect x="17" y="19" width="3" height="18"/>%0A <rect x="4" y="21" width="2" height="10"/>%0A <rect x="14" y="21" width="3" height="18"/>%0A <rect x="30" y="21" width="4" height="2"/>%0A <rect x="6" y="23" width="2" height="10"/>%0A <rect x="12" y="23" width="2" height="22"/>%0A <rect x="32" y="23" width="2" height="2"/>%0A <rect x="8" y="25" width="4" height="10"/>%0A <rect x="26" y="30" width="2" height="3"/>%0A <rect x="10" y="35" width="2" height="2"/>%0A <rect x="17" y="37" width="1" height="2"/>%0A <rect x="14" y="39" width="2" height="2"/>%0A <rect x="14" y="43" width="2" height="2"/>%0A <rect x="24" y="43" width="2" height="2"/>%0A</svg>%0A';var gt='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 35" fill="currentColor" shape-rendering="crispEdges">%0A <rect x="7" y="1" width="3" height="33"/>%0A <rect x="6" y="2" width="1" height="32"/>%0A <rect x="10" y="2" width="1" height="32"/>%0A <rect x="14" y="5" width="1" height="13"/>%0A <rect x="13" y="6" width="1" height="13"/>%0A <rect x="15" y="6" width="1" height="11"/>%0A <rect x="2" y="9" width="1" height="13"/>%0A <rect x="1" y="10" width="1" height="11"/>%0A <rect x="3" y="10" width="1" height="13"/>%0A <rect x="11" y="16" width="2" height="3"/>%0A <rect x="4" y="20" width="2" height="3"/>%0A</svg>%0A';var mt='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 50" fill="currentColor" shape-rendering="crispEdges">%0A <rect x="10" y="1" width="5" height="46"/>%0A <rect x="9" y="2" width="1" height="45"/>%0A <rect x="15" y="2" width="1" height="45"/>%0A <rect x="20" y="11" width="3" height="17"/>%0A <rect x="19" y="12" width="1" height="19"/>%0A <rect x="23" y="12" width="1" height="15"/>%0A <rect x="2" y="13" width="3" height="17"/>%0A <rect x="1" y="14" width="1" height="15"/>%0A <rect x="5" y="14" width="1" height="18"/>%0A <rect x="6" y="27" width="3" height="5"/>%0A <rect x="16" y="27" width="3" height="4"/>%0A <rect x="20" y="28" width="2" height="1"/>%0A <rect x="20" y="29" width="1" height="1"/>%0A <rect x="3" y="30" width="2" height="1"/>%0A <rect x="4" y="31" width="1" height="1"/>%0A <rect x="19" y="45" width="1" height="1"/>%0A <rect x="5" y="46" width="1" height="1"/>%0A <rect x="7" y="46" width="2" height="1"/>%0A <rect x="16" y="48" width="1" height="1"/>%0A</svg>%0A';var yt='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 46 40" fill="currentColor" shape-rendering="crispEdges">%0A <rect x="10" y="8" width="4" height="10"/>%0A <rect x="8" y="10" width="2" height="8"/>%0A <rect x="6" y="12" width="2" height="6"/>%0A <rect x="14" y="12" width="2" height="8"/>%0A <rect x="4" y="14" width="2" height="4"/>%0A <rect x="2" y="16" width="2" height="2"/>%0A <rect x="16" y="16" width="14" height="6"/>%0A <rect x="30" y="18" width="2" height="10"/>%0A <rect x="32" y="20" width="12" height="2"/>%0A <rect x="18" y="22" width="12" height="6"/>%0A <rect x="32" y="22" width="6" height="4"/>%0A <rect x="38" y="24" width="4" height="2"/>%0A <rect x="32" y="26" width="4" height="2"/>%0A <rect x="18" y="28" width="8" height="2"/>%0A <rect x="18" y="30" width="6" height="2"/>%0A <rect x="18" y="32" width="4" height="4"/>%0A <rect x="18" y="36" width="2" height="2"/>%0A</svg>%0A';var ft='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 46 40" fill="currentColor" shape-rendering="crispEdges">%0A <rect x="16" y="2" width="2" height="6"/>%0A <rect x="18" y="4" width="2" height="20"/>%0A <rect x="20" y="6" width="2" height="20"/>%0A <rect x="10" y="8" width="4" height="10"/>%0A <rect x="22" y="8" width="2" height="20"/>%0A <rect x="8" y="10" width="2" height="8"/>%0A <rect x="24" y="10" width="2" height="18"/>%0A <rect x="6" y="12" width="2" height="6"/>%0A <rect x="14" y="12" width="2" height="8"/>%0A <rect x="26" y="12" width="2" height="16"/>%0A <rect x="4" y="14" width="2" height="4"/>%0A <rect x="28" y="14" width="2" height="14"/>%0A <rect x="2" y="16" width="2" height="2"/>%0A <rect x="16" y="16" width="2" height="6"/>%0A <rect x="30" y="18" width="2" height="10"/>%0A <rect x="32" y="20" width="12" height="2"/>%0A <rect x="32" y="22" width="6" height="4"/>%0A <rect x="38" y="24" width="4" height="2"/>%0A <rect x="32" y="26" width="4" height="2"/>%0A</svg>%0A';var xt='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 46 14" fill="currentColor" shape-rendering="crispEdges">%0A <rect x="25" y="0" width="4" height="1"/>%0A <rect x="20" y="1" width="6" height="1"/>%0A <rect x="28" y="1" width="3" height="1"/>%0A <rect x="19" y="2" width="2" height="1"/>%0A <rect x="30" y="2" width="2" height="1"/>%0A <rect x="17" y="3" width="3" height="1"/>%0A <rect x="31" y="3" width="1" height="3"/>%0A <rect x="17" y="4" width="1" height="3"/>%0A <rect x="32" y="4" width="3" height="1"/>%0A <rect x="34" y="5" width="6" height="1"/>%0A <rect x="14" y="6" width="3" height="1"/>%0A <rect x="30" y="6" width="1" height="1"/>%0A <rect x="39" y="6" width="1" height="2"/>%0A <rect x="13" y="7" width="2" height="1"/>%0A <rect x="40" y="7" width="3" height="1"/>%0A <rect x="6" y="8" width="8" height="1"/>%0A <rect x="42" y="8" width="1" height="2"/>%0A <rect x="5" y="9" width="2" height="1"/>%0A <rect x="43" y="9" width="2" height="1"/>%0A <rect x="5" y="10" width="1" height="2"/>%0A <rect x="44" y="10" width="1" height="1"/>%0A <rect x="1" y="11" width="2" height="1"/>%0A <rect x="4" y="11" width="1" height="1"/>%0A <rect x="9" y="11" width="1" height="1"/>%0A <rect x="45" y="11" width="1" height="2"/>%0A <rect x="0" y="12" width="2" height="1"/>%0A <rect x="10" y="12" width="35" height="1"/>%0A</svg>%0A';var bt='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="570 829 32 32" fill="currentColor">%0A <!-- Crescent moon (SVG Repo "moon" icon). Recolored to currentColor so the%0A skin tints it; defs / title / desc / sketch + xlink namespaces stripped,%0A and there is no script / event handler / external reference. Centered in%0A a square viewBox so it renders undistorted in the square moon box. -->%0A <path d="M586.256,845 C586.256,838.1 590.735,832.236 597,829.991 C595.243,829.361 593.353,829 591.372,829 C582.33,829 575,836.164 575,845 C575,853.837 582.33,861 591.372,861 C593.353,861 595.243,860.639 597,860.009 C590.735,857.764 586.256,851.901 586.256,845"/>%0A</svg>%0A';var wt='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 9 9" fill="currentColor" shape-rendering="crispEdges">%0A <!-- four-point sparkle -->%0A <rect x="3" y="0" width="3" height="9"/>%0A <rect x="0" y="3" width="9" height="3"/>%0A <rect x="1" y="1" width="2" height="2"/>%0A <rect x="6" y="1" width="2" height="2"/>%0A <rect x="1" y="6" width="2" height="2"/>%0A <rect x="6" y="6" width="2" height="2"/>%0A</svg>%0A';var vt='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 12" fill="currentColor" shape-rendering="crispEdges" preserveAspectRatio="none">%0A <!-- continuous baseline; the strip tiles at width 600 so two copies loop seamlessly -->%0A <rect x="0" y="0" width="600" height="2"/>%0A <!-- scattered pebbles / bumps below the line (kept clear of the 0 / 600 seam) -->%0A <rect x="38" y="4" width="3" height="2"/>%0A <rect x="96" y="5" width="2" height="2"/>%0A <rect x="150" y="4" width="4" height="2"/>%0A <rect x="214" y="6" width="2" height="2"/>%0A <rect x="268" y="4" width="3" height="2"/>%0A <rect x="330" y="5" width="2" height="2"/>%0A <rect x="392" y="4" width="4" height="2"/>%0A <rect x="455" y="6" width="2" height="2"/>%0A <rect x="512" y="4" width="3" height="2"/>%0A <rect x="560" y="5" width="2" height="2"/>%0A</svg>%0A';var St='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36" fill="currentColor" shape-rendering="geometricPrecision">%0A <!-- reload arrow: open ring + arrowhead at the gap -->%0A <path fill="none" stroke="currentColor" stroke-width="3.5" d="M28 13 A11 11 0 1 0 29 18"/>%0A <path d="M22 4 L31 8 L24 14 Z"/>%0A</svg>%0A';var Rt='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">%0A <!-- speaker body -->%0A <path d="M3 9 H7 L13 4 V20 L7 15 H3 Z"/>%0A <!-- sound waves -->%0A <path d="M16 9 a4.5 4.5 0 0 1 0 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>%0A <path d="M18.5 6.5 a8 8 0 0 1 0 11" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>%0A</svg>%0A';var _t='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">%0A <!-- speaker body -->%0A <path d="M3 9 H7 L13 4 V20 L7 15 H3 Z"/>%0A <!-- muted: an x where the waves would be -->%0A <path d="M16 9 L22 15 M22 9 L16 15" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>%0A</svg>%0A';var Ar=["runner-idle","runner-run-1","runner-run-2","runner-jump","runner-duck-1","runner-duck-2","runner-crash","cactus-small","cactus-large","bird-1","bird-2","cloud","moon","star","ground","restart","sound-on","sound-off"],Mr={"runner-idle":"sprite_runner_idle","runner-run-1":"sprite_runner_run_1","runner-run-2":"sprite_runner_run_2","runner-jump":"sprite_runner_jump","runner-duck-1":"sprite_runner_duck_1","runner-duck-2":"sprite_runner_duck_2","runner-crash":"sprite_runner_crash","cactus-small":"sprite_cactus_small","cactus-large":"sprite_cactus_large","bird-1":"sprite_bird_1","bird-2":"sprite_bird_2",cloud:"sprite_cloud",moon:"sprite_moon",star:"sprite_star",ground:"sprite_ground",restart:"sprite_restart","sound-on":"sprite_sound_on","sound-off":"sprite_sound_off"},Dr={"runner-idle":at,"runner-run-1":ct,"runner-run-2":ht,"runner-jump":dt,"runner-duck-1":ut,"runner-duck-2":lt,"runner-crash":pt,"cactus-small":gt,"cactus-large":mt,"bird-1":yt,"bird-2":ft,cloud:xt,moon:bt,star:wt,ground:vt,restart:St,"sound-on":Rt,"sound-off":_t};function Bt(r){let e=/^data:image\/svg\+xml((?:;[^,]*)?),([\s\S]*)$/.exec(r);if(!e)return "";try{return /;base64/i.test(e[1])?atob(e[2]):decodeURIComponent(e[2])}catch{return ""}}function Lr(r){return r.length===0?"":r.replace(/<script\b[\s\S]*?<\/script\s*>/gi,"").replace(/<style\b[\s\S]*?<\/style\s*>/gi,"").replace(/[\s/]on[a-z]+\s*=\s*"[^"]*"/gi,"").replace(/[\s/]on[a-z]+\s*=\s*'[^']*'/gi,"").replace(/[\s/]on[a-z]+\s*=\s*[^\s>]+/gi,"").replace(/[\s/](?:xlink:href|href|src)\s*=\s*"\s*(?:javascript|data:text\/html)[^"]*"/gi,"").replace(/[\s/](?:xlink:href|href|src)\s*=\s*'\s*(?:javascript|data:text\/html)[^']*'/gi,"")}function Ut(r){let e={};for(let i of Ar){let n=r?.[Mr[i]],o="";typeof n=="string"&&n.length>0&&(o=Bt(n)),o.length===0&&(o=Bt(Dr[i])),e[i]=Lr(o);}return e}var Hr={ariaGame:"Endless runner. Jump the cactus, duck the birds, and run as far as you can.",headerScore:"Score",headerBest:"Best",startTitle:"Dino Runner",startBody:"Jump the cactus and duck the birds. Run as far as you can without crashing.",startButton:"Start",controlsHint:"Space or Up to jump, Down to duck",gameOverTitle:"Game over",gameOverScore:"Score {score}",gameOverBest:"Best {score}",restartButton:"Restart",ariaJump:"Jump",ariaDuck:"Duck",ariaSound:"Sound",announceStart:"Go",announceGameOver:"Game over. You scored {score}.",announceNewBest:"New best score: {score}."};function Et(r){return {direction:r?._direction==="rtl"?"rtl":"ltr",lang:r?._lang??"en",t(e,i){let n=(r&&typeof r[e]=="string"?r[e]:Hr[e])??"";return i?n.replace(/\{(\w+)\}/g,(o,a)=>{let h=i[a];return h===void 0?`{${a}}`:String(h)}):n}}}var Ir='"PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC", "Noto Sans CJK SC", "Source Han Sans SC", sans-serif',Pr='"PingFang TC", "Microsoft JhengHei", "Noto Sans TC", "Noto Sans CJK TC", "Source Han Sans TC", sans-serif',Yr='"Hiragino Sans", "Hiragino Kaku Gothic ProN", "Yu Gothic", "Meiryo", "Noto Sans JP", "Noto Sans CJK JP", sans-serif',Gr='"Apple SD Gothic Neo", "Malgun Gothic", "Noto Sans KR", "Noto Sans CJK KR", sans-serif',jr=new Set(["hant","tw","hk","mo"]);function kt(r){if(!r)return null;let e=r.toLowerCase().split(/[-_]/).filter(Boolean),i=e[0];if(!i)return null;switch(i){case "zh":return e.slice(1).some(n=>jr.has(n))?Pr:Ir;case "ja":return Yr;case "ko":return Gr;default:return null}}function Tt(r){let e=r.createElement("div");return e.setAttribute("role","status"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-atomic","true"),e.className="dr-announce",{element:e,say(i){e.textContent="",queueMicrotask(()=>{e.textContent=i;});}}}function Ct(r){return typeof r.matchMedia!="function"?false:r.matchMedia("(prefers-reduced-motion: reduce)").matches}function oe(r){return Math.floor(r*Ve)}function Ot(r,e,i){let n=r>=e&&r>i;return {pass:n,score:r,bestPassed:n?r:i}}function Jt(r,e,i,n){return r>=e?e:Math.min(e,r+i*n)}var be={resume(){},jump(){},score(){},hit(){},setMuted(){},dispose(){}};function Nr(r){let e=r.indexOf("base64,");if(e<0)return null;try{let i=atob(r.slice(e+7)),n=new Uint8Array(i.length);for(let o=0;o<i.length;o+=1)n[o]=i.charCodeAt(o);return n.buffer}catch{return null}}function At(r,e,i){if(!e)return be;let n=r,o=n.AudioContext??n.webkitAudioContext;if(typeof o!="function")return be;let a;try{a=new o;}catch{return be}let h={};Object.keys(i).forEach(g=>{let u=Nr(i[g]);if(u)try{let B=a.decodeAudioData(u.slice(0));B&&typeof B.then=="function"&&B.then(f=>{h[g]=f;}).catch(()=>{});}catch{}});let d=false;function p(g){let u=h[g];if(!(d||!u||a.state==="closed"))try{let B=a.createBufferSource();B.buffer=u,B.connect(a.destination),B.start(0);}catch{}}return {resume(){a.state==="suspended"&&a.resume();},jump(){p("jump");},score(){p("score");},hit(){p("hit");},setMuted(g){d=g;},dispose(){try{a.close();}catch{}}}}var Mt="data:application/ogg;base64,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";var Dt="data:application/ogg;base64,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";var Lt="data:application/ogg;base64,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";var Ht={jump:Mt,hit:Dt,score:Lt};var It=`
2
+ html, body, #cpt-root {
3
+ width: 100%;
4
+ height: 100%;
5
+ }
6
+
7
+ :host, .dr-root {
8
+ /* Defaults mirror the bundled light skin preset. game.ts overwrites each
9
+ one via style.setProperty when ctx.skin resolves. */
10
+ --dr-bg: #F7F7F7;
11
+ --dr-fg: #535353;
12
+ --dr-button-bg: #535353;
13
+ --dr-button-text: #F7F7F7;
14
+ --dr-button-hover: #333333;
15
+ --dr-button-secondary-text: #535353;
16
+ --dr-button-secondary-border: #535353;
17
+ --dr-button-secondary-hover-bg: #E2E2E2;
18
+ --dr-focus-ring: #535353;
19
+ --dr-scale: 1;
20
+ /* CJK locales override this with native fonts (game.ts via fonts.ts);
21
+ non-CJK locales keep the sans-serif tail so font-family stays valid. */
22
+ --dr-cjk: sans-serif;
23
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, var(--dr-cjk);
24
+ }
25
+
26
+ .dr-root {
27
+ position: relative;
28
+ width: 100%;
29
+ height: 100%;
30
+ overflow: hidden;
31
+ background: var(--dr-bg);
32
+ }
33
+
34
+ .dr-stage {
35
+ position: absolute;
36
+ inset: 0;
37
+ }
38
+
39
+ .dr-world {
40
+ position: absolute;
41
+ left: 50%;
42
+ top: 50%;
43
+ width: ${m}px;
44
+ height: ${U}px;
45
+ transform: translate(-50%, -50%) scale(var(--dr-scale));
46
+ transform-origin: center center;
47
+ color: var(--dr-fg);
48
+ }
49
+
50
+ /* Generic positioned entity: game.ts/horizon.ts set width/height + a
51
+ translate transform in world units. */
52
+ .dr-entity {
53
+ position: absolute;
54
+ left: 0;
55
+ top: 0;
56
+ will-change: transform;
57
+ }
58
+
59
+ .dr-entity svg {
60
+ display: block;
61
+ width: 100%;
62
+ height: 100%;
63
+ }
64
+
65
+ .dr-ground-tile {
66
+ position: absolute;
67
+ left: 0;
68
+ top: ${U-fe}px;
69
+ width: ${m}px;
70
+ height: ${fe}px;
71
+ will-change: transform;
72
+ }
73
+
74
+ .dr-cloud { opacity: 0.85; }
75
+
76
+ .dr-hud {
77
+ position: absolute;
78
+ top: 6px;
79
+ right: 10px;
80
+ display: flex;
81
+ gap: 12px;
82
+ font-variant-numeric: tabular-nums;
83
+ font-size: 13px;
84
+ font-weight: 600;
85
+ letter-spacing: 1px;
86
+ color: var(--dr-fg);
87
+ }
88
+
89
+ .dr-hud .label {
90
+ opacity: 0.6;
91
+ margin-inline-end: 4px;
92
+ font-weight: 500;
93
+ }
94
+
95
+ .dr-hud-best[data-hidden="true"],
96
+ .dr-hud-score[data-hidden="true"] { display: none; }
97
+
98
+ /* In-game sound toggle, top-left (mirrors the HUD top-right). Icon-only,
99
+ inherits the foreground color via currentColor. */
100
+ .dr-sound {
101
+ position: absolute;
102
+ top: 5px;
103
+ left: 8px;
104
+ width: 18px;
105
+ height: 18px;
106
+ padding: 0;
107
+ border: 0;
108
+ background: transparent;
109
+ color: var(--dr-fg);
110
+ cursor: pointer;
111
+ line-height: 0;
112
+ }
113
+ .dr-sound svg { display: block; width: 100%; height: 100%; }
114
+ .dr-sound:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--dr-focus-ring); border-radius: 3px; }
115
+
116
+ .dr-overlay {
117
+ position: absolute;
118
+ inset: 0;
119
+ display: flex;
120
+ flex-direction: column;
121
+ align-items: center;
122
+ justify-content: center;
123
+ gap: 8px;
124
+ text-align: center;
125
+ padding: 10px;
126
+ box-sizing: border-box;
127
+ color: var(--dr-fg);
128
+ /* The start / game-over screen sits over the live scene; a skin-colored
129
+ scrim keeps its text legible (otherwise the ground line + sprites bleed
130
+ through behind the copy, e.g. the controls hint reading like it's struck
131
+ through by the ground). Solid fallback first for browsers without
132
+ color-mix; the scrim leaves the scene faintly visible behind. */
133
+ background: var(--dr-bg);
134
+ background: color-mix(in srgb, var(--dr-bg) 90%, transparent);
135
+ }
136
+
137
+ .dr-overlay[data-hidden="true"] { display: none; }
138
+
139
+ .dr-overlay-icon {
140
+ width: 48px;
141
+ height: 48px;
142
+ color: var(--dr-fg);
143
+ }
144
+ .dr-overlay-icon svg { display: block; width: 100%; height: 100%; }
145
+
146
+ .dr-title {
147
+ margin: 0;
148
+ font-size: 22px;
149
+ font-weight: 700;
150
+ letter-spacing: 2px;
151
+ text-transform: uppercase;
152
+ }
153
+
154
+ .dr-line {
155
+ margin: 0;
156
+ font-size: 13px;
157
+ line-height: 1.4;
158
+ max-width: 42ch;
159
+ }
160
+
161
+ .dr-line--score {
162
+ font-variant-numeric: tabular-nums;
163
+ font-weight: 600;
164
+ letter-spacing: 1px;
165
+ }
166
+
167
+ .dr-hint {
168
+ margin: 2px 0 0;
169
+ font-size: 11px;
170
+ opacity: 0.6;
171
+ }
172
+
173
+ .dr-buttons {
174
+ display: flex;
175
+ gap: 8px;
176
+ flex-wrap: wrap;
177
+ justify-content: center;
178
+ margin-top: 4px;
179
+ }
180
+
181
+ .dr-button {
182
+ appearance: none;
183
+ border: 0;
184
+ font: inherit;
185
+ background: var(--dr-button-bg);
186
+ color: var(--dr-button-text);
187
+ padding: 8px 18px;
188
+ border-radius: 6px;
189
+ cursor: pointer;
190
+ font-weight: 600;
191
+ font-size: 14px;
192
+ display: inline-flex;
193
+ align-items: center;
194
+ gap: 6px;
195
+ }
196
+ .dr-button:hover { background: var(--dr-button-hover); }
197
+ .dr-button:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--dr-focus-ring); }
198
+
199
+ .dr-button-icon { width: 18px; height: 18px; }
200
+ .dr-button-icon svg { display: block; width: 100%; height: 100%; }
201
+
202
+ /* Touch controls live in the (unscaled) stage so the tap targets stay a
203
+ comfortable finger size regardless of world scale. Only shown on coarse
204
+ pointers; keyboard / mouse users never see them. */
205
+ .dr-touch {
206
+ position: absolute;
207
+ bottom: 10px;
208
+ left: 0;
209
+ right: 0;
210
+ display: none;
211
+ justify-content: space-between;
212
+ padding: 0 12px;
213
+ pointer-events: none;
214
+ }
215
+
216
+ @media (hover: none) and (pointer: coarse) {
217
+ .dr-touch[data-active="true"] { display: flex; }
218
+ }
219
+
220
+ .dr-touch-button {
221
+ appearance: none;
222
+ pointer-events: auto;
223
+ width: 64px;
224
+ height: 64px;
225
+ border-radius: 50%;
226
+ border: 2px solid var(--dr-button-secondary-border);
227
+ background: transparent;
228
+ color: var(--dr-button-secondary-text);
229
+ font-size: 12px;
230
+ font-weight: 600;
231
+ cursor: pointer;
232
+ touch-action: manipulation;
233
+ user-select: none;
234
+ }
235
+ .dr-touch-button:active { background: var(--dr-button-secondary-hover-bg); }
236
+
237
+ .dr-announce {
238
+ position: absolute;
239
+ width: 1px;
240
+ height: 1px;
241
+ margin: -1px;
242
+ padding: 0;
243
+ border: 0;
244
+ clip: rect(0 0 0 0);
245
+ overflow: hidden;
246
+ }
247
+ `;function Pt(r,e,i,n){let o=r.createElement("button");if(o.type="button",o.className="dr-button",n){let h=r.createElement("span");h.className="dr-button-icon",h.setAttribute("aria-hidden","true"),h.innerHTML=n,o.appendChild(h);}let a=r.createElement("span");return a.textContent=e,o.appendChild(a),o.addEventListener("click",i),o}function Yt(r,e,i){let n=r.createElement("div");n.className="dr-overlay dr-overlay--start",n.setAttribute("role","group");let o=r.createElement("h2");o.className="dr-title",o.textContent=e.t("startTitle");let a=r.createElement("p");a.className="dr-line",a.textContent=e.t("startBody");let h=r.createElement("div");h.className="dr-buttons",h.appendChild(Pt(r,e.t("startButton"),i));let d=r.createElement("p");return d.className="dr-hint",d.textContent=e.t("controlsHint"),n.append(o,a,h,d),n}function Gt(r,e,i){let n=r.createElement("div");n.className="dr-overlay dr-overlay--gameover",n.setAttribute("role","group");let o=r.createElement("h2");o.className="dr-title",o.textContent=e.t("gameOverTitle");let a=r.createElement("p");a.className="dr-line dr-line--score",a.textContent=e.t("gameOverScore",{score:i.score});let h=r.createElement("div");if(h.className="dr-buttons",h.appendChild(Pt(r,e.t("restartButton"),i.onRestart,i.restartIcon)),n.append(o,a),i.showBest){let d=r.createElement("p");d.className="dr-line dr-line--score",d.textContent=e.t("gameOverBest",{score:i.best}),n.appendChild(d);}return n.appendChild(h),n}var Xr=100,Vr=["bg","fg","button_bg","button_text","button_hover","button_secondary_text","button_secondary_border","button_secondary_hover_bg","focus_ring"],Kr=1e3/30;function jt(r){let{container:e,bridge:i,ctx:n}=r,o=e.ownerDocument,a=o.defaultView??window,h=r.raf??a.requestAnimationFrame.bind(a),d=r.caf??a.cancelAnimationFrame.bind(a),p=Et(n?.locale),g=Ut(n?.skin??null),u=st(n),B=Ct(a),f=At(a,u.sound,Ht);if(!o.getElementById("dr-styles")){let t=o.createElement("style");t.id="dr-styles",t.textContent=It,o.head.appendChild(t);}let w=y("div","dr-root");w.setAttribute("lang",p.lang),w.setAttribute("role","application"),w.setAttribute("aria-label",p.t("ariaGame")),p.direction==="rtl"&&w.setAttribute("dir","rtl");let we=n?.skin?._theme==="dark"?"dark":"light",ve=we==="dark";w.dataset.theme=we,ir(w,n);let Se=kt(p.lang);Se&&w.style.setProperty("--dr-cjk",Se);let O=y("div","dr-stage"),Re=y("div","dr-world"),_e=y("div","dr-sky-day"),se=y("div","dr-sky-night"),Be=y("div","dr-ground-layer"),Ue=Ge(),Ee=Ge();Be.append(Ue,Ee);let ke=y("div","dr-obstacle-layer"),V=y("div","dr-entity dr-runner"),I=er(),Te=u.sound?tr():null,P=y("div","dr-overlay-host");Re.append(se,_e,Be,ke,V,I.root),O.append(Re,P),Te&&O.appendChild(Te);let S=rr(),ae=Tt(o);w.append(O,S.root,ae.element),e.appendChild(w);let x=new z(u),Y=new ee,R=new te,_="waiting",E=u.startSpeed,G=0,D=0,Ce=-1,j=false,ce=0,he=0,de=null,ue=0,le=false,Oe=1,L=new Map,Je=[],Ae=[],H=ve?ye("moon"):null;H&&(H.classList.add("dr-moon"),$(H,40,40),se.appendChild(H));function Me(){let t=O.getBoundingClientRect();t.width<=0||t.height<=0||(Oe=Math.min(t.width/m,t.height/U),w.style.setProperty("--dr-scale",String(Oe)));}let N=null;typeof a.ResizeObserver=="function"&&(N=new a.ResizeObserver(()=>{le||Me();}),N.observe(O)),Me();function Nt(){f.resume(),_==="waiting"?ge():_==="running"?pe():_==="crashed"&&He();}function pe(){_==="running"&&(x.startJump(E),f.jump());}function F(t){t&&f.resume(),_==="running"&&x.setDuck(t);}function De(t){t.code==="Space"||t.code==="ArrowUp"||t.code==="KeyW"?(t.preventDefault(),t.repeat||Nt()):(t.code==="ArrowDown"||t.code==="KeyS")&&(t.preventDefault(),F(true));}function Le(t){t.code==="ArrowDown"||t.code==="KeyS"?F(false):(t.code==="Space"||t.code==="ArrowUp"||t.code==="KeyW")&&x.endJump();}function Ft(t){f.resume(),_==="running"&&(t.preventDefault(),pe());}o.addEventListener("keydown",De),o.addEventListener("keyup",Le),O.addEventListener("pointerdown",Ft),O.addEventListener("pointerup",()=>x.endJump()),S.jump.addEventListener("pointerdown",t=>{t.preventDefault(),f.resume(),pe();}),S.jump.addEventListener("pointerup",()=>x.endJump()),S.jump.addEventListener("pointercancel",()=>x.endJump()),S.duck.addEventListener("pointerdown",t=>{t.preventDefault(),F(true);}),S.duck.addEventListener("pointerup",()=>F(false)),S.duck.addEventListener("pointercancel",()=>F(false));function $t(){P.replaceChildren(Yt(o,p,ge)),S.root.dataset.active="false",Ie();}function ge(){P.replaceChildren(),_="running",x.start(),ce=0,he=0,S.root.dataset.active="true",ae.say(p.t("announceStart"));}function He(){x.reset(),Y.reset(),R.reset(),L.forEach(t=>t.remove()),L.clear(),E=u.startSpeed,G=0,ge();}function Qt(){_="crashed",x.crash(),f.hit(),S.root.dataset.active="false";let t=oe(G),s=t>D;s&&(D=t);let c=Ot(t,u.passScore,Ce);c.pass&&(Ce=c.bestPassed,i.pass({score:c.score,durationMs:Math.round(ce)})),ae.say(s?p.t("announceNewBest",{score:t}):p.t("announceGameOver",{score:t})),P.replaceChildren(Gt(o,p,{score:t,best:D,showBest:u.showBest,restartIcon:g.restart,onRestart:He})),me(),Ie();}function Ie(){let t=P.querySelector("button");t instanceof HTMLButtonElement&&t.focus();}function Xt(t){ce+=t;let s=t/k;G+=E*s,E=Jt(E,u.maxSpeed,u.acceleration,s),x.update(t,E),Y.update(t,E,u),R.update(t,E,B);let c=oe(G);c>D&&(D=c);let Q=Math.floor(c/Xr);Q>he&&(he=Q,f.score()),Vt()&&Qt();}function Vt(){let t=We(x);for(let s of Y.obstacles)if(!(s.x>t.x+60||s.x+s.width<t.x-10)&&nt(t,{x:s.x,y:s.y,boxes:s.boxes}))return true;return false}function me(){Kt(),Zt(),zt(),qt();}function Kt(){let t=x.frame();$(V,t.width,t.height),J(V,t.x,t.y),Wt(V,t.sprite);}function Zt(){let t=new Set(Y.obstacles);for(let[s,c]of L)t.has(s)||(c.remove(),L.delete(s));for(let s of Y.obstacles){let c=L.get(s);c?s.typeId==="bird"&&c.dataset.frame!==String(s.frame)&&(c.innerHTML=Pe(s),c.dataset.frame=String(s.frame)):(c=y("div","dr-entity dr-obstacle"),$(c,s.width,s.height),c.innerHTML=Pe(s),c.dataset.frame=String(s.frame),ke.appendChild(c),L.set(s,c)),J(c,s.x,s.y);}}function Pe(t){return tt(t).map(s=>`<span class="dr-tile" style="position:absolute;left:${s.dx}px;top:0;width:${s.width}px;height:${s.height}px">${g[s.sprite]}</span>`).join("")}function zt(){J(Ue,R.groundX,0),J(Ee,R.groundX+m,0),je(Je,R.clouds.length,()=>{let t=ye("cloud");return $(t,46,14),_e.appendChild(t),t}),R.clouds.forEach((t,s)=>J(Je[s],t.x,t.y)),ve&&(je(Ae,R.stars.length,()=>{let t=ye("star");return $(t,9,9),se.appendChild(t),t}),R.stars.forEach((t,s)=>J(Ae[s],t.x,t.y)),H&&J(H,R.moon.x,R.moon.y));}function qt(){I.best.dataset.hidden=u.showBest?"false":"true",I.score.dataset.hidden=u.showScore?"false":"true",u.showBest&&(I.best.innerHTML=`<span class="label">${p.t("headerBest")}</span>${Ne(D)}`),u.showScore&&(I.score.innerHTML=`<span class="label">${p.t("headerScore")}</span>${Ne(oe(G))}`);}function Ye(t){if(le)return;let s=de===null?0:Math.min(Kr,t-de);de=t,_==="running"&&s>0&&Xt(s),_!=="crashed"&&me(),ue=h(Ye);}$t(),me(),ue=h(Ye);function y(t,s){let c=o.createElement(t);return c.className=s,c}function Ge(){let t=y("div","dr-ground-tile");return t.innerHTML=g.ground,t}function ye(t){let s=y("div","dr-entity");return s.innerHTML=g[t],s}function Wt(t,s){t.dataset.sprite!==s&&(t.dataset.sprite=s,t.innerHTML=g[s]);}function $(t,s,c){t.style.width=`${s}px`,t.style.height=`${c}px`;}function J(t,s,c){t.style.transform=`translate(${s}px, ${c}px)`;}function je(t,s,c){for(;t.length<s;)t.push(c());for(;t.length>s;)t.pop().remove();}function er(){let t=y("div","dr-hud"),s=y("span","dr-hud-best"),c=y("span","dr-hud-score");return t.append(s,c),{root:t,best:s,score:c}}function tr(){let t=o.createElement("button");return t.type="button",t.className="dr-sound",t.setAttribute("role","switch"),t.setAttribute("aria-checked","true"),t.setAttribute("aria-label",p.t("ariaSound")),t.innerHTML=g["sound-on"],t.addEventListener("pointerdown",s=>s.stopPropagation()),t.addEventListener("click",()=>{j=!j,f.resume(),f.setMuted(j),t.innerHTML=g[j?"sound-off":"sound-on"],t.setAttribute("aria-checked",j?"false":"true");}),t}function rr(){let t=y("div","dr-touch");t.dataset.active="false";let s=o.createElement("button");s.type="button",s.className="dr-touch-button dr-touch-duck",s.textContent=p.t("ariaDuck"),s.setAttribute("aria-label",p.t("ariaDuck"));let c=o.createElement("button");return c.type="button",c.className="dr-touch-button dr-touch-jump",c.textContent=p.t("ariaJump"),c.setAttribute("aria-label",p.t("ariaJump")),t.append(s,c),{root:t,jump:c,duck:s}}function Ne(t){return String(Math.max(0,t)).padStart(5,"0")}function ir(t,s){let c=s?.skin??null;if(c){for(let Q of Vr){let Fe=c[Q];typeof Fe=="string"&&t.style.setProperty(`--dr-${Q.replace(/_/g,"-")}`,Fe);}c._theme&&(t.dataset.skinTheme=c._theme);}}return function(){le=true,d(ue),o.removeEventListener("keydown",De),o.removeEventListener("keyup",Le),N&&(N.disconnect(),N=null),f.dispose(),w.remove();}}var Zr=re;$e(Zr,(r,e,i)=>jt({container:r,bridge:e,ctx:i}));})();
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@caputchin/game-dino-runner",
3
+ "version": "0.2.0",
4
+ "description": "Jump the cactus, duck the birds, survive the endless run. A Caputchin first-party game.",
5
+ "keywords": [
6
+ "caputchin",
7
+ "caputchin-game",
8
+ "captcha",
9
+ "runner",
10
+ "dino",
11
+ "game"
12
+ ],
13
+ "homepage": "https://github.com/Caputchin/caputchin-games#readme",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/Caputchin/caputchin-games",
17
+ "directory": "packages/dino-runner"
18
+ },
19
+ "license": "MIT",
20
+ "private": false,
21
+ "type": "module",
22
+ "files": [
23
+ "dist",
24
+ "caputchin.json",
25
+ "THIRD-PARTY-NOTICES.md"
26
+ ],
27
+ "sideEffects": [
28
+ "./dist/dino-runner.js"
29
+ ],
30
+ "devDependencies": {
31
+ "@caputchin/game-sdk": "^2.1.0",
32
+ "@types/node": "^20.0.0",
33
+ "happy-dom": "^15.0.0",
34
+ "tsup": "^8.0.0",
35
+ "typescript": "^5.4.0",
36
+ "vitest": "^2.0.0"
37
+ },
38
+ "scripts": {
39
+ "build": "tsup",
40
+ "dev": "tsup --watch",
41
+ "typecheck": "tsc --noEmit",
42
+ "test": "vitest run",
43
+ "test:watch": "vitest"
44
+ }
45
+ }