# Параметры: громкость и сетка карты

Подготовлено 2026-10-28 после публикации 0.9.0. Публикация и браузерная проверка этого изменения выполняются интегратором отдельно; агент не пересобирал общий dist и не размещал изменения.

## Пользовательское поведение

В шапке остаётся кнопка **«Параметры»**. Прежний переключатель звука и кнопка сетки перенесены в бумажное модальное окно; ссылка «Ход разработки» убирается из шапки отдельной правкой интегратора. Окно содержит два независимых ползунка 0–100% с числовыми подписями, чекбокс сетки и крестик справа сверху. Оно не запрещено на ходу ИИ; клавиши ползунков не доходят до управления юнитами.

Громкость звуков управляет единственным master gain существующего Web Audio проигрывателя. Громкость музыки меняет `HTMLAudio.volume` одного уже созданного проигрывателя. Значение применяется сразу; изменение положительной громкости не создаёт другой AudioContext, не меняет src и не сбрасывает позицию музыки. Исходная относительная громкость отдельного эффекта/события сохраняется и не умножается на пользовательский уровень повторно. Шкала линейная; совпадение с кривой громкости Windows-оригинала не заявляется.

0% отключает только соответствующий канал. Музыка ставится на паузу; последующее включение жестом продолжает ту же запись. Эффекты останавливаются и контекст приостанавливается; уже завершённые эффекты не повторяются при включении. Ненулевые эффекты и музыка могут использоваться по отдельности.

Настройки хранятся в браузере отдельно от партии, не меняют ядро, команды, RNG и экспорт/импорт сохранения. Сетка применяется и после перезагрузки. Ctrl+G по-прежнему вызывает `.click()` того же чекбокса, даже когда окно закрыто; его `change` обрабатывает исключительно сетку и сохранение. Владелец отрисовки карты больше не привязывает обработчик к прежней кнопке.

## Хранение и миграция

`civilization-audio-preferences` содержит `{schemaVersion:1,effects:27,music:9}`. Уровни представлены целыми процентами. Конечные числовые значения 0–100 округляются, неправильное поле, строка, null, отрицательное или превышающее 100 значение становится 0 независимо от второго поля. Повреждённый JSON/неизвестная схема дают 0/0. Отказ localStorage не прерывает работу текущей страницы.

Если нового ключа ещё нет, прежнее `civilization-sound-enabled=true` переводится в **35% звуков / 12% музыки**, то есть прежние реальные уровни обоих master-каналов. Выключенное/отсутствующее/неправильное прежнее предпочтение даёт **0% / 0%**. Новый формат сохраняется отдельно; старый ключ не удаляется и не переписывается. После миграции именно новый ключ определяет поведение. Сетка использует отдельный `civilization-map-grid`; только точная строка `true` включает её.

Даже сохранённый ненулевой уровень не создаёт Audio/AudioContext и не запускает проигрывание до нового pointerdown/keydown. Настоящий `input` ползунка активирует свой канал непосредственно в том же жесте; это покрывает переход 0→положительное, когда capture pointerdown ещё видел старый нулевой уровень. Программный input/setAudioVolume меняет/сохраняет уровень, но не вызывает play/resume. Программный клик сетки не используется как аудиожест.

Для обоих каналов закрыта гонка pending play/resume → 0 → программное положительное значение: поздний результат старого запроса не отменяет выключение и не возобновляет звук без следующего разрешённого жеста. Музыкальные ошибки, ленивые MP3 и autoplay rejection обрабатываются прежним проигрывателем.

## Области реализации и проверка

`media/audio-preferences.mjs` отвечает за формат и валидацию; `runtime/resources.mjs` — применение, сохранение и активацию; `ui/preferences.mjs/preferences.css` — окно/ползунки/сетку. `media/audio-player.mjs` и `background-music.mjs` изменяют только громкость/жизненный цикл проигрывателей. Правки общего app/index/hud/atlants передаются интегратору патчем; исходные версии и схема партии агентом не повышаются.

Изолированно прошли **11 проверок** `tests/audio-preferences.test.mjs`: миграция, независимая валидация и хранилище, единственный master gain, отсутствие повторного умножения событий, один контекст, обе гонки громкости, независимые ресурсы/жесты, UI и сетка. Прежние семь проверок музыкального проигрывателя также прошли. Команды:

```bash
python3 scripts/run-limited.py -- node tests/audio-preferences.test.mjs
python3 scripts/run-limited.py -- node tests/background-music.test.mjs
```

Браузерные драйверы подготовлены, но агент их не запускал. `browser-preferences.cjs` проверяет видимые настройки, настоящий trusted 0→positive, программное изменение без autoplay, число AudioContext, live gain, сохранность текущей музыки, оба независимых mute, сохранение уровней/сетки после reload, Ctrl+G, неизменность игры/PRNG настройками, узкое окно 390px и настоящий эффект конца хода при выключенной музыке. `browser-native-music.cjs` адаптирован к ползунку музыки, сохранив проверки исходного ICO/MP3, ленивой загрузки, времени и повторной активации после reload. Отчёты: `data/browser-preferences-verification.json`, `data/browser-native-music-verification.json`.

```bash
python3 scripts/run-limited.py -- node tests/browser-preferences.cjs
python3 scripts/run-limited.py -- node tests/browser-native-music.cjs
```

Для публичного сайта используется `CIV3_BROWSER_URL=https://vm.ya-erm.ru/civilization/`. Прежние browser-media/interturn-performance/golden-age/visual-feedback переключены на ползунок эффектов через `tests/helpers/preferences-controls.cjs`, classic-controls проверяет `.checked` сетки. Общие сборка, DOM, полный прогон и отметки в пользовательских заметках выполняются интегратором после freeze.


## Публичная интеграцияv0.10.0

Опубликовано28.10.2026 в05:13 МСК, код`f1d04cdd4cd1b1be`. Сценарий реально выполнен вChromium локально и на основном HTTPS-сайте. [Текущий отчёт](data/browser-preferences-verification.json). Общая сверка146 файлов кода/данных/ресурсов не нашла расхождений; независимые служебные страницы сохранены. Полный Windows-оригинал этими проверками не объявлен воспроизведённым.
