Files
fishing-game/KODA.md
T
andrey a168e830cc Этап 46 ревизия: жильцы аквариума — те же SVG-портреты, что на карточке улова. (1) Слой #aqFishLayer (body, после canvas): position:fixed/inset:0/z-index:5/pointer-events:none, включается только под сценой (body.aq-open) — тапы по рыбам по-прежнему ловит canvas (aqTap). (2) aqBuildFishEl(i) — <div class=aqfish><svg viewBox="0 0 160 100"><use href="#fish-<id>"/></svg></div>, тот же спрайт FISH-SVG, что у дневника/карточки; размер от прежнего aqFishSize (длина 2·size, высота по 160:100); aqPlaceFish(el,rt) каждый кадр из updateAquarium ставит translate(x,y)+translate(-50%,-50%)+rotate(±4° по tail)+scale(dir,1). (3) Вариант — 1:1 с карточкой: перекраска --f1/--f3 inline (AQ_VARIANT_RECOLOR, цвета из CSS #card.<variant>-card) + класс .var<id> (AQ_VARIANT_CLASS/aqApplyVariant; silver — grayscale+brightness, rainbow — aqrainbowhue, остальные — цветной drop-shadow). Вместо hue-хака AQ_VARIANT_FX/AQ_VARIANT_HUE (удалены) и прежней искры. (4) initAquarium() пересобирает слой с clip-path по банке (index aqFishEls = index save.aqFish, битый вид → null без сдвига индексов); aqReleaseSel() удаляет DOM-элемент точечно (без телепорта остальных); на canvas остались вода/грунт/растения/пузыри/стекло + аура болотных (уходит ЗА портрет) и рамка выбора. drawFishShape в банке больше не используется (в сценах ловли не тронут). Геймплей/баланс/сейв/экономика не менялись. (5) Тесты 218→222: блок «портреты» в §AQ (use href = #fish-<id> на каждый экземпляр; .var<id>/--f1 по варианту; transform следует за aqFishRt индекс-в-индекс; вне сцены display:none/body без aq-open, pointer-events:none) + обновлён список глобалов в шапке §AQ. (6) Скриншоты: перегенерированы только кадры aquarium (6 PNG, оба набора). (7) Доки: AGENTS.md (пункт «Аквариум» + глобалы), KODA.md (код-карта/глобалы/грабли), README.md, docs/PLAN-AQUARIUM.md (риск №1 закрыт), DEVLOG (подраздел «Ревизия Этапа 46»). Проверено: 222/222 PASS exit 0, check.mjs OK, balance.mjs — пины без изменений (Река 65.1/125.9 · Озёра 194.1/249.0 · Болото 240.3/328.9 · Море 297.3/476.7 · Океан 329.1/552.7), pack.mjs 5/5 + pack-smoke 6/6.
2026-09-22 23:02:00 +03:00

28 KiB
Raw Blame History

KODA.md — инструкционный контекст проекта «Рыбалка»

Этот файл — рабочая шпаргалка для ИИ-агентов и разработчиков: что за проект, как его собирать, проверять и правильно править. Основной источник истины по деталям — AGENTS.md и docs/DEVLOG.md; документ, который вы читаете, сводит их воедино.


1. Обзор проекта

