packing-bingo 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -25
- package/dist/{chunk-ZPZZDZUH.js → chunk-A5JVNVWS.js} +654 -115
- package/dist/core.cjs +658 -117
- package/dist/core.d.cts +134 -6
- package/dist/core.d.ts +134 -6
- package/dist/core.js +7 -3
- package/dist/index.cjs +675 -123
- package/dist/index.d.cts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +24 -9
- package/dist/packing-bingo.global.js +119 -41
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -27,7 +27,8 @@ __export(src_exports, {
|
|
|
27
27
|
bingoLines: () => bingoLines,
|
|
28
28
|
createPackingBingo: () => createPackingBingo,
|
|
29
29
|
en: () => en,
|
|
30
|
-
ja: () => ja
|
|
30
|
+
ja: () => ja,
|
|
31
|
+
speedBonus: () => speedBonus
|
|
31
32
|
});
|
|
32
33
|
module.exports = __toCommonJS(src_exports);
|
|
33
34
|
|
|
@@ -137,11 +138,27 @@ var ja = {
|
|
|
137
138
|
shuffle: "\u{1F500} \u30B7\u30E3\u30C3\u30D5\u30EB",
|
|
138
139
|
soundOn: "\u{1F50A} \u97F3\u3042\u308A",
|
|
139
140
|
soundOff: "\u{1F507} \u97F3\u306A\u3057",
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
141
|
+
readyTitle: "Are you ready?",
|
|
142
|
+
readyPrompt: "",
|
|
143
|
+
soundHint: "\u7AEF\u672B\u306E\u97F3\u91CF\u3092MAX\u306B",
|
|
144
|
+
vibrationHint: "\u304A\u3084\u3059\u307F\u30E2\u30FC\u30C9\u3092\u30AA\u30D5",
|
|
145
|
+
readyTap: "\u30BF\u30C3\u30D7\u3067\u30B9\u30BF\u30FC\u30C8",
|
|
146
|
+
readyGo: "START!",
|
|
147
|
+
time: "\u30BF\u30A4\u30E0",
|
|
148
|
+
statTime: "\u30AF\u30EA\u30A2\u30BF\u30A4\u30E0",
|
|
149
|
+
modeLabel: "\u30E2\u30FC\u30C9",
|
|
150
|
+
modeNormal: "\u901A\u5E38",
|
|
151
|
+
modeInfinite: "\u221E \u7121\u9650",
|
|
152
|
+
normalTitle: "NORMAL",
|
|
153
|
+
normalSub: "\u901A\u5E38\u30E2\u30FC\u30C9",
|
|
154
|
+
infiniteTitle: "INFINITE",
|
|
155
|
+
infiniteSub: "\u7121\u9650\u30D3\u30F3\u30B4",
|
|
156
|
+
infiniteRule: "\u305D\u308D\u3063\u305F\u5217\u306F\u6D88\u3048\u3066\u3001\u307E\u305F\u57CB\u3081\u3089\u308C\u308B\u3002\u4F55\u5EA6\u3067\u3082\u30D3\u30F3\u30B4\uFF01",
|
|
157
|
+
finish: "\u{1F3C1} FINISH",
|
|
158
|
+
finishTitle: "\u30D5\u30A3\u30CB\u30C3\u30B7\u30E5\uFF01",
|
|
159
|
+
finishStamp: "\u221E",
|
|
160
|
+
finishTime: "\u30D7\u30EC\u30A4\u30BF\u30A4\u30E0",
|
|
161
|
+
again: "\u3082\u3046\u4E00\u5EA6",
|
|
145
162
|
stamp: "OK",
|
|
146
163
|
free: "FREE",
|
|
147
164
|
reachBadge: "\u30EA\u30FC\u30C1",
|
|
@@ -152,15 +169,15 @@ var ja = {
|
|
|
152
169
|
bannerNth: (n) => `${n} \u30E9\u30A4\u30F3\u76EE\uFF01`,
|
|
153
170
|
bannerMulti: (n) => `\u4E00\u6C17\u306B ${n} \u30E9\u30A4\u30F3\uFF01`,
|
|
154
171
|
statusStart: () => "",
|
|
155
|
-
statusLines: (
|
|
172
|
+
statusLines: () => "",
|
|
156
173
|
statusReach: () => "",
|
|
157
|
-
statusDone: "
|
|
174
|
+
statusDone: "",
|
|
158
175
|
perfectTag: "\u{1F3C6} PERFECT",
|
|
159
176
|
perfectStamp: "\u6E96\u5099\n\u5B8C\u4E86",
|
|
160
177
|
perfectTitle: "\u30D1\u30FC\u30D5\u30A7\u30AF\u30C8\uFF01",
|
|
161
|
-
perfectBody:
|
|
162
|
-
\u5FD8\u308C\u7269\u30BC\u30ED\u3067\u3001\u3044\u3063\u3066\u3089\u3063\u3057\u3083\u3044\uFF01`,
|
|
178
|
+
perfectBody: "",
|
|
163
179
|
perfectBonus: (n) => `PERFECT BONUS +${n}`,
|
|
180
|
+
speedBonus: (n) => `SPEED BONUS +${n.toLocaleString()}`,
|
|
164
181
|
statScore: "SCORE",
|
|
165
182
|
statCombo: "MAX COMBO",
|
|
166
183
|
statLines: "BINGO",
|
|
@@ -183,11 +200,27 @@ var en = {
|
|
|
183
200
|
shuffle: "\u{1F500} Shuffle",
|
|
184
201
|
soundOn: "\u{1F50A} Sound on",
|
|
185
202
|
soundOff: "\u{1F507} Muted",
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
203
|
+
readyTitle: "Are you ready?",
|
|
204
|
+
readyPrompt: "",
|
|
205
|
+
soundHint: "Turn your volume to MAX",
|
|
206
|
+
vibrationHint: "Turn off Do Not Disturb",
|
|
207
|
+
readyTap: "Tap to start",
|
|
208
|
+
readyGo: "GO!",
|
|
209
|
+
time: "Time",
|
|
210
|
+
statTime: "Time",
|
|
211
|
+
modeLabel: "Mode",
|
|
212
|
+
modeNormal: "Normal",
|
|
213
|
+
modeInfinite: "\u221E Infinite",
|
|
214
|
+
normalTitle: "NORMAL",
|
|
215
|
+
normalSub: "Normal mode",
|
|
216
|
+
infiniteTitle: "INFINITE",
|
|
217
|
+
infiniteSub: "Infinite bingo",
|
|
218
|
+
infiniteRule: "Completed lines clear, so you can fill them again. Bingo forever!",
|
|
219
|
+
finish: "\u{1F3C1} FINISH",
|
|
220
|
+
finishTitle: "Finish!",
|
|
221
|
+
finishStamp: "\u221E",
|
|
222
|
+
finishTime: "Time played",
|
|
223
|
+
again: "Play again",
|
|
191
224
|
stamp: "OK",
|
|
192
225
|
free: "FREE",
|
|
193
226
|
reachBadge: "REACH",
|
|
@@ -198,15 +231,15 @@ var en = {
|
|
|
198
231
|
bannerNth: (n) => `Line ${n}!`,
|
|
199
232
|
bannerMulti: (n) => `${n} lines at once!`,
|
|
200
233
|
statusStart: () => "",
|
|
201
|
-
statusLines: (
|
|
234
|
+
statusLines: () => "",
|
|
202
235
|
statusReach: () => "",
|
|
203
|
-
statusDone: "
|
|
236
|
+
statusDone: "",
|
|
204
237
|
perfectTag: "\u{1F3C6} PERFECT",
|
|
205
238
|
perfectStamp: "ALL\nSET",
|
|
206
239
|
perfectTitle: "Perfect!",
|
|
207
|
-
perfectBody:
|
|
208
|
-
Have a great trip!`,
|
|
240
|
+
perfectBody: "",
|
|
209
241
|
perfectBonus: (n) => `PERFECT BONUS +${n}`,
|
|
242
|
+
speedBonus: (n) => `SPEED BONUS +${n.toLocaleString()}`,
|
|
210
243
|
statScore: "SCORE",
|
|
211
244
|
statCombo: "MAX COMBO",
|
|
212
245
|
statLines: "BINGO",
|
|
@@ -347,6 +380,36 @@ function createSound(isMuted) {
|
|
|
347
380
|
tone(hz(7), 0.34, 0.12, "triangle", 0.14);
|
|
348
381
|
tone(hz(12), 0.42, 0.2, "triangle", 0.14);
|
|
349
382
|
},
|
|
383
|
+
count(go) {
|
|
384
|
+
if (go) {
|
|
385
|
+
tone(hz(12), 0, 0.5, "square", 0.07);
|
|
386
|
+
tone(hz(24), 0, 0.5, "triangle", 0.14);
|
|
387
|
+
noise(0, 0.2, 0.12, 3e3);
|
|
388
|
+
} else {
|
|
389
|
+
tone(hz(5), 0, 0.14, "square", 0.07);
|
|
390
|
+
tone(hz(17), 0, 0.14, "triangle", 0.12);
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
mode(infinite) {
|
|
394
|
+
if (infinite) {
|
|
395
|
+
tone(196, 0, 0.42, "sine", 0.12, 784);
|
|
396
|
+
[0, 2, 4, 7, 9, 12, 14, 16, 19, 21, 24].forEach((s, i) => tone(hz(s - 12), i * 0.032, 0.16, "triangle", 0.07));
|
|
397
|
+
[12, 16, 19, 24].forEach((s) => tone(hz(s), 0.38, 1.1, "sine", 0.07));
|
|
398
|
+
tone(hz(31), 0.38, 0.9, "triangle", 0.03);
|
|
399
|
+
noise(0, 0.5, 0.07, 6500);
|
|
400
|
+
noise(0.36, 0.7, 0.05, 9e3);
|
|
401
|
+
} else {
|
|
402
|
+
tone(hz(7), 0, 0.1, "square", 0.05);
|
|
403
|
+
tone(hz(0), 0.1, 0.28, "triangle", 0.14);
|
|
404
|
+
tone(160, 0.1, 0.2, "sine", 0.26, 55);
|
|
405
|
+
noise(0.1, 0.12, 0.14, 1400);
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
clear(squares) {
|
|
409
|
+
const n = Math.max(1, Math.min(8, squares));
|
|
410
|
+
for (let i = 0; i < n; i++) tone(step(SCALE.length - 1 - i * 2), i * 0.035, 0.18, "sine", 0.07);
|
|
411
|
+
noise(0, 0.3, 0.08, 7e3);
|
|
412
|
+
},
|
|
350
413
|
stop,
|
|
351
414
|
close() {
|
|
352
415
|
stop();
|
|
@@ -389,7 +452,10 @@ function createHaptics(enabled) {
|
|
|
389
452
|
const current = ++generation;
|
|
390
453
|
const tier = Math.min(3, Math.floor(combo / 3));
|
|
391
454
|
const lead = Math.max(80, impactAt - 40);
|
|
392
|
-
const pattern = moment === "off" ? 35 : moment === "perfect" ? [160, 45, 100] : moment === "bingo" ? lines >= 3 ? [lead, 40, 500, 50, 650, 50, 850] : lines === 2 ? [lead, 40, 500, 50, 650] : [lead, 40, 500] : moment === "reach" ? [140, 40, 240] : moment === "shuffle" ? [90, 35, 90, 35, 220] :
|
|
455
|
+
const pattern = moment === "off" ? 35 : moment === "perfect" ? [160, 45, 100] : moment === "bingo" ? lines >= 3 ? [lead, 40, 500, 50, 650, 50, 850] : lines === 2 ? [lead, 40, 500, 50, 650] : [lead, 40, 500] : moment === "reach" ? [140, 40, 240] : moment === "shuffle" ? [90, 35, 90, 35, 220] : moment === "count" ? 60 : moment === "go" ? 260 : (
|
|
456
|
+
// Switching modes: a quick rising flutter into a hold, or one firm stamp.
|
|
457
|
+
moment === "infinite" ? [25, 30, 35, 30, 45, 30, 220] : moment === "normal" ? [50, 70, 160] : moment === "finish" ? [120, 40, 120, 40, 320] : tier ? [100 + tier * 20, 15, 80 + tier * 20] : 90
|
|
458
|
+
);
|
|
393
459
|
if (!send(pattern)) return;
|
|
394
460
|
return (beat) => {
|
|
395
461
|
if (generation === current && owner === token) send([...beat]);
|
|
@@ -440,24 +506,17 @@ var CSS = `
|
|
|
440
506
|
.pkb-board.pkb-punch{animation:pkb-punch .4s cubic-bezier(.3,1.6,.5,1)}
|
|
441
507
|
@keyframes pkb-punch{30%{transform:scale(1.018)}}
|
|
442
508
|
.pkb-head{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px 16px;margin-bottom:18px}
|
|
443
|
-
.pkb-
|
|
444
|
-
.pkb-play-dialog:not([open]){display:none}
|
|
445
|
-
.pkb-play-dialog::backdrop{background:rgba(42,31,20,.6)}
|
|
446
|
-
.pkb-play-dialog h3{margin-bottom:16px;font-size:22px;font-weight:800;line-height:1.4}
|
|
447
|
-
.pkb-play-dialog p{margin-bottom:12px;font-size:15px;font-weight:700;line-height:1.7;overflow-wrap:anywhere}
|
|
448
|
-
.pkb-play-actions{display:grid;gap:10px;margin-top:22px}
|
|
449
|
-
.pkb-play-dialog button{padding:12px 16px;border:2px solid var(--pk-ink);border-radius:12px;color:var(--pk-ink);font-size:15px;font-weight:800;line-height:1.5;cursor:pointer}
|
|
450
|
-
.pkb-play-start{background:var(--pk-sun)}
|
|
451
|
-
.pkb-play-skip{background:transparent}
|
|
452
|
-
.pkb-play-dialog button:focus-visible{outline:3px solid var(--pk-ink);outline-offset:3px}
|
|
453
|
-
.pkb-play-dialog button:hover{filter:brightness(.96)}
|
|
454
|
-
.pkb-tags{display:flex;gap:8px;align-items:center}
|
|
509
|
+
.pkb-tags{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
|
|
455
510
|
.pkb-kicker{display:inline-block;background:var(--pk-ink);color:var(--pk-sun);font-weight:800;font-size:11px;letter-spacing:.14em;padding:4px 10px;border-radius:999px}
|
|
456
511
|
.pkb-kicker:empty{display:none}
|
|
457
512
|
.pkb-kicker.pkb-gold{color:#fff;background:linear-gradient(90deg,#E8473B,#FF9F1C,#1FA971,#2F6FED,#E8473B);background-size:200% 100%;animation:pkb-rb 2s linear infinite}
|
|
458
513
|
@keyframes pkb-rb{to{background-position:-200% 0}}
|
|
459
514
|
.pkb button{font-family:var(--pk-body)}
|
|
460
515
|
.pkb-snd{appearance:none;border:2px solid var(--pk-ink);background:var(--pk-paper);border-radius:999px;padding:2px 9px;font-weight:800;font-size:11px;line-height:1.4;color:var(--pk-ink);cursor:pointer}
|
|
516
|
+
.pkb-watch{display:inline-block;min-width:5.4em;padding:3px 10px 2px;border-radius:999px;background:var(--pk-ink);color:var(--pk-paper);font-family:var(--pk-display);font-size:13px;line-height:1.3;text-align:center;font-variant-numeric:tabular-nums;letter-spacing:.03em}
|
|
517
|
+
.pkb-watch::before{content:"\u23F1 ";font-family:var(--pk-body)}
|
|
518
|
+
.pkb-watch.pkb-run{color:var(--pk-sun)}
|
|
519
|
+
.pkb-watch.pkb-done{background:var(--pk-red);color:#fff}
|
|
461
520
|
.pkb-title{font-family:var(--pk-display);font-weight:400;font-size:34px;line-height:1.15;margin-top:8px;letter-spacing:.02em;color:inherit}
|
|
462
521
|
.pkb-sub{font-weight:800;font-size:13px;margin-top:4px;word-break:auto-phrase}
|
|
463
522
|
.pkb-side{display:flex;flex-direction:column;gap:8px;flex:none}
|
|
@@ -480,7 +539,7 @@ var CSS = `
|
|
|
480
539
|
.pkb-gw{position:relative}
|
|
481
540
|
.pkb-grid{display:grid;grid-template-columns:repeat(var(--pk-n),minmax(0,1fr));gap:calc(12px*var(--pk-d))}
|
|
482
541
|
.pkb-tile{position:relative;min-width:0;place-self:stretch;aspect-ratio:1/.82;background:var(--pk-paper);border:2.5px solid var(--pk-ink);border-radius:calc(16px*var(--pk-d));box-shadow:4px 4px 0 var(--pk-ink);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:calc(6px*var(--pk-d));cursor:pointer;user-select:none;-webkit-tap-highlight-color:transparent;transition:transform .1s,box-shadow .1s,background-color .25s}
|
|
483
|
-
.pkb-tile:active:not(.pkb-free){transform:translate(3px,3px);box-shadow:1px 1px 0 var(--pk-ink)}
|
|
542
|
+
.pkb-tile:active:not(.pkb-free):not(.pkb-lock){transform:translate(3px,3px);box-shadow:1px 1px 0 var(--pk-ink)}
|
|
484
543
|
.pkb-tile input{position:absolute;opacity:0;pointer-events:none;width:1px;height:1px}
|
|
485
544
|
.pkb-tile:has(input:focus-visible){outline:3px solid var(--pk-ink);outline-offset:3px}
|
|
486
545
|
.pkb-emo{font-size:max(14px,calc(32px*var(--pk-s)));line-height:1;transition:opacity .2s}
|
|
@@ -488,6 +547,7 @@ var CSS = `
|
|
|
488
547
|
.pkb-stamp{position:absolute;left:50%;top:50%;width:64%;aspect-ratio:1;border-radius:50%;border:3px solid var(--pk-red);box-shadow:inset 0 0 0 2px var(--pk-paper),inset 0 0 0 4px var(--pk-red);color:var(--pk-red);display:grid;place-items:center;font-family:var(--pk-display);font-size:max(9px,calc(20px*var(--pk-s)));letter-spacing:.04em;opacity:0;transform:translate(-50%,-50%) rotate(-16deg) scale(1.7);transition:transform .25s cubic-bezier(.3,1.5,.5,1),opacity .15s;pointer-events:none}
|
|
489
548
|
.pkb-tile.pkb-on .pkb-stamp{opacity:.9;transform:translate(-50%,-50%) rotate(-16deg) scale(1)}
|
|
490
549
|
.pkb-tile.pkb-on .pkb-emo,.pkb-tile.pkb-on .pkb-lbl{opacity:.35}
|
|
550
|
+
.pkb-tile.pkb-lock{cursor:default}
|
|
491
551
|
.pkb-tile.pkb-free{cursor:default;container-type:inline-size}
|
|
492
552
|
.pkb-tile.pkb-free .pkb-stamp{width:72%;opacity:1;transition:none;font-size:17cqi;letter-spacing:.02em;line-height:1;text-align:center;background:var(--pk-red);color:var(--pk-paper);border-color:var(--pk-red);box-shadow:inset 0 0 0 2px var(--pk-red),inset 0 0 0 4px var(--pk-paper)}
|
|
493
553
|
.pkb-tile.pkb-line{background-color:var(--pk-mint)}
|
|
@@ -515,10 +575,11 @@ var CSS = `
|
|
|
515
575
|
.pkb-foot{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px 10px;margin-top:18px;font-weight:800;font-size:13px;word-break:auto-phrase}
|
|
516
576
|
.pkb-foot>span{flex:1 1 14em}
|
|
517
577
|
.pkb-foot>span:empty{flex-basis:0}
|
|
518
|
-
.pkb-acts{display:flex;gap:8px;flex:
|
|
578
|
+
.pkb-acts{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px;flex:0 1 auto;max-width:100%;margin-left:auto}
|
|
519
579
|
.pkb-reset,.pkb-shuf{appearance:none;flex:none;border:2px solid var(--pk-ink);background:transparent;border-radius:999px;padding:5px 12px;font-weight:800;font-size:12px;line-height:1.4;color:var(--pk-ink);cursor:pointer}
|
|
520
580
|
.pkb-shuf{background:var(--pk-paper);box-shadow:2px 2px 0 var(--pk-ink)}
|
|
521
|
-
.pkb-shuf:active{transform:translate(2px,2px);box-shadow:none}
|
|
581
|
+
.pkb-shuf:active:not(:disabled){transform:translate(2px,2px);box-shadow:none}
|
|
582
|
+
.pkb-shuf:disabled{cursor:default;opacity:.4}
|
|
522
583
|
.pkb-flash{position:absolute;inset:0;z-index:7;background:#fff;opacity:0;pointer-events:none}
|
|
523
584
|
.pkb-flash.pkb-go{animation:pkb-flash .4s ease-out}
|
|
524
585
|
@keyframes pkb-flash{0%{opacity:.8}100%{opacity:0}}
|
|
@@ -535,8 +596,33 @@ var CSS = `
|
|
|
535
596
|
.pkb-float.pkb-big{font-size:clamp(14px,5.6cqi,34px);color:#FFE066}
|
|
536
597
|
.pkb-float.pkb-rch{font-size:20px;color:var(--pk-red);-webkit-text-stroke:4px var(--pk-paper)}
|
|
537
598
|
@keyframes pkb-rise{0%{opacity:0;transform:translate(-50%,-20%) scale(.4)}18%{opacity:1;transform:translate(-50%,-60%) scale(1.2)}30%{transform:translate(-50%,-70%) scale(1)}75%{opacity:1}100%{opacity:0;transform:translate(-50%,-190%) scale(1)}}
|
|
599
|
+
.pkb-ready{appearance:none;position:absolute;inset:0;z-index:12;display:grid;place-items:center;padding:20px;border:0;border-radius:inherit;background:rgba(42,31,20,.5);color:var(--pk-ink);font:inherit;text-align:center;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background-color .35s}
|
|
600
|
+
.pkb-ready[hidden]{display:none}
|
|
601
|
+
.pkb-ready:focus-visible{outline:3px solid var(--pk-ink);outline-offset:-6px}
|
|
602
|
+
.pkb-ready.pkb-gone{pointer-events:none;background-color:transparent}
|
|
603
|
+
.pkb-board.pkb-waiting .pkb-snd{position:relative;z-index:13}
|
|
604
|
+
.pkb-ready-in,.pkb-count{grid-area:1/1}
|
|
605
|
+
.pkb-ready-in{display:flex;flex-direction:column;align-items:center;width:min(400px,100%);padding:24px 22px 22px;border:2.5px solid var(--pk-ink);border-radius:22px;background:var(--pk-paper)}
|
|
606
|
+
.pkb-ready.pkb-counting .pkb-ready-in{visibility:hidden}
|
|
607
|
+
.pkb-ready-t{font-family:var(--pk-display);font-weight:400;font-size:clamp(26px,7.5cqi,36px);line-height:1.15;color:var(--pk-red)}
|
|
608
|
+
.pkb-ready-q{margin-top:6px;font-weight:800;font-size:18px;line-height:1.4}
|
|
609
|
+
.pkb-ready-l{display:flex;flex-direction:column;gap:4px;width:100%;margin-top:14px;padding:12px 14px;border-radius:14px;background:rgba(42,31,20,.07);font-weight:800;font-size:16px;line-height:1.6;text-align:left;overflow-wrap:anywhere;word-break:auto-phrase}
|
|
610
|
+
.pkb-ready-l>span::before{content:"\u30FB"}
|
|
611
|
+
.pkb-ready-go{margin-top:18px;padding:10px 30px;border:2.5px solid var(--pk-ink);border-radius:999px;background:var(--pk-sun);font-weight:800;font-size:16px;line-height:1.4;box-shadow:3px 3px 0 var(--pk-ink);animation:pkb-beat 1.2s ease-in-out infinite}
|
|
612
|
+
@keyframes pkb-beat{50%{transform:scale(1.06)}}
|
|
613
|
+
.pkb-count{font-family:var(--pk-display);font-weight:400;line-height:1.1;paint-order:stroke fill;font-size:clamp(90px,34cqi,200px);color:var(--pk-sun);-webkit-text-stroke:5px var(--pk-ink);text-shadow:7px 7px 0 var(--pk-ink);opacity:0;pointer-events:none}
|
|
614
|
+
.pkb-count.pkb-go{font-size:clamp(48px,17cqi,120px);color:var(--pk-red);white-space:nowrap}
|
|
615
|
+
.pkb-count.pkb-tick{animation:pkb-cnt 1s cubic-bezier(.2,1.4,.4,1) forwards}
|
|
616
|
+
.pkb-count.pkb-go.pkb-tick{animation:pkb-cnt-go .75s cubic-bezier(.2,1.4,.4,1) forwards}
|
|
617
|
+
@keyframes pkb-cnt{0%{opacity:0;transform:scale(2.2)}18%{opacity:1;transform:scale(.92)}30%{transform:scale(1)}75%{opacity:1}100%{opacity:0;transform:scale(.8)}}
|
|
618
|
+
@keyframes pkb-cnt-go{0%{opacity:0;transform:scale(.4) rotate(-8deg)}25%{opacity:1;transform:scale(1.12) rotate(-5deg)}40%{transform:scale(1) rotate(-5deg)}70%{opacity:1}100%{opacity:0;transform:scale(1.5) rotate(-5deg)}}
|
|
619
|
+
.pkb-ptime{display:inline-flex;align-items:baseline;gap:8px;margin:-4px 0 14px;padding:5px 16px 4px;border-radius:999px;background:var(--pk-ink);color:var(--pk-sun)}
|
|
620
|
+
.pkb-ptime[hidden]{display:none}
|
|
621
|
+
.pkb-ptime small{font-size:11px;font-weight:800;letter-spacing:.08em;color:#fff}
|
|
622
|
+
.pkb-ptime b{font-family:var(--pk-display);font-weight:400;font-size:22px;line-height:1.2;font-variant-numeric:tabular-nums}
|
|
538
623
|
.pkb-perfect{position:absolute;inset:0;z-index:8;display:grid;place-items:center;padding:20px;background:rgba(42,31,20,.5);opacity:0;visibility:hidden;transition:opacity .3s,visibility .3s}
|
|
539
|
-
.
|
|
624
|
+
/* Visible at once when opening (focus can't land on a hidden dialog); hidden only after the fade when closing. */
|
|
625
|
+
.pkb-perfect.pkb-show{opacity:1;visibility:visible;transition:opacity .3s,visibility 0s}
|
|
540
626
|
.pkb-card{width:min(400px,100%);background:var(--pk-paper);border:2.5px solid var(--pk-ink);border-radius:22px;box-shadow:6px 6px 0 var(--pk-ink);padding:26px 22px 20px;text-align:center;transform:scale(.6) translateY(30px);transition:transform .45s cubic-bezier(.3,1.5,.5,1)}
|
|
541
627
|
.pkb-perfect.pkb-show .pkb-card{transform:none}
|
|
542
628
|
.pkb-medal{width:124px;height:124px;margin:0 auto 12px;border-radius:50%;border:5px solid var(--pk-red);box-shadow:inset 0 0 0 4px var(--pk-paper),inset 0 0 0 7px var(--pk-red);color:var(--pk-red);display:grid;place-content:center;font-family:var(--pk-display);font-size:26px;line-height:1.15;white-space:pre-line;opacity:0;transform:rotate(-14deg) scale(2.6)}
|
|
@@ -546,6 +632,7 @@ var CSS = `
|
|
|
546
632
|
@keyframes pkb-slam{to{opacity:1;transform:rotate(-14deg) scale(1)}}
|
|
547
633
|
.pkb-ptitle{font-family:var(--pk-display);font-weight:400;font-size:30px;line-height:1.3;color:inherit}
|
|
548
634
|
.pkb-pbody{font-weight:800;font-size:13.5px;line-height:1.6;margin:6px 0 14px;white-space:pre-line;word-break:auto-phrase}
|
|
635
|
+
.pkb-pbody:empty{margin:0 0 14px}
|
|
549
636
|
.pkb-stats{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:16px}
|
|
550
637
|
.pkb-stats>div{border:2px solid var(--pk-ink);border-radius:12px;padding:6px 4px 5px;background:#fff}
|
|
551
638
|
.pkb-stats small{display:block;font-size:9.5px;font-weight:800;letter-spacing:.1em}
|
|
@@ -555,10 +642,72 @@ var CSS = `
|
|
|
555
642
|
.pkb-fx{position:absolute;inset:0;width:100%;height:100%;z-index:10;pointer-events:none}
|
|
556
643
|
.pkb-demo{max-width:720px;margin:12px auto 4px;display:flex;flex-wrap:wrap;align-items:center;gap:8px;font-family:var(--pk-body);font-size:12px;color:#666}
|
|
557
644
|
.pkb-demo button{appearance:none;border:1px solid #ccc;background:#f3f3f3;color:#222;border-radius:999px;padding:5px 12px;font-size:12px;cursor:pointer}
|
|
558
|
-
|
|
559
|
-
|
|
645
|
+
.pkb-mode{position:relative;isolation:isolate;display:inline-grid;grid-auto-flow:column;grid-auto-columns:1fr;padding:2px;border-radius:999px;background:var(--pk-ink);transition:box-shadow .4s}
|
|
646
|
+
.pkb-mode::after{content:"";position:absolute;inset:0;z-index:2;border-radius:inherit;pointer-events:none;background:linear-gradient(110deg,transparent 30%,rgba(255,255,255,.75) 45%,transparent 60%) no-repeat;background-size:250% 100%;background-position:200% 0;opacity:0}
|
|
647
|
+
.pkb-mode button{appearance:none;position:relative;z-index:1;border:0;background:none;border-radius:999px;padding:3px 11px 2px;font-weight:800;font-size:11px;line-height:1.4;letter-spacing:.04em;white-space:nowrap;color:var(--pk-paper);opacity:.72;cursor:pointer;transition:color .3s,opacity .3s;-webkit-tap-highlight-color:transparent}
|
|
648
|
+
.pkb-mode button[aria-checked="true"]{opacity:1;color:var(--pk-ink);cursor:default}
|
|
649
|
+
.pkb-mode button:focus-visible{outline:2px solid var(--pk-paper);outline-offset:-4px}
|
|
650
|
+
.pkb-mode-th{position:absolute;top:2px;bottom:2px;left:2px;width:calc(50% - 2px);border-radius:999px;background:var(--pk-sun);transition:transform .55s cubic-bezier(.3,1.45,.45,1)}
|
|
651
|
+
.pkb.pkb-inf .pkb-mode{box-shadow:0 0 14px 1px rgba(155,93,229,.6)}
|
|
652
|
+
.pkb.pkb-inf .pkb-mode::after{opacity:1;animation:pkb-glint 3.2s ease-in-out infinite}
|
|
653
|
+
.pkb.pkb-inf .pkb-mode-th{transform:translateX(100%);background:linear-gradient(90deg,#FF9F1C,#E8473B,#9B5DE5,#2F6FED,#1FA971,#FF9F1C) 0 0/200% 100%;animation:pkb-rb 2.4s linear infinite}
|
|
654
|
+
.pkb.pkb-inf .pkb-mode button[data-m="infinite"]{color:#fff;text-shadow:0 1px 1px rgba(42,31,20,.55)}
|
|
655
|
+
.pkb-board.pkb-waiting .pkb-mode,.pkb-board.pkb-switching .pkb-mode{z-index:15}
|
|
656
|
+
.pkb-board.pkb-switching .pkb-fx{z-index:16}
|
|
657
|
+
.pkb.pkb-inf .pkb-board{border-color:transparent;background:radial-gradient(rgba(42,31,20,.12) 1.3px,transparent 1.6px) 0 0/16px 16px padding-box,linear-gradient(var(--pk-sun),var(--pk-sun)) padding-box,linear-gradient(120deg,#FF9F1C,#E8473B 22%,#9B5DE5 45%,#2F6FED 68%,#1FA971 88%,#FF9F1C) border-box;box-shadow:0 0 0 2px var(--pk-ink),0 10px 34px -10px rgba(155,93,229,.7)}
|
|
658
|
+
.pkb.pkb-inf .pkb-demo [data-r="dP"],.pkb.pkb-inf .pkb-demo [data-r="dR"]{display:none}
|
|
659
|
+
.pkb-ready-b,.pkb-ready-r{display:none}
|
|
660
|
+
.pkb.pkb-inf .pkb-ready-in{border-color:transparent;background:linear-gradient(var(--pk-paper),var(--pk-paper)) padding-box,linear-gradient(120deg,#FF9F1C,#E8473B 22%,#9B5DE5 45%,#2F6FED 68%,#1FA971 88%,#FF9F1C) border-box;box-shadow:0 0 0 2px var(--pk-ink),0 0 32px rgba(155,93,229,.5)}
|
|
661
|
+
.pkb.pkb-inf .pkb-ready-b{display:inline-block;margin-bottom:10px;padding:4px 14px 3px;border-radius:999px;background:linear-gradient(90deg,#FF9F1C,#E8473B,#9B5DE5,#2F6FED,#1FA971,#FF9F1C) 0 0/200% 100%;color:#fff;font-weight:800;font-size:13px;line-height:1.4;letter-spacing:.08em;text-shadow:0 1px 1px rgba(42,31,20,.5);box-shadow:0 0 14px rgba(155,93,229,.55);animation:pkb-rb 2.4s linear infinite}
|
|
662
|
+
.pkb.pkb-inf .pkb-ready-r{display:block;margin-top:8px;font-weight:800;font-size:15px;line-height:1.5;word-break:auto-phrase}
|
|
663
|
+
.pkb-tile.pkb-clr{animation:pkb-clr .62s var(--pkb-cd,0ms) both}
|
|
664
|
+
@keyframes pkb-clr{0%{transform:perspective(600px) rotateY(0);animation-timing-function:cubic-bezier(.55,0,.9,.45)}42%{transform:perspective(600px) rotateY(90deg) scale(.9)}42.01%{transform:perspective(600px) rotateY(-90deg) scale(.9);animation-timing-function:cubic-bezier(.2,1.45,.4,1)}100%{transform:perspective(600px) rotateY(0)}}
|
|
665
|
+
.pkb-mfx{position:absolute;inset:0;z-index:14;display:grid;place-items:center;overflow:hidden;border-radius:inherit;pointer-events:none;visibility:hidden;opacity:0;text-align:center}
|
|
666
|
+
.pkb-mfx.pkb-show{visibility:visible;animation:pkb-mfx 1.7s ease-out forwards}
|
|
667
|
+
@keyframes pkb-mfx{0%{opacity:0}9%{opacity:1}72%{opacity:1}100%{opacity:0}}
|
|
668
|
+
.pkb-mfx-inf{background:radial-gradient(120% 95% at 50% 42%,#33205c 0%,#170f2e 52%,#08060f 100%)}
|
|
669
|
+
.pkb-mfx-n{background:var(--pk-sun) radial-gradient(rgba(42,31,20,.16) 1.3px,transparent 1.6px) 0 0/16px 16px}
|
|
670
|
+
.pkb-mfx::after{content:"";position:absolute;top:-30%;bottom:-30%;left:-50%;width:50%;background:linear-gradient(90deg,transparent,rgba(255,255,255,.55),transparent);transform:skewX(-18deg);opacity:0}
|
|
671
|
+
.pkb-mfx.pkb-show::after{animation:pkb-beam 1s cubic-bezier(.4,0,.2,1) .22s both}
|
|
672
|
+
@keyframes pkb-beam{0%{opacity:1;transform:skewX(-18deg) translateX(0)}100%{opacity:1;transform:skewX(-18deg) translateX(400%)}}
|
|
673
|
+
.pkb-mfx-glow{position:absolute;left:50%;top:46%;width:min(150cqi,1000px);aspect-ratio:1;translate:-50% -50%;border-radius:50%;background:conic-gradient(#FF9F1C,#E8473B,#9B5DE5,#2F6FED,#1FA971,#FF9F1C);-webkit-mask:radial-gradient(circle,transparent 14%,#000 30%,rgba(0,0,0,.4) 48%,transparent 68%);mask:radial-gradient(circle,transparent 14%,#000 30%,rgba(0,0,0,.4) 48%,transparent 68%);filter:blur(28px);opacity:0}
|
|
674
|
+
.pkb-mfx.pkb-show .pkb-mfx-glow{animation:pkb-spin 3s linear infinite,pkb-glow 1.7s ease-out both}
|
|
675
|
+
@keyframes pkb-glow{0%{opacity:0;scale:.35}30%{opacity:.8;scale:1}100%{opacity:.5;scale:1.12}}
|
|
676
|
+
.pkb-mfx i{position:absolute;left:var(--x);top:var(--y);width:calc(20px*var(--s));aspect-ratio:1;background:var(--c);clip-path:polygon(50% 0,62% 38%,100% 50%,62% 62%,50% 100%,38% 62%,0 50%,38% 38%);opacity:0}
|
|
677
|
+
.pkb-mfx.pkb-show i{animation:pkb-twk .85s ease-in-out var(--t) both}
|
|
678
|
+
@keyframes pkb-twk{0%{opacity:0;scale:0;rotate:0deg}45%{opacity:1;scale:1;rotate:70deg}100%{opacity:0;scale:.2;rotate:160deg}}
|
|
679
|
+
.pkb-mfx-in{position:relative;display:flex;flex-direction:column;align-items:center;padding:0 16px}
|
|
680
|
+
.pkb-mfx-sym{display:block;font-family:var(--pk-display);font-weight:400;line-height:1}
|
|
681
|
+
.pkb-mfx-inf .pkb-mfx-sym{font-size:clamp(110px,36cqi,230px);line-height:.9;padding:0 .06em;background:linear-gradient(90deg,#FFE066,#FF9F1C,#E8473B,#9B5DE5,#2F6FED,#1FA971,#FFE066) 0 0/200% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;filter:drop-shadow(0 0 22px rgba(155,93,229,.9)) drop-shadow(0 0 3px rgba(255,255,255,.8))}
|
|
682
|
+
.pkb-mfx.pkb-show.pkb-mfx-inf .pkb-mfx-sym{animation:pkb-sym 1.7s cubic-bezier(.2,1.25,.35,1) both,pkb-rb 1.7s linear both}
|
|
683
|
+
@keyframes pkb-sym{0%{opacity:0;transform:scale(.25) rotate(-30deg)}26%{opacity:1;transform:scale(1.1) rotate(4deg)}38%{transform:scale(1) rotate(0)}100%{opacity:1;transform:scale(1.06)}}
|
|
684
|
+
.pkb-mfx-n .pkb-mfx-sym{display:grid;place-items:center;width:clamp(110px,30cqi,170px);aspect-ratio:1;border-radius:50%;border:5px solid var(--pk-red);box-shadow:inset 0 0 0 4px var(--pk-sun),inset 0 0 0 7px var(--pk-red);color:var(--pk-red);font-size:clamp(34px,10cqi,56px);letter-spacing:.04em;transform:rotate(-14deg)}
|
|
685
|
+
.pkb-mfx.pkb-show.pkb-mfx-n .pkb-mfx-sym{animation:pkb-mslam .42s cubic-bezier(.5,0,.75,0) .08s both}
|
|
686
|
+
@keyframes pkb-mslam{from{opacity:0;transform:rotate(-14deg) scale(2.6)}to{opacity:1;transform:rotate(-14deg) scale(1)}}
|
|
687
|
+
.pkb-mfx-t{display:block;margin-top:6px;font-family:var(--pk-display);font-weight:400;font-size:clamp(24px,8.5cqi,52px);line-height:1.1;letter-spacing:.2em;margin-right:-.2em;white-space:nowrap}
|
|
688
|
+
.pkb-mfx-inf .pkb-mfx-t{color:#fff;text-shadow:0 0 18px rgba(155,93,229,.95),0 0 4px rgba(255,255,255,.9)}
|
|
689
|
+
.pkb-mfx-n .pkb-mfx-t{margin-top:16px;color:var(--pk-ink)}
|
|
690
|
+
.pkb-mfx.pkb-show .pkb-mfx-t{animation:pkb-track 1.7s cubic-bezier(.16,1,.3,1) .12s both}
|
|
691
|
+
@keyframes pkb-track{0%{opacity:0;letter-spacing:.9em;margin-right:-.9em;filter:blur(8px)}40%{opacity:1;letter-spacing:.2em;margin-right:-.2em;filter:blur(0)}100%{opacity:1;letter-spacing:.24em;margin-right:-.24em}}
|
|
692
|
+
.pkb-mfx-s{display:inline-block;margin-top:12px;padding:5px 16px 4px;border-radius:999px;font-weight:800;font-size:14px;line-height:1.4;letter-spacing:.06em}
|
|
693
|
+
.pkb-mfx-inf .pkb-mfx-s{color:#fff;background:rgba(255,255,255,.12);border:1.5px solid rgba(255,255,255,.4)}
|
|
694
|
+
.pkb-mfx-n .pkb-mfx-s{background:var(--pk-ink);color:var(--pk-sun)}
|
|
695
|
+
.pkb-mfx.pkb-show .pkb-mfx-s{animation:pkb-up .6s cubic-bezier(.2,1.3,.4,1) .4s both}
|
|
696
|
+
@keyframes pkb-up{from{opacity:0;transform:translateY(12px)}}
|
|
697
|
+
.pkb-finbtn{display:none;appearance:none;flex:none;border:2px solid var(--pk-ink);border-radius:999px;padding:5px 14px;font-weight:800;font-size:12px;line-height:1.4;letter-spacing:.04em;color:#fff;text-shadow:0 1px 1px rgba(42,31,20,.55);background:linear-gradient(90deg,#FF9F1C,#E8473B,#9B5DE5,#2F6FED,#1FA971,#FF9F1C) 0 0/200% 100%;box-shadow:2px 2px 0 var(--pk-ink),0 0 12px rgba(155,93,229,.45);cursor:pointer;animation:pkb-rb 2.4s linear infinite}
|
|
698
|
+
.pkb.pkb-inf .pkb-finbtn{display:inline-block}
|
|
699
|
+
.pkb-finbtn:active:not(:disabled){transform:translate(2px,2px);box-shadow:none}
|
|
700
|
+
.pkb-finbtn:disabled{cursor:default;opacity:.4;animation:none;box-shadow:2px 2px 0 var(--pk-ink)}
|
|
701
|
+
.pkb-board.pkb-over .pkb-grid{opacity:.55;filter:saturate(.5);transition:opacity .4s,filter .4s}
|
|
702
|
+
.pkb-pbtns{display:flex;flex-wrap:wrap;justify-content:center;gap:10px}
|
|
703
|
+
.pkb-perfect.pkb-fin .pkb-card{border-color:transparent;background:linear-gradient(var(--pk-paper),var(--pk-paper)) padding-box,linear-gradient(120deg,#FF9F1C,#E8473B 22%,#9B5DE5 45%,#2F6FED 68%,#1FA971 88%,#FF9F1C) border-box;box-shadow:0 0 0 2px var(--pk-ink),6px 6px 0 2px var(--pk-ink),0 0 40px rgba(155,93,229,.5)}
|
|
704
|
+
.pkb-perfect.pkb-fin .pkb-medal{border-color:transparent;background:linear-gradient(var(--pk-paper),var(--pk-paper)) padding-box,conic-gradient(#FF9F1C,#E8473B,#9B5DE5,#2F6FED,#1FA971,#FF9F1C) border-box;box-shadow:0 0 18px rgba(155,93,229,.55);color:#7B3FD0;font-size:clamp(44px,14cqi,64px);line-height:1;text-shadow:0 0 12px rgba(155,93,229,.45)}
|
|
705
|
+
.pkb-perfect.pkb-fin .pkb-again{background:linear-gradient(90deg,#FF9F1C,#E8473B,#9B5DE5,#2F6FED,#1FA971,#FF9F1C) 0 0/200% 100%;color:#fff;text-shadow:0 1px 1px rgba(42,31,20,.55);animation:pkb-rb 2.4s linear infinite}
|
|
706
|
+
.pkb-perfect.pkb-fin .pkb-close:not(.pkb-again){background:var(--pk-paper)}
|
|
707
|
+
@container (max-width:560px){.pkb-board{padding:18px 14px 16px}.pkb-head{flex-direction:column;align-items:stretch;gap:12px}.pkb-score{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}.pkb-pill,.pkb-pill.pkb-sc{min-width:0;padding:5px 4px 4px}.pkb-pill b{font-size:19px}.pkb-title{font-size:26px}.pkb-grid{gap:max(3px,calc(8px*var(--pk-d)))}.pkb-tile{border-radius:calc(12px*var(--pk-d));box-shadow:3px 3px 0 var(--pk-ink);gap:calc(3px*var(--pk-d));aspect-ratio:1/1}.pkb-emo{font-size:max(13px,calc(24px*var(--pk-s)))}.pkb-lbl{font-size:max(8.5px,calc(10.5px*var(--pk-s)));padding:0 calc(2px*var(--pk-d))}.pkb-stamp{font-size:max(9px,calc(14px*var(--pk-s)))}.pkb-tile.pkb-reach::after{font-size:9px;padding:2px 5px;top:-9px;right:-5px}.pkb-lines .pkb-u{stroke-width:calc(10px*var(--pk-d))}.pkb-lines .pkb-o{stroke-width:calc(5px*var(--pk-d))}.pkb-banner::before{width:380px;height:380px;margin:-190px 0 0 -190px}.pkb-banner span{font-size:12px}.pkb-medal{width:96px;height:96px;font-size:20px}.pkb-card{padding:20px 14px 16px}.pkb-ptitle{font-size:24px}.pkb-stats b{font-size:17px}.pkb-float.pkb-pt{font-size:17px}.pkb-float.pkb-cmb{font-size:13px}.pkb-ready-in{padding:20px 16px 18px}.pkb-ready-l{font-size:15px;padding:10px 12px}.pkb-ready-go{font-size:15px}.pkb-ptime b{font-size:19px}}
|
|
708
|
+
@container (max-width:400px){.pkb-board{padding:16px 10px 14px}.pkb-mode button{padding:3px 8px 2px}.pkb-grid{gap:max(3px,calc(6px*var(--pk-d)))}.pkb-lbl{font-size:max(8.5px,calc(10px*var(--pk-s)));padding:0 calc(1px*var(--pk-d))}.pkb-emo{font-size:max(12px,calc(22px*var(--pk-s)))}.pkb-stamp{font-size:max(9px,calc(13px*var(--pk-s)))}.pkb-foot{margin-top:14px;font-size:12px}.pkb-perfect,.pkb-ready{padding:12px}.pkb-stats{gap:6px}.pkb-stats>div{padding:6px 2px 5px}.pkb-stats small{letter-spacing:.02em}}
|
|
560
709
|
@container (max-width:320px){.pkb-board{padding-inline:8px}.pkb-grid{gap:max(3px,calc(5px*var(--pk-d)))}.pkb-lbl{font-size:max(8.5px,calc(9.5px*var(--pk-s)));padding:0}}
|
|
561
|
-
@media (prefers-reduced-motion:reduce){.pkb-board.pkb-shake,.pkb-board.pkb-punch,.pkb-flash.pkb-go,.pkb-tile.pkb-hop,.pkb-tile.pkb-squash,.pkb-tile.pkb-on .pkb-stamp.pkb-hit,.pkb-tile.pkb-reach,.pkb-tile.pkb-line::before,.pkb-pill.pkb-hot,.pkb-bump,.pkb-tile.pkb-reach::after,.pkb-banner::before,.pkb-lines .pkb-draw line,.pkb-kicker.pkb-gold{animation:none}.pkb-banner.pkb-show{animation:pkb-fade 1.8s forwards}.pkb-float{animation:pkb-fade 1s both}.pkb-perfect.pkb-show .pkb-medal{animation:none;opacity:1;transform:rotate(-14deg)}.pkb-card,.pkb-tile,.pkb-stamp{transition:none}}
|
|
710
|
+
@media (prefers-reduced-motion:reduce){.pkb-board.pkb-shake,.pkb-board.pkb-punch,.pkb-flash.pkb-go,.pkb-tile.pkb-hop,.pkb-tile.pkb-squash,.pkb-tile.pkb-on .pkb-stamp.pkb-hit,.pkb-tile.pkb-reach,.pkb-tile.pkb-line::before,.pkb-pill.pkb-hot,.pkb-bump,.pkb-tile.pkb-reach::after,.pkb-banner::before,.pkb-lines .pkb-draw line,.pkb-kicker.pkb-gold,.pkb-ready-go,.pkb.pkb-inf .pkb-mode-th,.pkb.pkb-inf .pkb-mode::after,.pkb.pkb-inf .pkb-ready-b,.pkb-tile.pkb-clr,.pkb-finbtn,.pkb-perfect.pkb-fin .pkb-again,.pkb-mfx.pkb-show.pkb-mfx-inf .pkb-mfx-sym,.pkb-mfx.pkb-show.pkb-mfx-n .pkb-mfx-sym,.pkb-mfx.pkb-show .pkb-mfx-t,.pkb-mfx.pkb-show .pkb-mfx-s,.pkb-mfx.pkb-show .pkb-mfx-glow,.pkb-mfx.pkb-show::after{animation:none}.pkb-count.pkb-tick,.pkb-count.pkb-go.pkb-tick{animation:pkb-fade 1s forwards}.pkb-mfx.pkb-show{animation:pkb-fade 1.4s forwards}.pkb-mode-th{transition:none}.pkb-ready{transition:none}.pkb-banner.pkb-show{animation:pkb-fade 1.8s forwards}.pkb-float{animation:pkb-fade 1s both}.pkb-perfect.pkb-show .pkb-medal{animation:none;opacity:1;transform:rotate(-14deg)}.pkb-card,.pkb-tile,.pkb-stamp{transition:none}}
|
|
562
711
|
@keyframes pkb-fade{0%,100%{opacity:0}15%,80%{opacity:1}}
|
|
563
712
|
`;
|
|
564
713
|
|
|
@@ -568,7 +717,6 @@ var CONFETTI = ["#E8473B", "#2F6FED", "#1FA971", "#FFFDF6", "#2A1F14", "#FF9F1C"
|
|
|
568
717
|
var COMBO_COLORS = ["#FF9F1C", "#1FA971", "#2F6FED", "#9B5DE5", "#E8473B"];
|
|
569
718
|
var COMBO_MS = 2500;
|
|
570
719
|
var MUTE_KEY = "packing-bingo-muted";
|
|
571
|
-
var playReady = /* @__PURE__ */ new WeakSet();
|
|
572
720
|
var ORDER_PREFIX = "packing-bingo-order:";
|
|
573
721
|
var esc = (s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
574
722
|
function readStore(key) {
|
|
@@ -586,7 +734,22 @@ function writeStore(key, value) {
|
|
|
586
734
|
}
|
|
587
735
|
var glintDelay = (c, n) => `${+((Math.floor(c / n) + c % n) * 0.36 / Math.max(1, n - 1)).toFixed(3)}s`;
|
|
588
736
|
var lineKey = (l) => l.slice().sort((a, b) => a - b).join(",");
|
|
737
|
+
var COUNT_MS = 1e3;
|
|
738
|
+
var GO_MS = 750;
|
|
739
|
+
function formatTime(ms, digits = 1) {
|
|
740
|
+
const unit = 10 ** digits;
|
|
741
|
+
const t = Math.floor(Math.max(0, ms) / (1e3 / unit));
|
|
742
|
+
const frac = digits ? "." + String(t % unit).padStart(digits, "0") : "";
|
|
743
|
+
const sec = Math.floor(t / unit), s = sec % 60, m = Math.floor(sec / 60) % 60, h = Math.floor(sec / 3600);
|
|
744
|
+
const pad = (v) => String(v).padStart(2, "0");
|
|
745
|
+
return (h ? `${h}:${pad(m)}` : String(m)) + `:${pad(s)}${frac}`;
|
|
746
|
+
}
|
|
747
|
+
function speedBonus(items, ms) {
|
|
748
|
+
return Math.round(items * 1e6 / Math.max(ms, 1));
|
|
749
|
+
}
|
|
589
750
|
var FLY_MS = 560;
|
|
751
|
+
var CLEAR_MS = 1e3;
|
|
752
|
+
var SPARK = ["#FFFFFF", "#FFE066", "#FF9F1C", "#E8473B", "#9B5DE5", "#2F6FED", "#1FA971"];
|
|
590
753
|
function ensureStyles() {
|
|
591
754
|
if (document.getElementById("pkb-styles")) return;
|
|
592
755
|
const s = document.createElement("style");
|
|
@@ -594,7 +757,8 @@ function ensureStyles() {
|
|
|
594
757
|
s.textContent = CSS;
|
|
595
758
|
document.head.appendChild(s);
|
|
596
759
|
}
|
|
597
|
-
|
|
760
|
+
var opt = (v, fallback) => v ?? fallback ?? "";
|
|
761
|
+
function template(L, M, { demo, shuffle, vibrate, ready, modes }) {
|
|
598
762
|
const n = L.size;
|
|
599
763
|
const input = vibrate ? `<input type="checkbox" switch role="checkbox">` : `<input type="checkbox">`;
|
|
600
764
|
const tiles = Array.from({ length: n * n }, (_, c) => {
|
|
@@ -606,16 +770,15 @@ function template(L, M, { demo, shuffle, vibrate }) {
|
|
|
606
770
|
}).join("");
|
|
607
771
|
const scale = `--pk-n:${n};--pk-s:${+Math.min(1.35, 4 / n).toFixed(3)};--pk-d:${+Math.min(1, 4 / n).toFixed(3)}`;
|
|
608
772
|
const demoBar = demo ? `<div class="pkb-demo"><span>${esc(M.demoLabel)}</span><button type="button" data-r="dB">${esc(M.demoBingo)}</button><button type="button" data-r="dP">${esc(M.demoPerfect)}</button><button type="button" data-r="dR">${esc(M.demoRestore)}</button></div>` : "";
|
|
609
|
-
const hints = [M.soundHint
|
|
610
|
-
const
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
<button type="button" class="pkb-play-skip">${esc(M.playSkip?.trim() || ja.playSkip || M.close)}</button></div></dialog>` : "";
|
|
773
|
+
const hints = [opt(M.soundHint, ja.soundHint), vibrate ? opt(M.vibrationHint, ja.vibrationHint) : ""].filter((h) => h.trim());
|
|
774
|
+
const prompt = opt(M.readyPrompt, ja.readyPrompt);
|
|
775
|
+
const rule = opt(M.infiniteRule, ja.infiniteRule);
|
|
776
|
+
const readyScreen = ready ? `<button type="button" class="pkb-ready" data-r="ready"><span class="pkb-ready-in"><span class="pkb-ready-b">\u221E ${esc(M.infiniteSub?.trim() || ja.infiniteSub)}</span><b class="pkb-ready-t">${esc(M.readyTitle?.trim() || ja.readyTitle)}</b>` + (prompt.trim() ? `<span class="pkb-ready-q">${esc(prompt)}</span>` : "") + (rule.trim() ? `<span class="pkb-ready-r">${esc(rule)}</span>` : "") + (hints.length ? `<span class="pkb-ready-l">${hints.map((h) => `<span>${esc(h)}</span>`).join("")}</span>` : "") + `<span class="pkb-ready-go">${esc(M.readyTap?.trim() || ja.readyTap)}</span></span><b class="pkb-count" data-r="count" aria-live="assertive"></b></button>` : "";
|
|
777
|
+
const timeLabel = M.time?.trim() || ja.time;
|
|
778
|
+
const modeSwitch = modes ? `<span class="pkb-mode" data-r="mode" role="radiogroup" aria-label="${esc(M.modeLabel?.trim() || ja.modeLabel)}"><i class="pkb-mode-th" data-r="modeTh"></i><button type="button" role="radio" data-m="normal">${esc(M.modeNormal?.trim() || ja.modeNormal)}</button><button type="button" role="radio" data-m="infinite">${esc(M.modeInfinite?.trim() || ja.modeInfinite)}</button></span>` : "";
|
|
616
779
|
return `<div class="pkb" style="${scale}"><div class="pkb-board">
|
|
617
780
|
<div class="pkb-head"><div>
|
|
618
|
-
<div class="pkb-tags"><span class="pkb-kicker" data-r="kick">${esc(M.tag)}</span
|
|
781
|
+
<div class="pkb-tags"><span class="pkb-kicker" data-r="kick">${esc(M.tag)}</span>${modeSwitch}<button type="button" class="pkb-snd" data-r="snd"></button><span class="pkb-watch" data-r="watch" role="timer" aria-label="${esc(timeLabel)}">${formatTime(0)}</span></div>
|
|
619
782
|
<h2 class="pkb-title">${esc(M.title)}</h2>${M.subtitle ? `<p class="pkb-sub">${esc(M.subtitle)}</p>` : ""}</div>
|
|
620
783
|
<div class="pkb-side"><div class="pkb-score">
|
|
621
784
|
<div class="pkb-pill"><small>${esc(M.packed)}</small><b data-r="cnt"></b></div>
|
|
@@ -624,13 +787,14 @@ ${hints.map((hint) => `<p>${esc(hint)}</p>`).join("")}
|
|
|
624
787
|
<div class="pkb-combo" data-r="combo"><b data-r="cmbN"></b><span class="pkb-bar"><i data-r="cbar"></i></span></div></div></div>
|
|
625
788
|
<div class="pkb-gw"><div class="pkb-grid">${tiles}</div><svg class="pkb-lines" aria-hidden="true"></svg>
|
|
626
789
|
<div class="pkb-banner" data-r="banner" aria-hidden="true"><b data-r="bT"></b><span data-r="bS"></span></div></div>
|
|
627
|
-
<div class="pkb-foot"><span data-r="msg" aria-live="polite"></span><div class="pkb-acts">${shuffle ? `<button type="button" class="pkb-shuf" data-r="shuf">${esc(M.shuffle)}</button>` : ""}<button type="button" class="pkb-reset" data-r="reset">${esc(M.reset)}</button></div></div>
|
|
790
|
+
<div class="pkb-foot"><span data-r="msg" aria-live="polite"></span><div class="pkb-acts"><button type="button" class="pkb-finbtn" data-r="fin" disabled>${esc(M.finish?.trim() || ja.finish)}</button>${shuffle ? `<button type="button" class="pkb-shuf" data-r="shuf">${esc(M.shuffle)}</button>` : ""}<button type="button" class="pkb-reset" data-r="reset">${esc(M.reset)}</button></div></div>
|
|
628
791
|
<div class="pkb-flash" data-r="flash"></div>
|
|
629
|
-
<div class="pkb-perfect" data-r="perfect"><div class="pkb-card" role="dialog" aria-label="${esc(M.perfectTitle)}">
|
|
630
|
-
<div class="pkb-medal">${esc(M.perfectStamp)}</div><h3 class="pkb-ptitle">${esc(M.perfectTitle)}</h3><p class="pkb-pbody" data-r="pbody">${esc(perfectText(M, L.items.length))}</p>
|
|
792
|
+
<div class="pkb-perfect" data-r="perfect"><div class="pkb-card" data-r="pcard" role="dialog" aria-label="${esc(M.perfectTitle)}">
|
|
793
|
+
<div class="pkb-medal" data-r="medal">${esc(M.perfectStamp)}</div><h3 class="pkb-ptitle" data-r="ptitle">${esc(M.perfectTitle)}</h3><p class="pkb-pbody" data-r="pbody">${esc(perfectText(M, L.items.length))}</p>
|
|
794
|
+
<p class="pkb-ptime" data-r="ptime" hidden><small data-r="pTl">${esc(M.statTime?.trim() || ja.statTime)}</small><b data-r="pT"></b></p>
|
|
631
795
|
<div class="pkb-stats"><div><small>${esc(M.statScore)}</small><b data-r="sScore">0</b></div><div><small>${esc(M.statCombo)}</small><b data-r="sCombo">\xD70</b></div><div><small>${esc(M.statLines)}</small><b data-r="sBingo">0</b></div></div>
|
|
632
|
-
<button type="button" class="pkb-close" data-r="close">${esc(M.close)}</button></div></div>
|
|
633
|
-
<canvas class="pkb-fx"></canvas></div>${demoBar}
|
|
796
|
+
<div class="pkb-pbtns"><button type="button" class="pkb-close pkb-again" data-r="again" hidden>${esc(M.again?.trim() || ja.again)}</button><button type="button" class="pkb-close" data-r="close">${esc(M.close)}</button></div></div></div>
|
|
797
|
+
<canvas class="pkb-fx"></canvas>${readyScreen}<div class="pkb-mfx" data-r="mfx" aria-hidden="true"></div></div>${demoBar}</div>`;
|
|
634
798
|
}
|
|
635
799
|
var perfectText = (M, total) => typeof M.perfectBody === "function" ? M.perfectBody(total) : M.perfectBody;
|
|
636
800
|
function createPackingBingo(container, options = {}) {
|
|
@@ -662,8 +826,15 @@ function createPackingBingo(container, options = {}) {
|
|
|
662
826
|
const orderKey = storageKey && ORDER_PREFIX + storageKey;
|
|
663
827
|
const demo = !!options.demo;
|
|
664
828
|
const vibrate = options.vibrate !== false;
|
|
829
|
+
const uncheck = options.uncheck === true;
|
|
665
830
|
const calm = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
666
831
|
const haptics = createHaptics(vibrate);
|
|
832
|
+
let mode = options.mode === "infinite" ? "infinite" : "normal";
|
|
833
|
+
const inf = () => mode === "infinite";
|
|
834
|
+
let packing = null;
|
|
835
|
+
let cleared = 0;
|
|
836
|
+
const pending = /* @__PURE__ */ new Set();
|
|
837
|
+
let clearTimer = 0, clearAt = 0;
|
|
667
838
|
let dead = false;
|
|
668
839
|
const timers = /* @__PURE__ */ new Set();
|
|
669
840
|
const later = (fn, ms) => {
|
|
@@ -676,12 +847,19 @@ function createPackingBingo(container, options = {}) {
|
|
|
676
847
|
const wait = (ms) => new Promise((r) => window.setTimeout(r, ms));
|
|
677
848
|
let gen = 0;
|
|
678
849
|
ensureStyles();
|
|
679
|
-
container.innerHTML = template(L, M, {
|
|
850
|
+
container.innerHTML = template(L, M, {
|
|
851
|
+
demo,
|
|
852
|
+
shuffle: options.shuffle !== false,
|
|
853
|
+
vibrate,
|
|
854
|
+
ready: options.ready !== false,
|
|
855
|
+
modes: options.modeSwitch !== false
|
|
856
|
+
});
|
|
680
857
|
const $ = (r) => {
|
|
681
858
|
const el = container.querySelector(`[data-r="${r}"]`);
|
|
682
859
|
if (!el) throw new Error(`[packing-bingo] internal: missing element "${r}"`);
|
|
683
860
|
return el;
|
|
684
861
|
};
|
|
862
|
+
const root = container.querySelector(".pkb");
|
|
685
863
|
const board = container.querySelector(".pkb-board");
|
|
686
864
|
const gw = container.querySelector(".pkb-gw");
|
|
687
865
|
const grid = container.querySelector(".pkb-grid");
|
|
@@ -693,21 +871,7 @@ function createPackingBingo(container, options = {}) {
|
|
|
693
871
|
const freeTile = L.center >= 0 ? cellTiles[L.center] : null;
|
|
694
872
|
const ins = tiles.map((t) => t.querySelector("input"));
|
|
695
873
|
const stamps = tiles.map((t) => t.querySelector(".pkb-stamp"));
|
|
696
|
-
const kick = $("kick"), snd = $("snd"), perfectEl = $("perfect");
|
|
697
|
-
const playDialog = container.querySelector('[data-r="play"]');
|
|
698
|
-
const beforePlay = (event) => {
|
|
699
|
-
if (!playDialog || playReady.has(container.ownerDocument) || typeof playDialog.showModal !== "function" || !(event.target instanceof Element) || !event.target.closest(".pkb-tile:not(.pkb-free)")) return;
|
|
700
|
-
event.preventDefault();
|
|
701
|
-
event.stopImmediatePropagation();
|
|
702
|
-
playDialog.showModal();
|
|
703
|
-
};
|
|
704
|
-
grid.addEventListener("click", beforePlay, true);
|
|
705
|
-
playDialog?.querySelectorAll("button").forEach((button) => {
|
|
706
|
-
button.addEventListener("click", () => playDialog.close());
|
|
707
|
-
});
|
|
708
|
-
playDialog?.addEventListener("close", () => {
|
|
709
|
-
if (!dead) playReady.add(container.ownerDocument);
|
|
710
|
-
});
|
|
874
|
+
const kick = $("kick"), snd = $("snd"), perfectEl = $("perfect"), mfx = $("mfx");
|
|
711
875
|
let checked = normalize(void 0);
|
|
712
876
|
let cur = [];
|
|
713
877
|
let prevLines = /* @__PURE__ */ new Set();
|
|
@@ -743,7 +907,7 @@ function createPackingBingo(container, options = {}) {
|
|
|
743
907
|
cv.width = Math.round(BW * dpr);
|
|
744
908
|
cv.height = Math.round(BH * dpr);
|
|
745
909
|
};
|
|
746
|
-
const piece = (x, y, vx, vy, g, s, life, shape2) => ({
|
|
910
|
+
const piece = (x, y, vx, vy, g, s, life, shape2, colors = CONFETTI) => ({
|
|
747
911
|
x,
|
|
748
912
|
y,
|
|
749
913
|
vx,
|
|
@@ -755,7 +919,7 @@ function createPackingBingo(container, options = {}) {
|
|
|
755
919
|
h: (3 + Math.random() * 5) * s,
|
|
756
920
|
r: Math.random() * 6,
|
|
757
921
|
vr: (Math.random() - 0.5) * 0.35,
|
|
758
|
-
c:
|
|
922
|
+
c: colors[Math.random() * colors.length | 0]
|
|
759
923
|
});
|
|
760
924
|
const go = () => {
|
|
761
925
|
if (!raf && ctx) raf = requestAnimationFrame(tick);
|
|
@@ -766,7 +930,7 @@ function createPackingBingo(container, options = {}) {
|
|
|
766
930
|
const a = (o.angle ?? -Math.PI / 2) + (Math.random() - 0.5) * (o.spread ?? Math.PI * 2);
|
|
767
931
|
const v = (o.speed ?? 10) * (0.35 + Math.random() * 0.85);
|
|
768
932
|
const sh = Math.random() < (o.star ?? 0.15) ? "star" : Math.random() < 0.3 ? "round" : "rect";
|
|
769
|
-
P.push(piece(x, y, Math.cos(a) * v, Math.sin(a) * v, o.g ?? 0.28, o.size ?? 1, (o.life ?? 110) + Math.random() * 40, sh));
|
|
933
|
+
P.push(piece(x, y, Math.cos(a) * v, Math.sin(a) * v, o.g ?? 0.28, o.size ?? 1, (o.life ?? 110) + Math.random() * 40, sh, o.colors));
|
|
770
934
|
}
|
|
771
935
|
go();
|
|
772
936
|
}
|
|
@@ -864,6 +1028,7 @@ function createPackingBingo(container, options = {}) {
|
|
|
864
1028
|
if (e.animationName === "pkb-stamp-hit") t.classList.remove("pkb-hit");
|
|
865
1029
|
if (t.classList.contains("pkb-tile") && (e.animationName === "pkb-hop" || e.animationName === "pkb-squash"))
|
|
866
1030
|
t.classList.remove(e.animationName);
|
|
1031
|
+
if (t.classList.contains("pkb-tile") && e.animationName === "pkb-clr") t.classList.remove("pkb-clr");
|
|
867
1032
|
});
|
|
868
1033
|
const ring = (t) => {
|
|
869
1034
|
const r = document.createElement("span");
|
|
@@ -882,20 +1047,20 @@ function createPackingBingo(container, options = {}) {
|
|
|
882
1047
|
board.appendChild(el);
|
|
883
1048
|
el.addEventListener("animationend", () => el.remove());
|
|
884
1049
|
};
|
|
885
|
-
const prog = (t,
|
|
1050
|
+
const prog = (t, t02, d) => Math.max(0, Math.min(1, (t - t02) / d));
|
|
886
1051
|
const ease = (k) => 1 - Math.pow(1 - k, 3);
|
|
887
1052
|
let tw = 0;
|
|
888
1053
|
function countScore() {
|
|
889
1054
|
cancelAnimationFrame(tw);
|
|
890
|
-
const a = shown, b = score,
|
|
1055
|
+
const a = shown, b = score, t02 = performance.now();
|
|
891
1056
|
const st = (t) => {
|
|
892
1057
|
if (dead) return;
|
|
893
|
-
const k = prog(t,
|
|
1058
|
+
const k = prog(t, t02, 450);
|
|
894
1059
|
shown = Math.round(a + (b - a) * ease(k));
|
|
895
1060
|
$("sc").textContent = shown.toLocaleString();
|
|
896
1061
|
if (k < 1) tw = requestAnimationFrame(st);
|
|
897
1062
|
};
|
|
898
|
-
st(
|
|
1063
|
+
st(t02);
|
|
899
1064
|
if (!calm) replay($("sp"), "pkb-bump");
|
|
900
1065
|
}
|
|
901
1066
|
const showGain = (pts, x, y, cls = "pt") => {
|
|
@@ -903,12 +1068,12 @@ function createPackingBingo(container, options = {}) {
|
|
|
903
1068
|
countScore();
|
|
904
1069
|
};
|
|
905
1070
|
function countUp(el, a, b, d, fmt, ticks = false) {
|
|
906
|
-
const
|
|
1071
|
+
const t02 = performance.now();
|
|
907
1072
|
const g = gen;
|
|
908
1073
|
let lt = 0;
|
|
909
1074
|
const st = (t) => {
|
|
910
1075
|
if (dead || g !== gen) return;
|
|
911
|
-
const k = prog(t,
|
|
1076
|
+
const k = prog(t, t02, d);
|
|
912
1077
|
el.textContent = fmt(Math.round(a + (b - a) * ease(k)));
|
|
913
1078
|
if (ticks && k < 1 && t - lt > 55) {
|
|
914
1079
|
sfx.tick();
|
|
@@ -917,7 +1082,7 @@ function createPackingBingo(container, options = {}) {
|
|
|
917
1082
|
if (k < 1) requestAnimationFrame(st);
|
|
918
1083
|
else if (!calm) replay(el, "pkb-bump");
|
|
919
1084
|
};
|
|
920
|
-
st(
|
|
1085
|
+
st(t02);
|
|
921
1086
|
}
|
|
922
1087
|
const ctr = (t) => {
|
|
923
1088
|
return { x: t.offsetLeft + t.offsetWidth / 2, y: t.offsetTop + t.offsetHeight / 2 };
|
|
@@ -936,6 +1101,120 @@ function createPackingBingo(container, options = {}) {
|
|
|
936
1101
|
return `<g${fresh.includes(k) ? ' class="pkb-draw"' : ""}><line class="pkb-u" ${at}/><line class="pkb-o" ${at}/></g>`;
|
|
937
1102
|
}).join("");
|
|
938
1103
|
}
|
|
1104
|
+
const readyEl = container.querySelector('[data-r="ready"]');
|
|
1105
|
+
const watch = $("watch");
|
|
1106
|
+
let phase = "idle";
|
|
1107
|
+
let t0 = 0, watchTimer = 0;
|
|
1108
|
+
let finalMs = null;
|
|
1109
|
+
const cdTimers = /* @__PURE__ */ new Set();
|
|
1110
|
+
const cd = (fn, ms) => {
|
|
1111
|
+
const id = window.setTimeout(() => {
|
|
1112
|
+
cdTimers.delete(id);
|
|
1113
|
+
if (!dead) fn();
|
|
1114
|
+
}, ms);
|
|
1115
|
+
cdTimers.add(id);
|
|
1116
|
+
};
|
|
1117
|
+
const stopTimers = () => {
|
|
1118
|
+
cdTimers.forEach((id) => clearTimeout(id));
|
|
1119
|
+
cdTimers.clear();
|
|
1120
|
+
clearInterval(watchTimer);
|
|
1121
|
+
};
|
|
1122
|
+
const elapsed = () => phase === "play" ? performance.now() - t0 : finalMs ?? 0;
|
|
1123
|
+
const showWatch = () => {
|
|
1124
|
+
watch.textContent = formatTime(elapsed());
|
|
1125
|
+
};
|
|
1126
|
+
function startWatch() {
|
|
1127
|
+
phase = "play";
|
|
1128
|
+
t0 = performance.now();
|
|
1129
|
+
finalMs = null;
|
|
1130
|
+
clearInterval(watchTimer);
|
|
1131
|
+
watchTimer = window.setInterval(showWatch, 100);
|
|
1132
|
+
watch.classList.remove("pkb-done");
|
|
1133
|
+
watch.classList.add("pkb-run");
|
|
1134
|
+
showWatch();
|
|
1135
|
+
syncFin();
|
|
1136
|
+
}
|
|
1137
|
+
const finBtn = $("fin");
|
|
1138
|
+
const shufBtn = container.querySelector('[data-r="shuf"]');
|
|
1139
|
+
const syncFin = () => {
|
|
1140
|
+
finBtn.disabled = !(inf() && phase === "play");
|
|
1141
|
+
if (shufBtn) shufBtn.disabled = inf() && phase === "done";
|
|
1142
|
+
};
|
|
1143
|
+
function finish() {
|
|
1144
|
+
if (phase === "play") {
|
|
1145
|
+
finalMs = performance.now() - t0;
|
|
1146
|
+
watch.classList.replace("pkb-run", "pkb-done");
|
|
1147
|
+
} else if (phase !== "done") {
|
|
1148
|
+
stopTimers();
|
|
1149
|
+
openGrid(false);
|
|
1150
|
+
}
|
|
1151
|
+
phase = "done";
|
|
1152
|
+
clearInterval(watchTimer);
|
|
1153
|
+
showWatch();
|
|
1154
|
+
}
|
|
1155
|
+
function openGrid(fade) {
|
|
1156
|
+
grid.removeAttribute("inert");
|
|
1157
|
+
if (!readyEl || readyEl.hidden) return;
|
|
1158
|
+
const focused = document.activeElement === readyEl;
|
|
1159
|
+
readyEl.classList.add("pkb-gone");
|
|
1160
|
+
board.classList.remove("pkb-waiting");
|
|
1161
|
+
readyEl.tabIndex = -1;
|
|
1162
|
+
if (focused) grid.querySelector("input")?.focus({ preventScroll: true });
|
|
1163
|
+
if (fade) cd(() => readyEl.hidden = true, GO_MS);
|
|
1164
|
+
else readyEl.hidden = true;
|
|
1165
|
+
}
|
|
1166
|
+
function toReady() {
|
|
1167
|
+
stopTimers();
|
|
1168
|
+
finalMs = null;
|
|
1169
|
+
watch.classList.remove("pkb-run", "pkb-done");
|
|
1170
|
+
phase = readyEl ? "ready" : "idle";
|
|
1171
|
+
showWatch();
|
|
1172
|
+
syncFin();
|
|
1173
|
+
board.classList.remove("pkb-over");
|
|
1174
|
+
grid.removeAttribute("inert");
|
|
1175
|
+
if (!readyEl) return;
|
|
1176
|
+
readyEl.hidden = false;
|
|
1177
|
+
readyEl.removeAttribute("tabindex");
|
|
1178
|
+
readyEl.classList.remove("pkb-gone", "pkb-counting");
|
|
1179
|
+
board.classList.add("pkb-waiting");
|
|
1180
|
+
$("count").textContent = "";
|
|
1181
|
+
grid.setAttribute("inert", "");
|
|
1182
|
+
}
|
|
1183
|
+
let resetAsked = false;
|
|
1184
|
+
const restartGame = () => {
|
|
1185
|
+
if (!controlled || !checked.some(Boolean)) toReady();
|
|
1186
|
+
else resetAsked = true;
|
|
1187
|
+
};
|
|
1188
|
+
function skipReady() {
|
|
1189
|
+
if (phase === "play" || phase === "done") return;
|
|
1190
|
+
stopTimers();
|
|
1191
|
+
openGrid(false);
|
|
1192
|
+
startWatch();
|
|
1193
|
+
}
|
|
1194
|
+
function countdown() {
|
|
1195
|
+
if (phase !== "ready") return;
|
|
1196
|
+
phase = "count";
|
|
1197
|
+
readyEl.classList.add("pkb-counting");
|
|
1198
|
+
const count = $("count");
|
|
1199
|
+
const words = ["3", "2", "1", M.readyGo?.trim() || ja.readyGo];
|
|
1200
|
+
words.forEach((word, k) => {
|
|
1201
|
+
const go2 = k === words.length - 1;
|
|
1202
|
+
const step = () => {
|
|
1203
|
+
count.textContent = word;
|
|
1204
|
+
count.classList.toggle("pkb-go", go2);
|
|
1205
|
+
replay(count, "pkb-tick");
|
|
1206
|
+
sfx.count(go2);
|
|
1207
|
+
haptics.play(go2 ? "go" : "count");
|
|
1208
|
+
if (go2) {
|
|
1209
|
+
startWatch();
|
|
1210
|
+
openGrid(true);
|
|
1211
|
+
}
|
|
1212
|
+
};
|
|
1213
|
+
if (k) cd(step, k * COUNT_MS);
|
|
1214
|
+
else step();
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1217
|
+
readyEl?.addEventListener("click", countdown);
|
|
939
1218
|
function tapFx(i, on, moment, fresh) {
|
|
940
1219
|
const p = rel(tiles[i]);
|
|
941
1220
|
if (!on) {
|
|
@@ -987,7 +1266,7 @@ function createPackingBingo(container, options = {}) {
|
|
|
987
1266
|
});
|
|
988
1267
|
later(() => sfx.reach(), 150);
|
|
989
1268
|
}
|
|
990
|
-
function bingo(fresh, pts) {
|
|
1269
|
+
function bingo(fresh, pts, total) {
|
|
991
1270
|
const nl = fresh.length;
|
|
992
1271
|
const seq = [...new Set(fresh.flatMap((k) => cellLines[k]))].map((c) => cellTiles[c]);
|
|
993
1272
|
const step = Math.min(70, 300 / seq.length);
|
|
@@ -1001,7 +1280,7 @@ function createPackingBingo(container, options = {}) {
|
|
|
1001
1280
|
);
|
|
1002
1281
|
later(() => {
|
|
1003
1282
|
$("bT").textContent = nl >= 3 ? M.bannerTriple : nl === 2 ? M.bannerDouble : M.bannerSingle;
|
|
1004
|
-
$("bS").textContent = nl > 1 ? M.bannerMulti(nl) : M.bannerNth(
|
|
1283
|
+
$("bS").textContent = nl > 1 ? M.bannerMulti(nl) : M.bannerNth(total);
|
|
1005
1284
|
replay($("banner"), "pkb-show");
|
|
1006
1285
|
if (!calm) {
|
|
1007
1286
|
replay($("flash"), "pkb-go");
|
|
@@ -1018,12 +1297,16 @@ function createPackingBingo(container, options = {}) {
|
|
|
1018
1297
|
perfectPending = true;
|
|
1019
1298
|
perfectEl.classList.remove("pkb-settled");
|
|
1020
1299
|
const bonus = gotP ? 0 : 1e3;
|
|
1300
|
+
const speed = gotP || finalMs == null ? 0 : speedBonus(N, finalMs);
|
|
1021
1301
|
gotP = true;
|
|
1022
|
-
score += bonus;
|
|
1302
|
+
score += bonus + speed;
|
|
1023
1303
|
cellTiles.forEach((t, c) => hop(t, far(c) * 180 / Math.max(1, n - 1)));
|
|
1024
1304
|
sfx.perfect();
|
|
1025
1305
|
if (!calm) replay($("flash"), "pkb-go");
|
|
1306
|
+
setResult(false);
|
|
1026
1307
|
$("pbody").textContent = perfectText(M, N);
|
|
1308
|
+
$("ptime").hidden = finalMs == null;
|
|
1309
|
+
$("pT").textContent = formatTime(finalMs ?? 0, 2);
|
|
1027
1310
|
$("sScore").textContent = "0";
|
|
1028
1311
|
$("sCombo").textContent = "\xD70";
|
|
1029
1312
|
$("sBingo").textContent = "0";
|
|
@@ -1039,9 +1322,11 @@ function createPackingBingo(container, options = {}) {
|
|
|
1039
1322
|
burst(BW, BH, 90, { angle: -Math.PI * 2 / 3, spread: 0.8, speed: 22, g: 0.32, star: 0.3 });
|
|
1040
1323
|
rain(3500);
|
|
1041
1324
|
if (bonus) floatText(M.perfectBonus(bonus), BW / 2, BH * 0.16, "big");
|
|
1325
|
+
const gap = Math.min(34, Math.max(14, BW * 0.056)) * 1.3 + 6;
|
|
1326
|
+
if (speed) floatText((M.speedBonus ?? ja.speedBonus)(speed), BW / 2, Math.max(gap / 2 + 4, BH * 0.16 - gap), "big");
|
|
1042
1327
|
countUp($("sScore"), 0, score, 1300, (v) => v.toLocaleString(), true);
|
|
1043
1328
|
countUp($("sCombo"), 0, maxCombo, 700, (v) => "\xD7" + v);
|
|
1044
|
-
countUp($("sBingo"), 0,
|
|
1329
|
+
countUp($("sBingo"), 0, resultLines(), 700, String);
|
|
1045
1330
|
countScore();
|
|
1046
1331
|
}, PERFECT_STAMP_DELAY_MS);
|
|
1047
1332
|
later(() => emojiRain(3e3), 1500);
|
|
@@ -1052,10 +1337,67 @@ function createPackingBingo(container, options = {}) {
|
|
|
1052
1337
|
beat?.(pulse);
|
|
1053
1338
|
}, at)
|
|
1054
1339
|
);
|
|
1055
|
-
const e = { score, maxCombo };
|
|
1340
|
+
const e = { score, maxCombo, time: finalMs == null ? null : Math.round(finalMs), speedBonus: speed };
|
|
1056
1341
|
events.push(() => options.onPerfect?.(e));
|
|
1057
1342
|
}
|
|
1343
|
+
function clearNow(animate) {
|
|
1344
|
+
clearTimeout(clearTimer);
|
|
1345
|
+
clearTimer = 0;
|
|
1346
|
+
if (!pending.size) return;
|
|
1347
|
+
const gone = [...pending].filter((i) => checked[i]);
|
|
1348
|
+
pending.clear();
|
|
1349
|
+
checked = checked.slice();
|
|
1350
|
+
gone.forEach((i) => {
|
|
1351
|
+
checked[i] = false;
|
|
1352
|
+
ins[i].checked = false;
|
|
1353
|
+
got.delete(i);
|
|
1354
|
+
});
|
|
1355
|
+
gotL.clear();
|
|
1356
|
+
const left = completeLines();
|
|
1357
|
+
left.forEach((k) => gotL.add(lineKey(lines[k])));
|
|
1358
|
+
prevLines = new Set(left);
|
|
1359
|
+
render(null, true);
|
|
1360
|
+
if (!animate || !gone.length) return;
|
|
1361
|
+
sfx.clear(gone.length);
|
|
1362
|
+
gone.forEach((i, j) => {
|
|
1363
|
+
const t = tiles[i];
|
|
1364
|
+
if (!calm) {
|
|
1365
|
+
t.style.setProperty("--pkb-cd", Math.min(j * 28, 280) + "ms");
|
|
1366
|
+
t.classList.remove("pkb-squash", "pkb-hop");
|
|
1367
|
+
replay(t, "pkb-clr");
|
|
1368
|
+
}
|
|
1369
|
+
const p = rel(t);
|
|
1370
|
+
burst(p.x, p.y, 9, { speed: 4.5, g: 0.06, life: 45, size: 0.75, star: 1, colors: SPARK.slice(1) });
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
function scheduleClear(ms) {
|
|
1374
|
+
const at = performance.now() + ms;
|
|
1375
|
+
if (clearTimer && at <= clearAt) return;
|
|
1376
|
+
clearTimeout(clearTimer);
|
|
1377
|
+
clearAt = at;
|
|
1378
|
+
clearTimer = window.setTimeout(() => {
|
|
1379
|
+
clearTimer = 0;
|
|
1380
|
+
if (!dead) clearNow(true);
|
|
1381
|
+
}, ms);
|
|
1382
|
+
}
|
|
1383
|
+
const dropClear = () => {
|
|
1384
|
+
clearTimeout(clearTimer);
|
|
1385
|
+
clearTimer = 0;
|
|
1386
|
+
pending.clear();
|
|
1387
|
+
};
|
|
1388
|
+
function setResult(fin) {
|
|
1389
|
+
const title = fin ? M.finishTitle?.trim() || ja.finishTitle : M.perfectTitle;
|
|
1390
|
+
perfectEl.classList.toggle("pkb-fin", fin);
|
|
1391
|
+
$("pcard").setAttribute("aria-label", title);
|
|
1392
|
+
$("medal").textContent = fin ? M.finishStamp?.trim() || ja.finishStamp : M.perfectStamp;
|
|
1393
|
+
$("ptitle").textContent = title;
|
|
1394
|
+
$("pTl").textContent = fin ? M.finishTime?.trim() || ja.finishTime : M.statTime?.trim() || ja.statTime;
|
|
1395
|
+
$("again").hidden = !fin;
|
|
1396
|
+
}
|
|
1397
|
+
const resultLines = () => inf() ? cleared : cur.length;
|
|
1398
|
+
const resultFocus = () => $(perfectEl.classList.contains("pkb-fin") ? "again" : "close");
|
|
1058
1399
|
function cancelFx() {
|
|
1400
|
+
if (pending.size) clearNow(false);
|
|
1059
1401
|
gen++;
|
|
1060
1402
|
perfectPending = false;
|
|
1061
1403
|
timers.forEach((id) => clearTimeout(id));
|
|
@@ -1076,21 +1418,32 @@ function createPackingBingo(container, options = {}) {
|
|
|
1076
1418
|
$("flash").classList.remove("pkb-go");
|
|
1077
1419
|
$("combo").classList.remove("pkb-on");
|
|
1078
1420
|
$("cbar").classList.remove("pkb-draining");
|
|
1079
|
-
cellTiles.forEach((t) => t.classList.remove("pkb-squash", "pkb-hop"));
|
|
1421
|
+
cellTiles.forEach((t) => t.classList.remove("pkb-squash", "pkb-hop", "pkb-clr"));
|
|
1080
1422
|
stamps.forEach((s) => s.classList.remove("pkb-hit"));
|
|
1081
|
-
board.classList.remove("pkb-punch", "pkb-shake");
|
|
1423
|
+
board.classList.remove("pkb-punch", "pkb-shake", "pkb-switching");
|
|
1424
|
+
mfx.classList.remove("pkb-show");
|
|
1082
1425
|
if (!keepPerfect) perfectEl.classList.remove("pkb-show", "pkb-settled");
|
|
1083
1426
|
}
|
|
1084
1427
|
const completeLines = () => lines.flatMap((l, k) => l.every((i) => checked[i]) ? [k] : []);
|
|
1085
1428
|
function render(src, quiet) {
|
|
1086
1429
|
const events = [];
|
|
1087
1430
|
const on = checked, c = on.filter(Boolean).length;
|
|
1431
|
+
const endless = inf();
|
|
1432
|
+
const all = c === N && !endless;
|
|
1088
1433
|
cur = completeLines();
|
|
1089
1434
|
const fresh = cur.filter((k) => !prevLines.has(k));
|
|
1090
1435
|
if ([...prevLines].some((k) => !cur.includes(k))) {
|
|
1091
1436
|
cancelFx();
|
|
1092
1437
|
if (c < N) perfectEl.classList.remove("pkb-show");
|
|
1093
1438
|
}
|
|
1439
|
+
if (endless) {
|
|
1440
|
+
if (!quiet) cleared += fresh.length;
|
|
1441
|
+
if (cur.length) {
|
|
1442
|
+
const known = pending.size;
|
|
1443
|
+
cur.forEach((k) => lines[k].forEach((i) => pending.add(i)));
|
|
1444
|
+
if (pending.size > known || !clearTimer) scheduleClear(!quiet && fresh.length ? CLEAR_MS : FLY_MS + 200);
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1094
1447
|
const hit = new Set(cur.flatMap((k) => cellLines[k]));
|
|
1095
1448
|
const reach = /* @__PURE__ */ new Set();
|
|
1096
1449
|
for (const l of lines) {
|
|
@@ -1099,26 +1452,31 @@ function createPackingBingo(container, options = {}) {
|
|
|
1099
1452
|
}
|
|
1100
1453
|
tiles.forEach((t, i) => {
|
|
1101
1454
|
t.classList.toggle("pkb-on", on[i]);
|
|
1455
|
+
const lock = on[i] && (!uncheck || pending.has(i));
|
|
1456
|
+
t.classList.toggle("pkb-lock", lock);
|
|
1457
|
+
if (lock) ins[i].setAttribute("aria-readonly", "true");
|
|
1458
|
+
else ins[i].removeAttribute("aria-readonly");
|
|
1102
1459
|
t.classList.toggle("pkb-line", hit.has(L.itemCell[slotOf[i]]));
|
|
1103
1460
|
t.classList.toggle("pkb-reach", reach.has(i));
|
|
1104
1461
|
});
|
|
1105
1462
|
freeTile?.classList.toggle("pkb-line", hit.has(L.center));
|
|
1106
|
-
board.classList.toggle("pkb-all",
|
|
1107
|
-
kick.classList.toggle("pkb-gold",
|
|
1108
|
-
kick.textContent =
|
|
1463
|
+
board.classList.toggle("pkb-all", all);
|
|
1464
|
+
kick.classList.toggle("pkb-gold", all);
|
|
1465
|
+
kick.textContent = all ? M.perfectTag : M.tag;
|
|
1109
1466
|
$("cnt").textContent = `${c}/${N}`;
|
|
1110
|
-
|
|
1467
|
+
const total = endless ? cleared : cur.length;
|
|
1468
|
+
$("bng").textContent = String(total);
|
|
1111
1469
|
const bp = $("bp");
|
|
1112
|
-
if (
|
|
1470
|
+
if (total > lastN) {
|
|
1113
1471
|
bp.classList.remove("pkb-hot");
|
|
1114
1472
|
void bp.offsetWidth;
|
|
1115
1473
|
}
|
|
1116
|
-
bp.classList.toggle("pkb-hot",
|
|
1117
|
-
lastN =
|
|
1474
|
+
bp.classList.toggle("pkb-hot", total > 0);
|
|
1475
|
+
lastN = total;
|
|
1118
1476
|
drawLines(fresh);
|
|
1119
1477
|
if (src !== null) {
|
|
1120
1478
|
const newReach = [...reach].some((i) => !prevReach.has(i));
|
|
1121
|
-
const moment = !quiet &&
|
|
1479
|
+
const moment = !quiet && all && !prevAll ? "perfect" : !quiet && fresh.length ? "bingo" : !quiet && newReach ? "reach" : "tap";
|
|
1122
1480
|
tapFx(src, on[src], moment, fresh);
|
|
1123
1481
|
}
|
|
1124
1482
|
const newL = fresh.map((k) => lineKey(lines[k])).filter((key) => !gotL.has(key));
|
|
@@ -1127,10 +1485,10 @@ function createPackingBingo(container, options = {}) {
|
|
|
1127
1485
|
score += linePts;
|
|
1128
1486
|
if (quiet) {
|
|
1129
1487
|
if (linePts) countScore();
|
|
1130
|
-
} else if (
|
|
1488
|
+
} else if (all && !prevAll) perfect(events);
|
|
1131
1489
|
else if (fresh.length) {
|
|
1132
|
-
bingo(fresh, linePts);
|
|
1133
|
-
const e = { newLines: fresh, totalLines:
|
|
1490
|
+
bingo(fresh, linePts, total);
|
|
1491
|
+
const e = { newLines: fresh, totalLines: total, score, mode };
|
|
1134
1492
|
events.push(() => options.onBingo?.(e));
|
|
1135
1493
|
} else if (src !== null && on[src]) {
|
|
1136
1494
|
const nr = [...reach].filter((i) => !prevReach.has(i));
|
|
@@ -1138,9 +1496,9 @@ function createPackingBingo(container, options = {}) {
|
|
|
1138
1496
|
}
|
|
1139
1497
|
prevLines = new Set(cur);
|
|
1140
1498
|
prevReach = reach;
|
|
1141
|
-
prevAll =
|
|
1499
|
+
prevAll = all;
|
|
1142
1500
|
const labels = [...reach].map((i) => items[i].label);
|
|
1143
|
-
$("msg").textContent =
|
|
1501
|
+
$("msg").textContent = all ? M.statusDone : labels.length ? M.statusReach(labels) : cur.length ? M.statusLines(cur.length, N - c) : M.statusStart(N - c);
|
|
1144
1502
|
return events;
|
|
1145
1503
|
}
|
|
1146
1504
|
const flush = (events) => {
|
|
@@ -1150,9 +1508,11 @@ function createPackingBingo(container, options = {}) {
|
|
|
1150
1508
|
}
|
|
1151
1509
|
};
|
|
1152
1510
|
const save = () => {
|
|
1153
|
-
if (storageKey) writeStore(storageKey, JSON.stringify(checked));
|
|
1511
|
+
if (storageKey && !inf()) writeStore(storageKey, JSON.stringify(checked));
|
|
1154
1512
|
};
|
|
1155
1513
|
function apply(next, src, quiet) {
|
|
1514
|
+
if (phase === "idle" && src !== null && next[src]) startWatch();
|
|
1515
|
+
if (!inf() && next.every(Boolean)) finish();
|
|
1156
1516
|
checked = next;
|
|
1157
1517
|
ins.forEach((x, i) => x.checked = checked[i]);
|
|
1158
1518
|
const events = render(src, quiet);
|
|
@@ -1160,8 +1520,12 @@ function createPackingBingo(container, options = {}) {
|
|
|
1160
1520
|
return events;
|
|
1161
1521
|
}
|
|
1162
1522
|
function baseline(next) {
|
|
1523
|
+
dropClear();
|
|
1524
|
+
cleared = 0;
|
|
1163
1525
|
cancelFx();
|
|
1164
1526
|
clearVisuals();
|
|
1527
|
+
if (!inf() && next.every(Boolean)) finish();
|
|
1528
|
+
else if ((phase === "play" || phase === "done") && !next.some(Boolean)) toReady();
|
|
1165
1529
|
checked = next;
|
|
1166
1530
|
ins.forEach((x, i) => x.checked = checked[i]);
|
|
1167
1531
|
got.clear();
|
|
@@ -1171,7 +1535,7 @@ function createPackingBingo(container, options = {}) {
|
|
|
1171
1535
|
done.forEach((k) => gotL.add(lineKey(lines[k])));
|
|
1172
1536
|
prevLines = new Set(done);
|
|
1173
1537
|
prevReach = /* @__PURE__ */ new Set();
|
|
1174
|
-
prevAll = checked.every(Boolean);
|
|
1538
|
+
prevAll = !inf() && checked.every(Boolean);
|
|
1175
1539
|
gotP = prevAll;
|
|
1176
1540
|
score = shown = got.size * 10 + gotL.size * 100;
|
|
1177
1541
|
combo = 0;
|
|
@@ -1181,7 +1545,9 @@ function createPackingBingo(container, options = {}) {
|
|
|
1181
1545
|
save();
|
|
1182
1546
|
}
|
|
1183
1547
|
function request(next, src, opts = {}) {
|
|
1184
|
-
|
|
1548
|
+
const game = inf();
|
|
1549
|
+
if (controlled && !game) {
|
|
1550
|
+
resetAsked = !!opts.reset && resetAsked;
|
|
1185
1551
|
ins.forEach((x, i) => x.checked = checked[i]);
|
|
1186
1552
|
options.onChange?.(next.slice());
|
|
1187
1553
|
return;
|
|
@@ -1189,7 +1555,7 @@ function createPackingBingo(container, options = {}) {
|
|
|
1189
1555
|
let events = [];
|
|
1190
1556
|
if (opts.reset) baseline(next);
|
|
1191
1557
|
else events = apply(next, src, !!opts.quiet);
|
|
1192
|
-
options.onChange?.(checked.slice());
|
|
1558
|
+
if (!game) options.onChange?.(checked.slice());
|
|
1193
1559
|
flush(events);
|
|
1194
1560
|
}
|
|
1195
1561
|
function place(next) {
|
|
@@ -1208,12 +1574,12 @@ function createPackingBingo(container, options = {}) {
|
|
|
1208
1574
|
focused.focus({ preventScroll: true });
|
|
1209
1575
|
}
|
|
1210
1576
|
const saveOrder = () => {
|
|
1211
|
-
if (!orderKey) return;
|
|
1577
|
+
if (!orderKey || inf()) return;
|
|
1212
1578
|
writeStore(orderKey, JSON.stringify(order));
|
|
1213
1579
|
};
|
|
1214
1580
|
let flights = [];
|
|
1215
1581
|
function arrange(next) {
|
|
1216
|
-
if (!checked.every(Boolean)) cancelFx();
|
|
1582
|
+
if (inf() || !checked.every(Boolean)) cancelFx();
|
|
1217
1583
|
const from = calm ? null : tiles.map((t) => t.getBoundingClientRect());
|
|
1218
1584
|
flights.forEach((a) => a.cancel());
|
|
1219
1585
|
flights = [];
|
|
@@ -1249,6 +1615,7 @@ function createPackingBingo(container, options = {}) {
|
|
|
1249
1615
|
svg.innerHTML = "";
|
|
1250
1616
|
}
|
|
1251
1617
|
function shuffleCard() {
|
|
1618
|
+
if (inf() && phase === "done") return;
|
|
1252
1619
|
let next = order.slice();
|
|
1253
1620
|
do {
|
|
1254
1621
|
for (let s = N - 1; s > 0; s--) {
|
|
@@ -1263,26 +1630,93 @@ function createPackingBingo(container, options = {}) {
|
|
|
1263
1630
|
const unshuffle = () => {
|
|
1264
1631
|
if (!isIdentity(order)) arrange(identity());
|
|
1265
1632
|
};
|
|
1633
|
+
const locked = (i) => checked[i] && (!uncheck || pending.has(i));
|
|
1634
|
+
grid.addEventListener("click", (e) => {
|
|
1635
|
+
const i = ins.indexOf(e.target);
|
|
1636
|
+
if (i >= 0 && locked(i)) e.preventDefault();
|
|
1637
|
+
});
|
|
1266
1638
|
grid.addEventListener("change", (e) => {
|
|
1267
1639
|
const i = ins.indexOf(e.target);
|
|
1268
1640
|
if (i < 0) return;
|
|
1641
|
+
if (locked(i)) {
|
|
1642
|
+
ins[i].checked = true;
|
|
1643
|
+
return;
|
|
1644
|
+
}
|
|
1269
1645
|
const next = checked.slice();
|
|
1270
1646
|
next[i] = !next[i];
|
|
1271
1647
|
request(next, i);
|
|
1272
1648
|
});
|
|
1273
1649
|
container.querySelector('[data-r="shuf"]')?.addEventListener("click", shuffleCard);
|
|
1650
|
+
const restartInfinite = () => {
|
|
1651
|
+
baseline(normalize(void 0));
|
|
1652
|
+
toReady();
|
|
1653
|
+
};
|
|
1274
1654
|
$("reset").addEventListener("click", () => {
|
|
1655
|
+
if (inf()) return restartInfinite();
|
|
1275
1656
|
unshuffle();
|
|
1657
|
+
restartGame();
|
|
1276
1658
|
request(normalize(void 0), null, { reset: true });
|
|
1277
1659
|
});
|
|
1278
1660
|
$("close").addEventListener("click", () => perfectEl.classList.remove("pkb-show"));
|
|
1661
|
+
$("again").addEventListener("click", () => {
|
|
1662
|
+
if (!inf()) return;
|
|
1663
|
+
restartInfinite();
|
|
1664
|
+
(readyEl && !readyEl.hidden ? readyEl : grid.querySelector("input"))?.focus({ preventScroll: true });
|
|
1665
|
+
});
|
|
1666
|
+
function finishGame() {
|
|
1667
|
+
if (dead || !inf() || phase !== "play") return;
|
|
1668
|
+
cancelFx();
|
|
1669
|
+
clearVisuals();
|
|
1670
|
+
finalMs = performance.now() - t0;
|
|
1671
|
+
phase = "done";
|
|
1672
|
+
clearInterval(watchTimer);
|
|
1673
|
+
watch.classList.replace("pkb-run", "pkb-done");
|
|
1674
|
+
showWatch();
|
|
1675
|
+
syncFin();
|
|
1676
|
+
combo = 0;
|
|
1677
|
+
grid.setAttribute("inert", "");
|
|
1678
|
+
board.classList.add("pkb-over");
|
|
1679
|
+
setResult(true);
|
|
1680
|
+
$("pbody").textContent = "";
|
|
1681
|
+
$("ptime").hidden = false;
|
|
1682
|
+
$("pT").textContent = formatTime(finalMs, 2);
|
|
1683
|
+
$("sScore").textContent = "0";
|
|
1684
|
+
$("sCombo").textContent = "\xD70";
|
|
1685
|
+
$("sBingo").textContent = "0";
|
|
1686
|
+
perfectEl.classList.remove("pkb-settled");
|
|
1687
|
+
replay(perfectEl, "pkb-show");
|
|
1688
|
+
resultFocus().focus({ preventScroll: true });
|
|
1689
|
+
sfx.perfect();
|
|
1690
|
+
const beat = haptics.play("finish");
|
|
1691
|
+
if (!calm) replay($("flash"), "pkb-go");
|
|
1692
|
+
later(() => {
|
|
1693
|
+
if (!calm) replay(board, "pkb-shake");
|
|
1694
|
+
sfx.boom();
|
|
1695
|
+
beat?.(PERFECT_STAMP_BEAT);
|
|
1696
|
+
burst(0, BH, 80, { angle: -Math.PI / 3, spread: 0.8, speed: 21, g: 0.32, star: 0.5, colors: SPARK });
|
|
1697
|
+
burst(BW, BH, 80, { angle: -Math.PI * 2 / 3, spread: 0.8, speed: 21, g: 0.32, star: 0.5, colors: SPARK });
|
|
1698
|
+
rain(2500);
|
|
1699
|
+
countUp($("sScore"), 0, score, 1300, (v) => v.toLocaleString(), true);
|
|
1700
|
+
countUp($("sCombo"), 0, maxCombo, 700, (v) => "\xD7" + v);
|
|
1701
|
+
countUp($("sBingo"), 0, cleared, 700, String);
|
|
1702
|
+
}, PERFECT_STAMP_DELAY_MS);
|
|
1703
|
+
[1250, 1700, 2200].forEach(
|
|
1704
|
+
(at, k) => later(() => {
|
|
1705
|
+
firework(BW * (0.15 + Math.random() * 0.7), BH * (0.12 + Math.random() * 0.3));
|
|
1706
|
+
beat?.(PERFECT_FIREWORKS[k * 2].beat);
|
|
1707
|
+
}, at)
|
|
1708
|
+
);
|
|
1709
|
+
const e = { score, maxCombo, lines: cleared, time: Math.round(finalMs) };
|
|
1710
|
+
options.onFinish?.(e);
|
|
1711
|
+
}
|
|
1712
|
+
finBtn.addEventListener("click", finishGame);
|
|
1279
1713
|
perfectEl.addEventListener("click", (e) => {
|
|
1280
1714
|
if (e.target === perfectEl) perfectEl.classList.remove("pkb-show");
|
|
1281
1715
|
});
|
|
1282
1716
|
if (demo) {
|
|
1283
1717
|
let busy = false;
|
|
1284
1718
|
const prepare = () => {
|
|
1285
|
-
if (checked.every(Boolean)) request(normalize(void 0), null, { reset: true });
|
|
1719
|
+
if (!inf() && checked.every(Boolean)) request(normalize(void 0), null, { reset: true });
|
|
1286
1720
|
else perfectEl.classList.remove("pkb-show");
|
|
1287
1721
|
};
|
|
1288
1722
|
const run = async (steps) => {
|
|
@@ -1290,6 +1724,7 @@ function createPackingBingo(container, options = {}) {
|
|
|
1290
1724
|
busy = true;
|
|
1291
1725
|
try {
|
|
1292
1726
|
prepare();
|
|
1727
|
+
skipReady();
|
|
1293
1728
|
const g = gen;
|
|
1294
1729
|
for (const s of steps) {
|
|
1295
1730
|
await wait(s.delay);
|
|
@@ -1303,21 +1738,100 @@ function createPackingBingo(container, options = {}) {
|
|
|
1303
1738
|
}
|
|
1304
1739
|
};
|
|
1305
1740
|
$("dB").addEventListener("click", () => {
|
|
1741
|
+
if (inf() && phase === "done") return;
|
|
1306
1742
|
const open = () => lines.map((l) => l.filter((i) => !checked[i])).filter((m) => m.length).sort((a, b) => a.length - b.length)[0] ?? [];
|
|
1307
|
-
const pick = checked.every(Boolean) ? [...lines[0]] : open();
|
|
1743
|
+
const pick = !inf() && checked.every(Boolean) ? [...lines[0]] : open();
|
|
1308
1744
|
void run(pick.map((i) => ({ i, delay: 320 })));
|
|
1309
1745
|
});
|
|
1310
1746
|
$("dP").addEventListener("click", () => {
|
|
1747
|
+
if (inf()) return;
|
|
1311
1748
|
const all = items.map((_, i) => i);
|
|
1312
1749
|
const miss = checked.every(Boolean) ? all : all.filter((i) => !checked[i]);
|
|
1313
1750
|
void run(miss.map((i, k) => k < miss.length - 1 ? { i, delay: 170, quiet: true } : { i, delay: 450 }));
|
|
1314
1751
|
});
|
|
1315
1752
|
$("dR").addEventListener("click", () => {
|
|
1316
|
-
if (busy) return;
|
|
1753
|
+
if (busy || inf()) return;
|
|
1317
1754
|
unshuffle();
|
|
1755
|
+
restartGame();
|
|
1318
1756
|
request(demoStart(), null, { reset: true });
|
|
1319
1757
|
});
|
|
1320
1758
|
}
|
|
1759
|
+
const modeEl = container.querySelector('[data-r="mode"]');
|
|
1760
|
+
const radios = modeEl ? Array.from(modeEl.querySelectorAll("button[data-m]")) : [];
|
|
1761
|
+
const syncMode = () => {
|
|
1762
|
+
root.classList.toggle("pkb-inf", inf());
|
|
1763
|
+
radios.forEach((b) => {
|
|
1764
|
+
const on = b.dataset.m === mode;
|
|
1765
|
+
b.setAttribute("aria-checked", String(on));
|
|
1766
|
+
b.tabIndex = on ? 0 : -1;
|
|
1767
|
+
});
|
|
1768
|
+
};
|
|
1769
|
+
function modeFx() {
|
|
1770
|
+
const endless = inf();
|
|
1771
|
+
const title = endless ? M.infiniteTitle?.trim() || ja.infiniteTitle : M.normalTitle?.trim() || ja.normalTitle;
|
|
1772
|
+
const sub = endless ? M.infiniteSub?.trim() || ja.infiniteSub : M.normalSub?.trim() || ja.normalSub;
|
|
1773
|
+
const sparks = endless && !calm ? Array.from({ length: 18 }, (_, k) => {
|
|
1774
|
+
const x = (5 + Math.random() * 90).toFixed(1), y = (6 + Math.random() * 88).toFixed(1);
|
|
1775
|
+
const s = (0.45 + Math.random() * 0.95).toFixed(2), t = Math.round(100 + Math.random() * 950);
|
|
1776
|
+
return `<i style="--x:${x}%;--y:${y}%;--s:${s};--t:${t}ms;--c:${SPARK[k % SPARK.length]}"></i>`;
|
|
1777
|
+
}).join("") : "";
|
|
1778
|
+
mfx.className = "pkb-mfx " + (endless ? "pkb-mfx-inf" : "pkb-mfx-n");
|
|
1779
|
+
mfx.innerHTML = (endless ? `<span class="pkb-mfx-glow"></span>` : "") + sparks + `<span class="pkb-mfx-in"><b class="pkb-mfx-sym">${endless ? "\u221E" : esc(M.stamp)}</b><b class="pkb-mfx-t">${esc(title)}</b><span class="pkb-mfx-s">${esc(sub)}</span></span>`;
|
|
1780
|
+
replay(mfx, "pkb-show");
|
|
1781
|
+
board.classList.add("pkb-switching");
|
|
1782
|
+
if (modeEl && !calm) replay(modeEl, "pkb-bump");
|
|
1783
|
+
sfx.mode(endless);
|
|
1784
|
+
haptics.play(endless ? "infinite" : "normal");
|
|
1785
|
+
const picked = radios.find((b) => b.dataset.m === mode);
|
|
1786
|
+
if (picked) {
|
|
1787
|
+
const p = rel(picked);
|
|
1788
|
+
burst(p.x, p.y, endless ? 46 : 24, { speed: endless ? 7 : 5, g: 0.1, life: 55, size: 0.8, star: endless ? 0.9 : 0.3, colors: endless ? SPARK : void 0 });
|
|
1789
|
+
}
|
|
1790
|
+
later(() => {
|
|
1791
|
+
const c = rel(gw);
|
|
1792
|
+
if (endless) burst(c.x, c.y, 90, { speed: 12, g: 0.07, life: 90, star: 0.75, colors: SPARK });
|
|
1793
|
+
else {
|
|
1794
|
+
if (!calm) replay(board, "pkb-punch");
|
|
1795
|
+
burst(c.x, c.y, 70, { speed: 11, star: 0.2 });
|
|
1796
|
+
}
|
|
1797
|
+
}, endless ? 260 : 430);
|
|
1798
|
+
}
|
|
1799
|
+
mfx.addEventListener("animationend", (e) => {
|
|
1800
|
+
if (e.target !== mfx) return;
|
|
1801
|
+
mfx.classList.remove("pkb-show");
|
|
1802
|
+
board.classList.remove("pkb-switching");
|
|
1803
|
+
mfx.innerHTML = "";
|
|
1804
|
+
});
|
|
1805
|
+
function setMode(next) {
|
|
1806
|
+
if (dead || next === mode || next !== "normal" && next !== "infinite") return;
|
|
1807
|
+
const back = packing;
|
|
1808
|
+
if (next === "infinite") packing = { checked: checked.slice(), order: order.slice() };
|
|
1809
|
+
else packing = null;
|
|
1810
|
+
mode = next;
|
|
1811
|
+
resetAsked = false;
|
|
1812
|
+
if (back && back.order.some((v, s) => v !== order[s])) {
|
|
1813
|
+
flights.forEach((a) => a.cancel());
|
|
1814
|
+
flights = [];
|
|
1815
|
+
clearTimeout(landing);
|
|
1816
|
+
landing = 0;
|
|
1817
|
+
place(back.order);
|
|
1818
|
+
}
|
|
1819
|
+
syncMode();
|
|
1820
|
+
toReady();
|
|
1821
|
+
baseline(back ? back.checked : normalize(void 0));
|
|
1822
|
+
modeFx();
|
|
1823
|
+
options.onModeChange?.(mode);
|
|
1824
|
+
}
|
|
1825
|
+
modeEl?.addEventListener("click", (e) => {
|
|
1826
|
+
const b = e.target.closest("button[data-m]");
|
|
1827
|
+
if (b) setMode(b.dataset.m);
|
|
1828
|
+
});
|
|
1829
|
+
modeEl?.addEventListener("keydown", (e) => {
|
|
1830
|
+
if (!["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].includes(e.key)) return;
|
|
1831
|
+
e.preventDefault();
|
|
1832
|
+
setMode(inf() ? "normal" : "infinite");
|
|
1833
|
+
radios.find((b) => b.dataset.m === mode)?.focus();
|
|
1834
|
+
});
|
|
1321
1835
|
let startOrder = null;
|
|
1322
1836
|
if (orderKey) {
|
|
1323
1837
|
try {
|
|
@@ -1339,7 +1853,10 @@ function createPackingBingo(container, options = {}) {
|
|
|
1339
1853
|
if (!initial && options.defaultChecked) initial = normalize(options.defaultChecked);
|
|
1340
1854
|
if (!initial && demo) initial = demoStart();
|
|
1341
1855
|
fit();
|
|
1342
|
-
|
|
1856
|
+
if (inf()) packing = { checked: initial ?? normalize(void 0), order: order.slice() };
|
|
1857
|
+
syncMode();
|
|
1858
|
+
baseline(inf() ? normalize(void 0) : initial ?? normalize(void 0));
|
|
1859
|
+
if (phase !== "done") toReady();
|
|
1343
1860
|
const ro = typeof ResizeObserver === "function" ? new ResizeObserver(() => {
|
|
1344
1861
|
fit();
|
|
1345
1862
|
drawLines();
|
|
@@ -1348,7 +1865,7 @@ function createPackingBingo(container, options = {}) {
|
|
|
1348
1865
|
ro?.observe(gw);
|
|
1349
1866
|
const onVisibility = () => {
|
|
1350
1867
|
if (!document.hidden) {
|
|
1351
|
-
if (restorePerfectFocus && perfectEl.classList.contains("pkb-show"))
|
|
1868
|
+
if (restorePerfectFocus && perfectEl.classList.contains("pkb-show")) resultFocus().focus({ preventScroll: true });
|
|
1352
1869
|
restorePerfectFocus = false;
|
|
1353
1870
|
return;
|
|
1354
1871
|
}
|
|
@@ -1359,7 +1876,7 @@ function createPackingBingo(container, options = {}) {
|
|
|
1359
1876
|
if (keepPerfect) {
|
|
1360
1877
|
$("sScore").textContent = score.toLocaleString();
|
|
1361
1878
|
$("sCombo").textContent = "\xD7" + maxCombo;
|
|
1362
|
-
$("sBingo").textContent = String(
|
|
1879
|
+
$("sBingo").textContent = String(resultLines());
|
|
1363
1880
|
perfectEl.classList.add("pkb-show", "pkb-settled");
|
|
1364
1881
|
}
|
|
1365
1882
|
combo = 0;
|
|
@@ -1369,6 +1886,14 @@ function createPackingBingo(container, options = {}) {
|
|
|
1369
1886
|
setChecked(next, opts = {}) {
|
|
1370
1887
|
if (dead) return;
|
|
1371
1888
|
const n2 = normalize(next);
|
|
1889
|
+
if (packing) {
|
|
1890
|
+
packing.checked = n2;
|
|
1891
|
+
resetAsked = false;
|
|
1892
|
+
if (storageKey) writeStore(storageKey, JSON.stringify(n2));
|
|
1893
|
+
return;
|
|
1894
|
+
}
|
|
1895
|
+
if (resetAsked && !n2.some(Boolean)) toReady();
|
|
1896
|
+
resetAsked = false;
|
|
1372
1897
|
const changed = n2.flatMap((v, i) => v !== checked[i] ? [i] : []);
|
|
1373
1898
|
if (!changed.length) return;
|
|
1374
1899
|
if (!opts.animate || changed.length !== 1) {
|
|
@@ -1379,12 +1904,20 @@ function createPackingBingo(container, options = {}) {
|
|
|
1379
1904
|
},
|
|
1380
1905
|
reset() {
|
|
1381
1906
|
if (dead) return;
|
|
1907
|
+
if (inf()) return restartInfinite();
|
|
1382
1908
|
unshuffle();
|
|
1909
|
+
restartGame();
|
|
1383
1910
|
request(normalize(void 0), null, { reset: true });
|
|
1384
1911
|
},
|
|
1385
1912
|
shuffle() {
|
|
1386
1913
|
if (!dead) shuffleCard();
|
|
1387
1914
|
},
|
|
1915
|
+
setMode(next) {
|
|
1916
|
+
setMode(next);
|
|
1917
|
+
},
|
|
1918
|
+
finish() {
|
|
1919
|
+
finishGame();
|
|
1920
|
+
},
|
|
1388
1921
|
refresh() {
|
|
1389
1922
|
if (!dead) render(null, true);
|
|
1390
1923
|
},
|
|
@@ -1394,20 +1927,25 @@ function createPackingBingo(container, options = {}) {
|
|
|
1394
1927
|
lines: cur.slice(),
|
|
1395
1928
|
score,
|
|
1396
1929
|
maxCombo,
|
|
1397
|
-
complete: checked.every(Boolean),
|
|
1930
|
+
complete: !inf() && checked.every(Boolean),
|
|
1931
|
+
mode,
|
|
1932
|
+
cleared,
|
|
1933
|
+
finished: inf() && phase === "done",
|
|
1934
|
+
packing: packing && { checked: packing.checked.slice(), order: packing.order.slice() },
|
|
1398
1935
|
size: n,
|
|
1399
1936
|
free: L.free,
|
|
1400
|
-
order: order.slice()
|
|
1937
|
+
order: order.slice(),
|
|
1938
|
+
time: Math.round(elapsed())
|
|
1401
1939
|
};
|
|
1402
1940
|
},
|
|
1403
1941
|
destroy() {
|
|
1404
1942
|
if (dead) return;
|
|
1405
1943
|
dead = true;
|
|
1406
|
-
|
|
1407
|
-
if (playDialog?.open) playDialog.close();
|
|
1944
|
+
stopTimers();
|
|
1408
1945
|
timers.forEach((id) => clearTimeout(id));
|
|
1409
1946
|
timers.clear();
|
|
1410
1947
|
clearTimeout(landing);
|
|
1948
|
+
clearTimeout(clearTimer);
|
|
1411
1949
|
flights.forEach((a) => a.cancel());
|
|
1412
1950
|
cancelAnimationFrame(raf);
|
|
1413
1951
|
cancelAnimationFrame(tw);
|
|
@@ -1423,11 +1961,12 @@ function createPackingBingo(container, options = {}) {
|
|
|
1423
1961
|
// src/react.tsx
|
|
1424
1962
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1425
1963
|
var PackingBingo = (0, import_react.forwardRef)(function PackingBingo2(props, ref) {
|
|
1426
|
-
const { checked, defaultChecked, defaultOrder, className, style, onChange, onBingo, onPerfect, messages, fonts: _fonts, ...config } = props;
|
|
1964
|
+
const { checked, defaultChecked, defaultOrder, className, style, onChange, onBingo, onPerfect, onModeChange, onFinish, mode, messages, fonts: _fonts, ...config } = props;
|
|
1427
1965
|
const host = (0, import_react.useRef)(null);
|
|
1428
1966
|
const inst = (0, import_react.useRef)(null);
|
|
1429
|
-
const callbacks = (0, import_react.useRef)({ onChange, onBingo, onPerfect });
|
|
1430
|
-
callbacks.current = { onChange, onBingo, onPerfect };
|
|
1967
|
+
const callbacks = (0, import_react.useRef)({ onChange, onBingo, onPerfect, onModeChange, onFinish });
|
|
1968
|
+
callbacks.current = { onChange, onBingo, onPerfect, onModeChange, onFinish };
|
|
1969
|
+
const latestMode = (0, import_react.useRef)(mode);
|
|
1431
1970
|
const messagesRef = (0, import_react.useRef)(messages);
|
|
1432
1971
|
messagesRef.current = messages;
|
|
1433
1972
|
const controlled = checked !== void 0;
|
|
@@ -1457,13 +1996,20 @@ var PackingBingo = (0, import_react.forwardRef)(function PackingBingo2(props, re
|
|
|
1457
1996
|
callbacks.current.onChange?.(next);
|
|
1458
1997
|
},
|
|
1459
1998
|
onBingo: (e) => callbacks.current.onBingo?.(e),
|
|
1460
|
-
onPerfect: (e) => callbacks.current.onPerfect?.(e)
|
|
1999
|
+
onPerfect: (e) => callbacks.current.onPerfect?.(e),
|
|
2000
|
+
onFinish: (e) => callbacks.current.onFinish?.(e),
|
|
2001
|
+
mode: latestMode.current,
|
|
2002
|
+
onModeChange: (m) => {
|
|
2003
|
+
latestMode.current = m;
|
|
2004
|
+
callbacks.current.onModeChange?.(m);
|
|
2005
|
+
}
|
|
1461
2006
|
});
|
|
1462
2007
|
inst.current = instance;
|
|
1463
2008
|
return () => {
|
|
1464
2009
|
const state = instance.getState();
|
|
1465
|
-
|
|
1466
|
-
|
|
2010
|
+
const packed = state.packing ?? state;
|
|
2011
|
+
if (!controlled) latest.current = packed.checked;
|
|
2012
|
+
latestOrder.current = packed.order;
|
|
1467
2013
|
latestKey.current = storageKey;
|
|
1468
2014
|
instance.destroy();
|
|
1469
2015
|
if (inst.current === instance) inst.current = null;
|
|
@@ -1475,11 +2021,16 @@ var PackingBingo = (0, import_react.forwardRef)(function PackingBingo2(props, re
|
|
|
1475
2021
|
(0, import_react.useEffect)(() => {
|
|
1476
2022
|
if (controlled && checked) inst.current?.setChecked(checked, { animate: true });
|
|
1477
2023
|
}, [controlled, checked]);
|
|
2024
|
+
(0, import_react.useEffect)(() => {
|
|
2025
|
+
if (mode) inst.current?.setMode(mode);
|
|
2026
|
+
}, [mode]);
|
|
1478
2027
|
(0, import_react.useImperativeHandle)(
|
|
1479
2028
|
ref,
|
|
1480
2029
|
() => ({
|
|
1481
2030
|
reset: () => inst.current?.reset(),
|
|
1482
2031
|
shuffle: () => inst.current?.shuffle(),
|
|
2032
|
+
setMode: (m) => inst.current?.setMode(m),
|
|
2033
|
+
finish: () => inst.current?.finish(),
|
|
1483
2034
|
setChecked: (next) => inst.current?.setChecked(next, { animate: true }),
|
|
1484
2035
|
getState: () => inst.current?.getState() ?? null
|
|
1485
2036
|
}),
|
|
@@ -1495,5 +2046,6 @@ var PackingBingo = (0, import_react.forwardRef)(function PackingBingo2(props, re
|
|
|
1495
2046
|
bingoLines,
|
|
1496
2047
|
createPackingBingo,
|
|
1497
2048
|
en,
|
|
1498
|
-
ja
|
|
2049
|
+
ja,
|
|
2050
|
+
speedBonus
|
|
1499
2051
|
});
|