plinkit 1.0.0-dev.0 → 1.0.0-dev.2

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 CHANGED
@@ -58,12 +58,79 @@ game.spawnBall()
58
58
  - `resize()` — перечитать ширину родителя и пересчитать viewport.
59
59
  - `destroy()` — остановить цикл и освободить ресурсы.
60
60
  - `getState()` — получить `{ balance, ballCost }`.
61
+ - `setBallCost(value)` — изменить цену шарика на лету. Обновляет `getState().ballCost`
62
+ и синхронно вызывает `onBalanceChange`, чтобы UI мог пересчитать disabled-состояние
63
+ кнопки. Уже летящие шарики сохраняют свой исходный `wager` — payout считается по
64
+ той цене, по которой шарик был запущен. Бросает `TypeError`, если передано не
65
+ конечное число или отрицательное значение.
66
+ - `setMuted(value)` — включить/выключить звук. No-op, если `options.audio` не задан.
67
+ - `isMuted()` — текущее состояние mute. Возвращает `true`, если аудио не сконфигурировано.
68
+ - `setVolume(value)` — общая громкость 0..1. No-op, если `options.audio` не задан.
69
+ - `playUiTap()` — проиграть UI-сэмпл из `options.audio.uiTapUrl`. Удобно вызывать
70
+ из click/pointerup-обработчиков кнопок продукта, чтобы UI-звуки разделяли
71
+ mute/volume/auto-unlock с физикой.
72
+
73
+ ### Звуки
74
+
75
+ Опциональный встроенный аудио-движок на Web Audio API. По умолчанию звук
76
+ **включён** (`muted: false`); UX-toggle делается через `setMuted(value)` и
77
+ `isMuted()`.
78
+
79
+ ```ts
80
+ const game = new Plinkit({
81
+ // ...остальные опции
82
+ audio: {
83
+ pegHitUrl: "/sounds/peg.mp3", // удар шарика о peg/guide
84
+ bucketHitUrl: "/sounds/bucket.mp3", // попадание в корзину (момент settle)
85
+ uiTapUrl: "/sounds/tap.mp3", // UI-клик, проигрывается через playUiTap()
86
+ masterVolume: 0.6,
87
+ },
88
+ })
89
+
90
+ spawnButton.addEventListener("click", () => {
91
+ game.playUiTap()
92
+ game.spawnBall()
93
+ })
94
+
95
+ soundToggle.addEventListener("click", () => {
96
+ game.setMuted(!game.isMuted())
97
+ })
98
+ ```
99
+
100
+ `pegHitUrl` срабатывает на каждом столкновении с пегом — громкость линейно
101
+ зависит от скорости столкновения, плюс throttling per-ball ≤1 звук в 35 мс.
102
+ `bucketHitUrl` играется один раз при попадании шарика в корзину (без throttling
103
+ и с фиксированной громкостью) — это сигнал «раунд завершён». `uiTapUrl` —
104
+ короткий сэмпл UI-клика; его проигрывает `playUiTap()`, который продукт
105
+ вызывает из своих click/pointerup-обработчиков. Все три звука разделяют общий
106
+ `masterVolume` и состояние mute.
107
+
108
+ Под капотом:
109
+
110
+ - **Авто-разблокировка на мобильных.** `PlinkitAudio` ставит глобальные слушатели
111
+ `pointerdown` / `pointerup` / `touchstart` / `touchend` / `keydown` на `window`
112
+ с `capture: true`. На первом же жесте `AudioContext.resume()` снимает
113
+ suspended-state; слушатели сразу же снимаются. Дополнительно играется
114
+ silent-buffer ping (`(1, 1, sampleRate)` с gain `0.00001`), чтобы окончательно
115
+ «разбудить» iOS Web Audio — без этого первый реальный звук на iOS Safari
116
+ иногда не воспроизводится.
117
+ - **`visibilitychange`.** При возврате вкладки в видимое состояние контекст
118
+ повторно резюмится — iOS/Safari часто оставляет его `interrupted` после фона.
119
+ - **Анти-«пулемёт».** Лимит одновременных голосов (16) и лёгкая вариация
120
+ `playbackRate` (`±8%`) — чтобы частые peg-удары не сливались в монотонный шум.
121
+ - **До первого жеста** `playHit` тихо игнорируется. Это нормальное поведение
122
+ любой Web Audio игры на iOS.
61
123
 
62
124
  ### Экономика
63
125
 
64
126
  - Значения передаются через `initialBalance`, `ballCost`, `multipliers`.
65
127
  - При попадании в корзину начисляется `ballCost * multiplier`.
66
128
  - Количество корзин = `multipliers.length` = `bottomPegCount - 1`.
129
+ - `multipliers` — это RTP-таблица и не должны меняться вместе с `ballCost`:
130
+ ставку масштабируйте через `setBallCost`, а множители оставляйте фиксированными.
131
+ - Для предсказуемого UX на лендинге держите одну ставку в пределах ~5% от банка
132
+ (`initialBalance`). Иначе один шарик с пиковым множителем (например `×7`) может
133
+ дать выплату, сравнимую со всем стартовым балансом.
67
134
 
68
135
  ### houseEdge
69
136