«Рыбалка» — браузерная мобильная игра про рыбалку, опубликованная как автономный файл index.html (HTML + CSS + JS в одном <script>, ~2940 строк). Ни сборки, ни зависимостей, ни бэкенда: файл открывается двойным кликом (file://) или раздаётся любым статическим хостингом. Целевая площадка публикации — Яндекс Игры (SDK подхватывается автоматически, локально работает встроенный стаб).

Ключевые архитектурные решения

Решение Зачем
Один самодостаточный index.html Нет сборки и зависимостей; требование площадки — строго один файл в архиве
Вся графика рисуется на <canvas> Работает офлайн, грузится мгновенно, нет внешних ассетов
Вшитый emoji-шрифт (base64 @font-face) Единый вид emoji на всех платформах; внешний .woff2 невозможен (один файл)
HTML-оверлеи для UI, Canvas — только мир Резкие доступные кнопки и текст; игровой мир — 60 fps
Звук — генеративный WebAudio, вибрация — navigator.vibrate Ноль внешних ресурсов
Прогресс в localStorage (ключ fishing-save-v1) Мобильная игра без аккаунтов и сервера; миграция старых сейвов обязательна
Портрет, управление одним пальцем, Pointer Events Работают и палец, и мышь — на этом держатся автотесты

Игровой цикл

бросок (удержание = мощность) → ожидание → клёв («!») → подсечка (тап в окно)
→ вываживание (тяни / отпускай) → {улов / обрыв шнура / срыв} → монеты → лавка

Машина состояний: MENU → IDLE → CHARGE → CAST → WAIT → APPROACH → BITE → REEL → SNAP / CARD → IDLE.

Основные технологии

  • «Чистый» JavaScript современного стандарта ('use strict', стрелочные функции), без фреймворков и сборщиков.
  • Canvas 2D — весь мир: небо, вода, лодка, поплавок, оснастка, рыбы, частицы, метры.
  • WebAudio API — 12 генеративных эффектов; navigator.vibrate — тактильный отклик.
  • SDK Яндекс Игр (ysdk) через единственную обёртку YG + собственный стаб-режим.
  • Инструментарий проверки: Node.js (без зависимостей), Chromium + puppeteer-core, Python + fonttools/brotli.

Структура index.html (секции помечены комментариями-разделителями)

Секция О чём
ДАННЫЕ SPECIES, SPECIES_NIGHT, LOCATIONS (5 локаций, этап 45 — +Болото), RIVER_* / LAKE_* / SEA_* / OCEAN_* / SWAMP_* + табличный SPECIES_BY_LOCATION(_NIGHT), BOSS_BY_LOCATION (босс каждой локации, этап 37; +Болото, этап 45), VARIANTS, WEATHER, RODS, LURES, апгрейды, скины. Здесь крутится баланс
СОХРАНЕНИЕ save, freshSave(), loadSave() (со всеми миграциями), persist()
M3: ЦИКЛ ДНЯ/НОЧИ DAY_LENGTH, dayTime, dayPhase, setDayTime()
M8: ПОГОДА WEATHER, таймеры, weatherActive(), updateWeather()
M2: НАГРАДЫ квесты дня (QUEST_TPLS), стрик (STREAK_REWARDS), 38 достижений (ACHIEVEMENTS), achBonus()
ХОЛСТ И РАЗМЕРЫ resize(), DPR, layout (waterY, boatX)
ЗВУК ac(), beep(), noiseBurst(), sfx(), vib()
СОСТОЯНИЕ ИГРЫ все мутабельные глобалы геймплея
ЛОГИКА pickSpecies(), biteChance(), rollWeight(), hook(), fishForce(), updateReel(), catchFish(), updateGame()
ОТРИСОВКА UI_FONT, drawSky/Water/Scenery/AmbientFish/Boat/Bobber/Meters/Weather(), свечение Болота (drawGlowAura()/fishGlow()/GLOW_RGBA), декор drawSwampDecor() (pads/frogs), draw()
АКВАРИУМ (Этап 46) aqOpen, initAquarium()/updateAquarium()/drawAquarium(), aqTap()/aqReleaseSel(), слоты (AQ_BASE_SLOTS/AQ_MAX_SLOTS/AQ_SLOT_COSTS), DOM-слой портретов #aqFishLayer (aqBuildFishEl()/aqPlaceFish()/aqFishEls, вариант .var<id>/--f1)
Y1: SDK Яндекс Игр обёртка YG, реклама (tryInterstitial(), #btnRv), пауза платформы
Y5: i18n RU/EN таблица T, t()/tt(), setLang(), resolveLang(), renderMenu()
ЦИКЛ frame() — requestAnimationFrame, дельта-тайм, пауза
ВВОД pointerdown / pointerup
UI: МАГАЗИН / ДНЕВНИК / КАРТОЧКА 6 вкладок лавки, карточка улова
КНОПКИ, СТАРТ обработчики, инициализация, ожидание emoji-шрифта

Контент игры (кратко)

  • 37 видов рыб (0.05–100 кг, 5 уровней редкости, цена 4–1500 🪙/кг; этап 38 — лестница веса: Озёра ×8, Море ×6, Океан ×4, кит ×2; этап 39 — цены Озёр/Моря/Океана урезаны), пять локаций со СВОЕЙ ихтиофауной (без пересечений, этапы 35/36/45; по цене воды: Река → Озёра → Болото → Море → Океан): Река — 8 базовых (бесплатно), Озёра — 10 озёрных (65 536 🪙; zander/chuch/crayfish этап 5, bleak/roundel/eelpout/tench/navaga этап 33, rudd/cisco этап 35), Море — 5 морских (131 072 🪙 = ×2 Озёр; herring/mackerel/flounder/tuna/swordfish этап 35), Океан — 9 глубоководных (196 608 🪙 = ×1.5 Моря; moray/grouper/turtle/angler/octopus/manta/shark/squid/whale этап 36), Волшебное болото — 5 светящихся (90 000 🪙; glowfin/moonfish/emberfin/violetlure/spiritfin этап 45, ночью светятся разными цветами).
  • 8 вариантов рыбы: обычная, медная ×1.5, серебряная ×2, кварцевая ×2.5, золотая ×3, изумрудная ×4, алмазная ×4.5, радужная ×5 (топ). P(особой) ≈ 31%, шанс усиливают наживка и дождь.
  • 4 удочки и 4 приманки, каждая с апгрейдами до 4 уровня (фонарик для ночи, леска, тормоз, катушка, клёв, редкие/крупные).
  • Цикл суток ~5 минут, погода (дождь 🌧️ ×3 к особым, туман 🌫️ ×1.5 к клёву), босс в каждой локации (Река — осётр, Озёра — гигантский рак, Море — рыба-меч, Океан — синий кит, Болото — дух болота; босс весит 20–50 кг) в 3 фазы (награда ×5).
  • Награды: 3 квеста дня (детерминированы от даты), стрик входов до 7 дней со «щитом», 38 достижений с постоянными бонусами.
  • Лавка с вкладками: Лавка 🛒, Дневник 📔, Награды 🎯, Достижения 🏅, Скины 🎨, Локации 🗺️. Плюс хаб «Меню» #gamemenu из паузы.
  • Аквариум 🐠 (этап 46): декоративная сцена коллекции экземпляров (вид+вариант+вес), вход из хаба «Меню»; кнопка «В аквариум» на карточке улова кладёт бесплатную копию (модель C — экономика не затронута), слоты 6→12 за монеты (1500…100000 🪙), тап по рыбе — поповер с весом/редкостью и «Выпустить» (без монет).
  • Монетизация: interstitial (2 мин сессии, кулдаун 3 мин) и rewarded «×2 за улов».
  • Локализация RU/EN через ysdk.environment.i18n.lang.

2. Сборка, запуск и проверка

Запуск игры

# Просто открыть в браузере
xdg-open index.html            # или открыть файл руками (file:// работает)

# Локальный dev-сервер
python3 -m http.server 8000    # → http://localhost:8000

# Отладка с реальным SDK Яндекса локально
# открыть с ?sdk=1 и положить sdk.js рядом с index.html

Линт и типы

Отсутствуют. Сборки, package.json в корне, ESLint/TypeScript/Prettier нет. Единственная статическая проверка — синтаксис JS, вынутый из HTML:

node tools/check.mjs           # блок-за-блоком прогоняет JS через node --check
                               # при ошибке печатает номер строки в index.html

Автотесты (главный рубеж)

cd tests
npm install                    # ставит puppeteer-core
npm test                       # = node test.js  → ждать 222/222 PASS и exit 0
  • Браузер берётся из process.env.CHROMIUM || '/usr/bin/chromium-browser' — при необходимости поправьте путь.
  • URL игры резолвится от каталога репо; если тест падает на старте — проверьте путь к Chromium.
  • Граница полуночи: если прогон стартует в пределах 5 минут от 00:00, test.js сам ждёт безопасного окна (иначе падают date-ассерты квестов/стрика). Это не баг.
  • Тест управляет игрой мышью (Pointer Events) и читает/подталкивает внутренние глобалы через page.evaluate.

Прочие проверки и артефакты

node tests/gen-screenshots.js [каталог-сборки] [папка-вывода] [язык]
    # 15 кадров × 3 вьюпорта (десктоп 1280×720, моб. портрет 360×640@2x,
    # моб. ландшафт 800×450@1.6x) = 45 PNG, 24-бит без альфа.
    # Без аргументов → screenshots-wiki/ (RU); с 'en' → screenshots-wiki-en/.
    # README ссылается на screenshots-wiki/mobp-* (мобильный портрет).

node tests/orca-playtest.js    # браузерный плейтест через Orca CLI (76 проверок).
    # Требует ЗАПУЩЕННЫЙ Orca app и orca-ide (или ORCA_CLI_COMMAND).
    # Если Orca не запущен — шаг ПРОПУСКАЕТСЯ и НЕ блокирует коммит.
    # Пишет orca-*.png в каталог запуска. Только stdlib Node.

node tools/pack.mjs            # → dist/fishing-yandex.zip + самопроверка 5 пунктов
node tests/pack-smoke.js       # smoke играбельности КОПИИ из архива, 6 проверок

Перегенерация встроенного emoji-шрифта

python3 -m venv tools/.venv && tools/.venv/bin/pip install fonttools brotli
python3 tools/emoji-font.py            # ОБЯЗАТЕЛЬНО при добавлении новых emoji
python3 tools/emoji-font.py --check    # падает, если субсет не покрывает index.html

Исходник шрифта — tools/fonts-src/NotoColorEmoji-Regular.ttf (в .gitignore). Блок @font-face в index.html заключён в маркеры <!-- EMOJI-FONT:BEGIN/END --> и руками не редактируется.

Публикация

node tools/pack.mjs        # сборка архива (только index.html в корне) + 5 самопроверок
node tests/pack-smoke.js   # прогон игры из распакованной копии
# затем загрузить dist/fishing-yandex.zip в консоль Яндекс Игр:
#   монетизация — включена, облачные сейвы — выключены, платформы — без Android TV,
#   debug-mode=16 — для проверки лоадера

Публикация в Gitea (git.shstk.ru): git push; токен — кратковременный, после push отзывается, в репозиторий и документы попадают только плейсхолдеры <ТОКЕН>.


3. Правила разработки

Стиль и соглашения

  • Весь UI, комментарии и документация — на русском. При правках сохраняйте этот стиль (EN-строки живут только в i18n-таблицах *En / T.en).
  • Один файл, один <script>, 'use strict'; новый код размещать внутри соответствующей помеченной секции.
  • Компактный «плотный» синтаксис (короткие имена, однострочные ветки) — исторически сложившийся стиль проекта; новые функции именовать понятно и в духе соседнего кода (updateReel, variantChance, drawMeters).
  • Каждая фича помечена меткой этапа (M2, M3, M4, M5, M6, M8, Y1…Y5, Этап 5/22/28/29/…) — новые правки тоже подписывать, откуда они.
  • Ввод — только Pointer Events (pointerdown/pointerup); заменять на touch/mouse-обработчики нельзя (на них держатся тесты).
  • Любая новая величина, влияющая на баланс (шансы, силы, цены), — в константах секции ДАННЫЕ, а не разбросана по логике. Единственные «точки истины»: variantChance() — все вероятности вариантов, weatherActive() — все чтения погоды, fishForce()/updateReel() — физика боя, effRod()/effLure() — эффективные статы снастей.

Обязательный цикл изменений

# 1. поправить index.html
node tools/check.mjs                     # 2. синтаксис
cd tests && npm install && npm test      # 3. главный рубеж: ждать 222/222 PASS, exit 0
node tools/emoji-font.py                 #    (если добавляли emoji) + --check
node tests/gen-screenshots.js            # 4. переснять скриншоты, если менялась картинка
node tools/pack.mjs && node tests/pack-smoke.js   # 5. перед публикацией
git add -A && git commit -m "…" && git push

После правок index.html или tests/test.js плейтест запускать обязательно.

Тесты и их ограничения

  • Тесты гоняют игру через внутренние глобалы в page.evaluate: state, save, biteTimer, reel, SPECIES, SPECIES_NIGHT, rod(), lure(), SHINY_CHANCE, forceBoss, forceBite, forceVariant, VARIANTS, rollVariant(), variantChance(), fishForce(), stateBoss, bossPhase, dayPhase, pickSpecies(), setDayTime(), todayStr(), dateShift(), isoWeek(), achBonus(), forceWeather, weather, weatherT, weatherGap, WEATHER, WEATHER_GAP_MIN/MAX, WEATHER_DURATION, weatherActive(), BOAT_SKINS, BOBBER_SKINS, LOCATIONS, RIVER_SPECIES(_NIGHT), LAKE_SPECIES(_NIGHT), SEA_SPECIES(_NIGHT), OCEAN_SPECIES(_NIGHT), SWAMP_SPECIES(_NIGHT) (+ *_IDS/*_WEIGHTS локаций), SAVE_KEY, loadSave, freshSave, ACHIEVEMENTS, checkAchievements, questText, YG (+ YG.counts), forceLang, setLang(), LANG, trName(), trRarity(), pauseOpen, uiOpen, dayTime и имена состояний MENU/IDLE/CHARGE/CAST/WAIT/APPROACH/BITE/REEL/SNAP/CARD; аквариум (этап 46): aqOpen, aqFishRt, aqSel, aqTank, AQ_BASE_SLOTS, AQ_MAX_SLOTS, AQ_SLOT_COSTS, AQ_VARIANT_RECOLOR, AQ_VARIANT_CLASS, aqApplyVariant, aqFishEls, aqBuildFishEl, aqPlaceFish, initAquarium, updateAquarium, drawAquarium, aqFishSize, aqTap, showAqPop, hideAqPop, renderAqBar, aqReleaseSel, openAquarium, closeAquarium, syncKeepBtn, cardSpec, save.aqFish/save.aqSlots. Переименование любого из них ломает тесты молча — имена держать, либо обновлять тест в том же коммите.
  • Числа проверок зафиксированы в README (222/222 в tests/test.js, 76/76 в Orca, 6/6 в pack-smoke) — при добавлении проверок обновлять доки в том же коммите.
  • Изменение физики вываживания требует Monte-Carlo-проверки точными формулами (см. docs/PHYSICS-RESEARCH.md, §4.7) и синхронной правки тестов.
  • Пины шансов в тестах снимаются на нейтральной снасти (снапшот/рестор save.lure/lureLv внутри eval).
  • В браузере Orca (живой плейтест) нельзя использовать фиксированные sleep для ожидания состояний — rAF троттлится; только polling с таймаутом.

Сейвы и обратная совместимость

  • Ключ localStorage — fishing-save-v1, не менять.
  • Любое изменение формата сейва → новая миграция в loadSave(). Старые сейвы обязаны оставаться читаемыми; новые счётчики желательно читать через ||0 (тогда миграция не нужна).
  • Не менять id существующих записей (SPECIES, ACHIEVEMENTS, VARIANTS, QUEST_TPLS) — на них завязаны старые сейвы и пины тестов.
  • Река (RIVER_SPECIES(_NIGHT)) — только 8 базовых видов: новые виды добавлять лишь в таблицы СВОЕЙ воды (*_IDS + *_DAY_WEIGHTS/*_NIGHT_WEIGHTS, как Болото этапа 45), иначе сломаются роллы и пины тестов.
  • Новые виды — только в КОНЕЦ SPECIES (после spiritfin, этап 45): SPECIES[0..7] запинены тестами и квестами, вставка в середину ломает их молча. Новому id обязательны ключи в NIGHT_WEIGHTS и в дневной/ночной таблице весов его локации (*_DAY_WEIGHTS/*_NIGHT_WEIGHTS); тест lc4 (3000 роллов локации) вызывает pickSpecies() без аргумента — штраф minDist не действует, так что для гарантии выпадения нужен вес ≥ ~2.2. Новая локация → запись в LOCATIONS, табличный SPECIES_BY_LOCATION(_NIGHT) (порядок = LOCATIONS) и guard «вне 0..4 → Река» в pickSpecies() (этап 45). Новый emoji → перегенерация субсета tools/emoji-font.py.
  • Баланс «стало дороже» правится сдвигом долей, а не масштабом. pickSpecies() нормирует веса → ожидаемая выручка за улов (Σ(w·V)/Σw, V = середина веса × цена) не меняется от умножения всех весов на константу. Пины при нейтральной снасти: Река 65.1/125.9 · Озёра 194.1/249.0 · Болото 240.3/328.9 · Море 297.3/476.7 · Океан 329.1/552.7 🪙 (день/ночь) — считать через tools/balance.mjs (10 таблиц, сводка по ЦЕНЕ воды: Река → Озёра → Болото → Море → Океан); при правке *_WEIGHTS держать инвариант «новая (по цене) вода доходнее предыдущей в ОБЕ фазы» и мин. вес вида ≥ ~2.2 (иначе lc4 начинает плавать).

Известные «грабли»

  • #gamemenu идёт в DOM после #shop/#resetDlg при том же z-index — при открытии лавки/сброса из «Меню» гменю надо прятать (флаг shopFromMenu).
  • Видимость #btnPause/#btnRecall синхронится в syncDom() каждый кадр — иначе кнопка «залипает» под оверлеями.
  • catchFish() обнуляет reel — все обращения к reel в updateGame() под гардом if(state==='REEL'&&reel).
  • shadowBlur (неоновый поплавок, свечение босса) всегда в паре save/restore.
  • save.caught[id] — объект {n, best, cp, sv, qz, sn, em, dm, rb} (не число); sn исторически = «shiny» = gold-вариант.
  • Аквариум: #aqBar{pointer-events:none} + .aqpill{pointer-events:auto} (иначе невидимая полоса перехватывает тапы по сцене), body.aq-open прячет #hud, сентинел aqSel=-1, «выпустить» — без монет (продажу из банки не вводить). Жильцы — DOM-слой #aqFishLayer (pointer-events:none, body.aq-open, clip-path по банке), индекс aqFishEls обязан совпадать с save.aqFish/aqFishRt (в initAquarium битый вид даёт null, но не сдвигает индексы; aqReleaseSel удаляет DOM точечно) — иначе рыбы «перепутаются».
  • Пустой клёв и «ночь без фонарика» — легальные состояния ветки WAIT, а не ошибки; forceBite не работает ночью без hasLight().
  • Headless Chromium в этом чекауте может не видеть /tmp по file:// (smoke авто-откатывается во временный каталог под $HOME).

Гигиена секретов

  • Секреты (.gitea_token, логи, PDF) — в .gitignore; в код, коммиты и документы попадают только плейсхолдеры.
  • Токены Gitea — кратковременные, с минимальными правами, после push отзываются; если токен передавался в URL — удалять его из git remote сразу после push.

4. Карта файлов

Путь Назначение
index.html Вся игра: HTML + CSS + JS + встроенный emoji-шрифт
README.md Описание игры для игрока/читателя, «как играть», команды запуска и публикации
AGENTS.md Инструкция для агентов: порядок проверок, подводные камни, «где крутить» баланс
docs/DEVLOG.md Журнал разработки: этапы 1–46, найденные баги, решения, Monte-Carlo
docs/BROWSER-TESTING.md Плейтест через Orca CLI: таблица шагов, 13 подводных камней, справочник CLI
docs/PHYSICS-RESEARCH.md Исследование физики вываживания и формулы баланса
tests/test.js Главный автотест (headless Chromium + puppeteer-core), 222 проверок
tests/gen-screenshots.js Генератор скриншотов wiki: 15 кадров × 3 вьюпорта, RU/EN
tests/orca-playtest.js Плейтест в живом браузере Orca через CLI, 76 проверок
tests/pack-smoke.js Smoke-прогон игры из распакованного архива, 6 проверок
tests/package.json Зависимость puppeteer-core; скрипты test, shots
tools/check.mjs Синтаксическая проверка JS из index.html (node --check)
tools/pack.mjs Сборка dist/fishing-yandex.zip + 5 самопроверок (архив, размер, имена, sha256, синтаксис)
tools/emoji-font.py Генерация/проверка встроенного субсета Noto Color Emoji
screenshots-wiki/, screenshots-wiki-en/ Кадры для README (RU и EN): desk-*, mobl-*, mobp-*
dist/ Артефакт сборки (в .gitignore)

5. Быстрые ориентиры «где менять»

Задача Куда смотреть
Баланс рыбы/удилищ/приманок SPECIES, RODS, LURES, цены апгрейдов upgCost() / UPG_MULT
Шансы особых рыб VARIANTS, variantChance(), variantBoost(), SHINY_CHANCE
Сложность и физика боя updateReel(), fishForce(), константы внутри hook() (d0, st0, drag)
Ночь и фонарик DAY_LENGTH, NIGHT_WEIGHTS / LAKE_NIGHT_WEIGHTS / SWAMP_NIGHT_WEIGHTS, hasLight(), ветка WAIT
Погода WEATHER, updateWeather(), weatherActive()
Квесты / стрик / достижения QUEST_TPLS, STREAK_REWARDS, ACHIEVEMENTS, achBonus()
Скины и локации BOAT_SKINS, BOBBER_SKINS, LOCATIONS, RIVER_* / LAKE_* / SEA_* / OCEAN_* / SWAMP_*, свечение/декор Болота (glow, drawGlowAura(), drawSwampDecor())
Аквариум секция АКВАРИУМ (Этап 46), AQ_BASE_SLOTS/AQ_MAX_SLOTS/AQ_SLOT_COSTS, initAquarium()/drawAquarium(), #btnKeep/syncKeepBtn(), openAquarium()
Тексты интерфейса таблица T = {ru, en}, t() / tt(), renderMenu()
SDK и реклама секция Y1: SDK Яндекс Игр, tryInterstitial(), YG.showRewarded
Миграции сейвов loadSave()
Отрисовка секция ОТРИСОВКА, draw(), UI_FONT