Files
fishing-game/index.html
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

4833 lines
409 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
<meta name="theme-color" content="#0d3b66">
<meta name="apple-mobile-web-app-capable" content="yes">
<title>Рыбалка</title>
<!-- EMOJI-FONT:BEGIN — генерируется tools/emoji-font.py, не редактировать руками -->
<style>
@font-face{
font-family:'EmojiEmbedded';
font-style:normal;
font-weight:400;
font-display:block;
src:url(data:font/woff2;base64,AAEAAAAPAIAAAwBwQ09MUoW5ghYAANTEAAAw2UNQQUxvjD5lAAEFoAAAA8JHU1VCuPq49AABCWQAAAAqT1MvMkFfYDoAAAF4AAAAYGNtYXBQYvNYAAAG8AAAA5FnbHlmJIFejQAAD5wAAMOQaGVhZDGn9acAAAD8AAAANmhoZWEIwAgoAAABNAAAACRobXR4Fycd9gAAAdgAAAUYbG9jYYbmt2gAAAqEAAAFGG1heHACpgDQAAABWAAAACBuYW1lGcQzwwAA0ywAAAF2cG9zdP+2ADMAANSkAAAAIHZoZWEHMAKXAAEJkAAAACR2bXR4BOIAAAABCbQAAAUYAAEAAAADDhQgaq9sXw889QADBAAAAAAA5q5QlQAAAADm2FNFADP/BgTVA7gAAAAGAAIAAAAAAAAAAQAAA7b/BgAABPsAMwAmBNUEAAAAAAAAAAAAAAAAAAAAAAEAAQAAAosAzwAaAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAEBPsBkAAFAAACmgJmAAAATQKaAmYAAAFmADMBMwAAAAAAAAAAAAAAAAAAAAACAMCgAAAAAAAAAABOT05FAMAhlP//A7b/BgAAA7YA+gAAAAEAAAAAAgACzQAAACAACQT7ADMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJcASwE3AEoAVAQ8AIAA9QFvAeECUgK7AdIAfwBCAGIARQBZAEsChANLAScAUABPAj8BEwGEAr0DegOTAk4BmQElAtQASQKvAEkA9wBJATIBSADSAOsATQLNAsUDPANNA5sDPQORAMgA0QGkAN8A9gE5Ae4CyQNxAEYAUgHYAPcCOQNKAdkA9QItA0EBAgD9Ak4BHwEaA1oAeABkA/YAZAJyAn0BSwBJAW0AtAG6APADDQK1A0MDvQKwA9EAcQDmAUMBrgILAngCbgK0AukDIQNQAxkDpAIaAb4CQAE4AWIBEwFuAbAAQwBiAkQB+wG3AZwCGgIzAWUC2gFUAWgCFwCVAEYD5AMbAWID4wB3AUABKAGAAJ0DnwG1AbUCzgLOAKcAXgHfAogB/AH7AGcATgFrAPoBDwF2AkAC7wCaAlcBtwCaAKICCQK3A0UDoQKDA2wAPwImAjsCVAQVBC4AhgNsAUsDfQDDAP8AvgEVAU8C9gCrAGsETwI9AEsARgGsA5EAbwA+AXYBQgCsAKwCRwGSAq4CYwFlAeECggMqAtEAgQGcAh4CfAOiA9AA6AFtANMBAwC5AMAB4wHjAeMChQKVAEYB6wBvBFUCfACkAG8AjgHqANkB9wE7ANkBkwDZAUYBxwDZANkBVwHbANkBRgDZAa8A2QGhA2ICvAPlArwDtQK8A4sCvAO8A/4CvAOTArwD3QK8A1gCvAM6AjcEAAD1AVQBwgL8AcIDRQKaAbkCmgG5AcIBwgI7AYwBVAEgAOUBNQC6AJsBZwJ0AdsASgB0AIcAlwByA9wA5AQKA+sA7QD/AQQATgBwAXICUgEpAF8BqQC7AkkCSwDXAHkAhAD0AE8AbgFZAiUBFgBfAYsAsgIcAh4AywB2AbAB6gKdAp0CowDPAMYBBgEJApEBFQBLAPQAxAGiAQ8CQAJlAQ8COQJXAbQBNQE/ALMA6ACDA6sAowBLAGMAhwH4AiUCAgNsBAgDhQPNA/EEOwOxARkApACdAQ8BeAICAlsCyQMTAuICTwIkAyUDJQIqAioASwDfAiUBAAMxAzQCCQINA0wCKQDaASABDADhAhICVAJCAhICUQJXAjsCDABIAEkA2wBQAjUC8wJBAGoC6gMFAvgC3gO0AL4B1wJDAs4BvgE1AWgBawITAJICFgISAFoCKAIoAUQCJABOAVsBlgCIAMYBFwF7AewCcQL1A2YDygQgBGEAkABkAVcAwQFdAzkBVwFUATUBjgF/AX8BVAGeATQDQgMyAzIBjgD3APcBuQHPA4MB8QIvAFwD3QKSA4kCbgMDA20ESwO4A1gAUgGJAMAAhAHFAE8AxABLALwASwF8AaUCZgN1A9QDBAHhAJIAPgEoAPEBNQBLAL8ASwBlAvwDqAJtAoADzAE+AGQCeQJ5AG8CGgC4Ao4BrgKNAn4BkQJdAjAA5AJFAbwCWgJBAEYARABEAEQAdgD4AN0DNwM1AzUDZQPpA88ASABFAjQCSQI0AU0AxwDaAaUBOAKSAJQC8QDIAS0BvALxAvMC9AFwAdcAhwJlAsUDJACPArIBuwM7AzIATwEjASYBPwE6AhwBjAJaATgBbABLAAAABQAAAAMAAAAsAAAABAAAAKgAAAAF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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('woff2');
unicode-range:U+2194,U+21A9,U+23F8,U+2600,U+2692-U+2693,U+2696,U+26A0-U+26A1,U+26F5,U+2728,U+2753,U+2B06,U+2B50,U+1F300,U+1F308,U+1F30A,U+1F319,U+1F327,U+1F32B,U+1F3A3,U+1F3A8,U+1F3AD,U+1F3AF,U+1F3C5-U+1F3C6,U+1F3F9,U+1F420,U+1F446,U+1F451,U+1F4A4-U+1F4A5,U+1F4AA,U+1F4B0,U+1F4D4,U+1F4D6,U+1F4DC,U+1F4FA,U+1F504,U+1F507,U+1F50A,U+1F512,U+1F525,U+1F5B1,U+1F5D1,U+1F5FA,U+1F6A2,U+1F6D2,U+1F948,U+1F9F9,U+1FA99,U+1FA9D,U+1FAB1;
}
</style>
<!-- EMOJI-FONT:END -->
<style>
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none}
html,body{width:100%;height:100%;overflow:hidden;background:#0d3b66;font-family:'EmojiEmbedded',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif} /* Э28: встроенный emoji-шрифт первым — единый вид на всех платформах (генерация: tools/emoji-font.py) */
#game{position:fixed;inset:0;touch-action:none;display:block}
button{font-family:inherit;cursor:pointer}
.hidden{display:none!important}
/* ---------- HUD ---------- */
#hud{position:fixed;top:0;left:0;right:0;display:flex;justify-content:space-between;align-items:center;
padding:calc(8px + env(safe-area-inset-top)) 10px 8px;z-index:10;pointer-events:none}
/* Этап 46: под сценой аквариума HUD скрыт — кошелёк/лавка не перехватывают клики сцены */
body.aq-open #hud{display:none}
.hudleft{display:flex;gap:8px;align-items:center}
#coins,#weather{background:linear-gradient(rgba(9,32,58,.72),rgba(6,25,45,.62));padding:8px 15px;border-radius:999px;color:#ffe9a8;font-weight:700;
font-size:17px;border:1px solid rgba(255,233,168,.22);
box-shadow:0 3px 12px rgba(0,0,0,.35),inset 0 1px 0 rgba(255,255,255,.12);text-shadow:0 1px 2px rgba(0,0,0,.4)}
/* Клик по монетам открывает лавку (вкладка «Лавка») — HUD сам pointer-events:none */
#coins{pointer-events:auto;cursor:pointer;transition:filter .15s,transform .12s}
#coins:hover{filter:brightness(1.18);transform:translateY(-1px)}
#coins:active{transform:translateY(1px)}
.hudbtns{display:flex;gap:8px;pointer-events:auto}
.hudbtns button{width:46px;height:46px;font-size:21px;border-radius:14px;
border:1px solid rgba(255,255,255,.16);background:linear-gradient(rgba(9,32,58,.72),rgba(6,25,45,.62));color:#fff;
box-shadow:0 3px 10px rgba(0,0,0,.3),inset 0 1px 0 rgba(255,255,255,.1);
transition:background .15s,transform .12s,box-shadow .12s}
.hudbtns button:hover{background:rgba(30,60,95,.8);transform:translateY(-1px)}
.hudbtns button:active{background:rgba(30,60,95,.85);transform:translateY(1px);box-shadow:0 1px 4px rgba(0,0,0,.3)}
#btnRecall{position:fixed;right:12px;bottom:calc(16px + env(safe-area-inset-bottom));width:54px;height:54px;
border-radius:50%;font-size:24px;background:linear-gradient(rgba(9,32,58,.8),rgba(6,25,45,.7));color:#fff;
border:1px solid rgba(255,255,255,.22);z-index:10;
box-shadow:0 4px 14px rgba(0,0,0,.35),inset 0 1px 0 rgba(255,255,255,.12);
transition:background .15s,transform .12s}
#btnRecall:hover{background:rgba(30,60,95,.85);transform:scale(1.05)}
#btnRecall:active{background:rgba(30,60,95,.9);transform:scale(.96)}
#toast{position:fixed;left:50%;bottom:calc(92px + env(safe-area-inset-bottom));transform:translateX(-50%);
background:linear-gradient(rgba(9,32,58,.94),rgba(5,20,38,.92));color:#fff;padding:11px 20px;border-radius:14px;font-size:15px;font-weight:600;
z-index:30;opacity:0;transition:opacity .25s;pointer-events:none;white-space:normal;text-align:center;max-width:min(88vw,560px);
border:1px solid rgba(255,255,255,.18);
box-shadow:0 6px 20px rgba(0,0,0,.45),inset 0 1px 0 rgba(255,255,255,.1)}
/* ---------- Overlays ---------- */
.overlay{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;
background:rgba(4,16,32,.62);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);z-index:20;padding:16px}
.panel{position:relative;background:linear-gradient(165deg,#1a4d7d 0%,#123a61 55%,#0d2f4e 100%);
border:1px solid rgba(120,180,235,.35);border-radius:22px;
padding:22px;width:min(92vw,370px);max-height:86vh;overflow-y:auto;color:#eaf4ff;text-align:center;
box-shadow:0 24px 60px rgba(0,0,0,.55),0 4px 14px rgba(0,0,0,.35),inset 0 1px 0 rgba(255,255,255,.14)}
.panel::-webkit-scrollbar{width:8px}
.panel::-webkit-scrollbar-thumb{background:rgba(255,255,255,.18);border-radius:99px}
.panel::-webkit-scrollbar-track{background:transparent}
.title{font-size:34px;font-weight:800;letter-spacing:1px;margin-bottom:10px;
text-shadow:0 2px 10px rgba(0,0,0,.4),0 1px 0 rgba(255,255,255,.15)}
.panel p{font-size:14.5px;line-height:1.5;color:#bcd6ee;margin-bottom:4px}
.big{width:100%;margin-top:16px;padding:15px;font-size:19px;font-weight:800;color:#04263d;
background:linear-gradient(#ffe08a,#f5a623);border:none;border-radius:14px;
box-shadow:0 4px 0 #b97708,0 8px 18px rgba(245,166,35,.35);
transition:transform .12s,box-shadow .12s,filter .12s}
.big:hover{filter:brightness(1.07)}
.big:active{transform:translateY(2px);box-shadow:0 2px 0 #b97708,0 4px 10px rgba(245,166,35,.3)}
.panelhead{font-size:22px;font-weight:800;margin-bottom:4px;text-shadow:0 1px 6px rgba(0,0,0,.35)}
#btnCloseShop{position:absolute;top:12px;right:12px;width:36px;height:36px;border-radius:50%;border:none;
background:rgba(255,255,255,.12);color:#fff;font-size:17px;transition:background .15s,transform .12s}
#btnCloseShop:hover{background:rgba(255,255,255,.22);transform:rotate(90deg)}
#btnCloseShop:active{transform:rotate(90deg) scale(.92)}
.tabs{display:flex;gap:8px;margin:10px 0 6px}
.tabs button{flex:1;padding:11px;border-radius:12px;border:none;background:rgba(255,255,255,.1);
color:#cfe4f7;font-weight:700;font-size:15px;box-shadow:inset 0 1px 0 rgba(255,255,255,.08);
transition:background .15s,color .15s,transform .12s,box-shadow .15s}
.tabs button:hover{background:rgba(255,255,255,.17)}
.tabs button:active{transform:scale(.96)}
.tabs button.active{background:linear-gradient(#ffd76e,#f5a623);color:#04263d;
box-shadow:0 3px 12px rgba(245,166,35,.4),inset 0 1px 0 rgba(255,255,255,.35)}
.section-title{font-size:12px;letter-spacing:1.5px;color:#8fb8dd;text-transform:uppercase;margin:14px 0 8px;text-align:left}
.item{display:flex;align-items:center;justify-content:space-between;gap:10px;background:rgba(255,255,255,.06);
border:1px solid rgba(255,255,255,.09);border-radius:12px;padding:10px 12px;margin-bottom:8px;text-align:left;
transition:background .15s,border-color .15s,transform .12s}
.item:hover{background:rgba(255,255,255,.1);border-color:rgba(255,255,255,.2);transform:translateY(-1px)}
.itemname{font-weight:700;font-size:15px}
.itemdesc{font-size:12px;color:#9fc3e2;margin-top:2px;line-height:1.35}
.buybtn{min-width:96px;padding:10px 10px;border-radius:10px;border:none;font-weight:700;font-size:13px;
background:#2e6da3;color:#fff;white-space:nowrap;flex-shrink:0;
box-shadow:0 2px 6px rgba(0,0,0,.25),inset 0 1px 0 rgba(255,255,255,.15);transition:filter .12s,transform .12s,background .15s}
.buybtn:hover{filter:brightness(1.15)}
.buybtn:active{transform:scale(.96)}
.buybtn.owned{background:rgba(255,255,255,.14)}
.buybtn.owned:hover{filter:none;background:rgba(255,255,255,.22)}
.buybtn.current{background:#2ecc71;color:#04263d;box-shadow:0 2px 8px rgba(46,204,113,.4)}
.buybtn:disabled{opacity:.45;cursor:default}
.buybtn.upgbt{background:#5c55a8}
.rowbtns{display:flex;flex-direction:column;gap:6px;flex-shrink:0;max-width:48%;align-items:stretch}
.rowbtns .buybtn{min-width:0;white-space:normal}
.lvtag{color:#ffd75e;font-weight:800;font-size:12px;margin-left:5px;white-space:nowrap}
.item.locked{opacity:.5}
.item.locked:hover{background:rgba(255,255,255,.06);border-color:rgba(255,255,255,.09);transform:none}
.statsline{font-size:13px;color:#9fc3e2;margin-bottom:6px}
.jrow{display:flex;align-items:center;gap:10px;padding:9px 10px;background:rgba(255,255,255,.05);
border:1px solid rgba(255,255,255,.06);border-radius:10px;margin-bottom:6px;font-size:14px;text-align:left;
transition:background .15s,border-color .15s}
.jrow:hover{background:rgba(255,255,255,.09);border-color:rgba(255,255,255,.14)}
.jrow .je{font-size:22px;width:30px;text-align:center}
/* SVG-портрет вида в дневнике (спрайт <use href="#fish-<id>">); ❓ для не пойманных — по-прежнему эмодзи */
.jrow .je svg{width:44px;height:28px;display:block}
.jrow .je .qfish{width:24px;height:15px;display:inline-block;vertical-align:-2px} /* Э42: иконка вида в строке квеста (выше по специфичности, чем правило портрета .je svg) */
.jrow .jn{flex:1}
.jrow .jc{color:#9fc3e2;font-weight:600;font-size:13px}
/* M6: мини-счётчики вариантов в дневнике (обычная/серебряная/золотая/радужная) */
.jrow .jv{font-weight:800;font-size:11px;margin-left:5px;white-space:nowrap}
.jrow .jv-n{color:#8fb8dd}
.jrow .jv-cp{color:#d99a62}
.jrow .jv-sv{color:#dbe7f3}
.jrow .jv-qz{color:#cdbfe0}
.jrow .jv-sn{color:#ffd75e}
.jrow .jv-em{color:#2ebd6b}
.jrow .jv-dm{color:#6db8f2}
.jrow .jv-rb{color:#c56cf0}
.jrow.unknown .je,.jrow.unknown .jn{color:#5d7f9e}
.jrow .adesc{font-size:11px;color:#9fc3e2}
.jrow.locked{opacity:.5}
.jrow.got .jc{color:#2ecc71;font-weight:800;font-size:16px}
.jrow.qdone{background:rgba(46,204,113,.14)}
.jrow.qdone .jc{color:#2ecc71;font-weight:700}
.streak{display:flex;gap:6px;justify-content:center;margin:8px 0 4px}
.st-dot{width:34px;height:34px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:800;
background:rgba(255,255,255,.08);color:#5d7f9e;border:2px solid rgba(255,255,255,.12);
transition:transform .15s,box-shadow .15s}
.st-dot.on{background:linear-gradient(#ffd76e,#f5a623);color:#04263d;border-color:#ffd75e}
.st-dot.cur{box-shadow:0 0 12px rgba(255,215,94,.85);transform:scale(1.12)}
#card .bigemoji{font-size:74px;margin:4px 0 2px;display:block;animation:bob 1.2s ease-in-out infinite;
filter:drop-shadow(0 6px 10px rgba(0,0,0,.4))}
/* SVG-портрет улова в карточке (спрайт <use href="#fish-<id>">, showCard ставит href у #cardFishUse) */
#card .bigemoji svg{width:240px;height:150px;display:block;margin:0 auto}
@keyframes bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-9px)}}
#card .rarity{display:inline-block;font-weight:800;font-size:12px;letter-spacing:1.5px;text-transform:uppercase;
padding:5px 14px;border-radius:999px;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.15)}
#card .cname{font-size:24px;font-weight:800;margin:6px 0 6px;text-shadow:0 1px 6px rgba(0,0,0,.4)}
#card .cval{font-size:20px;font-weight:800;color:#ffe9a8;margin-bottom:6px;text-shadow:0 1px 4px rgba(0,0,0,.3)}
#card .csub{font-size:13px;color:#9fc3e2;margin-bottom:4px}
.record{display:inline-block;background:linear-gradient(#ffd76e,#f5a623);color:#04263d;font-weight:800;font-size:14px;letter-spacing:1px;padding:4px 14px;border-radius:999px;animation:recpulse .8s ease-in-out infinite;box-shadow:0 0 14px rgba(255,215,94,.5)}
@keyframes recpulse{0%,100%{transform:scale(1)}50%{transform:scale(1.09)}}
/* M6: бейдж варианта и оформление карточки (silver/gold/rainbow; normal — без оформления) */
.variant-badge{display:inline-block;font-weight:800;font-size:14px;letter-spacing:1px;padding:4px 14px;border-radius:999px;margin-left:8px;
border:1px solid #fff;animation:recpulse .8s ease-in-out infinite}
#card.silver-card .variant-badge{background:linear-gradient(#f7fafd,#c3d4e6);color:#2f4d6d;border-color:#dbe7f3;box-shadow:0 0 14px rgba(200,220,240,.8)}
#card.gold-card .variant-badge{background:linear-gradient(#fff6c9,#ffd75e);color:#6b4a00;border-color:#ffd75e;box-shadow:0 0 14px rgba(255,215,94,.75)}
#card.rainbow-card .variant-badge{background:linear-gradient(#f3ddff,#c56cf0);color:#4b1a70;border-color:#e0b3ff;box-shadow:0 0 14px rgba(197,108,240,.8)}
#card.copper-card .variant-badge{background:linear-gradient(#fbe9d7,#d99a62);color:#5b3a1e;border-color:#e8b98a;box-shadow:0 0 14px rgba(217,154,98,.7)}
#card.quartz-card .variant-badge{background:linear-gradient(#f6f1fa,#cdbfe0);color:#463355;border-color:#e3d7f0;box-shadow:0 0 14px rgba(205,191,224,.8)}
#card.emerald-card .variant-badge{background:linear-gradient(#d8f7e3,#2ebd6b);color:#063d1f;border-color:#7ee2a8;box-shadow:0 0 14px rgba(46,189,107,.8)}
#card.diamond-card .variant-badge{background:linear-gradient(#e8f6ff,#6db8f2);color:#0a3a66;border-color:#a8d8ff;box-shadow:0 0 14px rgba(109,184,242,.85)}
#card.silver-card .panel{border-color:#c3d4e6;box-shadow:0 0 34px rgba(200,220,240,.45),0 10px 40px rgba(0,0,0,.5)}
#card.silver-card .bigemoji{filter:grayscale(.85) brightness(1.25) drop-shadow(0 0 12px rgba(210,228,245,.9))}
#card.gold-card .panel{border-color:#ffd75e;box-shadow:0 0 34px rgba(255,215,94,.5),0 10px 40px rgba(0,0,0,.5)}
#card.gold-card .bigemoji{filter:drop-shadow(0 0 12px rgba(255,215,94,.9))}
#card.rainbow-card .panel{border-color:#c56cf0;box-shadow:0 0 34px rgba(197,108,240,.5),0 10px 40px rgba(0,0,0,.5)}
#card.rainbow-card .bigemoji{animation:bob 1.2s ease-in-out infinite,rainbowhue 2.4s linear infinite}
@keyframes rainbowhue{from{filter:hue-rotate(0deg) drop-shadow(0 0 12px rgba(255,120,220,.8))}to{filter:hue-rotate(360deg) drop-shadow(0 0 12px rgba(120,220,255,.8))}}
#card.copper-card .panel{border-color:#d99a62;box-shadow:0 0 34px rgba(217,154,98,.45),0 10px 40px rgba(0,0,0,.5)}
#card.copper-card .bigemoji{filter:drop-shadow(0 0 12px rgba(217,154,98,.9))}
#card.quartz-card .panel{border-color:#cdbfe0;box-shadow:0 0 34px rgba(205,191,224,.5),0 10px 40px rgba(0,0,0,.5)}
#card.quartz-card .bigemoji{filter:drop-shadow(0 0 12px rgba(205,191,224,.9))}
#card.emerald-card .panel{border-color:#2ebd6b;box-shadow:0 0 34px rgba(46,189,107,.5),0 10px 40px rgba(0,0,0,.5)}
#card.emerald-card .bigemoji{filter:drop-shadow(0 0 12px rgba(46,189,107,.9))}
#card.diamond-card .panel{border-color:#6db8f2;box-shadow:0 0 34px rgba(109,184,242,.5),0 10px 40px rgba(0,0,0,.5)}
#card.diamond-card .bigemoji{filter:drop-shadow(0 0 12px rgba(109,184,242,.95))}
.boss-badge{display:inline-block;background:linear-gradient(#fff6c9,#ffd75e);color:#6b4a00;font-weight:800;font-size:14px;letter-spacing:1px;padding:4px 14px;border-radius:999px;margin-left:8px;
border:1px solid #ffd75e;box-shadow:0 0 14px rgba(255,215,94,.75);animation:recpulse .8s ease-in-out infinite}
#card.boss-card .panel{border-color:#ffd75e;box-shadow:0 0 34px rgba(255,215,94,.55),0 10px 40px rgba(0,0,0,.5)}
#card.boss-card .bigemoji{filter:drop-shadow(0 0 12px rgba(255,215,94,.95))}
/* Фаза свечения «Болота»: светящийся вид в normal-варианте — цветной drop-shadow по --glow
(цвет ставит showCard; класс только для normal, чтобы не спорить с фильтрами вариантов) */
#card.glow-card .bigemoji{filter:drop-shadow(0 0 16px var(--glow,#5ff2ff)) drop-shadow(0 6px 10px rgba(0,0,0,.4))}
/* Перекраска SVG-портрета под вариант: --f1 (тело) / --f3 (плавники) — переменные наследуются
сквозь <use> в спрайт; silver/rainbow закрываются filter-правилами выше, boss — тоже */
#card.copper-card .bigemoji{--f1:hsl(25,55%,55%);--f3:hsl(25,50%,35%)}
#card.gold-card .bigemoji{--f1:#ffd75e;--f3:hsl(45,70%,40%)}
#card.quartz-card .bigemoji{--f1:hsl(270,25%,72%);--f3:hsl(270,25%,45%)}
#card.emerald-card .bigemoji{--f1:hsl(150,55%,42%);--f3:hsl(150,50%,28%)}
#card.diamond-card .bigemoji{--f1:hsl(200,60%,78%);--f3:hsl(200,50%,45%)}
#card{animation:pop .3s ease}
@keyframes pop{from{transform:scale(.75);opacity:0}to{transform:scale(1);opacity:1}}
/* Y2: rewarded «монеты ×2» (карточка улова) — тёмно-синяя в духе карточки,
янтарная обводка «читается» как бонус; простой текстовый button (треб. 1.16 —
НЕ имитация рекламного блока платформы) */
.rvbtn{width:100%;min-height:44px;margin-top:10px;padding:11px 12px;font-size:15px;font-weight:800;color:#ffe9a8;
background:linear-gradient(#14406b,#0b2a49);border:1.5px solid #ffd75e;border-radius:12px;
box-shadow:0 0 12px rgba(255,215,94,.22),inset 0 1px 0 rgba(255,255,255,.08);
transition:transform .12s,box-shadow .12s,filter .12s}
.rvbtn:hover{filter:brightness(1.18);box-shadow:0 0 16px rgba(255,215,94,.4),inset 0 1px 0 rgba(255,255,255,.1)}
.rvbtn:active{transform:scale(.97)}
.rvbtn:disabled{opacity:.55;cursor:default;filter:none;transform:none}
/* Этап 46: «В аквариум» на карточке улова — по образцу .rvbtn, бирюзовая «морская» гамма */
.aqbtn{width:100%;min-height:44px;margin-top:10px;padding:11px 12px;font-size:15px;font-weight:800;color:#c8fff4;
background:linear-gradient(#0f5f6b,#083f49);border:1.5px solid #57e0c8;border-radius:12px;
box-shadow:0 0 12px rgba(87,224,200,.22),inset 0 1px 0 rgba(255,255,255,.08);
transition:transform .12s,box-shadow .12s,filter .12s}
.aqbtn:hover{filter:brightness(1.18);box-shadow:0 0 16px rgba(87,224,200,.4),inset 0 1px 0 rgba(255,255,255,.1)}
.aqbtn:active{transform:scale(.97)}
.aqbtn:disabled{opacity:.55;cursor:default;filter:none;transform:none}
/* Этап 46: бар аквариума (#aqBar) и поповер выбранной рыбы (#aqPop) — DOM поверх canvas-сцены */
#aqBar{position:fixed;top:calc(8px + env(safe-area-inset-top));left:10px;right:10px;z-index:26;
display:flex;align-items:center;gap:8px;pointer-events:none}
/* Бар сам клики не ловит (бокс на всю ширину) — кликабельны только кнопки-пилюли внутри */
#aqBar .aqpill{pointer-events:auto}
.aqpill{background:linear-gradient(rgba(9,32,58,.8),rgba(6,25,45,.7));color:#eaf4ff;font-weight:700;font-size:15px;
padding:9px 14px;border-radius:999px;border:1px solid rgba(87,224,200,.35);white-space:nowrap;
box-shadow:0 3px 12px rgba(0,0,0,.35),inset 0 1px 0 rgba(255,255,255,.1);transition:filter .15s,transform .12s}
.aqpill:hover{filter:brightness(1.18);transform:translateY(-1px)}
.aqpill:active{transform:translateY(1px)}
.aqpill:disabled{opacity:.5;cursor:default;filter:none;transform:none}
#aqCount{margin-left:auto;color:#c8fff4;font-weight:800;font-size:15px;padding:9px 14px;border-radius:999px;
background:linear-gradient(rgba(9,32,58,.8),rgba(6,25,45,.7));border:1px solid rgba(255,255,255,.18);
box-shadow:0 3px 12px rgba(0,0,0,.35),inset 0 1px 0 rgba(255,255,255,.1)}
#aqPop{position:fixed;left:50%;transform:translateX(-50%);bottom:calc(18px + env(safe-area-inset-bottom));z-index:26;
background:linear-gradient(rgba(9,32,58,.95),rgba(5,20,38,.93));border:1px solid rgba(87,224,200,.45);border-radius:16px;
padding:12px 16px 14px;color:#eaf4ff;text-align:center;min-width:210px;max-width:86vw;
box-shadow:0 12px 34px rgba(0,0,0,.55),inset 0 1px 0 rgba(255,255,255,.1)}
#aqPopName{font-size:17px;font-weight:800}
#aqPopSub{font-size:12px;color:#9fc3e2;margin:4px 0 10px}
/* Ревизия: жильцы банки — те же SVG-портреты, что в карточке улова. Слой поверх canvas (вода/
стекло/ауры рисуются в drawAquarium), но под баром/поповером (#aqBar/#aqPop, z-index 26).
Вне сцены выключен (body.aq-open); клики не перехватывает (pointer-events:none). */
#aqFishLayer{position:fixed;inset:0;z-index:5;pointer-events:none;display:none;overflow:hidden}
body.aq-open #aqFishLayer{display:block}
#aqFishLayer .aqfish{position:absolute;left:0;top:0;will-change:transform}
#aqFishLayer .aqfish svg{width:100%;height:100%;display:block;overflow:visible}
/* Вариант — те же правила, что у карточки улова (#card.<variant>-card .bigemoji):
перекраска --f1/--f3 идёт inline (наследуется сквозь <use>), фильтры — классами */
#aqFishLayer .aqfish.varcopper{filter:drop-shadow(0 0 12px rgba(217,154,98,.9))}
#aqFishLayer .aqfish.varsilver{filter:grayscale(.85) brightness(1.25) drop-shadow(0 0 12px rgba(210,228,245,.9))}
#aqFishLayer .aqfish.vargold{filter:drop-shadow(0 0 12px rgba(255,215,94,.9))}
#aqFishLayer .aqfish.varquartz{filter:drop-shadow(0 0 12px rgba(205,191,224,.9))}
#aqFishLayer .aqfish.varemerald{filter:drop-shadow(0 0 12px rgba(46,189,107,.9))}
#aqFishLayer .aqfish.vardiamond{filter:drop-shadow(0 0 12px rgba(109,184,242,.95))}
#aqFishLayer .aqfish.varrainbow{animation:aqrainbowhue 2.4s linear infinite}
@keyframes aqrainbowhue{
from{filter:hue-rotate(0deg) drop-shadow(0 0 12px rgba(255,120,220,.8))}
to{filter:hue-rotate(360deg) drop-shadow(0 0 12px rgba(120,220,255,.8))}
}
.twobtns{display:flex;gap:10px;margin-top:16px}
.twobtns .big{margin-top:0;width:auto;flex:1}
/* Кнопки диалога «Меню» (#gamemenu): тёмные пилюли в духе HUD-кнопок */
.mbtns{display:flex;flex-direction:column;gap:10px;margin-top:16px}
.mbtn{width:100%;padding:13px 16px;font-size:16px;font-weight:700;color:#eaf4ff;text-align:left;
background:linear-gradient(rgba(9,32,58,.85),rgba(6,25,45,.72));border:1px solid rgba(255,255,255,.18);border-radius:12px;
box-shadow:inset 0 1px 0 rgba(255,255,255,.1);transition:background .15s,transform .12s}
.mbtn:hover{background:rgba(30,60,95,.9);transform:translateY(-1px)}
.mbtn:active{background:rgba(30,60,95,.95);transform:translateY(1px)}
.big.danger{background:linear-gradient(#ff8a7a,#e74c3c);box-shadow:0 4px 0 #96281b,0 8px 18px rgba(231,76,60,.35);color:#fff}
.big.danger:hover{filter:brightness(1.08)}
.big.danger:active{box-shadow:0 2px 0 #96281b,0 4px 10px rgba(231,76,60,.3)}
/* ---------- Адаптив: узкие и низкие экраны ---------- */
@media (max-width:420px){
.title{font-size:27px}
.panel{padding:16px;border-radius:18px}
.panelhead{font-size:19px}
.panel p{font-size:13.5px}
.big{padding:13px;font-size:17px;border-radius:12px}
#card .bigemoji{font-size:58px}
#card .bigemoji svg{width:188px;height:118px}
#card .cname{font-size:21px}
.hudbtns button{width:42px;height:42px;font-size:19px;border-radius:12px}
#coins,#weather{font-size:15px;padding:7px 12px}
.tabs button{padding:10px 6px;font-size:13.5px}
.jrow .je{font-size:20px;width:26px}
.jrow .je svg{width:40px;height:26px}
}
@media (max-height:620px){
.panel{max-height:92vh;padding:14px 16px}
.title{font-size:24px;margin-bottom:6px}
.panel p{font-size:13px;line-height:1.4}
#card .bigemoji{font-size:50px;margin:2px 0 0}
#card .bigemoji svg{width:162px;height:101.2px}
.big{margin-top:10px;padding:11px;font-size:16px}
.tabs button{padding:8px}
}
</style>
</head>
<body>
<!-- FISH-SVG:BEGIN (генерируется tools/embed-fish.mjs — не править руками) -->
<svg id="fishSprites" width="0" height="0" style="position:absolute" aria-hidden="true">
<symbol id="fish-carass" viewBox="0 0 160 100">
<path d="M40 50 L14 30 L24 50 L14 70 Z" fill="var(--f3,hsl(45,50%,32%))"/>
<path d="M36 50 Q60 22 100 24 Q132 27 144 50 Q132 73 100 76 Q60 78 36 50 Z" fill="var(--f1,hsl(45,45%,48%))"/>
<path d="M144 50 Q132 73 100 76 Q60 78 36 50 Q64 66 104 66 Q130 64 144 50 Z" fill="var(--f2,hsl(45,30%,70%))"/>
<path d="M76 28 Q90 6 114 21 Q96 24 80 29 Z" fill="var(--f3,hsl(45,50%,32%))"/>
<path d="M102 54 Q124 58 120 74 Q106 70 98 60 Z" fill="var(--f3,hsl(45,50%,32%))"/>
<path d="M84 72 Q96 84 108 78 Q98 74 88 70 Z" fill="var(--f3,hsl(45,50%,32%))"/>
<circle cx="124" cy="42" r="7" fill="#fff"/>
<circle cx="125.5" cy="42" r="3.2" fill="#142638"/>
</symbol>
<symbol id="fish-perch" viewBox="0 0 160 100">
<path d="M36 50 L12 28 L22 50 L12 72 Z" fill="var(--f3,hsl(120,50%,32%))"/>
<path d="M50 38 L60 11 L72 18 L84 12 L96 22 L102 30 Z" fill="var(--f3,hsl(120,50%,32%))"/>
<path d="M32 50 Q58 21 100 23 Q133 26 148 50 Q133 74 100 77 Q58 79 32 50 Z" fill="var(--f1,hsl(120,45%,48%))"/>
<path d="M148 50 Q133 74 100 77 Q58 79 32 50 Q62 64 104 64 Q132 62 148 50 Z" fill="var(--f2,hsl(120,30%,70%))"/>
<path d="M47 37.9 Q52 34.3 57 33.1 L57 67.3 Q52 65.2 47 60.6 Z" fill="var(--f4,hsl(120,45%,26%))"/>
<path d="M61 30.8 Q66 28.1 71 27.8 L71 72.2 Q66 71.8 61 68.9 Z" fill="var(--f4,hsl(120,45%,26%))"/>
<path d="M75 26.8 Q80 24.9 85 25.3 L85 74.7 Q80 75.2 75 73.3 Z" fill="var(--f4,hsl(120,45%,26%))"/>
<path d="M89 25 Q94 23.8 99 25 L99 75 Q94 76.2 89 75 Z" fill="var(--f4,hsl(120,45%,26%))"/>
<path d="M103 25.4 Q108 25.1 113 27.2 L113 72.7 Q108 74.9 103 74.6 Z" fill="var(--f4,hsl(120,45%,26%))"/>
<path d="M108 52 Q130 56 126 72 Q112 68 104 58 Z" fill="var(--f3,hsl(120,50%,32%))"/>
<path d="M88 74 Q100 88 112 80 Q102 76 94 72 Z" fill="var(--f3,hsl(120,50%,32%))"/>
<circle cx="133" cy="43" r="6.5" fill="#fff"/>
<circle cx="134.5" cy="43" r="3" fill="#142638"/>
</symbol>
<symbol id="fish-bream" viewBox="0 0 160 100">
<path d="M38 50 L16 36 L26 50 L16 64 Z" fill="var(--f3,hsl(210,50%,32%))"/>
<path d="M36 50 Q40 18 86 16 Q130 18 146 50 Q130 82 86 84 Q40 82 36 50 Z" fill="var(--f1,hsl(210,45%,48%))"/>
<path d="M36 50 Q40 82 86 84 Q130 82 146 50 Q128 66 86 66 Q50 64 36 50 Z" fill="var(--f2,hsl(210,30%,70%))"/>
<path d="M44 30 Q76 2 128 16 Q136 26 134 36 Q84 12 44 30 Z" fill="var(--f3,hsl(210,50%,32%))"/>
<path d="M98 56 Q120 60 116 76 Q102 72 94 62 Z" fill="var(--f3,hsl(210,50%,32%))"/>
<path d="M80 82 Q92 92 108 86 Q96 82 86 80 Z" fill="var(--f3,hsl(210,50%,32%))"/>
<path d="M46 42 Q92 34 140 46 Q92 48 46 42 Z" fill="var(--f4,hsl(210,45%,26%))"/>
<path d="M64 28 Q70 24 76 28 Q70 34 64 28 Z" fill="var(--f4,hsl(210,45%,26%))"/>
<path d="M84 24 Q90 20 96 24 Q90 30 84 24 Z" fill="var(--f4,hsl(210,45%,26%))"/>
<path d="M104 30 Q110 26 116 30 Q110 36 104 30 Z" fill="var(--f4,hsl(210,45%,26%))"/>
<circle cx="126" cy="42" r="7" fill="#fff"/>
<circle cx="127.5" cy="42" r="3.2" fill="#142638"/>
</symbol>
<symbol id="fish-carp" viewBox="0 0 160 100">
<path d="M 36 50 L 10 24 Q 30 50 10 76 Z" fill="var(--f3,hsl(30,50%,32%))"/>
<path d="M 44 24 C 56 7 84 3 106 10 C 90 12 70 17 58 25 Z" fill="var(--f3,hsl(30,50%,32%))"/>
<path d="M 54 78 C 56 90 64 97 75 97 C 69 89 65 83 61 78 Z" fill="var(--f3,hsl(30,50%,32%))"/>
<path d="M 28 50 C 30 28 58 14 90 14 C 118 14 136 28 142 46 C 143 49 143 51 142 54 C 136 72 118 86 90 86 C 58 86 30 72 28 50 Z" fill="var(--f1,hsl(30,45%,48%))"/>
<path d="M 142 54 C 136 72 118 86 90 86 C 58 86 30 72 28 50 L 34 54 C 44 68 62 76 90 76 C 116 76 134 68 142 54 Z" fill="var(--f2,hsl(30,30%,70%))"/>
<path d="M 100 46 C 112 48 120 56 121 67 C 112 63 103 57 98 51 Z" fill="var(--f3,hsl(30,50%,32%))"/>
<circle cx="64" cy="38" r="4" fill="var(--f4,hsl(30,45%,26%))"/>
<circle cx="84" cy="50" r="3.5" fill="var(--f4,hsl(30,45%,26%))"/>
<circle cx="100" cy="34" r="3" fill="var(--f4,hsl(30,45%,26%))"/>
<circle cx="76" cy="62" r="3" fill="var(--f4,hsl(30,45%,26%))"/>
<circle cx="92" cy="26" r="2.5" fill="var(--f4,hsl(30,45%,26%))"/>
<circle cx="124" cy="40" r="7" fill="#fff"/>
<circle cx="126" cy="40" r="3.5" fill="#142638"/>
<path d="M 140 48 C 146 50 149 53 150 57" fill="none" stroke="var(--f4,hsl(30,45%,26%))" stroke-width="2" stroke-linecap="round"/>
<path d="M 138 54 C 144 57 146 61 147 65" fill="none" stroke="var(--f4,hsl(30,45%,26%))" stroke-width="2" stroke-linecap="round"/>
</symbol>
<symbol id="fish-pike" viewBox="0 0 160 100">
<!-- хвост: мелкая вилка -->
<path fill="var(--f3,hsl(100,50%,32%))" d="M20,46 L7,34 L14,44 L14,50 L7,60 L20,50 Z"/>
<!-- спинной плавник: смещён к хвосту, низкий, округлый -->
<path fill="var(--f3,hsl(100,50%,32%))" d="M28,41 C31,31 42,25 54,26 C57,26.5 58,29 57,33.5 Z"/>
<!-- брюшной плавник: маленький, снизу -->
<path fill="var(--f3,hsl(100,50%,32%))" d="M58,63 L53,71 L63,64 Z"/>
<!-- тело: удлинённое, плоская заострённая морда-«клюв» -->
<path fill="var(--f1,hsl(100,45%,48%))" d="M142,45 C137,41 128,37 116,34 C96,31 66,31 45,35 C33,38 25,42 20,47 L18,51 C25,56 33,60 45,63 C66,66 96,65 116,59 C128,56 138,52 141,48 Z"/>
<!-- брюхо: светлее, лунка вдоль нижней кромки -->
<path fill="var(--f2,hsl(100,30%,70%))" d="M18,51 C25,56 33,60 45,63 C66,66 96,65 116,59 C128,56 138,52 141,48 L142,45 C130,49 110,52 88,53 C66,54 40,53 22,50 Z"/>
<!-- грудной плавник -->
<path fill="var(--f3,hsl(100,50%,32%))" d="M98,50 C94,55 94,59 98,62 C102,58 103,54 101,50 Z"/>
<!-- пасть: тёмная щель -->
<path fill="var(--f4,hsl(100,45%,26%))" d="M142,45 L141,48 L122,46.5 Z"/>
<!-- узор: тёмные пятна по бокам -->
<ellipse fill="var(--f4,hsl(100,45%,26%))" cx="45" cy="47" rx="4" ry="2.5"/>
<ellipse fill="var(--f4,hsl(100,45%,26%))" cx="62" cy="45" rx="4.5" ry="2.5"/>
<ellipse fill="var(--f4,hsl(100,45%,26%))" cx="80" cy="46" rx="4.5" ry="2.5"/>
<ellipse fill="var(--f4,hsl(100,45%,26%))" cx="98" cy="48" rx="4" ry="2.5"/>
<!-- глаз -->
<circle fill="#fff" cx="124" cy="41" r="3.2"/>
<circle fill="#142638" cx="124.5" cy="41" r="1.6"/>
</symbol>
<symbol id="fish-catfish" viewBox="0 0 160 100">
<path d="M28 50 L12 36 L20 50 L12 64 Z" fill="var(--f3,hsl(260,50%,32%))"/>
<path d="M26 50 Q36 30 70 27 L124 27 Q140 30 142 45 Q139 58 128 62 Q108 76 80 77 Q46 75 26 50 Z" fill="var(--f1,hsl(260,45%,48%))"/>
<path d="M30 55 Q48 74 80 75 Q108 74 126 61 Q114 76 80 77 Q47 75 30 55 Z" fill="var(--f2,hsl(260,30%,70%))"/>
<path d="M74 27 Q94 10 112 26 Q92 22 74 27 Z" fill="var(--f3,hsl(260,50%,32%))"/>
<path d="M100 58 Q118 62 114 78 Q102 71 96 61 Z" fill="var(--f3,hsl(260,50%,32%))"/>
<path d="M58 40 Q65 36 72 40 Q65 46 58 40 Z" fill="var(--f4,hsl(260,45%,26%))"/>
<path d="M80 46 Q87 42 94 46 Q87 52 80 46 Z" fill="var(--f4,hsl(260,45%,26%))"/>
<path d="M48 52 Q55 48 62 52 Q55 58 48 52 Z" fill="var(--f4,hsl(260,45%,26%))"/>
<path d="M116 54 Q132 52 142 45 Q137 58 126 61 Q118 58 116 54 Z" fill="var(--f4,hsl(260,45%,26%))"/>
<path d="M141 44 Q152 36 156 26" fill="none" stroke="var(--f4,hsl(260,45%,26%))" stroke-width="2" stroke-linecap="round"/>
<path d="M142 50 Q154 52 158 62" fill="none" stroke="var(--f4,hsl(260,45%,26%))" stroke-width="2" stroke-linecap="round"/>
<path d="M127 55 Q116 51 106 48" fill="none" stroke="var(--f4,hsl(260,45%,26%))" stroke-width="2" stroke-linecap="round"/>
<path d="M124 60 Q112 66 102 66" fill="none" stroke="var(--f4,hsl(260,45%,26%))" stroke-width="2" stroke-linecap="round"/>
<circle cx="116" cy="36" r="5" fill="#fff"/>
<circle cx="117.5" cy="36" r="2.4" fill="#142638"/>
</symbol>
<symbol id="fish-sturge" viewBox="0 0 160 100">
<!-- хвост: разнорострый, верхняя лопасть заметно длиннее нижней -->
<path fill="var(--f3,hsl(200,50%,32%))" d="M26,42 C18,34 10,29 5,25 C9,34 14,41 18,45 L18,55 C15,57 13,58.5 11,60 C14,58.5 18,56 22,54 Z"/>
<!-- спинной плавник: маленький, смещён к хвосту -->
<path fill="var(--f3,hsl(200,50%,32%))" d="M36,41 C40,30 49,27 56,31 C51,36 45,39 38,41 Z"/>
<!-- анальный плавник: маленький, сзади -->
<path fill="var(--f3,hsl(200,50%,32%))" d="M40,63 L36,70 L46,64 Z"/>
<!-- грудной плавник -->
<path fill="var(--f3,hsl(200,50%,32%))" d="M102,50 C96,57 96,65 103,71 C109,65 111,57 109,50 Z"/>
<!-- тело: удлинённое, заострённое коническое рыло -->
<path fill="var(--f1,hsl(200,45%,48%))" d="M154,47 C144,40 130,35.5 112,34 C96,33 80,33.5 64,35 C48,36.5 34,39 26,42 L24,44 L24,56 L26,58 C34,61 48,64 64,66 C80,67.5 96,68 112,66 C130,63.5 144,55 154,47 Z"/>
<!-- брюхо: светлее, лунка вдоль нижней кромки -->
<path fill="var(--f2,hsl(200,30%,70%))" d="M154,47 C144,55 130,63.5 112,66 C96,68 80,67.5 64,66 C48,64 34,61 26,58 L24,56 L24,51 C34,54.5 52,57 76,58 C100,59 132,55 154,47 Z"/>
<!-- кырьиши: ряд костных бугорков вдоль спинки -->
<path fill="var(--f4,hsl(200,45%,26%))" d="M35.5,39.7 L40,33.6 L44.5,38.2 Z"/>
<path fill="var(--f4,hsl(200,45%,26%))" d="M47.5,37.2 L52,32.4 L56.5,36.3 Z"/>
<path fill="var(--f4,hsl(200,45%,26%))" d="M59.5,35.9 L64,31.4 L68.5,35.2 Z"/>
<path fill="var(--f4,hsl(200,45%,26%))" d="M71.5,34.8 L76,30.4 L80.5,34.4 Z"/>
<path fill="var(--f4,hsl(200,45%,26%))" d="M83.5,34.3 L88,29.8 L92.5,34.2 Z"/>
<path fill="var(--f4,hsl(200,45%,26%))" d="M95.5,34.1 L100,30.2 L104.5,34.4 Z"/>
<!-- пасть: тёмная щель на острие рыла -->
<path fill="var(--f4,hsl(200,45%,26%))" d="M154,47 L152.2,50.2 L137,48.4 Z"/>
<!-- глаз -->
<circle fill="#fff" cx="131" cy="42" r="3.4"/>
<circle fill="#142638" cx="131.6" cy="42" r="1.7"/>
</symbol>
<symbol id="fish-gold" viewBox="0 0 160 100">
<path d="M74 40 Q60 34 52 22 Q42 12 22 10 Q34 20 30 30 Q26 38 38 42 Q50 46 58 46 Q66 48 74 48 Z" fill="var(--f3,hsl(38,78%,42%))"/>
<path d="M74 60 Q60 66 52 78 Q42 88 22 90 Q34 80 30 70 Q26 62 38 58 Q50 54 58 54 Q66 52 74 52 Z" fill="var(--f3,hsl(38,78%,42%))"/>
<path d="M71 50 Q71 18 103 18 Q135 18 135 50 Q135 82 103 82 Q71 82 71 50 Z" fill="var(--f1,hsl(42,72%,55%))"/>
<path d="M73 55 Q103 63 133 55 Q130 80 103 81.5 Q76 80 73 55 Z" fill="var(--f2,hsl(45,65%,76%))"/>
<path d="M86 24 Q100 4 120 20 Q106 22 92 28 Z" fill="var(--f3,hsl(38,78%,42%))"/>
<path d="M106 58 Q124 64 120 78 Q108 72 102 62 Z" fill="var(--f3,hsl(38,78%,42%))"/>
<path d="M90 80 Q100 94 112 86 Q102 82 94 78 Z" fill="var(--f3,hsl(38,78%,42%))"/>
<circle cx="90" cy="40" r="4" fill="var(--f4,hsl(30,62%,36%))"/>
<circle cx="112" cy="34" r="3" fill="var(--f4,hsl(30,62%,36%))"/>
<circle cx="98" cy="66" r="3.5" fill="var(--f4,hsl(30,62%,36%))"/>
<ellipse cx="131" cy="56" rx="2.8" ry="2.2" fill="var(--f4,hsl(30,62%,36%))"/>
<path d="M78 36 Q84 22 103 21 Q122 22 128 36 Q120 27 103 26 Q86 27 78 36 Z" fill="#fff6d8"/>
<circle cx="90" cy="32" r="2.5" fill="#fff6d8"/>
<circle cx="112" cy="28" r="2" fill="#fff6d8"/>
<circle cx="100" cy="48" r="1.5" fill="#fff6d8"/>
<circle cx="118" cy="42" r="7" fill="#fff"/>
<circle cx="119.5" cy="42" r="3.4" fill="#142638"/>
</symbol>
<symbol id="fish-zander" viewBox="0 0 160 100">
<!-- хвост: вилочный -->
<path fill="var(--f3,hsl(180,50%,32%))" d="M40 47 L15 26 L27 50 L15 74 L40 53 Z"/>
<!-- спинной плавник (передний, колючий) -->
<path fill="var(--f3,hsl(180,50%,32%))" d="M56 36 Q68 8 86 30.5 Z"/>
<!-- спинной плавник (задний, мягкий) -->
<path fill="var(--f3,hsl(180,50%,32%))" d="M88 30 Q99 10 111 32.5 Z"/>
<!-- тело: удлинённое, заострённая морда -->
<path fill="var(--f1,hsl(180,45%,48%))" d="M36 48 C46 35 70 28 96 29 C118 30 136 40 146 50 C136 60 118 70 96 71 C70 72 46 65 36 52 Z"/>
<!-- брюхо: светлее -->
<path fill="var(--f2,hsl(180,30%,70%))" d="M36 52 C46 65 70 72 96 71 C118 70 136 60 146 50 C128 55.5 102 60 78 60.5 C58 61 43 57.5 36 52 Z"/>
<!-- узор: тёмная полоса вдоль бока от головы к хвосту -->
<path fill="var(--f4,hsl(180,45%,26%))" d="M118 41.5 C102 39.8 90 39.8 78 40.5 C60 41.5 47 44 39 47.5 L39 53.5 C47 56 60 53 78 51.5 C98 51.8 110 52.3 118 52 Z"/>
<!-- грудной плавник -->
<path fill="var(--f3,hsl(180,50%,32%))" d="M111 54 C106 59 106 63.5 111 66.5 C114 62 115 58 113 54 Z"/>
<!-- брюшной плавник -->
<path fill="var(--f3,hsl(180,50%,32%))" d="M64 66.5 L59 75 L69 67.5 Z"/>
<!-- анальный плавник -->
<path fill="var(--f3,hsl(180,50%,32%))" d="M88 70 L82 78.5 L94 71 Z"/>
<!-- глаз -->
<circle fill="#fff" cx="126" cy="43" r="4.5"/>
<circle fill="#142638" cx="127" cy="43" r="2.2"/>
</symbol>
<symbol id="fish-chuch" viewBox="0 0 160 100">
<path fill="var(--f3,hsl(25,50%,32%))" d="M36 44 C24 36 15 28 10 22 C14 38 19 46 26 52 C19 58 14 68 12 82 C20 74 28 66 38 60 Z"/>
<path fill="var(--f3,hsl(25,50%,32%))" d="M106 40 C102 18 92 6 80 6 C60 6 42 22 30 46 C55 38 85 36 106 40 Z"/>
<path fill="var(--f1,hsl(25,45%,48%))" d="M146 53 C142 42 133 35 117 32 C92 27 56 29 34 44 L30 52 C42 66 66 74 92 73 C117 72 140 64 146 53 Z"/>
<path fill="var(--f2,hsl(25,30%,70%))" d="M146 53 C140 64 117 72 92 73 C66 74 42 66 30 52 C50 60 75 63 98 62 C120 60 138 57 146 53 Z"/>
<path fill="var(--f4,hsl(25,45%,26%))" d="M97 34 C99 44 99 54 97 60 L104 60 C106 54 106 44 104 34 Z"/>
<path fill="var(--f4,hsl(25,45%,26%))" d="M81 33 C83 43 83 53 81 62 L88 62 C90 53 90 43 88 33 Z"/>
<path fill="var(--f4,hsl(25,45%,26%))" d="M65 33 C67 43 67 53 65 62 L72 62 C74 53 74 43 72 33 Z"/>
<path fill="var(--f4,hsl(25,45%,26%))" d="M49 36 C51 44 51 52 49 59 L55 59 C57 52 57 44 55 36 Z"/>
<path fill="var(--f3,hsl(25,50%,32%))" d="M114 54 C106 58 102 64 103 70 C110 68 116 62 118 56 Z"/>
<path fill="var(--f3,hsl(25,50%,32%))" d="M74 68 C70 74 68 80 70 84 C76 80 79 75 80 70 Z"/>
<path fill="var(--f3,hsl(25,50%,32%))" d="M56 66 C52 72 51 78 53 82 C58 78 61 73 62 68 Z"/>
<path fill="var(--f4,hsl(25,45%,26%))" d="M146 56 C141 58 136 58 132 56 C136 60 142 60 146 58 Z"/>
<circle cx="128" cy="47" r="6" fill="#fff"/>
<circle cx="129.5" cy="47" r="3" fill="#142638"/>
</symbol>
<symbol id="fish-crayfish" viewBox="0 0 160 100">
<ellipse cx="25" cy="52" rx="10.5" ry="5.5" fill="var(--f3,hsl(0,50%,32%))"/>
<ellipse cx="24" cy="52" rx="12" ry="5" fill="var(--f3,hsl(0,50%,32%))" transform="rotate(-27 36 52)"/>
<ellipse cx="24" cy="52" rx="12" ry="5" fill="var(--f3,hsl(0,50%,32%))" transform="rotate(27 36 52)"/>
<ellipse cx="25" cy="52" rx="11" ry="4.5" fill="var(--f3,hsl(0,50%,32%))" transform="rotate(-55 36 52)"/>
<ellipse cx="25" cy="52" rx="11" ry="4.5" fill="var(--f3,hsl(0,50%,32%))" transform="rotate(55 36 52)"/>
<ellipse cx="44" cy="52" rx="11" ry="13.5" fill="var(--f1,hsl(0,45%,48%))"/>
<ellipse cx="58" cy="52" rx="10" ry="12" fill="var(--f1,hsl(0,45%,48%))"/>
<ellipse cx="71" cy="52" rx="10" ry="11" fill="var(--f1,hsl(0,45%,48%))"/>
<ellipse cx="50" cy="52" rx="1.8" ry="9" fill="var(--f4,hsl(0,45%,26%))"/>
<ellipse cx="64" cy="52" rx="1.8" ry="8" fill="var(--f4,hsl(0,45%,26%))"/>
<ellipse cx="48" cy="59" rx="9" ry="3.5" fill="var(--f2,hsl(0,30%,70%))"/>
<path d="M74 37 Q86 28 100 30 Q112 32 113 42 Q114 50 109 57 Q103 65 93 68 Q80 71 73 63 Q67 51 74 37 Z" fill="var(--f1,hsl(0,45%,48%))"/>
<ellipse cx="85" cy="60" rx="11" ry="3.5" fill="var(--f2,hsl(0,30%,70%))"/>
<path d="M105 44 L114 43 L115 49 L106 49 Z" fill="var(--f3,hsl(0,50%,32%))"/>
<path d="M112 39 Q121 33 126 38 Q129 42 126 44 L120 45 Q125 47 123 51 Q120 55 113 52 Q109 47 112 39 Z" fill="var(--f3,hsl(0,50%,32%))"/>
<path d="M98 58 Q110 56 116 62 L114 70 Q104 68 96 64 Z" fill="var(--f3,hsl(0,50%,32%))"/>
<path d="M114 56 Q125 47 134 51 Q141 55 139 61 L128 62 Q137 65 136 71 Q134 78 124 75 Q113 71 111 63 Q111 58 114 56 Z" fill="var(--f3,hsl(0,50%,32%))"/>
<path d="M85 65 Q83 75 79 82" fill="none" stroke="var(--f4,hsl(0,45%,26%))" stroke-width="2" stroke-linecap="round"/>
<path d="M73 63 Q71 74 67 82" fill="none" stroke="var(--f4,hsl(0,45%,26%))" stroke-width="2" stroke-linecap="round"/>
<path d="M61 62 Q60 71 57 78" fill="none" stroke="var(--f4,hsl(0,45%,26%))" stroke-width="2" stroke-linecap="round"/>
<path d="M106 34 Q128 25 146 20" fill="none" stroke="var(--f4,hsl(0,45%,26%))" stroke-width="2" stroke-linecap="round"/>
<path d="M107 39 Q130 35 148 42" fill="none" stroke="var(--f4,hsl(0,45%,26%))" stroke-width="2" stroke-linecap="round"/>
<path d="M98 31 L101 23 L104 25 L101 33 Z" fill="var(--f4,hsl(0,45%,26%))"/>
<path d="M107 32 L112 25 L115 27 L110 34 Z" fill="var(--f4,hsl(0,45%,26%))"/>
<circle cx="102.5" cy="22" r="2.8" fill="#142638"/>
<circle cx="113.5" cy="24" r="2.8" fill="#142638"/>
</symbol>
<symbol id="fish-bleak" viewBox="0 0 160 100">
<!-- хвост: мелкий вилочный -->
<path fill="var(--f3,hsl(200,50%,32%))" d="M35 42 L14 32 L24.5 47 L14 62 L35 52 Z"/>
<!-- тело: невысокое удлинённое, заострённая морда -->
<path fill="var(--f2,hsl(200,30%,70%))" d="M144 47 C132 36 110 30 84 30 C56 30 40 35 34 41 L34 53 C40 59 56 63 84 63 C110 63 132 58 144 47 Z"/>
<!-- брюхо: светлее -->
<path fill="var(--f2b,hsl(200,25%,82%))" d="M144 47 C132 58 110 63 84 63 C56 63 40 59 34 53 C40 55 56 58.5 84 58.5 C110 58.5 132 54.5 144 47 Z"/>
<!-- тонкая тёмная полоса вдоль верхнего края спины -->
<path fill="var(--f1,hsl(200,45%,48%))" d="M144 47 C132 36 110 30 84 30 C56 30 40 35 34 41 C40 39 56 34.5 84 34.5 C110 34.5 132 39.5 144 47 Z"/>
<!-- спинной плавник: низкий -->
<path fill="var(--f3,hsl(200,50%,32%))" d="M68 31 Q83 10 102 31.5 Z"/>
<!-- грудной плавник -->
<path fill="var(--f3,hsl(200,50%,32%))" d="M112 51 C107 56 107 61.5 112 64.5 C115.5 60 116.5 55.5 114 51.5 Z"/>
<!-- брюшной плавник -->
<path fill="var(--f3,hsl(200,50%,32%))" d="M82 63 L77 71 L89 64 Z"/>
<!-- анальный плавник -->
<path fill="var(--f3,hsl(200,50%,32%))" d="M62 62.5 L56 70.5 L68 63.5 Z"/>
<!-- узор: тёмные точки вдоль бока -->
<ellipse fill="var(--f4,hsl(200,45%,26%))" cx="56" cy="48" rx="3.5" ry="2.2"/>
<ellipse fill="var(--f4,hsl(200,45%,26%))" cx="76" cy="46.5" rx="3.5" ry="2.2"/>
<ellipse fill="var(--f4,hsl(200,45%,26%))" cx="96" cy="45.5" rx="3.5" ry="2.2"/>
<!-- глаз -->
<circle fill="#fff" cx="127" cy="42" r="4.5"/>
<circle fill="#142638" cx="128" cy="42" r="2.2"/>
</symbol>
<symbol id="fish-roundel" viewBox="0 0 160 100">
<!-- хвост: вилочный -->
<path fill="var(--f3,hsl(215,50%,32%))" d="M40 50 L14 28 L27 50 L14 72 Z"/>
<!-- тело: высокое широкое, серебристое -->
<path fill="var(--f2,hsl(215,30%,70%))" d="M148 48 C140 34 116 25 90 24 C64 23 46 31 36 42 L36 58 C46 69 64 77 90 76 C116 75 140 62 148 48 Z"/>
<!-- брюхо: светлее -->
<path fill="var(--f2b,hsl(215,25%,82%))" d="M148 48 C140 62 116 75 90 76 C64 77 46 69 36 58 C46 64 64 68 90 66 C116 64 140 56 148 48 Z"/>
<!-- спинной плавник -->
<path fill="var(--f3,hsl(215,50%,32%))" d="M62 27 Q82 6 106 26 Q84 20 62 27 Z"/>
<!-- тёмная спинная кромка: тонкая полоса вдоль верхнего края -->
<path fill="var(--f1,hsl(215,45%,48%))" d="M148 48 C140 34 116 25 90 24 C64 23 46 31 36 42 C46 36 64 28 90 29 C116 30 140 38 148 48 Z"/>
<!-- узор: линия жаберной крышки -->
<path fill="var(--f4,hsl(215,45%,26%))" d="M116 36 C122 43 122 55 116 62 C119 55 119 43 116 36 Z"/>
<!-- грудной плавник -->
<path fill="var(--f3,hsl(215,50%,32%))" d="M110 55 C104 60 103 66 108 70 C112 64 114 59 112 55 Z"/>
<!-- брюшной плавник -->
<path fill="var(--f3,hsl(215,50%,32%))" d="M80 73 L74 83 L88 75 Z"/>
<!-- анальный плавник -->
<path fill="var(--f3,hsl(215,50%,32%))" d="M58 68 L52 78 L66 70 Z"/>
<!-- глаз -->
<circle fill="#fff" cx="127" cy="44" r="5.5"/>
<circle fill="#142638" cx="128.5" cy="44" r="2.8"/>
</symbol>
<symbol id="fish-eelpout" viewBox="0 0 160 100">
<!-- тело: изогнутая волной тонкая лента, сужается к хвосту -->
<path fill="var(--f1,hsl(35,45%,48%))" d="M22 57.5 C32 62 38 61 45 59.5 C55 54 63 43 75 41 C85 39.5 92 42 100 45 C112 47.5 124 40 135 34 C138.5 33 142 34.5 143.5 38 C144.5 40.5 144 43.5 142 45.5 C141 46.3 140 46.5 139 46.5 C130 47.5 122 49.5 112 53.5 C104 56.8 88 57 75 53 C64 52.5 54 60 45 66.5 C36 71.5 28 65 22 60.5 C21.3 59.6 21.3 58.4 22 57.5 Z"/>
<!-- брюхо: нижняя полоса ленты светлее -->
<path fill="var(--f2,hsl(35,30%,70%))" d="M22 59.4 C33 62 40 64.5 45 65 C55 60.5 65 51 75 48.8 C85 50.5 95 52 100 52.8 C112 52.5 124 45.5 135 41.8 C140 42.5 142.5 44.5 142 45.5 C141 46.3 140 46.5 139 46.5 C130 47.5 122 49.5 112 53.5 C104 56.8 88 57 75 53 C64 52.5 54 60 45 66.5 C36 71.5 28 65 22 60.5 C21.6 60 21.6 59.8 22 59.4 Z"/>
<!-- спинной плавник: низкий, вдоль хребта до самой головы -->
<path fill="var(--f3,hsl(35,50%,32%))" d="M45 59.5 C56 52 64 38 75 36.5 C86 35 93 38.5 100 40.5 C112 43 125 34 136 29.8 C139 29 141.5 30.5 141 32.5 C139 33.5 137 34 135 34 C124 40 112 47.5 100 45 C92 42 85 39.5 75 41 C63 43 55 54 45 59.5 Z"/>
<!-- узор: тёмные пятна вдоль ленты -->
<ellipse fill="var(--f4,hsl(35,45%,26%))" cx="48" cy="61.5" rx="2.6" ry="2.2"/>
<ellipse fill="var(--f4,hsl(35,45%,26%))" cx="75" cy="47.5" rx="3.2" ry="2.8"/>
<ellipse fill="var(--f4,hsl(35,45%,26%))" cx="98" cy="50.5" rx="3.2" ry="2.8"/>
<ellipse fill="var(--f4,hsl(35,45%,26%))" cx="118" cy="46" rx="2.9" ry="2.5"/>
<!-- открытый рот на конце головы -->
<path fill="var(--f4,hsl(35,45%,26%))" d="M137.5 39.5 C140.5 38.6 143 39.6 143.4 41.3 C143 43.4 140.6 44.4 138 43.6 C139.2 42 139.2 40.8 137.5 39.5 Z"/>
<!-- глаз: крошечный -->
<circle fill="#fff" cx="130" cy="38.5" r="2.2"/>
<circle fill="#142638" cx="130.8" cy="38.5" r="1.1"/>
</symbol>
<symbol id="fish-tench" viewBox="0 0 160 100">
<path d="M46 50 C34 37 24 31 17 26 C20 37 22 44 27 50 C22 56 20 63 17 74 C24 69 34 63 46 50 Z" fill="var(--f3,hsl(95,50%,32%))"/>
<path d="M54 31 C56 12 86 5 103 14 C106 16 107 21 105 28 C90 24 68 26 54 31 Z" fill="var(--f3,hsl(95,50%,32%))"/>
<path d="M140 50 C128 32 105 23 78 23 C58 23 44 30 38 42 C36 46 36 54 38 58 C44 70 58 77 78 77 C105 77 128 68 140 50 Z" fill="var(--f1,hsl(95,45%,48%))"/>
<path d="M38 58 C44 70 58 77 78 77 C105 77 128 68 140 50 C133 57 123 62 112 65 C94 70 68 70 52 64 C46 61 41 59 38 58 Z" fill="var(--f2,hsl(95,30%,70%))"/>
<path d="M103 55 C111 51 120 54 122 62 C115 65 106 63 102 58 Z" fill="var(--f3,hsl(95,50%,32%))"/>
<path d="M58 75 C62 88 76 93 88 89 C87 84 86 80 85 76 Z" fill="var(--f3,hsl(95,50%,32%))"/>
<circle cx="122" cy="55" r="4" fill="var(--f4,hsl(95,45%,26%))"/>
<circle cx="104" cy="55" r="5.5" fill="var(--f4,hsl(95,45%,26%))"/>
<circle cx="85" cy="56" r="6" fill="var(--f4,hsl(95,45%,26%))"/>
<circle cx="65" cy="55" r="6" fill="var(--f4,hsl(95,45%,26%))"/>
<circle cx="48" cy="52" r="5" fill="var(--f4,hsl(95,45%,26%))"/>
<circle cx="119" cy="44" r="6.5" fill="#fff"/>
<circle cx="120.5" cy="44" r="3" fill="#142638"/>
</symbol>
<symbol id="fish-navaga" viewBox="0 0 160 100">
<!-- хвост: мелкий вилочный -->
<path fill="var(--f3,hsl(222,40%,32%))" d="M40 46 L16 34 L26 50 L16 66 L40 54 Z"/>
<!-- тело: тонкое удлинённое, заострённая морда -->
<path fill="var(--f2,hsl(222,22%,66%))" d="M144 50 C134 39 112 35 86 35 C60 35 44 41 38 47 L38 53 C44 59 60 65 86 65 C112 65 134 61 144 50 Z"/>
<!-- брюхо: ещё светлее -->
<path fill="var(--f2b,hsl(222,18%,80%))" d="M144 50 C134 61 112 65 86 65 C60 65 44 59 38 53 C44 55.5 60 59 86 59 C112 59 134 55.5 144 50 Z"/>
<!-- спинка: тёмная стальная полоса вдоль верхнего края -->
<path fill="var(--f1,hsl(222,35%,44%))" d="M144 50 C134 39 112 35 86 35 C60 35 44 41 38 47 C44 44.5 60 40.5 86 40.5 C112 40.5 134 44.5 144 50 Z"/>
<!-- оливковая полоска: тёплая, тонкая, от головы к хвосту по верхнему краю (черта наваги) -->
<path fill="var(--f4,hsl(75,40%,42%))" d="M136 47.4 C120 44.4 100 41.6 84 41.6 C62 41.6 46 44.6 40 47.6 L40 49.8 C46 47 62 44 84 44 C100 44 120 46.8 136 49.8 Z"/>
<!-- спинной плавник: маленький, смещён к хвосту -->
<path fill="var(--f3,hsl(222,40%,32%))" d="M60 38.5 Q71 18 82 37 Z"/>
<!-- грудной плавник -->
<path fill="var(--f3,hsl(222,40%,32%))" d="M104 61.5 L98 70 L110 62.5 Z"/>
<!-- брюшной плавник: ближе к хвосту -->
<path fill="var(--f3,hsl(222,40%,32%))" d="M62 60.5 L56 68.5 L68 61.5 Z"/>
<!-- рот: маленький тёмный на конце морды -->
<path fill="var(--f4,hsl(222,45%,26%))" d="M134 50.2 C136.8 50.4 139.2 51.1 140.2 52.4 C138.4 54 135.8 53.7 134.2 52.4 Z"/>
<!-- узор: мелкие тёмные точки вдоль бока -->
<ellipse fill="var(--f4,hsl(222,45%,26%))" cx="70" cy="47.5" rx="2.6" ry="1.6"/>
<ellipse fill="var(--f4,hsl(222,45%,26%))" cx="88" cy="47.2" rx="2.6" ry="1.6"/>
<ellipse fill="var(--f4,hsl(222,45%,26%))" cx="106" cy="47.2" rx="2.6" ry="1.6"/>
<!-- глаз: самый крупный из тройки -->
<circle fill="#fff" cx="126" cy="45.5" r="5.4"/>
<circle fill="#142638" cx="127" cy="45.5" r="2.9"/>
</symbol>
<symbol id="fish-rudd" viewBox="0 0 160 100">
<path d="M46 50 L20 20 L38 50 L20 80 Z" fill="var(--f3,hsl(5,70%,52%))"/>
<path d="M40 50 Q46 20 88 20 Q128 24 140 50 Q128 76 88 80 Q46 80 40 50 Z" fill="var(--f1,hsl(25,45%,48%))"/>
<path d="M40 50 Q46 80 88 80 Q128 76 140 50 Q116 64 84 64 Q54 62 40 50 Z" fill="var(--f2,hsl(25,30%,70%))"/>
<path d="M54 26 Q82 4 122 14 Q128 21 126 26 Q88 13 54 26 Z" fill="var(--f3,hsl(5,70%,52%))"/>
<path d="M114 54 Q138 58 130 78 Q116 70 106 60 Z" fill="var(--f3,hsl(5,70%,52%))"/>
<path d="M94 74 Q98 92 80 88 Q86 80 88 74 Z" fill="var(--f3,hsl(5,70%,52%))"/>
<path d="M68 72 Q64 90 50 86 Q56 78 58 68 Z" fill="var(--f3,hsl(5,70%,52%))"/>
<path d="M50 46 Q90 38 134 46 Q90 52 50 46 Z" fill="var(--f4,hsl(25,45%,26%))"/>
<path d="M68 30 Q74 26 80 30 Q74 36 68 30 Z" fill="var(--f4,hsl(25,45%,26%))"/>
<path d="M94 26 Q100 22 106 26 Q100 32 94 26 Z" fill="var(--f4,hsl(25,45%,26%))"/>
<path d="M116 32 Q122 28 128 32 Q122 38 116 32 Z" fill="var(--f4,hsl(25,45%,26%))"/>
<circle cx="124" cy="40" r="6.5" fill="#fff"/>
<circle cx="125.5" cy="40" r="3" fill="#142638"/>
</symbol>
<symbol id="fish-cisco" viewBox="0 0 160 100">
<!-- хвост: вилочный -->
<path fill="var(--f3,hsl(172,48%,32%))" d="M40 45 L12 30 L24 49 L12 68 L40 53 Z"/>
<!-- тело: удлинённое бирюзово-серебристое, чуть выше, чем у сельди, заострённая морда -->
<path fill="var(--f2,hsl(172,28%,68%))" d="M146 49 C134 39 112 31 86 31 C58 31 42 37 34 43 L34 55 C42 62 58 67 86 67 C112 67 134 61 146 49 Z"/>
<!-- брюхо: светлее -->
<path fill="var(--f2b,hsl(172,22%,82%))" d="M146 49 C134 61 112 67 86 67 C58 67 42 62 34 55 C42 58 58 63 86 63 C112 63 134 58 146 49 Z"/>
<!-- тёмная спинная кромка: тонкая полоса -->
<path fill="var(--f1,hsl(172,42%,46%))" d="M146 49 C134 39 112 31 86 31 C58 31 42 37 34 43 C42 40.5 58 35.5 86 35.5 C112 35.5 134 41 146 49 Z"/>
<!-- спинной плавник: высокий (характерно для сиговых) -->
<path fill="var(--f3,hsl(172,48%,32%))" d="M64 33.5 Q80 -10 98 32.5 Z"/>
<!-- грудной плавник -->
<path fill="var(--f3,hsl(172,48%,32%))" d="M112 54 C107 59 107 64.5 112 67.5 C115.5 63 116.5 58.5 114 54.5 Z"/>
<!-- брюшной плавник -->
<path fill="var(--f3,hsl(172,48%,32%))" d="M84 67 L79 75 L91 68.5 Z"/>
<!-- анальный плавник -->
<path fill="var(--f3,hsl(172,48%,32%))" d="M62 65.5 L56 73.5 L68 66.5 Z"/>
<!-- узор: тёмная боковая линия — ровная полоска вдоль середины тела от жабер к хвосту -->
<path fill="var(--f4,hsl(172,42%,26%))" d="M118 47.9 L38 48.4 L38 50.6 L118 50.1 Z"/>
<!-- глаз -->
<circle fill="#fff" cx="128" cy="43.5" r="4.5"/>
<circle fill="#142638" cx="129" cy="43.5" r="2.2"/>
</symbol>
<symbol id="fish-herring" viewBox="0 0 160 100">
<!-- хвост: мелкий вилочный -->
<path fill="var(--f3,hsl(205,50%,32%))" d="M37 50 L18 40 L26.5 50 L18 60 L37 50 Z"/>
<!-- тело: самое тонкое из тройки, удлинённое, заострённая морда -->
<path fill="var(--f2,hsl(205,30%,70%))" d="M146 50 C136 40 114 36 88 36 C62 36 44 40 37 46 L37 54 C44 60 62 64 88 64 C114 64 136 60 146 50 Z"/>
<!-- брюхо: ещё светлее -->
<path fill="var(--f2b,hsl(205,25%,82%))" d="M146 50 C136 60 114 64 88 64 C62 64 44 60 37 54 C44 57 62 59.5 88 59.5 C114 59.5 136 57 146 50 Z"/>
<!-- спинная кромка: тонкая тёмная полоса вдоль верхнего края -->
<path fill="var(--f1,hsl(205,45%,48%))" d="M146 50 C136 40 114 36 88 36 C62 36 44 40 37 46 C44 43 62 40.5 88 40.5 C114 40.5 136 43 146 50 Z"/>
<!-- спинной плавник: низкий, смещён к хвосту -->
<path fill="var(--f3,hsl(205,50%,32%))" d="M64 37.5 Q76 22 88 37 Z"/>
<!-- грудной плавник -->
<path fill="var(--f3,hsl(205,50%,32%))" d="M116 54.5 C112 58 111.5 62.5 115 65.5 C118 61.5 118.5 57.5 117.5 55 Z"/>
<!-- брюшной плавник: далеко сдвинут к хвосту (черта сельди) -->
<path fill="var(--f3,hsl(205,50%,32%))" d="M88 63.5 L84 70.5 L95 63.8 Z"/>
<!-- анальный плавник: ближе к хвосту -->
<path fill="var(--f3,hsl(205,50%,32%))" d="M61 63 L56 69.5 L67 63.4 Z"/>
<!-- рот: маленький тёмный на конце морды -->
<path fill="var(--f4,hsl(205,45%,26%))" d="M138 50.3 C140.5 50.6 142.5 51.5 143.5 52.8 C141.8 54.2 139.4 53.9 138 52.6 Z"/>
<!-- узор: 6 коротких тёмных вертикалей вдоль верхнего края спины (~30% высоты тела, не доходят до брюха) -->
<path fill="var(--f4,hsl(205,45%,26%))" d="M117.1 39.5 L118.9 39.5 L118.9 47.5 L117.1 47.5 Z"/>
<path fill="var(--f4,hsl(205,45%,26%))" d="M105.1 38 L106.9 38 L106.9 46 L105.1 46 Z"/>
<path fill="var(--f4,hsl(205,45%,26%))" d="M93.1 37.2 L94.9 37.2 L94.9 45.2 L93.1 45.2 Z"/>
<path fill="var(--f4,hsl(205,45%,26%))" d="M81.1 37.3 L82.9 37.3 L82.9 45.3 L81.1 45.3 Z"/>
<path fill="var(--f4,hsl(205,45%,26%))" d="M69.1 37.9 L70.9 37.9 L70.9 45.9 L69.1 45.9 Z"/>
<path fill="var(--f4,hsl(205,45%,26%))" d="M57.1 39.5 L58.9 39.5 L58.9 47.5 L57.1 47.5 Z"/>
<!-- глаз: маленький -->
<circle fill="#fff" cx="130" cy="45" r="3.6"/>
<circle fill="#142638" cx="130.8" cy="45" r="1.9"/>
</symbol>
<symbol id="fish-mackerel" viewBox="0 0 160 100">
<!-- хвост: вилочный -->
<path fill="var(--f3,hsl(190,50%,32%))" d="M32 45 L12 34 L23 50 L12 66 L32 55 Z"/>
<!-- спинной плавник: низкий, заострённый -->
<path fill="var(--f3,hsl(190,50%,32%))" d="M58 36 Q73 17 88 35 Z"/>
<!-- анальный плавник: смещён к хвосту (черта скумбрии) -->
<path fill="var(--f3,hsl(190,50%,32%))" d="M31 57.5 L26 65.5 L40 62.3 Z"/>
<!-- брюшной плавник -->
<path fill="var(--f3,hsl(190,50%,32%))" d="M90 63.3 L87 71 L99 65.6 Z"/>
<!-- тело: удлинённое, тёмная полосатая спина -->
<path fill="var(--f1,hsl(190,45%,48%))" d="M144 50 C132 40 106 33 78 33 C54 33 38 37 30 43 L30 57 C38 63 54 67 78 67 C106 67 132 60 144 50 Z"/>
<!-- брюхо: светлое, контраст со спиной -->
<path fill="var(--f2,hsl(190,30%,70%))" d="M144 50 C132 60 106 67 78 67 C54 67 38 63 30 57 C38 60.5 54 60.5 78 60.5 C106 60.5 132 55.5 144 50 Z"/>
<!-- узор: волнистые тёмные полосы вдоль спинки (до ~40% высоты тела) -->
<path fill="var(--f4,hsl(190,45%,26%))" d="M54 35.8 C57.5 40.8 51.5 44.8 56 49.8 L60 48.6 C55.8 44.4 60.8 40.2 58.2 35.8 Z"/>
<path fill="var(--f4,hsl(190,45%,26%))" d="M70 34 C73.5 39 67.5 43 72 48 L76 46.8 C71.8 42.6 76.8 38.4 74.2 34 Z"/>
<path fill="var(--f4,hsl(190,45%,26%))" d="M86 33.9 C89.5 38.9 83.5 42.9 88 47.9 L92 46.7 C87.8 42.5 92.8 38.3 90.2 33.9 Z"/>
<path fill="var(--f4,hsl(190,45%,26%))" d="M102 35.8 C105.5 40.8 99.5 44.8 104 49.8 L108 48.6 C103.8 44.4 108.8 40.2 106.2 35.8 Z"/>
<!-- грудной плавник -->
<path fill="var(--f3,hsl(190,50%,32%))" d="M113 51 C109 55 108.5 60 112 63 C115 59 115.5 54 114.5 51.5 Z"/>
<!-- рот: маленький тёмный на конце морды -->
<path fill="var(--f4,hsl(190,45%,26%))" d="M136.5 50.4 C139.5 50.7 141.5 51.7 143 53.1 C141 54.4 138.5 54 137 52.8 Z"/>
<!-- глаз -->
<circle fill="#fff" cx="125" cy="45" r="4"/>
<circle fill="#142638" cx="125.8" cy="45" r="2.1"/>
</symbol>
<symbol id="fish-flounder" viewBox="0 0 160 100">
<!-- хвост: маленький, закруглённый -->
<path fill="var(--f3,hsl(40,50%,32%))" d="M30 52 C20 41 10 42 8 52 C10 62 20 63 30 52 Z"/>
<!-- спинной плавник: низкая полоса вдоль верхнего края -->
<path fill="var(--f3,hsl(40,50%,32%))" d="M32 39 C52 24 96 22 118 38.4 C92 33 60 32.5 32 39 Z"/>
<!-- тело: плоская широкая «пластина» ~2.5:1, заострённая морда справа -->
<path fill="var(--f1,hsl(40,45%,48%))" d="M140 52 C138 44 124 35 100 32 C72 29 42 31 28 40 C21 45 21 59 28 64 C42 73 72 76 100 73 C124 70 138 60 140 52 Z"/>
<!-- брюхо: светлая полоса вдоль нижнего края -->
<path fill="var(--f2,hsl(40,30%,70%))" d="M132 57 C126 66 110 71 92 72.5 C70 74.5 46 71.5 33 61 C40 66.5 58 63.5 80 63 C102 62.5 122 63 132 57 Z"/>
<!-- узор: мраморные пятна-камуфляж -->
<ellipse fill="var(--f4,hsl(40,45%,26%))" cx="46" cy="47" rx="7" ry="4.5" transform="rotate(-14 46 47)"/>
<ellipse fill="var(--f4,hsl(40,45%,26%))" cx="62" cy="58" rx="6" ry="4" transform="rotate(10 62 58)"/>
<ellipse fill="var(--f4,hsl(40,45%,26%))" cx="80" cy="45" rx="8" ry="5" transform="rotate(-8 80 45)"/>
<ellipse fill="var(--f4,hsl(40,45%,26%))" cx="95" cy="57" rx="5.5" ry="3.5" transform="rotate(12 95 57)"/>
<ellipse fill="var(--f4,hsl(40,45%,26%))" cx="58" cy="38" rx="4.5" ry="3" transform="rotate(8 58 38)"/>
<ellipse fill="var(--f4,hsl(40,45%,26%))" cx="74" cy="66" rx="5" ry="3" transform="rotate(-12 74 66)"/>
<ellipse fill="var(--f4,hsl(40,45%,26%))" cx="90" cy="38" rx="4.5" ry="3" transform="rotate(-6 90 38)"/>
<circle fill="var(--f4,hsl(40,45%,26%))" cx="36" cy="57" r="3"/>
<circle fill="var(--f4,hsl(40,45%,26%))" cx="102" cy="52" r="3.5"/>
<!-- рот: тёмная щель у заострённой морды -->
<path fill="var(--f4,hsl(40,45%,26%))" d="M139 52 C133 48.5 126 49 122 52 C127 55 134 55 139 52 Z"/>
<!-- грудной плавник -->
<ellipse fill="var(--f3,hsl(40,50%,32%))" cx="103" cy="62" rx="10" ry="5.5" transform="rotate(-15 103 62)"/>
<!-- глаза: ОБА на верхней стороне, рядом друг с другом (камбала лежит на боку) -->
<circle fill="#fff" cx="110" cy="43.5" r="5.8"/>
<circle fill="#142638" cx="112" cy="43.5" r="2.7"/>
<circle fill="#fff" cx="122" cy="45" r="5"/>
<circle fill="#142638" cx="124" cy="45" r="2.2"/>
</symbol>
<symbol id="fish-tuna" viewBox="0 0 160 100">
<!-- хвост: полумесяц — верхняя серповидная лопасть -->
<path fill="var(--f3,hsl(210,50%,32%))" d="M37 46 C28 38 17 31 10 27 C13 38 19 45 29 50 L37 46 Z"/>
<!-- хвост: полумесяц — нижняя серповидная лопасть -->
<path fill="var(--f3,hsl(210,50%,32%))" d="M37 54 C28 62 17 69 10 73 C13 62 19 55 29 50 L37 54 Z"/>
<!-- спинной плавник: маленький, впереди -->
<path fill="var(--f3,hsl(210,50%,32%))" d="M101 34.5 L93 18 L115 34.2 Z"/>
<!-- жировые плавнички позади спинного: 3 маленьких треугольника, к хвосту мельчают -->
<path fill="var(--f3,hsl(210,50%,32%))" d="M91 34.3 L85 24 L79 34.6 Z"/>
<path fill="var(--f3,hsl(210,50%,32%))" d="M77 34.8 L72 26.5 L66 35 Z"/>
<path fill="var(--f3,hsl(210,50%,32%))" d="M64 35.6 L60 29 L55 35.9 Z"/>
<!-- анальный плавник: смещён к хвосту -->
<path fill="var(--f3,hsl(210,50%,32%))" d="M43 58.5 L38 66 L51 62 Z"/>
<!-- брюшной плавник -->
<path fill="var(--f3,hsl(210,50%,32%))" d="M107 62.5 L104 69 L113 64.5 Z"/>
<!-- тело: торпеда, сужается к хвостовому стеблю -->
<path fill="var(--f1,hsl(210,45%,48%))" d="M151 50 C139 40 111 33 81 33 C61 33 47 38 36 45.5 L36 54.5 C47 62 61 67 81 67 C111 67 139 60 151 50 Z"/>
<!-- брюхо: светлое, контраст тёмная спина / светлое брюхо -->
<path fill="var(--f2,hsl(210,30%,70%))" d="M151 50 C139 60 111 67 81 67 C61 67 47 62 36 54.5 C47 58.5 61 60 81 60 C111 60 139 55.5 151 50 Z"/>
<!-- узор: тонкие тёмные линии по нижней половине бока (черта тунца) -->
<path fill="var(--f4,hsl(210,45%,26%))" d="M57 54.6 L73 54.6 L73 55.9 L57 55.9 Z"/>
<path fill="var(--f4,hsl(210,45%,26%))" d="M79 54.6 L95 54.6 L95 55.9 L79 55.9 Z"/>
<path fill="var(--f4,hsl(210,45%,26%))" d="M101 54.6 L117 54.6 L117 55.9 L101 55.9 Z"/>
<path fill="var(--f4,hsl(210,45%,26%))" d="M123 54.6 L133 54.6 L133 55.9 L123 55.9 Z"/>
<!-- рот: маленький тёмный на конце морды -->
<path fill="var(--f4,hsl(210,45%,26%))" d="M143.5 50.4 C146.5 50.7 148.5 51.7 150 53.1 C148 54.4 145.5 54 144 52.8 Z"/>
<!-- глаз -->
<circle fill="#fff" cx="135" cy="45" r="4"/>
<circle fill="#142638" cx="135.8" cy="45" r="2.1"/>
</symbol>
<symbol id="fish-swordfish" viewBox="0 0 160 100">
<!-- хвост-полумесяц: верхняя серповидная лопасть -->
<path fill="var(--f3,hsl(250,50%,32%))" d="M43,47.5 C37,41 29,32 17,25 C20,35 24,43 30,49.5 C34,49.8 38.5,48.8 43,47.5 Z"/>
<!-- хвост-полумесяц: нижняя серповидная лопасть -->
<path fill="var(--f3,hsl(250,50%,32%))" d="M43,52.5 C37,59 29,68 17,75 C20,65 24,57 30,50.5 C34,50.2 38.5,51.2 43,52.5 Z"/>
<!-- спинной плавник: большой, высокий -->
<path fill="var(--f3,hsl(250,50%,32%))" d="M64,39.5 C70,30 80,25.5 92,27.5 C86,32 82,35.5 80,38.5 C75,39 69,39.3 64,39.5 Z"/>
<!-- тело: удлинённое ~3:1, голова справа -->
<path fill="var(--f1,hsl(250,45%,48%))" d="M112,43 C98,37 78,36 60,40 C50,42.5 44,46 41,50 C44,54 50,57.5 60,60 C78,64 98,63 112,57 C115,53.5 115,46.5 112,43 Z"/>
<!-- брюхо: светлее, лунка вдоль нижней кромки -->
<path fill="var(--f2,hsl(250,30%,70%))" d="M43,48.5 C47,52 53,54.5 61,56 C78,59 97,58 110.5,53 L112,57 C98,63 78,64 60,60 C50,57.5 44,54 41,50 C41.6,49.6 42.3,49 43,48.5 Z"/>
<!-- анальный плавник: маленький, сзади -->
<path fill="var(--f3,hsl(250,50%,32%))" d="M58,59.3 C62,63.5 68,66 75,65.8 C70.5,62.5 66.5,60.3 63,59.2 Z"/>
<!-- брюшной плавник: маленький, у головы -->
<path fill="var(--f3,hsl(250,50%,32%))" d="M90,58.2 C93,62 97,64.3 103,64.3 C99,61 95.5,59 93,58.1 Z"/>
<!-- грудной плавник: у головы, под жаберной крышкой -->
<path fill="var(--f3,hsl(250,50%,32%))" d="M99,51.5 C103,54.5 107.5,56.5 112.5,57 C108.5,53.5 104.5,51.5 101.5,50.3 Z"/>
<!-- меч: длинный заострённый клинок спереди, ~30% ширины рыбы -->
<path fill="var(--f3,hsl(250,50%,32%))" d="M144,50 L106,44 L106,53 Z"/>
<!-- жаберная крышка: тонкий полумесяц-узор -->
<path fill="var(--f4,hsl(250,45%,26%))" d="M95,42.5 C98.5,46 98.5,54 95,57.5 C97.3,53.5 97.3,46.5 95,42.5 Z"/>
<!-- глаз -->
<circle fill="#fff" cx="104" cy="47" r="3.8"/>
<circle fill="#142638" cx="105" cy="47" r="1.9"/>
</symbol>
<symbol id="fish-moray" viewBox="0 0 160 100">
<!-- тело: толстая S-образная лента, сужается к хвосту, хвостового плавника нет -->
<path fill="var(--f1,hsl(50,45%,48%))" d="M24 37.5 C30 33 34 28 40 27 C48 26 50 33 56 34 C64 36 70 42 78 43 C86 44 92 35 98 34 C104 33 110 29 118 29 C130 29 138 33 141 40 C143 44 143 50 140 55 C137 60 132 63 126 63 C118 63 108 62 98 62 C90 62 84 69 78 69 C70 69 62 59 56 58 C50 57 46 48 40 45 C34 42 28 43 24 42.5 C20 41 20 39 24 37.5 Z"/>
<!-- брюхо: нижняя полоса тела и нижняя челюсть светлее -->
<path fill="var(--f2,hsl(50,30%,70%))" d="M118 55 C108 54 102 54 98 54 C88 54 82 58 78 59 C70 61 62 52 56 49 C50 46 46 40 40 37 C34 34.5 28 36 24.5 38.5 L24 42.5 C28 43 34 42 40 45 C46 48 50 57 56 58 C62 59 70 69 78 69 C84 69 90 62 98 62 C108 62 118 63 126 63 C132 63 137 60 140 55 Z"/>
<!-- узор: тёмные пятна вдоль тела -->
<ellipse fill="var(--f4,hsl(50,45%,26%))" cx="104" cy="45" rx="4.5" ry="5.5" transform="rotate(-15 104 45)"/>
<ellipse fill="var(--f4,hsl(50,45%,26%))" cx="89" cy="49.5" rx="5" ry="6" transform="rotate(18 89 49.5)"/>
<ellipse fill="var(--f4,hsl(50,45%,26%))" cx="76" cy="55" rx="4.5" ry="5.5" transform="rotate(-22 76 55)"/>
<ellipse fill="var(--f4,hsl(50,45%,26%))" cx="63" cy="49.5" rx="4.5" ry="5" transform="rotate(12 63 49.5)"/>
<ellipse fill="var(--f4,hsl(50,45%,26%))" cx="51" cy="42" rx="4" ry="4.5" transform="rotate(-18 51 42)"/>
<ellipse fill="var(--f4,hsl(50,45%,26%))" cx="39" cy="35" rx="3.5" ry="4" transform="rotate(14 39 35)"/>
<!-- большая открытая пасть: тёмная щель-улыбка на передней части головы -->
<path fill="var(--f4,hsl(50,45%,26%))" d="M120 45.5 C128 44 137 45 142 49 C142 54 137 60 129 62 C122 59 118 51 120 45.5 Z"/>
<!-- намёк на зубы: мелкие белые клыки вдоль верхней губы -->
<path fill="#fff" d="M126.5 46.8 L129.5 47.2 L128 50.6 Z M133 47.5 L136 48 L134.5 51.6 Z M138.3 48.6 L140.3 49.3 L139.4 52.3 Z"/>
<!-- глаз: крошечный, злой -->
<circle fill="#fff" cx="126" cy="38" r="3"/>
<circle fill="#142638" cx="127" cy="38" r="1.7"/>
<!-- злая бровь над глазом -->
<path fill="var(--f4,hsl(50,45%,26%))" d="M120 32 L131 34.5 L121 37 Z"/>
</symbol>
<symbol id="fish-grouper" viewBox="0 0 160 100">
<!-- хвост: большой округлый «веер» -->
<path fill="var(--f3,hsl(15,50%,32%))" d="M40 40 C24 28 10 32 8 50 C10 68 24 72 40 60 C42 54 42 46 40 40 Z"/>
<!-- спинной плавник: крупный, округлый, вдоль всей спины -->
<path fill="var(--f3,hsl(15,50%,32%))" d="M44 40 C50 14 106 8 134 30 C118 26 88 24 58 30 C52 33 47 36 44 40 Z"/>
<!-- грудной плавник: большой округлый, у головы -->
<path fill="var(--f3,hsl(15,50%,32%))" d="M96 54 C98 70 118 80 132 72 C128 61 116 54 106 52 C102 51 98 52 96 54 Z"/>
<!-- анальный плавник: округлый, снизу -->
<path fill="var(--f3,hsl(15,50%,32%))" d="M60 72 C63 84 78 88 90 82 C83 78 74 74 66 72 C63 71 61 71 60 72 Z"/>
<!-- тело: высокое ~2:1, крупная голова справа с большим ртом -->
<path fill="var(--f1,hsl(15,45%,48%))" d="M38 40 C58 24 95 20 122 27 C138 31 147 40 149 49 L150 50 C149 59 143 66 134 70 C112 79 78 80 55 74 C46 71 40 66 38 58 Z"/>
<!-- брюхо: светлая нижняя полоса -->
<path fill="var(--f2,hsl(15,30%,70%))" d="M150 50 C148 59 142 66 134 70 C112 79 78 80 55 74 C46 71 40 66 38 58 C42 60 50 61 60 62 C85 64 112 65 132 62 C140 58 146 53 150 50 Z"/>
<!-- узор: тёмные пятна разного размера по телу и плавникам -->
<circle fill="var(--f4,hsl(15,45%,26%))" cx="58" cy="42" r="5"/>
<circle fill="var(--f4,hsl(15,45%,26%))" cx="76" cy="56" r="6.5"/>
<circle fill="var(--f4,hsl(15,45%,26%))" cx="94" cy="38" r="4.5"/>
<circle fill="var(--f4,hsl(15,45%,26%))" cx="108" cy="54" r="5.5"/>
<circle fill="var(--f4,hsl(15,45%,26%))" cx="86" cy="67" r="4"/>
<circle fill="var(--f4,hsl(15,45%,26%))" cx="99" cy="48" r="3"/>
<circle fill="var(--f4,hsl(15,45%,26%))" cx="66" cy="63" r="3.5"/>
<circle fill="var(--f4,hsl(15,45%,26%))" cx="114" cy="36" r="3.5"/>
<circle fill="var(--f4,hsl(15,45%,26%))" cx="24" cy="50" r="3"/>
<circle fill="var(--f4,hsl(15,45%,26%))" cx="90" cy="15" r="3"/>
<!-- рот: крупный тёмный у массивной морды -->
<path fill="var(--f4,hsl(15,45%,26%))" d="M150 50 C145 45 137 45 132 49 C136 53 145 53 150 50 Z"/>
<!-- глаз -->
<circle fill="#fff" cx="130" cy="43" r="5.5"/>
<circle fill="#142638" cx="131.5" cy="43" r="2.6"/>
</symbol>
<symbol id="fish-turtle" viewBox="0 0 160 100">
<ellipse cx="28" cy="58" rx="6" ry="3.5" transform="rotate(-20 28 58)" fill="var(--f3,hsl(140,50%,32%))"/>
<ellipse cx="76" cy="62" rx="42" ry="18" fill="var(--f2,hsl(140,30%,70%))"/>
<ellipse cx="76" cy="50" rx="48" ry="24" fill="var(--f1,hsl(140,45%,48%))"/>
<ellipse cx="48" cy="50" rx="6" ry="14" fill="var(--f4,hsl(140,45%,26%))"/>
<ellipse cx="62" cy="50" rx="7" ry="18" fill="var(--f4,hsl(140,45%,26%))"/>
<ellipse cx="76" cy="50" rx="7" ry="20" fill="var(--f4,hsl(140,45%,26%))"/>
<ellipse cx="90" cy="50" rx="7" ry="18" fill="var(--f4,hsl(140,45%,26%))"/>
<ellipse cx="104" cy="50" rx="6" ry="14" fill="var(--f4,hsl(140,45%,26%))"/>
<path d="M 37 56 Q 25 64 25 80 Q 34 70 47 64 Z" fill="var(--f3,hsl(140,50%,32%))"/>
<path d="M 115 56 Q 124 64 127 80 Q 112 79 105 64 Z" fill="var(--f3,hsl(140,50%,32%))"/>
<ellipse cx="134" cy="44" rx="13" ry="10" fill="var(--f2,hsl(140,30%,70%))"/>
<circle cx="139" cy="42" r="4.2" fill="#fff"/>
<circle cx="140" cy="42" r="2.2" fill="#142638"/>
</symbol>
<symbol id="fish-angler" viewBox="0 0 160 100">
<!-- хвост: маленький «веер» слева -->
<path fill="var(--f3,hsl(280,50%,32%))" d="M42 50 C32 38 16 36 10 46 C18 49 20 53 16 60 C12 68 20 74 30 68 C37 63 41 57 42 50 Z"/>
<!-- спинной плавник: небольшой горб на спине -->
<path fill="var(--f3,hsl(280,50%,32%))" d="M60 24 C66 12 82 11 92 20 C80 18 68 20 62 25 Z"/>
<!-- тело: круглый «пузырь» ~1.5:1, голова справа перетекает в тело без шеи -->
<path fill="var(--f1,hsl(280,45%,48%))" d="M38 52 C38 32 60 20 88 21 C114 22 133 36 134 56 C134 74 114 88 86 88 C58 88 38 74 38 52 Z"/>
<!-- брюхо: светлая нижняя полоса -->
<path fill="var(--f2,hsl(280,30%,70%))" d="M134 56 C134 74 114 88 86 88 C58 88 38 74 38 52 C44 62 56 70 70 73 C96 78 120 72 134 56 Z"/>
<!-- узор: тёмные пятна на теле -->
<circle fill="var(--f4,hsl(280,45%,26%))" cx="66" cy="46" r="4.5"/>
<circle fill="var(--f4,hsl(280,45%,26%))" cx="82" cy="34" r="3.5"/>
<circle fill="var(--f4,hsl(280,45%,26%))" cx="58" cy="62" r="3"/>
<!-- рот: широкая тёмная щель от носа до под глазом -->
<path fill="var(--f4,hsl(280,45%,26%))" d="M133 54 C121 60 106 61 95 58 C107 70 127 67 133 54 Z"/>
<!-- зубы: белые треугольнички в щели -->
<path fill="#fff" d="M119 57 L126 58 L122.5 63 Z M109 59 L116 59.5 L112.5 64.5 Z M99 59.5 L106 60 L102.5 65 Z M92 58.8 L98 59.3 L95 63.8 Z"/>
<!-- «удочка»: тонкая дуга от лба вверх-вперёд -->
<path fill="none" stroke="var(--f4,hsl(280,45%,26%))" stroke-width="3" stroke-linecap="round" d="M117 25 C116 12 128 5 145 8"/>
<!-- светящийся шарик на конце удочки -->
<circle fill="#ffe9a8" cx="145" cy="8" r="4"/>
<!-- глаз: белый круг + зрачок -->
<circle fill="#fff" cx="108" cy="41" r="6.5"/>
<circle fill="#142638" cx="109.5" cy="41" r="3.2"/>
</symbol>
<symbol id="fish-octopus" viewBox="0 0 160 100">
<path d="M 47.8 40.4 L 45.8 43.1 L 43.7 45.8 L 41.7 48.5 L 39.7 51.1 L 37.7 53.6 L 35.7 56.2 L 33.8 58.6 L 32.0 61.1 L 30.2 63.4 L 28.4 65.8 L 26.8 68.1 L 25.2 70.3 L 23.8 72.5 L 22.4 74.7 L 21.2 76.8 L 19.7 80.1 L 21.5 85.3 L 19.5 86.8 L 18.0 86.0 L 18.1 84.9 L 18.7 84.7 L 18.8 84.9 L 18.3 85.4 L 21.1 86.5 L 21.7 84.0 L 19.1 81.5 L 15.1 83.1 L 14.9 88.1 L 20.2 90.7 L 25.6 86.3 L 24.3 79.9 L 25.2 79.4 L 26.6 77.6 L 28.2 75.8 L 29.8 73.9 L 31.6 72.0 L 33.5 70.0 L 35.5 68.0 L 37.5 65.9 L 39.7 63.8 L 41.9 61.6 L 44.2 59.4 L 46.5 57.2 L 48.9 54.8 L 51.3 52.5 L 53.7 50.1 L 56.2 47.6 Z" fill="var(--f1,hsl(350,45%,48%))"/>
<path d="M 55.1 43.5 L 53.5 47.3 L 51.9 50.9 L 50.4 54.4 L 48.8 57.8 L 47.2 61.1 L 45.7 64.3 L 44.2 67.3 L 42.8 70.3 L 41.3 73.2 L 40.0 76.0 L 38.7 78.7 L 37.4 81.4 L 36.2 83.9 L 35.1 86.4 L 34.1 88.9 L 33.2 91.2 L 36.8 92.8 L 37.9 90.6 L 39.1 88.4 L 40.4 86.1 L 41.8 83.8 L 43.3 81.4 L 45.0 78.9 L 46.7 76.3 L 48.5 73.7 L 50.4 70.9 L 52.3 68.1 L 54.3 65.1 L 56.4 62.1 L 58.5 58.9 L 60.6 55.5 L 62.7 52.1 L 64.9 48.5 Z" fill="var(--f3,hsl(350,50%,32%))"/>
<path d="M 63.8 46.3 L 62.7 50.3 L 61.6 54.1 L 60.5 57.6 L 59.4 61.0 L 58.3 64.1 L 57.2 67.1 L 56.1 69.9 L 55.1 72.6 L 54.0 75.1 L 53.0 77.4 L 52.0 79.7 L 51.1 81.8 L 50.2 83.7 L 49.4 85.6 L 48.7 87.3 L 47.8 90.4 L 50.0 95.3 L 48.1 97.0 L 46.5 96.3 L 46.5 95.2 L 47.1 95.0 L 47.2 95.2 L 46.8 95.7 L 49.7 96.5 L 50.1 94.0 L 47.2 91.7 L 43.4 93.7 L 43.7 98.7 L 49.2 100.8 L 54.1 95.9 L 52.2 89.6 L 52.9 89.6 L 53.9 88.0 L 55.0 86.4 L 56.2 84.6 L 57.5 82.6 L 58.8 80.5 L 60.2 78.3 L 61.7 75.9 L 63.2 73.3 L 64.7 70.6 L 66.3 67.6 L 67.9 64.5 L 69.5 61.1 L 71.1 57.5 L 72.7 53.7 L 74.2 49.7 Z" fill="var(--f1,hsl(350,45%,48%))"/>
<path d="M 71.6 47.1 L 71.1 51.5 L 70.7 55.7 L 70.2 59.6 L 69.8 63.4 L 69.3 67.0 L 68.9 70.4 L 68.4 73.6 L 68.0 76.7 L 67.6 79.6 L 67.2 82.4 L 66.8 85.0 L 66.4 87.5 L 66.0 90.0 L 65.7 92.3 L 65.4 94.5 L 65.0 96.6 L 69.0 97.4 L 69.4 95.3 L 70.0 93.2 L 70.6 90.9 L 71.3 88.6 L 72.0 86.2 L 72.8 83.6 L 73.6 80.9 L 74.5 78.1 L 75.4 75.1 L 76.3 71.9 L 77.3 68.6 L 78.3 65.1 L 79.3 61.4 L 80.3 57.4 L 81.4 53.3 L 82.4 48.9 Z" fill="var(--f2,hsl(350,30%,70%))"/>
<path d="M 79.5 48.7 L 80.4 53.1 L 81.3 57.2 L 82.1 61.2 L 82.9 64.9 L 83.8 68.4 L 84.6 71.8 L 85.3 74.9 L 86.1 77.9 L 86.8 80.8 L 87.5 83.5 L 88.2 86.1 L 88.9 88.5 L 89.5 90.9 L 90.1 93.1 L 90.6 95.3 L 91.0 97.4 L 95.0 96.6 L 94.7 94.5 L 94.4 92.3 L 94.1 90.0 L 93.8 87.6 L 93.5 85.1 L 93.2 82.5 L 92.9 79.7 L 92.7 76.8 L 92.4 73.7 L 92.1 70.5 L 91.8 67.2 L 91.5 63.6 L 91.3 59.8 L 91.0 55.9 L 90.7 51.7 L 90.5 47.3 Z" fill="var(--f2,hsl(350,30%,70%))"/>
<path d="M 87.6 49.3 L 88.9 53.2 L 90.2 57.1 L 91.5 60.7 L 92.7 64.1 L 94.0 67.3 L 95.3 70.4 L 96.5 73.3 L 97.7 76.1 L 98.9 78.8 L 100.1 81.4 L 101.2 84.0 L 102.3 86.5 L 103.3 89.0 L 104.3 91.5 L 105.2 94.0 L 106.1 96.6 L 109.9 95.4 L 109.2 92.8 L 108.5 90.1 L 107.8 87.5 L 107.0 84.9 L 106.3 82.2 L 105.5 79.5 L 104.8 76.7 L 104.0 73.9 L 103.3 71.0 L 102.5 68.0 L 101.8 64.8 L 101.0 61.6 L 100.3 58.1 L 99.7 54.6 L 99.0 50.8 L 98.4 46.7 Z" fill="var(--f1,hsl(350,45%,48%))"/>
<path d="M 96.0 48.3 L 98.0 51.9 L 100.0 55.4 L 102.0 58.7 L 104.0 62.0 L 106.0 65.1 L 107.9 68.0 L 109.8 70.9 L 111.6 73.6 L 113.4 76.3 L 115.1 78.9 L 116.7 81.3 L 118.2 83.7 L 119.6 86.1 L 120.9 88.4 L 122.1 90.6 L 123.2 92.8 L 126.8 91.2 L 125.9 88.9 L 124.9 86.4 L 123.8 83.9 L 122.6 81.4 L 121.4 78.7 L 120.1 76.0 L 118.8 73.2 L 117.4 70.4 L 116.0 67.4 L 114.6 64.3 L 113.1 61.2 L 111.7 57.9 L 110.2 54.5 L 108.8 51.1 L 107.4 47.5 L 106.0 43.7 Z" fill="var(--f3,hsl(350,50%,32%))"/>
<path d="M 104.7 47.4 L 107.0 49.9 L 109.3 52.4 L 111.6 54.7 L 113.9 57.1 L 116.1 59.4 L 118.3 61.6 L 120.5 63.8 L 122.6 65.9 L 124.6 68.0 L 126.5 70.0 L 128.4 72.0 L 130.2 73.9 L 131.8 75.8 L 133.4 77.6 L 134.8 79.4 L 137.1 82.1 L 142.5 82.7 L 143.1 85.2 L 141.7 86.2 L 140.7 85.7 L 140.8 85.1 L 141.0 85.0 L 141.3 85.7 L 143.4 83.6 L 141.5 82.0 L 138.1 83.3 L 137.8 87.6 L 142.2 89.9 L 146.9 86.2 L 145.2 79.5 L 138.9 77.9 L 138.8 76.8 L 137.6 74.7 L 136.2 72.5 L 134.8 70.3 L 133.3 68.1 L 131.6 65.8 L 129.9 63.5 L 128.2 61.1 L 126.4 58.7 L 124.6 56.2 L 122.7 53.7 L 120.8 51.2 L 118.9 48.6 L 117.0 46.0 L 115.2 43.3 L 113.3 40.6 Z" fill="var(--f1,hsl(350,45%,48%))"/>
<path d="M 80 8 C 103 8 115 24 115 40 C 115 55 100 62 80 62 C 60 62 45 55 45 40 C 45 24 57 8 80 8 Z" fill="var(--f1,hsl(350,45%,48%))"/>
<ellipse cx="64" cy="22" rx="4" ry="3" fill="var(--f4,hsl(350,45%,26%))"/>
<ellipse cx="100" cy="21" rx="3.5" ry="3" fill="var(--f4,hsl(350,45%,26%))"/>
<ellipse cx="80" cy="46" rx="4" ry="3.5" fill="var(--f4,hsl(350,45%,26%))"/>
<circle cx="74" cy="34" r="9" fill="#fff"/>
<circle cx="96" cy="34" r="9" fill="#fff"/>
<circle cx="77" cy="34" r="4.5" fill="#142638"/>
<circle cx="99" cy="34" r="4.5" fill="#142638"/>
</symbol>
<symbol id="fish-manta" viewBox="0 0 160 100">
<!-- заднее крыло (влево-назад) -->
<polygon points="62,43.8 94,43 14,10" fill="var(--f1,hsl(220,45%,48%))"/>
<!-- переднее крыло (вправо-вперёд) -->
<polygon points="66,58.5 96,55.8 146,90" fill="var(--f1,hsl(220,45%,48%))"/>
<!-- хвост-хлыст -->
<polygon points="55,47.8 12,55 55,52.2" fill="var(--f3,hsl(220,50%,32%))"/>
<!-- рожки (cephalic fins) -->
<polygon points="100,46 128,32 105,51" fill="var(--f3,hsl(220,50%,32%))"/>
<polygon points="100,54 128,68 105,49" fill="var(--f3,hsl(220,50%,32%))"/>
<!-- брюхо (нижняя половина тела) -->
<path d="M 52 50 Q 79 74 106 50 Z" fill="var(--f2,hsl(220,30%,70%))"/>
<!-- тело (верхняя половина) -->
<path d="M 52 50 Q 79 26 106 50 Z" fill="var(--f1,hsl(220,45%,48%))"/>
<!-- узор: пятна на спине -->
<circle cx="66" cy="44" r="1.7" fill="var(--f4,hsl(220,45%,26%))"/>
<circle cx="75" cy="42" r="2.6" fill="var(--f4,hsl(220,45%,26%))"/>
<circle cx="85" cy="42" r="2.2" fill="var(--f4,hsl(220,45%,26%))"/>
<!-- глаза -->
<circle cx="90" cy="44.8" r="2.3" fill="#fff"/>
<circle cx="91" cy="44.8" r="1.15" fill="#142638"/>
<circle cx="90" cy="55.2" r="2.3" fill="#fff"/>
<circle cx="91" cy="55.2" r="1.15" fill="#142638"/>
</symbol>
<symbol id="fish-shark" viewBox="0 0 160 100">
<!-- хвост: вилка — верхняя (длиннейшая) лопасть -->
<path fill="var(--f3,hsl(210,50%,32%))" d="M37 47 C29 40 18 30 11 22 C12 33 18 42 30 50 L37 47 Z"/>
<!-- хвост: вилка — нижняя (короткая) лопасть -->
<path fill="var(--f3,hsl(210,50%,32%))" d="M37 53 C30 58 21 63 15 67 C17 59 24 53 30 50 L37 53 Z"/>
<!-- спинной плавник: высокий треугольник посередине спины -->
<path fill="var(--f3,hsl(210,50%,32%))" d="M84 35.5 L100 14 L112 36.5 Z"/>
<!-- второй спинной плавник: маленький, к хвосту -->
<path fill="var(--f3,hsl(210,50%,32%))" d="M50 39 L58 30.5 L64 37.5 Z"/>
<!-- анальный плавник: сзади под брюхом -->
<path fill="var(--f3,hsl(210,50%,32%))" d="M46 59.5 L42 68.5 L55 62 Z"/>
<!-- тело: торпеда, коническая заострённая морда, сужается к хвостовому стеблю -->
<path fill="var(--f1,hsl(210,45%,48%))" d="M154 50 C143 44 122 35 95 33.5 C72 32.5 50 37 34 45.5 L34 54.5 C50 63 72 67.5 95 66.5 C122 65 143 56 154 50 Z"/>
<!-- брюхо: светлое, контраст «серая спина / белое брюхо» -->
<path fill="var(--f2,hsl(210,30%,70%))" d="M154 50 C143 56 122 65 95 66.5 C72 67.5 50 63 34 54.5 C45 56.5 65 57.5 95 57 C122 56 143 53 154 50 Z"/>
<!-- грудной плавник: широкий, заострённый назад -->
<path fill="var(--f3,hsl(210,50%,32%))" d="M131 50.5 L113 66.5 L136 54 Z"/>
<!-- рот: тёмная пологая щель на конце морды -->
<path fill="var(--f4,hsl(210,45%,26%))" d="M143 50.4 C146 50.7 148.5 51.2 150.5 51.8 C148.5 52.5 145.5 52 143.5 51.2 Z"/>
<!-- жаберные щели: 3 короткие вертикальные позади головы -->
<path fill="none" stroke="var(--f4,hsl(210,45%,26%))" stroke-width="2" stroke-linecap="round" d="M134 44.5 L134 52.5"/>
<path fill="none" stroke="var(--f4,hsl(210,45%,26%))" stroke-width="2" stroke-linecap="round" d="M129 44 L129 53"/>
<path fill="none" stroke="var(--f4,hsl(210,45%,26%))" stroke-width="2" stroke-linecap="round" d="M124 44.5 L124 53"/>
<!-- глаз: маленький тёмный, с крошечным бликом -->
<circle fill="#142638" cx="140" cy="47" r="2.2"/>
<circle fill="#fff" cx="139.3" cy="46.3" r="0.9"/>
</symbol>
<symbol id="fish-squid" viewBox="0 0 160 100">
<path d="M 136 58 C 136 70, 134 80, 130 88 C 129 90, 127 90, 126 89 L 126 87 C 130 80, 132 70, 131 58 Z" fill="var(--f3,hsl(300,50%,32%))"/>
<path d="M 134 62 C 133 74, 130 86, 124 95 L 122 94 C 127 86, 130 74, 128 62 Z" fill="var(--f3,hsl(300,50%,32%))"/>
<path d="M 130 64 C 129 76, 122 88, 110 97 L 108 95 C 119 88, 125 76, 124 64 Z" fill="var(--f3,hsl(300,50%,32%))"/>
<path d="M 126 64 C 124 78, 112 88, 96 96 L 94 94 C 109 87, 120 77, 120 64 Z" fill="var(--f3,hsl(300,50%,32%))"/>
<path d="M 122 62 C 120 76, 108 86, 88 92 C 85 93, 83 92, 82 90 C 88 88, 104 80, 116 62 Z" fill="var(--f3,hsl(300,50%,32%))"/>
<path d="M 118 60 C 116 72, 102 80, 76 85 L 75 83 C 100 79, 113 71, 112 60 Z" fill="var(--f3,hsl(300,50%,32%))"/>
<path d="M 114 58 C 111 69, 96 75, 66 78 L 65 76 C 94 74, 108 67, 108 58 Z" fill="var(--f3,hsl(300,50%,32%))"/>
<path d="M 111 55 C 107 63, 92 67, 58 68 L 58 66 C 90 66, 104 62, 105 55 Z" fill="var(--f3,hsl(300,50%,32%))"/>
<path d="M 96 30 C 104 18, 116 14, 122 18 C 116 26, 108 32, 100 34 Z" fill="var(--f3,hsl(300,50%,32%))"/>
<path d="M 96 68 C 104 80, 116 84, 122 80 C 116 72, 108 66, 100 64 Z" fill="var(--f3,hsl(300,50%,32%))"/>
<path d="M 26 47 C 52 30, 86 26, 112 26 C 118 26, 120 30, 120 36 L 120 60 C 120 66, 118 70, 112 70 C 86 70, 52 64, 26 47 Z" fill="var(--f1,hsl(300,45%,48%))"/>
<path d="M 30 48 C 56 60, 88 66, 112 65 L 112 69 C 86 70, 52 64, 30 50 Z" fill="var(--f2,hsl(300,30%,70%))"/>
<ellipse cx="126" cy="44" rx="15" ry="17" fill="var(--f1,hsl(300,45%,48%))"/>
<ellipse cx="50" cy="46" rx="4.5" ry="3" fill="var(--f4,hsl(300,45%,26%))"/>
<ellipse cx="72" cy="42" rx="5" ry="3.5" fill="var(--f4,hsl(300,45%,26%))"/>
<ellipse cx="94" cy="48" rx="4.5" ry="3" fill="var(--f4,hsl(300,45%,26%))"/>
<ellipse cx="106" cy="58" rx="3.5" ry="2.5" fill="var(--f4,hsl(300,45%,26%))"/>
<circle cx="130" cy="40" r="8" fill="#fff"/>
<circle cx="132" cy="40" r="4" fill="#142638"/>
</symbol>
<symbol id="fish-whale" viewBox="0 0 160 100">
<!-- хвост-весло: верхняя горизонтальная лопасть (влево-вверх) -->
<path fill="var(--f3,hsl(230,50%,32%))" d="M27 47 C18 43.5 9.5 39.5 6 34 C9.5 40.5 17 46.5 27 51.5 Z"/>
<!-- хвост-весло: нижняя горизонтальная лопасть (влево-вниз) -->
<path fill="var(--f3,hsl(230,50%,32%))" d="M27 55 C18 58.5 9.5 62.5 6 68 C9.5 61.5 17 55.5 27 50.5 Z"/>
<!-- тело: массивная гладкая «труба», округлая голова спереди, сужение к хвостовому стеблю -->
<path fill="var(--f1,hsl(230,45%,48%))" d="M150 48 C146 40 131 32.5 107 30.5 C82 28.5 56 31 40 37.5 C33 40.5 27.5 43.5 24 46.5 L24 55.5 C27.5 58.5 33 61.5 40 64.5 C56 71 82 73.5 107 71.5 C131 69.5 146 62 150 48 Z"/>
<!-- брюхо: светлое, пологая полоса снизу (толще к голове) -->
<path fill="var(--f2,hsl(230,30%,70%))" d="M150 48 C146 62 131 69.5 107 71.5 C82 73.5 56 71 40 64.5 C33 61.5 27.5 58.5 24 55.5 C31 58.8 38 61.3 47 63.3 C68 67.5 94 68 117 64.5 C134 61.5 145.5 55.5 150 48 Z"/>
<!-- боковой плавник: маленький серп низко на теле, заострён назад -->
<path fill="var(--f3,hsl(230,50%,32%))" d="M114 59 C107 65 98 71.5 86 76.5 C96 75.5 107 69 114 59 Z"/>
<!-- рот: пологая тёмная щель вдоль нижней части головы (не открытая пасть) -->
<path fill="var(--f4,hsl(230,45%,26%))" d="M149.5 49 C142.5 57.5 132.5 65 120.5 69 C133 67.5 144 59.5 149.5 49 Z"/>
<!-- складки горла: 4 пологие поперечные линии вдоль нижней челюсти -->
<path fill="none" stroke="var(--f4,hsl(230,45%,26%))" stroke-width="2" stroke-linecap="round" d="M143.5 55 Q141 58.5 143 62"/>
<path fill="none" stroke="var(--f4,hsl(230,45%,26%))" stroke-width="2" stroke-linecap="round" d="M134 61 Q131.5 64.5 133.5 68"/>
<path fill="none" stroke="var(--f4,hsl(230,45%,26%))" stroke-width="2" stroke-linecap="round" d="M124.5 64.5 Q122 67.5 124 70.2"/>
<path fill="none" stroke="var(--f4,hsl(230,45%,26%))" stroke-width="2" stroke-linecap="round" d="M115 66.5 Q112.5 69 114.5 71.3"/>
<!-- глаз: крошечный — белый круг + зрачок -->
<circle fill="#fff" cx="137" cy="45" r="2.9"/>
<circle fill="#142638" cx="137.8" cy="45" r="2.2"/>
</symbol>
<symbol id="fish-glowfin" viewBox="0 0 160 100">
<!-- свечение хвостового плавника -->
<circle fill="#5ff2ff" cx="19" cy="38" r="1.7"/>
<circle fill="#5ff2ff" cx="19" cy="62" r="1.7"/>
<circle fill="#d6fdff" cx="24.5" cy="50" r="1.4"/>
<!-- хвост: мелкий вилочный -->
<path fill="var(--f3,hsl(150,30%,26%))" d="M35 43 L14 31 L24 48.5 L14 69 L35 57 Z"/>
<!-- тело: мелкое, стройное, заострённая морда -->
<path fill="var(--f1,hsl(150,22%,30%))" d="M146 50 C136 42 114 38 88 38 C62 38 44 42 34 47 L34 53 C44 58 62 62 88 62 C114 62 136 58 146 50 Z"/>
<!-- брюшко: чуть светлее (налёт полупрозрачности) -->
<path fill="var(--f2,hsl(150,18%,46%))" d="M146 50 C136 58 114 62 88 62 C62 62 44 58 34 53 C44 56 62 59 88 59 C114 59 136 56 146 50 Z"/>
<!-- просвет тела: тонкая светлая жилка-полупрозрачность -->
<path fill="var(--f2,hsl(150,18%,46%))" d="M134 47.5 C122 43.5 106 41.8 88 41.8 C70 41.8 56 43.5 46 47 C56 45 70 44 88 44 C106 44 122 45.5 134 47.5 Z"/>
<!-- спинная кромка: тонкая тёмная полоса -->
<path fill="var(--f4,hsl(150,25%,17%))" d="M146 50 C136 42 114 38 88 38 C62 38 44 42 34 47 C44 44 62 41.8 88 41.8 C114 41.8 136 44 146 50 Z"/>
<!-- спинной плавник: низкий, со свечением -->
<path fill="var(--f3,hsl(150,30%,26%))" d="M70 39.5 Q82 24 96 39 Z"/>
<circle fill="#5ff2ff" cx="79" cy="33" r="1.4"/>
<circle fill="#5ff2ff" cx="89" cy="35" r="1.4"/>
<!-- грудной плавник -->
<path fill="var(--f3,hsl(150,30%,26%))" d="M116 53 C112 56.5 111.5 60.5 115 63.5 C118 60 118.5 56.5 117.5 53.5 Z"/>
<circle fill="#5ff2ff" cx="115.5" cy="58" r="1.2"/>
<!-- брюшной плавник -->
<path fill="var(--f3,hsl(150,30%,26%))" d="M86 62 L82 68.5 L94 62.5 Z"/>
<!-- анальный плавник -->
<path fill="var(--f3,hsl(150,30%,26%))" d="M62 61.5 L57 68 L68 62.2 Z"/>
<circle fill="#5ff2ff" cx="63" cy="64" r="1.2"/>
<!-- рот: маленькая тёмная щель на конце морды (внутри силуэта) -->
<path fill="var(--f4,hsl(150,25%,17%))" d="M143 49.6 C145.5 49.9 147 50.6 147.8 51.6 C146.2 52.8 144.6 52.4 143.2 51.2 Z"/>
<!-- свечение вдоль бока: ряд циановых чёрточек -->
<ellipse fill="#5ff2ff" cx="50" cy="50.2" rx="2.3" ry="1.4"/>
<ellipse fill="#5ff2ff" cx="60" cy="50" rx="2.3" ry="1.4"/>
<ellipse fill="#5ff2ff" cx="70" cy="49.6" rx="2.3" ry="1.4"/>
<ellipse fill="#5ff2ff" cx="80" cy="49.2" rx="2.3" ry="1.4"/>
<ellipse fill="#5ff2ff" cx="90" cy="48.8" rx="2.3" ry="1.4"/>
<ellipse fill="#5ff2ff" cx="100" cy="48.4" rx="2.3" ry="1.4"/>
<ellipse fill="#5ff2ff" cx="110" cy="48" rx="2.3" ry="1.4"/>
<!-- яркие ядрышки свечения -->
<ellipse fill="#e8feff" cx="50" cy="49.6" rx="0.9" ry="0.6"/>
<ellipse fill="#e8feff" cx="70" cy="49" rx="0.9" ry="0.6"/>
<ellipse fill="#e8feff" cx="90" cy="48.2" rx="0.9" ry="0.6"/>
<ellipse fill="#e8feff" cx="110" cy="47.4" rx="0.9" ry="0.6"/>
<!-- фотофоры: точки свечения вдоль брюха -->
<circle fill="#5ff2ff" cx="49" cy="55.6" r="1.05"/>
<circle fill="#5ff2ff" cx="60" cy="57.6" r="1.05"/>
<circle fill="#5ff2ff" cx="71" cy="58.9" r="1.05"/>
<circle fill="#5ff2ff" cx="83" cy="59.4" r="1.05"/>
<circle fill="#5ff2ff" cx="95" cy="58.8" r="1.05"/>
<circle fill="#5ff2ff" cx="107" cy="57.6" r="1.05"/>
<circle fill="#5ff2ff" cx="117" cy="55.8" r="1.05"/>
<circle fill="#5ff2ff" cx="126" cy="53.2" r="1.05"/>
<!-- глаз: светится цианом -->
<circle fill="var(--f4,hsl(150,25%,17%))" cx="126.5" cy="47.5" r="4.1"/>
<circle fill="#5ff2ff" cx="126.5" cy="47.5" r="3.2"/>
<circle fill="#bdfbff" cx="126.5" cy="47.5" r="1.7"/>
<circle fill="#f2ffff" cx="126.5" cy="47" r="0.85"/>
</symbol>
<symbol id="fish-moonfish" viewBox="0 0 160 100">
<!-- хвост: короткий полумесячный -->
<path fill="var(--f3,hsl(220,40%,34%))" d="M36 50 L9 31 L21 50 L9 69 Z"/>
<!-- спинной плавник: мягкий округлый -->
<path fill="var(--f3,hsl(220,40%,34%))" d="M60 22 C72 2 104 1 120 18 C99 12 76 14 60 22 Z"/>
<!-- анальный плавник -->
<path fill="var(--f3,hsl(220,40%,34%))" d="M64 84 C76 98 102 99 116 88 C98 90 78 88 64 84 Z"/>
<!-- тело: округлое лунное, серо-голубое -->
<path fill="var(--f1,hsl(220,25%,58%))" d="M30 50 C30 24 54 10 88 10 C124 10 148 28 148 50 C148 72 124 90 88 90 C54 90 30 76 30 50 Z"/>
<!-- брюхо: светлее, холодный отблеск -->
<path fill="var(--f2,hsl(220,25%,80%))" d="M30 50 C30 76 54 90 88 90 C124 90 148 72 148 50 C147 66 122 76 88 76 C58 76 32 66 30 50 Z"/>
<!-- узор: бледно-голубые (#b9c8ff = hsl(227,100%,86%)) полумесячные лунки на боку -->
<path fill="var(--f4,hsl(227,100%,86%))" d="M74 34 A17 17 0 1 0 74 62 A30 30 0 0 1 74 34 Z"/>
<path fill="var(--f4,hsl(227,100%,86%))" d="M98 52 A11 11 0 1 0 98 70 A20 20 0 0 1 98 52 Z"/>
<path fill="var(--f4,hsl(227,100%,86%))" d="M112 25 A7 7 0 1 0 112 39 A13 13 0 0 1 112 25 Z"/>
<!-- грудной плавник -->
<path fill="var(--f3,hsl(220,40%,34%))" d="M106 56 C116 58 122 68 118 77 C110 71 104 64 102 58 Z"/>
<!-- рот: маленький, на конце морды -->
<path fill="var(--f3,hsl(220,40%,34%))" d="M143 52 C147 53 149 55 150 57 C147 57 144 55 142 54 Z"/>
<!-- глаз: крупный, светлый, с бледно-голубой радужкой и блеском -->
<circle cx="123" cy="40" r="9.5" fill="#fff"/>
<circle cx="123" cy="40" r="6.5" fill="var(--f4,hsl(227,100%,86%))"/>
<circle cx="124" cy="40.5" r="3.2" fill="#142638"/>
<circle cx="121.5" cy="37.5" r="1.8" fill="#fff"/>
</symbol>
<symbol id="fish-emberfin" viewBox="0 0 160 100">
<!-- хвостовой плавник: три рваных языка пламени с вырезами посередине -->
<path d="M47,38 C41,29 34,20 27,12 C23,7 17,3 9,1 C16,8 18,14 16,19 L23,21 L14,26 C17,31 23,35 31,39 C37,42 42,41 47,42 Z" fill="var(--f3,hsl(150,55%,27%))"/>
<path d="M47,43 C38,39 28,35 18,32 C13,30 8,29 1,31 C8,34 10,38 9,42 L16,44 L8,48 C12,52 18,53 27,52 C35,51 42,48 47,48 Z" fill="var(--f3,hsl(150,55%,27%))"/>
<path d="M47,54 C42,61 36,68 29,75 C25,79 20,83 12,89 C18,80 19,75 18,70 L25,68 L17,63 C20,58 25,55 33,53 C39,52 43,54 47,54 Z" fill="var(--f3,hsl(150,55%,27%))"/>
<!-- горящая сердцевина пламени: акцентная изумрудная вспышка -->
<path d="M45,41 C39,34 33,27 27,22 C23,19 18,17 13,16 C18,21 19,25 17,29 L22,30 L16,34 C20,38 26,41 33,42 C39,42 42,41 45,41 Z" fill="#57ffa8" fill-opacity="0.8"/>
<path d="M45,46 C37,44 29,41 21,39 C17,38 13,38 8,39 C13,41 14,44 13,47 L18,48 L13,51 C17,53 22,53 29,51 C35,49 41,47 45,47 Z" fill="#57ffa8" fill-opacity="0.6"/>
<!-- тело: плотное, смотрит вправо -->
<path d="M40,50 C56,24 78,20 104,22 C130,25 143,36 148,50 C143,64 130,75 104,78 C78,80 56,76 40,50 Z" fill="var(--f1,hsl(150,52%,40%))"/>
<!-- брюхо: светлее, блик вдоль нижней кромки -->
<path d="M148,50 C143,64 130,75 104,78 C78,80 56,76 40,50 C56,66 82,70 108,68 C130,66 141,60 148,50 Z" fill="var(--f2,hsl(150,45%,64%))"/>
<!-- спинной плавник -->
<path d="M78,26 C88,12 100,8 116,22 C100,23 88,25 82,30 Z" fill="var(--f3,hsl(150,55%,27%))"/>
<!-- грудной плавник -->
<path d="M104,56 C118,60 124,66 120,78 C112,72 104,66 100,60 Z" fill="var(--f3,hsl(150,55%,27%))"/>
<!-- брюшной плавник -->
<path d="M84,76 C92,86 100,89 108,84 C100,81 94,78 90,73 Z" fill="var(--f3,hsl(150,55%,27%))"/>
<!-- жаберная дуга -->
<path d="M120,32 C114,42 114,50 120,58" fill="none" stroke="var(--f4,hsl(150,60%,19%))" stroke-width="1.8" stroke-linecap="round"/>
<!-- искры по телу: свечение + ядро -->
<circle cx="70" cy="33" r="4" fill="#57ffa8" fill-opacity="0.22"/>
<circle cx="70" cy="33" r="2.2" fill="#57ffa8"/>
<circle cx="92" cy="29" r="3.4" fill="#57ffa8" fill-opacity="0.22"/>
<circle cx="92" cy="29" r="1.8" fill="#57ffa8"/>
<circle cx="110" cy="40" r="4.6" fill="#57ffa8" fill-opacity="0.25"/>
<circle cx="110" cy="40" r="2.6" fill="#57ffa8"/>
<circle cx="82" cy="50" r="3.6" fill="#57ffa8" fill-opacity="0.22"/>
<circle cx="82" cy="50" r="2" fill="#57ffa8"/>
<circle cx="122" cy="57" r="4" fill="#57ffa8" fill-opacity="0.22"/>
<circle cx="122" cy="57" r="2.2" fill="#57ffa8"/>
<circle cx="98" cy="64" r="3.4" fill="#57ffa8" fill-opacity="0.22"/>
<circle cx="98" cy="64" r="1.8" fill="#57ffa8"/>
<circle cx="62" cy="57" r="4.2" fill="#57ffa8" fill-opacity="0.25"/>
<circle cx="62" cy="57" r="2.4" fill="#57ffa8"/>
<circle cx="134" cy="47" r="3" fill="#57ffa8" fill-opacity="0.22"/>
<circle cx="134" cy="47" r="1.6" fill="#57ffa8"/>
<circle cx="76" cy="68" r="2.8" fill="#57ffa8" fill-opacity="0.22"/>
<circle cx="76" cy="68" r="1.5" fill="#57ffa8"/>
<!-- угольки, слетающие с пламени -->
<circle cx="18" cy="20" r="2.6" fill="#57ffa8" fill-opacity="0.2"/>
<circle cx="18" cy="20" r="1.4" fill="#57ffa8"/>
<circle cx="14" cy="64" r="2.2" fill="#57ffa8" fill-opacity="0.2"/>
<circle cx="14" cy="64" r="1.2" fill="#57ffa8"/>
<circle cx="10" cy="86" r="2.4" fill="#57ffa8" fill-opacity="0.2"/>
<circle cx="10" cy="86" r="1.3" fill="#57ffa8"/>
<!-- светящийся глаз -->
<circle cx="130" cy="40" r="9" fill="#57ffa8" fill-opacity="0.3"/>
<circle cx="130" cy="40" r="6.5" fill="#fff"/>
<circle cx="131.5" cy="40" r="3" fill="#142638"/>
</symbol>
<symbol id="fish-violetlure" viewBox="0 0 160 100">
<!-- хвостовой плавник: узкая вилочка с вырезом -->
<path fill="var(--f3,hsl(280,52%,34%))" d="M46 50 C38 42 28 34 14 30 C24 38 28 44 26 50 C28 56 24 62 14 70 C28 66 38 58 46 50 Z"/>
<!-- спинной плавник: длинный низкий «парус» -->
<path fill="var(--f3,hsl(280,52%,34%))" d="M74 28 C82 12 100 6 118 20 C100 22 86 26 78 32 Z"/>
<!-- анальный плавник -->
<path fill="var(--f3,hsl(280,52%,34%))" d="M80 74 C88 88 102 92 114 84 C100 84 88 80 82 72 Z"/>
<!-- тело: стройное, вытянутое, смотрит вправо -->
<path fill="var(--f1,hsl(280,48%,52%))" d="M44 50 C52 33 74 24 100 24 C126 24 143 35 150 50 C143 65 126 76 100 76 C74 76 52 67 44 50 Z"/>
<!-- брюхо: светлее, сиреневый отблеск снизу -->
<path fill="var(--f2,hsl(280,38%,74%))" d="M150 50 C143 65 126 76 100 76 C74 76 52 67 44 50 C58 64 80 70 104 69 C127 68 142 60 150 50 Z"/>
<!-- светящиеся полоски вдоль бока: мягкое свечение + яркая сердцевина -->
<path fill="none" stroke="#d17bff" stroke-width="4.5" stroke-linecap="round" opacity="0.25" d="M58 44 C82 38 110 37 140 44"/>
<path fill="none" stroke="#d17bff" stroke-width="2" stroke-linecap="round" opacity="0.9" d="M58 44 C82 38 110 37 140 44"/>
<path fill="none" stroke="#d17bff" stroke-width="4.5" stroke-linecap="round" opacity="0.25" d="M62 54 C86 60 112 60 138 53"/>
<path fill="none" stroke="#d17bff" stroke-width="2" stroke-linecap="round" opacity="0.9" d="M62 54 C86 60 112 60 138 53"/>
<path fill="none" stroke="#d17bff" stroke-width="4" stroke-linecap="round" opacity="0.22" d="M70 63 C90 68 110 67 128 60"/>
<path fill="none" stroke="#d17bff" stroke-width="1.8" stroke-linecap="round" opacity="0.8" d="M70 63 C90 68 110 67 128 60"/>
<!-- искорки-огоньки на полосках -->
<circle fill="#d17bff" cx="70" cy="41" r="3.2" opacity="0.25"/>
<circle fill="#d17bff" cx="70" cy="41" r="1.5"/>
<circle fill="#d17bff" cx="96" cy="48" r="3.6" opacity="0.25"/>
<circle fill="#d17bff" cx="96" cy="48" r="1.7"/>
<circle fill="#d17bff" cx="124" cy="47" r="3" opacity="0.25"/>
<circle fill="#d17bff" cx="124" cy="47" r="1.4"/>
<!-- грудной плавник -->
<path fill="var(--f3,hsl(280,52%,34%))" d="M110 58 C122 60 128 68 124 78 C116 72 108 66 105 60 Z"/>
<!-- жаберная дуга у головы -->
<path fill="none" stroke="var(--f4,hsl(282,48%,24%))" stroke-width="2" stroke-linecap="round" opacity="0.5" d="M118 30 C112 40 112 60 119 72"/>
<!-- рот: маленький, на конце морды -->
<path fill="var(--f4,hsl(282,48%,24%))" d="M149 52 C143 56 137 56 132 54 C139 59 147 57 149 52 Z"/>
<!-- «удочка»-иллюций: тонкая дуга от лба вверх-вперёд -->
<path fill="none" stroke="var(--f4,hsl(282,48%,24%))" stroke-width="3" stroke-linecap="round" d="M124 28 C124 15 134 8 150 9"/>
<!-- приманка: яркий фиолетовый огонёк с ореолом свечения -->
<circle fill="#d17bff" cx="150" cy="9" r="9" opacity="0.22"/>
<circle fill="#d17bff" cx="150" cy="9" r="5" opacity="0.4"/>
<circle fill="#e9c4ff" cx="150" cy="9" r="3.4"/>
<!-- глаз: белый круг + зрачок, вокруг — фиалковое свечение -->
<circle fill="#d17bff" cx="126" cy="40" r="9.5" opacity="0.22"/>
<circle fill="#fff" cx="126" cy="40" r="7"/>
<circle fill="#142638" cx="127.5" cy="40" r="3.3"/>
<circle fill="#fff" cx="125" cy="37.8" r="1.5"/>
</symbol>
<symbol id="fish-spiritfin" viewBox="0 0 160 100">
<!-- ореол: светящиеся дуги вокруг головы (сзади тела) -->
<path fill="none" stroke="#ffe07a" stroke-width="7" stroke-linecap="round" opacity="0.08" d="M76 18 Q118 -6 158 26"/>
<path fill="none" stroke="#ffe07a" stroke-width="3" stroke-linecap="round" opacity="0.55" d="M78 18 Q118 -5 158 26"/>
<path fill="none" stroke="#ffe07a" stroke-width="2.6" stroke-linecap="round" opacity="0.7" d="M88 18 Q122 1 158 32"/>
<path fill="none" stroke="#ffe07a" stroke-width="2.4" stroke-linecap="round" opacity="0.6" d="M102 18 Q132 5 158 38"/>
<path fill="none" stroke="#ffe07a" stroke-width="5" stroke-linecap="round" opacity="0.1" d="M138 82 Q162 66 158 42"/>
<path fill="none" stroke="#ffe07a" stroke-width="2.4" stroke-linecap="round" opacity="0.6" d="M138 82 Q162 66 158 42"/>
<!-- души-точки (задний план) -->
<circle fill="#ffe07a" cx="88" cy="10" r="5" opacity="0.18"/>
<circle fill="#ffe07a" cx="88" cy="10" r="2.2"/>
<circle fill="#ffe07a" cx="34" cy="18" r="6" opacity="0.18"/>
<circle fill="#ffe07a" cx="34" cy="18" r="2.6"/>
<circle fill="#ffe07a" cx="16" cy="70" r="5" opacity="0.18"/>
<circle fill="#ffe07a" cx="16" cy="70" r="2.2"/>
<!-- духовные отростки вместо хвоста: растекающиеся вниз струи -->
<path fill="var(--f3,hsl(184,34%,28%))" opacity="0.45" d="M40 30 C33 22 26 18 17 18 C26 23 32 28 36 35 Z"/>
<path fill="var(--f3,hsl(184,34%,28%))" opacity="0.8" d="M28 60 C24 72 22 84 25 97 C31 89 33 78 36 66 Z"/>
<path fill="var(--f3,hsl(184,34%,28%))" opacity="0.7" d="M44 73 C41 83 42 92 47 99 C52 91 51 82 53 73 Z"/>
<path fill="var(--f3,hsl(184,34%,28%))" opacity="0.6" d="M60 80 C59 88 62 95 68 100 C71 92 68 86 67 80 Z"/>
<path fill="var(--f3,hsl(184,34%,28%))" opacity="0.5" d="M76 84 C76 90 79 95 84 98 C86 92 84 87 82 83 Z"/>
<!-- спинной и грудной плавники: призрачные, полупрозрачные -->
<path fill="var(--f3,hsl(184,34%,28%))" opacity="0.85" d="M56 27 C64 12 82 7 96 15 C82 15 68 20 60 29 Z"/>
<path fill="var(--f3,hsl(184,34%,28%))" opacity="0.8" d="M116 60 C108 68 100 78 102 88 C112 80 120 70 124 62 Z"/>
<!-- тело: крупная призрачная «капля», полупрозрачная, тянется влево -->
<path fill="var(--f1,hsl(182,30%,40%))" opacity="0.85" d="M150 52 C150 38 138 27 118 23 C96 18.5 68 20 48 29 C38 33.5 30 40 25 47 C22 51 22 55 25 59 C30 66 38 72 48 76 C68 85 96 86.5 118 82 C138 78 150 66 150 52 Z"/>
<!-- брюхо: светлая пологая полоса снизу -->
<path fill="var(--f2,hsl(178,22%,64%))" opacity="0.85" d="M150 52 C150 66 138 78 118 82 C96 86.5 68 85 48 76 C38 72 30 66 25 59 C35 64.5 48 69 62 72 C90 78 122 74 150 52 Z"/>
<!-- духовные вихри на теле -->
<path fill="none" stroke="var(--f2,hsl(178,22%,64%))" stroke-width="2" stroke-linecap="round" opacity="0.5" d="M70 40 Q90 30 112 33"/>
<path fill="none" stroke="var(--f4,hsl(186,38%,20%))" stroke-width="2" stroke-linecap="round" opacity="0.3" d="M64 58 Q88 64 116 60"/>
<path fill="none" stroke="var(--f2,hsl(178,22%,64%))" stroke-width="1.8" stroke-linecap="round" opacity="0.4" d="M78 72 Q98 78 120 72"/>
<!-- жаберная линия у головы -->
<path fill="none" stroke="var(--f4,hsl(186,38%,20%))" stroke-width="2.5" stroke-linecap="round" opacity="0.32" d="M110 30 Q103 52 112 76"/>
<!-- пасть: тёмная щель на нижней части головы -->
<path fill="var(--f4,hsl(186,38%,20%))" d="M150 54 C140 61 128 65 118 65 C130 70 146 65 150 54 Z"/>
<!-- глаз: большой светящийся золотой -->
<circle fill="#ffe07a" cx="122" cy="44" r="13" opacity="0.18"/>
<circle fill="#ffe07a" cx="122" cy="44" r="9" opacity="0.35"/>
<circle fill="#fff3c4" cx="122" cy="44" r="6.5"/>
<circle fill="#142638" cx="122" cy="44" r="3.2"/>
<circle fill="#fff" cx="123.6" cy="42.2" r="1.4"/>
<!-- души-точки (передний план) -->
<circle fill="#ffe07a" cx="150" cy="24" r="6.5" opacity="0.2"/>
<circle fill="#ffe07a" cx="150" cy="24" r="3"/>
<circle fill="#ffe07a" cx="138" cy="88" r="5" opacity="0.2"/>
<circle fill="#ffe07a" cx="138" cy="88" r="2.2"/>
<circle fill="#ffe07a" cx="104" cy="92" r="7" opacity="0.2"/>
<circle fill="#ffe07a" cx="104" cy="92" r="3.2"/>
<circle fill="#ffe07a" cx="58" cy="94" r="5.5" opacity="0.2"/>
<circle fill="#ffe07a" cx="58" cy="94" r="2.4"/>
<circle fill="#ffe07a" cx="72" cy="70" r="4" opacity="0.35"/>
<circle fill="#ffe07a" cx="72" cy="70" r="1.8"/>
</symbol>
</svg>
<!-- FISH-SVG:END -->
<canvas id="game"></canvas>
<!-- Этап 46 (ревизия): рыбы-жильцы банки — DOM-слой тех же SVG-портретов, что на карточке улова
(спрайт FISH-SVG + вариантное оформление). Слой выключен вне сцены (body.aq-open) -->
<div id="aqFishLayer" aria-hidden="true"></div>
<div id="hud">
<div class="hudleft">
<div id="coins">🪙 0</div>
<span id="weather" class="hidden"></span>
</div>
<div class="hudbtns">
<button id="btnShop">🛒</button>
<button id="btnPause" class="hidden" title="Пауза">⏸</button>
</div>
</div>
<button id="btnRecall" class="hidden">↩</button>
<div id="toast"></div>
<!-- Меню -->
<div id="menu" class="overlay">
<div class="panel">
<div class="title" id="menuTitle">🎣 РЫБАЛКА</div>
<!-- Y5: i18n — содержимое <p> и кнопки заполняет renderMenu() из T (RU-значения те же) -->
<p id="menuP1">👆 <b>Держи палец</b> — заряжай бросок,<br>
<b>отпусти</b> — кидай удочку.<br>
Дальше бросок = реже и жирнее рыба!</p>
<p id="menuP2" style="margin-top:8px">Видишь <b style="color:#ff6b5e">!</b> — быстро жми, чтобы зацепить.<br>
При вываживании: тяни, когда рыба отдыхает,<br>
отпусти, когда она <b>тянет</b> — иначе лопнет!<br>
Не тяни слишком долго — рыба <b>сбегает</b> (красная полоса)!</p>
<p id="menuP3" style="margin-top:8px">У каждой рыбы свой <b>вес</b>, продажа — по цене за кг.<br>
У удочек есть <b>предел веса</b>: перевесишь — шнур лопнет! <br>
Лучшие снасти = больше шансов на трофей 🏆</p>
<p id="menuP4" style="margin-top:8px">Не каждый заброс — с клёвом: если молчит — подожди.<br>
У удилищ есть <b>тормоз (drag)</b>: рывок сильнее — леска скользит!</p>
<button id="btnPlay" class="big">Играть</button>
</div>
</div>
<!-- Магазин -->
<div id="shop" class="overlay hidden">
<div class="panel">
<button id="btnCloseShop">×</button>
<div class="panelhead" id="shopTitle">🛒 Лавка рыбака</div>
<div class="tabs">
<button id="tabTackle" class="active" title="Лавка">🛒</button>
<button id="tabJournal" title="Дневник">📔</button>
<button id="tabRewards" title="Награды">🎯</button>
<button id="tabAch" title="Достижения">🏅</button>
<button id="tabSkins" title="Скины">🎨</button>
<button id="tabLocations" title="Локации">🗺️</button>
</div>
<div id="shopBody"></div>
</div>
</div>
<!-- Подтверждение сброса -->
<div id="resetDlg" class="overlay hidden">
<div class="panel">
<div class="panelhead" id="resetTitle">🗑️ Сбросить прогресс?</div>
<p id="resetText" style="margin-top:8px">Монеты, снасти и коллекция будут стёрты.<br>Это действие <b>нельзя отменить</b>.</p>
<div class="twobtns">
<button id="btnResetNo" class="big">Отмена</button>
<button id="btnResetYes" class="big danger">Сбросить</button>
</div>
</div>
</div>
<!-- Пауза (треб. Яндекс Игр 6.3) -->
<div id="pause" class="overlay hidden">
<div class="panel">
<div class="panelhead" id="pauseTitle">⏸ Пауза</div>
<div class="twobtns">
<button id="btnPauseResume" class="big">Продолжить</button>
<button id="btnPauseMenu" class="big">В меню</button>
</div>
</div>
</div>
<!-- Меню (открывается из паузы «В меню»): хаб — игра остаётся на паузе-оверлее,
текущая сессия (заброс/вываживание) сохраняется до «Продолжить» -->
<div id="gamemenu" class="overlay hidden">
<div class="panel">
<div class="panelhead" id="gmTitle">🎣 Меню</div>
<div class="mbtns">
<button id="btnGmResume" class="mbtn">Продолжить</button>
<button id="btnGmShop" class="mbtn">🛒 Магазин</button>
<button id="btnGmAch" class="mbtn">🏅 Достижения</button>
<button id="btnGmJournal" class="mbtn">📔 Дневник</button>
<button id="btnGmAquarium" class="mbtn">🐠 Аквариум</button>
<button id="btnGmHowto" class="mbtn">📖 Инструкции</button>
<button id="btnMute" class="mbtn">🔊 Звук</button>
<button id="btnReset" class="mbtn" title="Сбросить прогресс">🗑️ Очистить</button>
</div>
</div>
</div>
<!-- Карточка улова -->
<div id="card" class="overlay hidden">
<div class="panel">
<div class="rarity" id="cardRarity">Обычная</div>
<div><span class="record hidden" id="cardRecord">🏆 РЕКОРД!</span><span class="variant-badge hidden" id="cardVariant">ЗОЛОТАЯ</span><span class="boss-badge hidden" id="cardBoss">🏆 БОСС!</span></div>
<span class="bigemoji" id="cardEmoji"><svg viewBox="0 0 160 100"><use id="cardFishUse" href="#fish-carass"/></svg></span>
<div class="cname" id="cardName">Карась</div>
<div class="csub" id="cardSub">0.0 кг · 0 🪙/кг</div>
<div class="cval" id="cardVal">+0 🪙</div>
<!-- Y2: rewarded-видео «монеты ×2» (чистый бонус; 4.5.1 — что получим, написано прямо на кнопке) -->
<button id="btnRv" class="rvbtn hidden" title="Посмотрите рекламу — двойная цена за этот улов">📺 Реклама: ×2 за этот улов</button>
<!-- Этап 46: «В аквариум» (модель C — бесплатная копия экземпляра; catchFish не трогаем) -->
<button id="btnKeep" class="aqbtn">🐠 В аквариум</button>
<div class="twobtns">
<button id="btnCardOk" class="big">Отлично!</button>
</div>
</div>
</div>
<!-- Этап 46: сцена аквариума — DOM-бар и поповер поверх canvas (сама сцена рисуется в drawAquarium) -->
<div id="aqBar" class="hidden">
<button id="btnAqBack" class="aqpill">← Назад</button>
<span id="aqCount">0/6</span>
<button id="btnAqSlot" class="aqpill">+ Слот</button>
</div>
<div id="aqPop" class="hidden">
<div id="aqPopName"></div>
<div id="aqPopSub"></div>
<button id="btnAqRelease" class="aqpill">Выпустить</button>
</div>
<script>
'use strict';
/* =========================================================
ДАННЫЕ
========================================================= */
const SPECIES=[
{id:'carass', name:'Карась', nameEn:'Crucian carp', rarity:0, weight:100, minDist:0.00, strength:0.25, wMin:0.1, wMax:0.8, price:20, hue:45},
{id:'perch', name:'Окунь', nameEn:'Perch', rarity:0, weight:55, minDist:0.05, strength:0.30, wMin:0.1, wMax:1.5, price:22, hue:120},
{id:'bream', name:'Лещ', nameEn:'Bream', rarity:1, weight:26, minDist:0.30, strength:0.40, wMin:0.3, wMax:3.0, price:25, hue:210},
{id:'carp', name:'Карп', nameEn:'Carp', rarity:1, weight:22, minDist:0.35, strength:0.45, wMin:0.5, wMax:8.0, price:28, hue:30},
{id:'pike', name:'Щука', nameEn:'Pike', rarity:2, weight:9, minDist:0.50, strength:0.60, wMin:1.0, wMax:10.0, price:35, hue:100},
{id:'catfish',name:'Сом', nameEn:'Catfish', rarity:2, weight:5, minDist:0.65, strength:0.75, wMin:2.0, wMax:20.0, price:45, hue:260},
{id:'sturge', name:'Осётр', nameEn:'Sturgeon', rarity:3, weight:2.5, minDist:0.80, strength:0.85, wMin:5.0, wMax:40.0, price:65, hue:200},
{id:'gold', name:'Золотая рыбка', nameEn:'Golden Fish', rarity:4, weight:1.2, minDist:0.90, strength:0.90, wMin:0.05, wMax:1.0, price:1500, hue:45},
/* Этап 38: лестница веса по локациям (дороже вода = тяжелее рыба = сложнее бой):
Озёра ×8, Море ×6 (Рыба-меч ×4), Океан ×4 (Синий кит ×2 — чтобы остался вываживаемым);
Река не меняется (пины §9j/M4/M6). Правятся только wMin/wMax — ролл-веса
(RIVER_*, LAKE_*, SEA_*, OCEAN_*) не тронуты */
/* Этап 5: жильцы Озёр (в реку не заходят — ролл реки по RIVER_*; порядок 0..7 не трогаем);
weight у новых видов не используется для ролла: река — RIVER_*, Озёра — LAKE_DAY_WEIGHTS/LAKE_NIGHT_WEIGHTS */
{id:'zander', name:'Судак', nameEn:'Zander', rarity:2, weight:8, minDist:0.45, strength:0.60, wMin:8.0, wMax:48.0, price:8, hue:180},
{id:'chuch', name:'Хариус', nameEn:'Grayling', rarity:1, weight:18, minDist:0.35, strength:0.35, wMin:1.6, wMax:16.0, price:6, hue:25},
{id:'crayfish',name:'Гигантский рак', nameEn:'Giant Crayfish', rarity:3, weight:1.5, minDist:0.70, strength:0.50, wMin:1.6, wMax:8.0, price:40, hue:0},
/* Этап 33: ещё пять жильцов Озёр (в реку не заходят — ролл реки по RIVER_*; порядок 0..7 и записи
этапа 5 не тронуты); weight не используется для ролла Озёр — там LAKE_*_WEIGHTS */
{id:'bleak', name:'Плотва', nameEn:'Roach', rarity:0, weight:70, minDist:0.05, strength:0.28, wMin:0.4, wMax:4.8, price:4, hue:200},
{id:'roundel',name:'Густера', nameEn:'White bream', rarity:0, weight:34, minDist:0.15, strength:0.32, wMin:0.8, wMax:7.2, price:5, hue:215},
{id:'eelpout',name:'Вьюн', nameEn:'Weather loach', rarity:1, weight:12, minDist:0.40, strength:0.30, wMin:0.4, wMax:3.2, price:11, hue:35},
{id:'tench', name:'Линь', nameEn:'Tench', rarity:2, weight:10, minDist:0.55, strength:0.55, wMin:2.4, wMax:32.0, price:16, hue:95},
{id:'navaga', name:'Навага', nameEn:'Navaga', rarity:3, weight:2, minDist:0.75, strength:0.70, wMin:2.4, wMax:20.0, price:50, hue:195},
/* Этап 35: у каждой локации — своя ихтиофауна (без пересечения с Рекой).
Две новые озёрные рыбы (в Озёра, в реку не заходят); weight не для ролла — там LAKE_*_WEIGHTS */
{id:'rudd', name:'Краснопёрка', nameEn:'Rudd', rarity:1, weight:20, minDist:0.25, strength:0.34, wMin:0.8, wMax:9.6, price:5, hue:25},
{id:'cisco', name:'Сиг', nameEn:'Cisco', rarity:2, weight:11, minDist:0.58, strength:0.58, wMin:4.0, wMax:28.0, price:18, hue:200},
/* Этап 35: Море — 5 морских видов (только в Море, id с конца; ролл — SEA_*_WEIGHTS) */
{id:'herring', name:'Сельдь', nameEn:'Herring', rarity:0, weight:60, minDist:0.05, strength:0.28, wMin:0.6, wMax:4.8, price:8, hue:205},
{id:'mackerel',name:'Скумбрия', nameEn:'Mackerel', rarity:1, weight:30, minDist:0.30, strength:0.42, wMin:1.8, wMax:15.0, price:18, hue:190},
{id:'flounder',name:'Камбала', nameEn:'Flounder', rarity:2, weight:12, minDist:0.50, strength:0.60, wMin:3.0, wMax:30.0, price:33, hue:40},
{id:'tuna', name:'Тунец', nameEn:'Tuna', rarity:3, weight:4, minDist:0.72, strength:0.80, wMin:12.0, wMax:72.0, price:46, hue:210},
{id:'swordfish',name:'Рыба-меч', nameEn:'Swordfish', rarity:4, weight:1.5, minDist:0.88, strength:0.92, wMin:20.0, wMax:80.0, price:81, hue:250},
/* Этап 36: Океан — 9 глубоководных видов (только Океан; ролл — OCEAN_*_WEIGHTS).
minDist/strength — кривые по редкости; портреты — спрайт FISH-SVG (эмодзи рыб убраны в этапе 42) */
{id:'moray', name:'Гигантская мурена', nameEn:'Giant Moray Eel', rarity:0, weight:30, minDist:0.10, strength:0.35, wMin:6.0, wMax:24.0, price:5, hue:50},
{id:'grouper', name:'Горбуна', nameEn:'Grouper', rarity:0, weight:25, minDist:0.20, strength:0.35, wMin:8.0, wMax:40.0, price:5, hue:15},
{id:'turtle', name:'Морская черепаха', nameEn:'Green Sea Turtle', rarity:1, weight:10, minDist:0.35, strength:0.45, wMin:20.0, wMax:56.0, price:9, hue:140},
{id:'angler', name:'Удильщик', nameEn:'Anglerfish', rarity:2, weight:6, minDist:0.50, strength:0.55, wMin:4.0, wMax:16.0, price:21, hue:280},
{id:'octopus', name:'Гигантский осьминог', nameEn:'Giant Octopus', rarity:2, weight:4, minDist:0.55, strength:0.60, wMin:16.0, wMax:48.0, price:23, hue:350},
{id:'manta', name:'Скат-манта', nameEn:'Manta Ray', rarity:3, weight:3, minDist:0.75, strength:0.75, wMin:24.0, wMax:60.0, price:29, hue:220},
{id:'shark', name:'Большая белая акула', nameEn:'Great White Shark', rarity:3, weight:2.5, minDist:0.78, strength:0.78, wMin:20.0, wMax:56.0, price:31, hue:210},
{id:'squid', name:'Гигантский кальмар', nameEn:'Giant Squid', rarity:4, weight:2.5, minDist:0.85, strength:0.85, wMin:16.0, wMax:48.0, price:37, hue:300},
{id:'whale', name:'Синий кит', nameEn:'Blue Whale', rarity:4, weight:2, minDist:0.92, strength:0.95, wMin:40.0, wMax:100.0, price:67, hue:230},
/* Этап 45: Волшебное болото — 5 светящихся видов (только в конец массива: SPECIES[0..7]
запинены тестами/квестами). Новое поле glow — цвет ночного свечения (у старых видов нет) */
{id:'glowfin', name:'Светлячок', nameEn:'Glowfin', rarity:0, weight:38, minDist:0.05, strength:0.28, wMin:0.3, wMax:3.0, price:8, hue:185, glow:'#5ff2ff'},
{id:'moonfish', name:'Лунная рыбка', nameEn:'Moonfish', rarity:1, weight:24, minDist:0.30, strength:0.40, wMin:0.8, wMax:8.0, price:13, hue:225, glow:'#b9c8ff'},
{id:'emberfin', name:'Изумрудный светляк', nameEn:'Emerald Glowfish', rarity:2, weight:14, minDist:0.55, strength:0.58, wMin:1.5, wMax:16.0, price:20, hue:150, glow:'#57ffa8'},
{id:'violetlure',name:'Фиалковый огонёк', nameEn:'Violet Lure', rarity:3, weight:5, minDist:0.75, strength:0.80, wMin:2.0, wMax:24.0, price:38, hue:280, glow:'#d17bff'},
{id:'spiritfin', name:'Дух болота', nameEn:'Swamp Spirit', rarity:4, weight:2.5, minDist:0.88, strength:0.88, wMin:0.5, wMax:6.0, price:70, hue:45, glow:'#ffe07a'},
];
const RARITY_NAMES=['Обычная','Необычная','Редкая','Эпическая','Легендарная'];
const RARITY_NAMES_EN=['Common','Uncommon','Rare','Epic','Legendary']; // Y5: i18n — EN-названия редкостей
const RARITY_COLORS=['#9fc3e2','#2ecc71','#4da3ff','#c56cf0','#ffd75e'];
/* M3: ночная таблица — те же виды, но веса спавна смещены к ночной рыбе (клон SPECIES);
Этап 5: +ночные веса новых видов (zander 6 / chuch 10 / crayfish 2.5);
Этап 33: +озёрные ночные веса пяти новых видов (значения нужны ВСЕМ видам — SPECIES_NIGHT
строится как NIGHT_WEIGHTS[s.id], у пропущенного id вес стал бы undefined) */
const NIGHT_WEIGHTS={carass:40,perch:80,bream:10,carp:8,pike:22,catfish:18,sturge:4,gold:1.2,zander:6,chuch:10,crayfish:2.5,bleak:20,roundel:14,eelpout:18,tench:12,navaga:3,rudd:14,cisco:9,herring:30,mackerel:16,flounder:8,tuna:3,swordfish:1.5,moray:20,grouper:18,turtle:8,angler:6,octopus:4,manta:2.5,shark:2.5,squid:2,whale:2,glowfin:38,moonfish:24,emberfin:14,violetlure:5,spiritfin:2.5}; // Этап 36: +океанские id; Этап 45: +болотные (у Болота свои таблицы — значения декоративные, главное — ключи есть, иначе weight=undefined)
const SPECIES_NIGHT=SPECIES.map(s=>Object.assign({},s,{weight:NIGHT_WEIGHTS[s.id]}));
/* Этап 5: Локации — Река (родная вода, бесплатно) и Озёра (65536 🪙, новые рыбы).
Этап 35: +Море (131072 🪙). Этап 36: +Океан (196608 🪙). Этап 45: +Волшебное болото (90000 🪙).
У каждой локации СВОЯ ихтиофауна, без пересечений:
Река=8, Озёра=10, Море=5, Океан=9, Болото=5 (см. *_IDS ниже).
water: [dayTop, dayMid, dayBot, nightTop, nightMid, nightBot] — базовые пары цветов
градиента drawWater(): день ↔ ночь плавно через nightMix (логика не меняется).
Этап 43: визуальное различение локаций — (1) вода различима по цвету (Река/Озёра не
тронуты; Море — светлая бирюза, Океан — глубокий тёмно-синий; тест пинит только ФОРМАТ
6 hex на локацию — значения можно двигать); (2) waves — характер волн поверхности в
drawWater(): amp/amp2 — амплитуда, freq/freq2 — частота, speed/speed2 — скорость;
amp/amp2=0 — линии НЕ рисуются (Река — без волн, спокойная вода — ревизия Этапа 44);
Озёра — длинная медленная гладь; Море — умеренная волна; Океан — крупные зыби;
Этап 45: Волшебное болото — слабая рябь поверх мрачной тёмно-моховой воды.
Чисто визуал, на механику/баланс не влияет. */
const LOCATIONS=[
{id:0, name:'Река', nameEn:'River', cost:0, desc:'родная вода', descEn:'home waters',
water:['#2f9bd8','#1f6fb0','#0a2f52','#0d2246','#091c3a','#041226'],
waves:{amp:0,amp2:0,freq:0,freq2:0,speed:0,speed2:0}}, // Река — без волновых линий (спокойная вода)
{id:1, name:'Озёра', nameEn:'Lakes', cost:65536, desc:'озёрные рыбы: судак, хариус, линь, навага, сиг, гигантский рак и другие', descEn:'lake fish: zander, grayling, tench, navaga, cisco, giant crayfish and more',
water:['#3d9c8a','#20726a','#0c423c','#0f2b33','#0a2129','#04141a'],
waves:{amp:1.2,amp2:1.6,freq:0.03,freq2:0.025,speed:0.8,speed2:0.5}},
{id:2, name:'Море', nameEn:'Sea', cost:131072, desc:'морская рыба: скумбрия, камбала, тунец, рыба-меч и другие', descEn:'sea fish: mackerel, flounder, tuna, swordfish and more',
water:['#41b1e8','#2287c9','#0b3d6e','#112e5c','#0a2348','#04102a'],
waves:{amp:3.5,amp2:4.5,freq:0.045,freq2:0.035,speed:1.8,speed2:1.2}},
{id:3, name:'Океан', nameEn:'Ocean', cost:196608, desc:'глубоководные гиганты: синий кит, гигантский кальмар, скат-манта, гигантский осьминог и другие', descEn:'deep-sea giants: blue whale, giant squid, manta ray, giant octopus and more',
water:['#1a5fa8','#0c3d74','#031a3c','#081430','#050d22','#020611'],
waves:{amp:6,amp2:8,freq:0.035,freq2:0.028,speed:1.5,speed2:1.0}},
{id:4, name:'Волшебное болото', nameEn:'Magic Swamp', cost:90000, desc:'светящиеся обитатели: светлячок, лунная рыбка, изумрудный светляк, фиалковый огонёк и дух болота', descEn:'glowing dwellers: glowfin, moonfish, emerald glowfish, violet lure and swamp spirit',
water:['#4d6b38','#2e4a28','#122818','#0d1f12','#08160c','#030a06'], // Этап 45: мрачно-болотная тёмно-зелёная вода (ночь — почти чёрная с зеленью)
waves:{amp:0.8,amp2:0.5,freq:0.025,freq2:0.02,speed:0.5,speed2:0.35}} // слабая рябь (тише Озёр)
];
/* Река — только 8 базовых видов: роллы реки остаются прежними (новые виды заходят только в Озёра).
Фильтр по id: порядок и веса записей — ровно как в SPECIES/SPECIES_NIGHT */
const RIVER_IDS=['carass','perch','bream','carp','pike','catfish','sturge','gold'];
const RIVER_SPECIES=SPECIES.filter(s=>RIVER_IDS.indexOf(s.id)>=0);
const RIVER_SPECIES_NIGHT=SPECIES_NIGHT.filter(s=>RIVER_IDS.indexOf(s.id)>=0);
/* Этап 35: у каждой локации — ТОЛЬКО свои виды (без пересечений):
Река=8 (RIVER_IDS), Озёра=10 (LAKE_IDS), Море=5 (SEA_IDS); Этап 36: +Океан=9 (OCEAN_IDS).
SPECIES_NIGHT — источник ночных весов только для реки; у Озёр, Моря и Океана свои таблицы.
Этап 34 (до разделения): доход озёр держался на речной рыбе (carass/perch) — после
эксклюзивности доли перераспределены на своих жильцов, доход СОХРАНЁН и поднят:
день ≈121 🪙 (было 110.4 с речными), ночь ≈134 (было 145.5; ночь реки 125.9 — озёра
снова впереди). Море (этап 35): день ≈190, ночь ≈222 — топ-локация за 300000 🪙.
Пол редких: мин. вес ≥ ~1.2 (swordfish) — за 3000 роллов ожидается ≥40 попаданий,
тест «каждый вид ≥1» проходит с запасом.
Этап 40: ночные *_WEIGHTS подтянуты к «ночь доходнее» (Озёра: +навaga/сиг/судак,
−вьюн/хариус/плотва; Море: ночь смещена к камбале/тунцу/мечу сильнее; Океан:
+кит/манта/акула/кальмар/осьминог, −мурена/горбуна) + доля сельди/скумбрии в Море
снижена (день 82→79%, ночь 78→64%); инвариант «каждая новая вода доходнее в ОБЕ
фазы» сохраняется (Океан/Море день ×1.11 — тонкий запас, см. AGENTS.md) */
const LAKE_IDS=['zander','chuch','crayfish','bleak','roundel','eelpout','tench','navaga','rudd','cisco'];
const LAKE_DAY_WEIGHTS={zander:20,chuch:8,crayfish:4,bleak:8,roundel:6,eelpout:3,tench:20,navaga:4.5,rudd:6,cisco:14};
const LAKE_NIGHT_WEIGHTS={zander:32,chuch:3,crayfish:5,bleak:3,roundel:2.5,eelpout:4,tench:24,navaga:10,rudd:2.5,cisco:18}; // Этап 40: ночь доходнее (крупная/дорогая: судак/линь/навага/сиг)
const LAKE_SPECIES=LAKE_IDS.map(id=>Object.assign({},SPECIES.find(s=>s.id===id),{weight:LAKE_DAY_WEIGHTS[id]}));
const LAKE_SPECIES_NIGHT=LAKE_IDS.map(id=>Object.assign({},SPECIES.find(s=>s.id===id),{weight:LAKE_NIGHT_WEIGHTS[id]}));
/* Этап 35: Море — только морские виды; ночь смещена к хищникам (камбала/тунец/рыба-меч).
Этап 40: доля сельди/скумбрии снижена (день 82→79%, ночь 78→64%) — средневесовое
Море тяжелее; день ограничен инвариантом «Море < Океан» (Океан/Море ×1.11) */
const SEA_IDS=['herring','mackerel','flounder','tuna','swordfish'];
const SEA_DAY_WEIGHTS={herring:32,mackerel:32,flounder:12,tuna:3,swordfish:1.5};
const SEA_NIGHT_WEIGHTS={herring:18,mackerel:20,flounder:15,tuna:4,swordfish:2.2};
const SEA_SPECIES=SEA_IDS.map(id=>Object.assign({},SPECIES.find(s=>s.id===id),{weight:SEA_DAY_WEIGHTS[id]}));
const SEA_SPECIES_NIGHT=SEA_IDS.map(id=>Object.assign({},SPECIES.find(s=>s.id===id),{weight:SEA_NIGHT_WEIGHTS[id]}));
/* Этап 36: Океан — только свои 9 видов; ночь — смещение к крупным (паттерн LAKE/SEA).
Этап 40: ночь сильнее к гигантам (кит 2.5→4, манта 3.5→5, акула/кальмар 3→4.5, осьминог 6→8;
мурена/горбуна 32/28→26/22) — ночь доходнее */
const OCEAN_IDS=['moray','grouper','turtle','angler','octopus','manta','shark','squid','whale'];
const OCEAN_DAY_WEIGHTS={moray:40,grouper:32,turtle:10,angler:8,octopus:5,manta:3,shark:2.5,squid:2.5,whale:2};
const OCEAN_NIGHT_WEIGHTS={moray:26,grouper:22,turtle:11,angler:8,octopus:8,manta:5,shark:4.5,squid:4.5,whale:4};
const OCEAN_SPECIES=OCEAN_IDS.map(id=>Object.assign({},SPECIES.find(s=>s.id===id),{weight:OCEAN_DAY_WEIGHTS[id]}));
const OCEAN_SPECIES_NIGHT=OCEAN_IDS.map(id=>Object.assign({},SPECIES.find(s=>s.id===id),{weight:OCEAN_NIGHT_WEIGHTS[id]}));
/* Этап 45: Волшебное болото — только свои 5 светящихся видов (паттерн LAKE/SEA/OCEAN).
Баланс (tools/balance.mjs, нейтральная снасть): день ≈240.3 🪙, ночь ≈328.9 🪙 —
между Озёрами (194.1/249.0) и Морем (297.3/476.7) в ОБЕ фазы; ночь/день ×1.37.
Ночью доля редких/дорогих (violetlure/emberfin) выше, дешёвый glowfin и spiritfin падают.
Минимальный вес ≥2.5 (тест «каждый вид ≥1» за 3000 роллов проходит с запасом).
Значения NIGHT_WEIGHTS новых id — декоративные (у Болота своя таблица). */
const SWAMP_IDS=['glowfin','moonfish','emberfin','violetlure','spiritfin'];
const SWAMP_DAY_WEIGHTS={glowfin:3,moonfish:4,emberfin:28,violetlure:13,spiritfin:4.5};
const SWAMP_NIGHT_WEIGHTS={glowfin:2.5,moonfish:2.5,emberfin:20,violetlure:30,spiritfin:4};
const SWAMP_SPECIES=SWAMP_IDS.map(id=>Object.assign({},SPECIES.find(s=>s.id===id),{weight:SWAMP_DAY_WEIGHTS[id]}));
const SWAMP_SPECIES_NIGHT=SWAMP_IDS.map(id=>Object.assign({},SPECIES.find(s=>s.id===id),{weight:SWAMP_NIGHT_WEIGHTS[id]}));
/* Этап 45: табличный выбор таблицы ролла по save.location (0..4) — используется в pickSpecies();
guard «вне 0..4 → Река» там же. Порядок обязан совпадать с LOCATIONS */
const SPECIES_BY_LOCATION=[RIVER_SPECIES,LAKE_SPECIES,SEA_SPECIES,OCEAN_SPECIES,SWAMP_SPECIES];
const SPECIES_BY_LOCATION_NIGHT=[RIVER_SPECIES_NIGHT,LAKE_SPECIES_NIGHT,SEA_SPECIES_NIGHT,OCEAN_SPECIES_NIGHT,SWAMP_SPECIES_NIGHT];
/* Этап 37: босс в каждой локации — id босса по локации (Река=осётр как в M4,
Озёра=гигантский рак, Море=рыба-меч, Океан=синий кит). Босс — существующий вид
с runtime-флагом boss:true (паттерн M4), новых видов нет. Чтение — в startBite()
Этап 45: Болото=дух болота (spiritfin) */
const BOSS_BY_LOCATION={0:'sturge',1:'crayfish',2:'swordfish',3:'whale',4:'spiritfin'};
/* M6: шанс gold-варианта (let — автотест форсирует значения, как раньше) */
let SHINY_CHANCE=0.06;
/* M6: варианты рыб — таблица (порядок = лестница ценности и порядок ролла).
Ролл по порядку: copper → silver → quartz → gold (SHINY_CHANCE) → emerald → diamond → rainbow, иначе normal.
P(особой)≈31% без буста снасти, E[mult]≈1.36; топ лестницы — радужная (×5, самая редкая).
Снасть (тип наживки + её уровень) усиливает шансы — см. variantBoost()/variantChance() */
const VARIANTS=[
{id:'normal', name:'Обычная', nameEn:'Normal', mult:1, chance:0, css:''},
{id:'copper', name:'Медная', nameEn:'Copper', mult:1.5, chance:0.12, css:'copper'},
{id:'silver', name:'Серебряная', nameEn:'Silver', mult:2, chance:0.08, css:'silver'},
{id:'quartz', name:'Кварцевая', nameEn:'Quartz', mult:2.5, chance:0.07, css:'quartz'},
{id:'gold', name:'Золотая', nameEn:'Gold', mult:3, chance:0.06, css:'gold'}, // chance — только для читабельности: у gold живая SHINY_CHANCE
{id:'emerald', name:'Изумрудная', nameEn:'Emerald', mult:4, chance:0.01, css:'emerald'},
{id:'diamond', name:'Алмазная', nameEn:'Diamond', mult:4.5, chance:0.007, css:'diamond'},
{id:'rainbow', name:'Радужная', nameEn:'Rainbow', mult:5, chance:0.005, css:'rainbow'},
];
let forceVariant=null; // M6: тест-форс варианта (id из VARIANTS/null — всегда даёт его, паттерн forceBoss)
/* M6: счётчики вариантов в caught[id] (normal — без счётчика; sn — «shiny» = gold) */
const VARIANT_KEY={copper:'cp',silver:'sv',quartz:'qz',gold:'sn',emerald:'em',diamond:'dm',rainbow:'rb'};
/* M6+: буст вариантов от снасти — тип наживки (её эффективный rare-стат: 0/0.2/0.8/2.0,
у 3–4 ур. +1.0) и уровень апгрейда наживки (1..4). Простая наживка 1 ур. — B=0 (шансы базовые) */
function variantBoost(){
return lure().rare*0.15+(lureLvOf(save.lure)-1)*0.05;
}
function variantChance(v){
let c=v.id==='gold'?SHINY_CHANCE:v.chance; // chance gold — живая SHINY_CHANCE
if(v.id!=='normal'){
// M8: дождь — особые варианты ярче (×3; босс всегда normal — не доходит сюда)
if(weatherActive()==='rain') c*=WEATHER.rain.variantMult;
// M6+: снасть усиливает варианты — множитель ×(1+B·r), r — номер в лестнице VARIANTS (1..7):
// чем реже вариант, тем сильнее прибавка (медь ×(1+B) … радуга ×(1+7B))
c*=1+variantBoost()*VARIANTS.indexOf(v);
}
return c;
}
/* M6: ролл варианта улова */
function rollVariant(){
if(forceVariant){
const v=VARIANTS.find(x=>x.id===forceVariant);
if(v) return v;
}
for(const v of VARIANTS){ if(v.id==='normal') continue;
if(Math.random()<variantChance(v)) return v;
}
return VARIANTS.find(x=>x.id==='normal');
}
/* M4: босс-рыба (Этап 37: в каждой локации) — тест-форс появления (let — автотест, как SHINY_CHANCE) */
let forceBoss=false;
/* Спринт 4: пустые клёвы — тест-форс клёва (let — автотест, паттерн forceBoss) */
let forceBite=false;
/* M8: погода — дождь (варианты ×3) и туман (клёв ×1,5); события раз в 120–240 с, длятся 120 с */
const WEATHER={
rain:{icon:'🌧️', variantMult:3, toast:'Дождь: шанс особых рыб ×3'},
fog:{icon:'🌫️', biteMult:1.5, toast:'Туман: клёв чаще ×1,5'}
};
const WEATHER_GAP_MIN=120, WEATHER_GAP_MAX=240; // сек до следующего события (случайный разброс)
const WEATHER_DURATION=120; // сек активного события
let forceWeather=null; // M8: тест-форс погоды ('rain'/'fog'/null — активна всегда, паттерн forceBoss)
const RODS=[
{id:0, name:'Дедова удочка', nameEn:"Grandpa's Rod", icon:'🧹', cost:0, maxW:3, line:100, drag:34, reel:1.0, size:0.00, desc:'До 3 кг · Нагрузка 100 · Drag 34 · Подсечка ×1.0', descEn:'Up to 3 kg · Load 100 · Drag 34 · Hookset ×1.0'},
{id:1, name:'Стеклопластик', nameEn:'Fiberglass', icon:'🎣', cost:875, maxW:8, line:140, drag:47, reel:1.3, size:0.08, desc:'До 8 кг · Нагрузка 140 · Drag 47 · Подсечка ×1.3', descEn:'Up to 8 kg · Load 140 · Drag 47 · Hookset ×1.3'},
{id:2, name:'Карбоновая', nameEn:'Carbon', icon:'🏹', cost:4500, maxW:18, line:190, drag:63, reel:1.7, size:0.16, desc:'До 18 кг · Нагрузка 190 · Drag 63 · Подсечка ×1.7', descEn:'Up to 18 kg · Load 190 · Drag 63 · Hookset ×1.7'},
{id:3, name:'Титан', nameEn:'Titanium', icon:'💪', cost:15000, maxW:40, line:260, drag:87, reel:2.3, size:0.28, desc:'До 40 кг · Нагрузка 260 · Drag 87 · Подсечка ×2.3', descEn:'Up to 40 kg · Load 260 · Drag 87 · Hookset ×2.3'},
];
const LURES=[
{id:0, name:'Простой крючок', nameEn:'Simple Hook', icon:'🪝', cost:0, cast:1.00, bite:1.0, rare:0.0, size:0.00, desc:'Надёжно, но рыба не в восторге', descEn:'Reliable, but the fish are not thrilled'},
{id:1, name:'Червь', nameEn:'Worm', icon:'🪱', cost:625, cast:1.00, bite:1.3, rare:0.2, size:0.12, desc:'Клёв на 30% чаще, шанс крупной выше', descEn:'30% more bites, better chance of big fish'},
{id:2, name:'Воблер', nameEn:'Wobbler', icon:'🎣', cost:3000, cast:1.15, bite:1.2, rare:0.8, size:0.28, desc:'Дальше бросок, приманивает крупных', descEn:'Farther casts, attracts big fish'}, // Э42: иконка 🎣 (эмодзи рыб убраны)
{id:3, name:'Золотая блёстка', nameEn:'Gold Spinner', icon:'✨', cost:11250, cast:1.20, bite:1.4, rare:2.0, size:0.50, desc:'Лучшее, что есть. К ней плывут легенды', descEn:'The best there is — legends come to it'},
];
/* M5: апгрейды снастей — уровни 1..4 (макс). id 0 (база 0) — фиксированные цены
переходов 1→2, 2→3, 3→4; снасти id>0 — доля UPG_MULT от их базовой стоимости */
const ROD_UPG_COST=[70,210,500];
const LURE_UPG_COST=[60,160,400];
const UPG_MULT=[0.2,0.6,1.2]; // доля базы: →2 ур ×0.2, →3 ур ×0.6, →4 ур ×1.2
/* Что даёт следующий уровень (индекс = текущий уровень − 1) — подпись в лавке */
const ROD_UPG_NEXT=['фонарик (ночь), леска ×1.15','тормоз ×1.15, катушка ×1.10','леска ещё ×1.15, крупные +0.10'];
const LURE_UPG_NEXT=['клёв ×1.15','редкие +1.0','крупные +0.15'];
/* Y5: i18n — EN-версии подписей уровней (индексы те же) */
const ROD_UPG_NEXT_EN=['flashlight (night), line ×1.15','drag ×1.15, reel ×1.10','line ×1.15 more, big fish +0.10'];
const LURE_UPG_NEXT_EN=['bites ×1.15','rare +1.0','big fish +0.15'];
/* Цена перехода снасти i на следующий уровень (lv — текущий 1..3): id 0 — фикс, id>0 — база×множитель */
function upgCost(type,i,lv){
if(i===0) return (type==='rod'?ROD_UPG_COST:LURE_UPG_COST)[lv-1];
const base=(type==='rod'?RODS:LURES)[i].cost;
return Math.round(base*UPG_MULT[lv-1]/10)*10;
}
/* Этап 4: скины лодки и поплавка — чистая косметика (coin-sink), в лавке вкладка «Скины» */
const BOAT_SKINS=[
{id:0, name:'Обычная', nameEn:'Classic', cost:0},
{id:1, name:'Красная лодка', nameEn:'Red Boat', cost:800},
{id:2, name:'Лодка-домик', nameEn:'Boat House', cost:2000}
];
const BOBBER_SKINS=[
{id:0, name:'Обычный', nameEn:'Classic', cost:0},
{id:1, name:'Красный', nameEn:'Red', cost:500},
{id:2, name:'Неоновый', nameEn:'Neon', cost:1500}
];
/* Этап 46: Аквариум — декоративная сцена коллекции ЭКЗЕМПЛЯРОВ (вид+вариант+вес), не вода:
в LOCATIONS не добавляем (иначе сломаются роллы/боссы/баланс). Слоты: старт AQ_BASE_SLOTS,
максимум AQ_MAX_SLOTS; цена следующего слота — AQ_SLOT_COSTS[aqSlots-AQ_BASE_SLOTS]
(в масштабе цен локаций 65k–196k это доступный coin-sink). Экономику улова (catchFish
платит как раньше) не трогаем — «В аквариум» лишь копирует экземпляр (модель C). */
const AQ_BASE_SLOTS=6;
const AQ_MAX_SLOTS=12;
const AQ_SLOT_COSTS=[1500,4000,9000,20000,45000,100000];
/* =========================================================
СОХРАНЕНИЕ
========================================================= */
const SAVE_KEY='fishing-save-v1';
let save={coins:0,rod:0,lure:0,rods:[0],lures:[0],rodLv:{0:1},lureLv:{0:1},caught:{},muted:false,casts:0,earned:0,
snaps:0,ach:{},quests:null,streak:{last:null,days:0,shieldWeek:null},questsDoneTotal:0,
bossBeaten:false,bossCount:0,bosses:{},
boatSkins:[0],boatSkin:0,bobberSkins:[0],bobberSkin:0,
locations:[0],location:0,
aqFish:[],aqSlots:AQ_BASE_SLOTS, /* Этап 46: аквариум — экземпляры {id,v,w} и число слотов */
kgTotal:0,nightCatch:false,wxRainV:0,wxFog:0}; // Этап 4: скины; Этап 5: локации; Этап 22: счётчики ачивок (кг/ночь/погода); Этап 37: bosses — пер-босс; Этап 46: аквариум
function freshSave(muted){
return {coins:0,rod:0,lure:0,rods:[0],lures:[0],rodLv:{0:1},lureLv:{0:1},caught:{},muted:muted,casts:0,earned:0,
snaps:0,ach:{},quests:null,streak:{last:null,days:0,shieldWeek:null},questsDoneTotal:0,
bossBeaten:false,bossCount:0,bosses:{},
boatSkins:[0],boatSkin:0,bobberSkins:[0],bobberSkin:0,
locations:[0],location:0,
aqFish:[],aqSlots:AQ_BASE_SLOTS, // Этап 46: аквариум
kgTotal:0,nightCatch:false,wxRainV:0,wxFog:0}; // Этап 4: скины; Этап 5: локации; Этап 22: счётчики ачивок; Этап 37: bosses
}
function loadSave(){
try{ const s=localStorage.getItem(SAVE_KEY); if(s) Object.assign(save,JSON.parse(s)); }catch(e){}
// миграция старых сейвов: caught[id] был числом → теперь {n, best}
for(const k in save.caught){
if(typeof save.caught[k]==='number') save.caught[k]={n:save.caught[k],best:0};
}
// миграция M1: у записи появился счётчик shiny (sn)
for(const k in save.caught){
const c=save.caught[k];
if(c&&typeof c==='object'&&typeof c.sn!=='number') c.sn=0;
}
// миграция M6: счётчики всех вариантов (cp/sv/qz/sn/em/dm/rb)
for(const k in save.caught){
const c=save.caught[k];
if(c&&typeof c==='object'){
for(const key of Object.values(VARIANT_KEY)) if(typeof c[key]!=='number') c[key]=0;
}
}
// миграция M2: новые поля (старые сейвы обязаны читаться)
if(typeof save.snaps!=='number') save.snaps=0;
if(!save.ach||typeof save.ach!=='object') save.ach={};
if(typeof save.questsDoneTotal!=='number') save.questsDoneTotal=0;
if(!save.quests||typeof save.quests!=='object') save.quests=null;
if(!save.streak||typeof save.streak!=='object') save.streak={last:null,days:0,shieldWeek:null};
// M3: приманки — дефолт для старых сейвов
if(!save.lures||!Array.isArray(save.lures)) save.lures=[0];
if(!save.rods||!Array.isArray(save.rods)) save.rods=[0];
// M5: фонарик больше не приманка (id=4) — убираем из старых сейвов
if(save.lures.includes(4)) save.lures=save.lures.filter(x=>x!==4);
if(!save.lures.length) save.lures=[0];
if(save.lure===4) save.lure=0;
// M5: уровни снастей 1..4 — в старых сейвах их нет: купленным — уровень 1
if(!save.rodLv||typeof save.rodLv!=='object') save.rodLv={};
if(!save.lureLv||typeof save.lureLv!=='object') save.lureLv={};
for(const i of save.rods) if(!(save.rodLv[i]>=1)) save.rodLv[i]=1;
for(const i of save.lures) if(!(save.lureLv[i]>=1)) save.lureLv[i]=1;
delete save.lureLv[4];
// M3: цикл дня/ночи — без dayTime старт с утра; день идёт в реальном времени,
// пока игрока не было, сдвиг догоняет (mod 1)
if(typeof save.dayTime!=='number'||save.dayTime<0) save.dayTime=0;
setDayTime(save.dayTime+(Date.now()-(save.lastSaveMs||Date.now()))/1000/DAY_LENGTH);
save.lastSaveMs=Date.now();
// M4: босс (Этап 37: в каждой локации) — новые поля, дефолты для старых сейвов
if(typeof save.bossBeaten!=='boolean') save.bossBeaten=false;
if(typeof save.bossCount!=='number') save.bossCount=0;
// Этап 37: босс в каждой локации — save.bosses {id: true}, дефолт для старых сейвов
if(!save.bosses||typeof save.bosses!=='object') save.bosses={};
// Этап 4: скины лодки/поплавка — дефолты для старых сейвов и чинка битых значений
if(!save.boatSkins||!Array.isArray(save.boatSkins)) save.boatSkins=[0];
if(save.boatSkins.indexOf(0)<0) save.boatSkins.push(0); // бесплатный скин 0 гарантирован (битый сейв)
if(!save.bobberSkins||!Array.isArray(save.bobberSkins)) save.bobberSkins=[0];
if(save.bobberSkins.indexOf(0)<0) save.bobberSkins.push(0);
if(typeof save.boatSkin!=='number'||save.boatSkin<0||save.boatSkin>2) save.boatSkin=0;
if(typeof save.bobberSkin!=='number'||save.bobberSkin<0||save.bobberSkin>2) save.bobberSkin=0;
if(save.boatSkins.indexOf(save.boatSkin)<0) save.boatSkin=0; // экипированного нет в купленных (битый сейв)
if(save.bobberSkins.indexOf(save.bobberSkin)<0) save.bobberSkin=0;
// Этап 5: локации (Река/Озёра) — дефолты для старых сейвов и чинка битых значений (паттерн скинов)
// Этап 36: граница — из LOCATIONS.length (баг этапа 5: жёсткое >1 сбрасывало Море при reload)
if(!save.locations||!Array.isArray(save.locations)) save.locations=[0];
if(save.locations.indexOf(0)<0) save.locations.push(0); // родная Река гарантирована (битый сейв)
if(typeof save.location!=='number'||save.location<0||save.location>=LOCATIONS.length) save.location=0;
if(save.locations.indexOf(save.location)<0) save.location=0; // выбранной локации нет в купленных (битый сейв)
// Этап 46: аквариум — дефолты/чинка битых значений (паттерн скинов/локаций):
// экземпляр валиден, если id есть в SPECIES и w — число >0; вариант нормализуем по VARIANTS → 'normal'
if(!Array.isArray(save.aqFish)) save.aqFish=[];
save.aqFish=save.aqFish
.filter(f=>f&&SPECIES.some(s=>s.id===f.id)&&Number.isFinite(f.w)&&f.w>0) // Этап 46: Infinity/NaN отбрасываем
.map(f=>({id:f.id,v:VARIANTS.some(x=>x.id===f.v)?f.v:'normal',w:f.w}));
if(typeof save.aqSlots!=='number'||!Number.isFinite(save.aqSlots)) save.aqSlots=AQ_BASE_SLOTS;
save.aqSlots=Math.trunc(save.aqSlots); // Этап 46: нецелое (6.5) давало AQ_SLOT_COSTS[0.5]=undefined → NaN
if(save.aqSlots<AQ_BASE_SLOTS) save.aqSlots=AQ_BASE_SLOTS;
if(save.aqSlots>AQ_MAX_SLOTS) save.aqSlots=AQ_MAX_SLOTS;
if(save.aqFish.length>save.aqSlots) save.aqFish.length=save.aqSlots; // больше слотов, чем есть, — обрезаем
// M2: стрик входа и квесты дня (новый день — перегенерация)
applyStreak();
refreshQuests();
}
function persist(){
save.dayTime=dayTime; // M3: время суток храним в сейве
save.lastSaveMs=Date.now(); // M3: время последнего сейва (для догона дня)
try{ localStorage.setItem(SAVE_KEY,JSON.stringify(save)); }catch(e){}
}
/* =========================================================
M3: ЦИКЛ ДНЯ/НОЧИ
========================================================= */
const DAY_LENGTH=300; // секунд на полный день
let dayTime=0; // 0..1 — позиция в сутках
let dayPhase='morning'; // morning (0-0.25) day (0.25-0.5) evening (0.5-0.75) night (0.75-1.0)
let nightNoLightToast=false; // тост «Ночью нужен фонарик» — разово, не каждый кадр
function phaseOf(t){ return t<0.25?'morning':(t<0.5?'day':(t<0.75?'evening':'night')); }
function setDayTime(t){ t%=1; if(t<0)t+=1; dayTime=t; dayPhase=phaseOf(dayTime); }
/* =========================================================
M8: ПОГОДА — ДОЖДЬ И ТУМАН
========================================================= */
/* Случайный разброс до следующего события (сек) */
function weatherGapNext(){ return WEATHER_GAP_MIN+Math.random()*(WEATHER_GAP_MAX-WEATHER_GAP_MIN); }
/* Эффективная погода: тест-форс имеет приоритет (срабатывает синхронно, без ожидания кадра),
иначе естественное состояние. Единственная точка чтения для эффектов/визуала/HUD */
function weatherActive(){ return forceWeather||weather; }
/* Таймер — в реальном времени (как DAY_LENGTH в frame): между событиями разброс WEATHER_GAP_*,
само событие длится WEATHER_DURATION. При forceWeather погода активна всегда — таймеры не гоняем */
function updateWeather(dt){
if(forceWeather) return; // форс: таймеры не гоняем, weatherT не тикает
if(weather){
weatherT-=dt;
if(weatherT<=0){
weather=null; weatherGap=weatherGapNext();
toast(t('wx_clear'));
}
}else{
weatherGap-=dt;
if(weatherGap<=0){
weather=Math.random()<0.5?'rain':'fog'; // rain/fog 50/50
weatherT=WEATHER_DURATION;
// gap следующего события не роллим здесь: его задаст ветка конца события
toast(t('wx_'+weather)); // Y5: i18n — тост погоды из T (RU/EN)
}
}
}
/* Пилюля #weather в HUD рядом с #coins: иконка + остаток секунд («🌧️ 97с»);
при форсе показываем полную длительность (событие «всегда активно»). Обновляется по таймеру;
сравниваем ЦЕЛИК text (смена иконки rain↔fog не меняет секунд) */
let weatherHudTxt='';
function updateWeatherHud(){
const el=$('weather');
const id=weatherActive();
if(!id){
if(!el.classList.contains('hidden')) el.classList.add('hidden');
weatherHudTxt='';
return;
}
if(el.classList.contains('hidden')) el.classList.remove('hidden');
const sec=forceWeather?WEATHER_DURATION:Math.max(0,Math.ceil(weatherT));
const txt=WEATHER[id].icon+' '+sec+t('sec_short'); // Y5: i18n — «с»/«s»
if(txt!==weatherHudTxt){
weatherHudTxt=txt;
el.textContent=txt;
}
}
/* =========================================================
M2: НАГРАДЫ — КВЕСТЫ ДНЯ, СТРИК ВХОДА, ДОСТИЖЕНИЯ
========================================================= */
const STREAK_REWARDS=[25,50,75,100,125,150,200]; // награды дней 1-7 стрика
function todayStr(){
const d=new Date();
return d.getFullYear()+'-'+String(d.getMonth()+1).padStart(2,'0')+'-'+String(d.getDate()).padStart(2,'0');
}
function dateShift(dateStr,days){
const d=new Date(dateStr+'T00:00:00');
d.setDate(d.getDate()+days);
return d.getFullYear()+'-'+String(d.getMonth()+1).padStart(2,'0')+'-'+String(d.getDate()).padStart(2,'0');
}
/* ISO-неделя «YYYY-Www» (детерминированно — тесты повторяют расчёт) */
function isoWeek(dateStr){
const d=new Date(dateStr+'T12:00:00');
const day=(d.getDay()+6)%7;
d.setDate(d.getDate()-day+3); // четверг этой ISO-недели
const year=d.getFullYear();
const jan4=new Date(year,0,4);
const jan4day=(jan4.getDay()+6)%7;
const week1Thu=new Date(year,0,4-jan4day+3);
const week=1+Math.round((d-week1Thu)/(7*24*3600*1000));
return year+'-W'+String(week).padStart(2,'0');
}
/* Детерминированный hash строки даты (FNV-1a) — тесты смогут повторить генерацию */
function daySeed(dateStr){
let h=2166136261>>>0;
for(let i=0;i<dateStr.length;i++){
h^=dateStr.charCodeAt(i);
h=Math.imul(h,16777619);
}
return h>>>0;
}
function seededRand(seed){
let a=seed>>>0;
return function(){
a=(a+0x6D2B79F5)>>>0;
let t=a;
t=Math.imul(t^(t>>>15),t|1);
t^=t+Math.imul(t^(t>>>7),t|61);
return ((t^(t>>>14))>>>0)/4294967296;
};
}
/* --- Квесты дня --- */
const QUEST_NAMES={carass:['карась','карася','карасей'],perch:['окунь','окуня','окуней'],bream:['лещ','леща','лещей']};
const QUEST_NAMES_EN={carass:'Crucian carp',perch:'Perch',bream:'Bream'}; // Y5: i18n — EN-имена для квестов
const QUEST_TPLS=[
{tpl:'catch_species', make(r){ const sp=SPECIES[[0,1,2][Math.floor(r()*3)]];
return {tpl:'catch_species', param:sp.id, goal:3+Math.floor(r()*3), reward:100+Math.floor(r()*51)}; }},
{tpl:'earn_coins', make(r){
return {tpl:'earn_coins', param:0, goal:[150,250,400][Math.floor(r()*3)], reward:80+Math.floor(r()*41)}; }},
{tpl:'catch_weight', make(r){ const n=[2,3,5][Math.floor(r()*3)];
return {tpl:'catch_weight', param:n, goal:1, reward:120+Math.floor(r()*41)}; }},
{tpl:'casts', make(r){
return {tpl:'casts', param:0, goal:[8,10,15][Math.floor(r()*3)], reward:60+Math.floor(r()*41)}; }},
{tpl:'catch_rarity', make(r){
return {tpl:'catch_rarity', param:[1,2][Math.floor(r()*2)], goal:1, reward:150}; }},
{tpl:'catch_shiny', make(r){
return {tpl:'catch_shiny', param:0, goal:1, reward:200}; }},
];
function pluralN(n,forms){
const m10=n%10,m100=n%100;
if(m10===1&&m100!==11) return forms[0];
if(m10>=2&&m10<=4&&(m100<12||m100>14)) return forms[1];
return forms[2];
}
function questText(q){
if(LANG==='en'){ // Y5: i18n — EN-ветка (RU-ветка ниже — побуквенно без изменений)
switch(q.tpl){
case 'catch_species': return 'Catch '+q.goal+' '+QUEST_NAMES_EN[q.param];
case 'earn_coins': return 'Earn '+q.goal+' coins today';
case 'catch_weight': return 'Catch a fish from '+q.param+' kg';
case 'casts': return 'Make '+q.goal+' casts today';
case 'catch_rarity': return 'Catch a '+trRarity(q.param)+'+ fish';
case 'catch_shiny': return 'Catch a golden fish';
}
return '?';
}
switch(q.tpl){
case 'catch_species': return 'Поймай '+q.goal+' '+pluralN(q.goal,QUEST_NAMES[q.param]);
case 'earn_coins': return 'Заработай '+q.goal+' монет за день';
case 'catch_weight': return 'Поймай рыбу от '+q.param+' кг';
case 'casts': return 'Сделай '+q.goal+' забросов за день';
case 'catch_rarity': return 'Поймай рыбу редкости '+RARITY_NAMES[q.param]+'+';
case 'catch_shiny': return 'Поймай золотую рыбу'; // M6: «shiny» = gold-вариант
}
return '?';
}
function questIcon(q){
// Э42: catch_species — мини-SVG портрет вида (спрайт FISH-SVG) вместо эмодзи
if(q.tpl==='catch_species'){ const sp=SPECIES.find(s=>s.id===q.param); return sp?'<svg class="qfish" viewBox="0 0 160 100"><use href="#fish-'+sp.id+'"/></svg>':'❓'; }
return {earn_coins:'🪙', catch_weight:'⚖', casts:'🎣', catch_rarity:'⭐', catch_shiny:'✨'}[q.tpl]||'🎯';
}
/* 3 квеста дня из 6 шаблонов, без повторов, детерминированно от даты */
function genQuests(dateStr){
const r=seededRand(daySeed(dateStr));
const ids=QUEST_TPLS.map((t,i)=>i);
for(let i=ids.length-1;i>0;i--){ const j=Math.floor(r()*(i+1)); const tmp=ids[i]; ids[i]=ids[j]; ids[j]=tmp; }
return ids.slice(0,3).map(i=>{ const q=QUEST_TPLS[i].make(r); q.prog=0; q.done=false; return q; });
}
function refreshQuests(){
const today=todayStr();
if(!(save.quests&&save.quests.date===today)){
save.quests={date:today, list:genQuests(today)};
persist();
}
}
/* --- Стрик входа (с еженедельным щитом на один пропуск) --- */
function streakIdx(days){ return ((days-1)%7)+1; }
function nextStreakReward(days){ return STREAK_REWARDS[streakIdx((days||0)+1)-1]; }
function applyStreak(){
const today=todayStr();
if(save.streak.last===today) return;
const fresh=!save.streak.last; // первый визит — стрик запускается (награда дня с неё не выдаётся)
if(save.streak.last===dateShift(today,-1)){
save.streak.days=(save.streak.days||0)+1; // вчерашний визит — стрик растёт
}else if(save.streak.last===dateShift(today,-2)&&save.streak.shieldWeek!==isoWeek(today)){
save.streak.days=(save.streak.days||0)+1; // один пропуск в неделю прощает щит
save.streak.shieldWeek=isoWeek(today);
toast(t('shield'));
}else{
save.streak.days=1;
}
save.streak.last=today;
if(!fresh){
const coins=STREAK_REWARDS[streakIdx(save.streak.days)-1];
save.coins+=coins;
toast(tt('streak_day',{d:save.streak.days,c:coins}));
}
persist();
checkAchievements(); // streak_7
}
/* --- Прогресс квестов (delta — на сколько шагнуть: 1 улов / N монет и т.п.) --- */
function questHit(tpl,condFn,delta){
if(!save.quests) return;
for(const q of save.quests.list){
if(q.done||q.tpl!==tpl) continue;
if(!condFn(q)) continue;
q.prog+=delta||1;
if(q.prog>=q.goal){
q.prog=q.goal; q.done=true;
save.coins+=q.reward; save.questsDoneTotal++;
updateHUD(); // награда может прийти на забросе (вне catchFish) — счётчик монет обновить сразу
sfx('coin');
toast(tt('quest_done',{text:questText(q),r:q.reward}));
checkAchievements(); // quests_5
}
}
}
/* Новый день: стрик + перегенерация квестов (loadSave, cast, catch) */
function newDayCheck(){ applyStreak(); refreshQuests(); }
/* --- Достижения с постоянными бонусами --- */
function caughtN(s,id){ const c=s.caught&&s.caught[id]; return c?c.n:0; }
function caughtBest(s,id){ const c=s.caught&&s.caught[id]; return c?c.best:0; }
/* Y5: i18n — у достижений nameEn/descEn (RU name/desc — побуквенно те же) */
const ACHIEVEMENTS=[
{id:'first_catch', name:'Первый улов', nameEn:'First Catch', desc:'Поймай первую рыбу', descEn:'Catch your first fish', icon:'🏅', bonus:{coinPct:0.01}, check:s=>SPECIES.some(sp=>caughtN(s,sp.id)>0)}, // Э42: иконка 🏅 (эмодзи рыб убраны)
{id:'casts_10', name:'Разминка', nameEn:'Warm-up', desc:'Сделай 10 забросов', descEn:'Make 10 casts', icon:'🎣', bonus:{coinPct:0.01}, check:s=>s.casts>=10},
{id:'casts_100', name:'Моряк', nameEn:'Sailor', desc:'Сделай 100 забросов', descEn:'Make 100 casts', icon:'⛵', bonus:{coinPct:0.01}, check:s=>s.casts>=100},
{id:'earned_1000', name:'Первая тысяча', nameEn:'First Thousand', desc:'Заработай 1000 монет', descEn:'Earn 1,000 coins', icon:'🪙', bonus:{coinPct:0.01}, check:s=>s.earned>=1000},
{id:'earned_10000', name:'Рыбацкий капитал', nameEn:'Fishing Capital', desc:'Заработай 10 000 монет', descEn:'Earn 10,000 coins', icon:'💰', bonus:{coinPct:0.02}, check:s=>s.earned>=10000},
{id:'gear_max', name:'Максимальная снасть', nameEn:'Max Gear', desc:'Улучши снасть до 4 уровня', descEn:'Upgrade a gear to level 4', icon:'⚒️', bonus:{coinPct:0.01}, check:s=>Object.values(s.rodLv||{}).some(v=>v>=4)||Object.values(s.lureLv||{}).some(v=>v>=4)}, // Этап 22: M5 апгрейды
{id:'titan', name:'Титан', nameEn:'Titan', desc:'Купи удочку «Титан»', descEn:'Buy the Titanium rod', icon:'💪', bonus:{coinPct:0.02}, check:s=>(s.rods||[]).indexOf(3)>=0},
{id:'species_3', name:'Ботаник', nameEn:'Botanist', desc:'Поймай 3 вида рыб', descEn:'Catch 3 fish species', icon:'📖', bonus:{coinPct:0.01}, check:s=>SPECIES.filter(sp=>caughtN(s,sp.id)>0).length>=3},
{id:'species_8', name:'Энциклопедия', nameEn:'Encyclopedia', desc:'Поймай все виды', descEn:'Catch all species', icon:'🏆', bonus:{coinPct:0.02}, check:s=>SPECIES.every(sp=>caughtN(s,sp.id)>0)}, // Этап 5: все 11 видов, этап 33: все 16, этап 35: все 23, этап 36: все 32, этап 45: все 37 (id старый сейв; desc и чек — «все», масштабируются автоматически)
{id:'rare_fish', name:'Редкий гость', nameEn:'Rare Guest', desc:'Поймай рыбу редкости «Редкая»+ ', descEn:'Catch a Rare+ fish', icon:'⭐', bonus:{coinPct:0.01}, check:s=>SPECIES.some(sp=>sp.rarity>=2&&caughtN(s,sp.id)>0)},
{id:'legendary_fish',name:'Легенда', nameEn:'Legend', desc:'Поймай рыбу редкости «Легендарная»', descEn:'Catch a Legendary fish', icon:'👑',bonus:{coinPct:0.02}, check:s=>SPECIES.some(sp=>sp.rarity>=4&&caughtN(s,sp.id)>0)},
{id:'first_shiny', name:'Золотая жила', nameEn:'Gold Rush', desc:'Поймай золотую рыбу', descEn:'Catch a golden fish', icon:'✨', bonus:{coinPct:0.01}, check:s=>SPECIES.some(sp=>{const c=s.caught&&s.caught[sp.id]; return c&&c.sn>0;})},
{id:'first_silver', name:'Серебро', nameEn:'Silver', desc:'Поймай серебряную рыбу', descEn:'Catch a silver fish', icon:'🥈', bonus:{coinPct:0.01}, check:s=>SPECIES.some(sp=>{const c=s.caught&&s.caught[sp.id]; return c&&(c.sv||0)>0;})}, // Этап 22: M6 варианты
{id:'first_rainbow',name:'Радужный улов', nameEn:'Rainbow Catch', desc:'Поймай радужную рыбу', descEn:'Catch a rainbow fish', icon:'🌈', bonus:{coinPct:0.02}, check:s=>SPECIES.some(sp=>{const c=s.caught&&s.caught[sp.id]; return c&&(c.rb||0)>0;})},
{id:'full_variants',name:'Полная коллекция', nameEn:'Full Collection', desc:'Поймай все виды во всех вариантах', descEn:'Catch every species in every variant', icon:'🌈',bonus:{coinPct:0.05}, check:s=>SPECIES.every(sp=>{const c=s.caught&&s.caught[sp.id]; return c&&c.n>0&&VARIANTS.every(v=>{const k=VARIANT_KEY[v.id]; return !k||(c[k]||0)>0;});})},
{id:'weight_10', name:'Трофей', nameEn:'Trophy', desc:'Поймай рыбу 10+ кг', descEn:'Catch a fish of 10+ kg', icon:'⚖', bonus:{windowS:0.05}, check:s=>SPECIES.some(sp=>caughtBest(s,sp.id)>=10)},
{id:'weight_20', name:'Монстр глубин', nameEn:'Deep Monster', desc:'Поймай рыбу 20+ кг', descEn:'Catch a fish of 20+ kg', icon:'💪', bonus:{line:5}, check:s=>SPECIES.some(sp=>caughtBest(s,sp.id)>=20)}, // Э42: иконка 💪, лестница ⚖/💪/🚢 (эмодзи рыб убраны)
{id:'weight_1000', name:'Тонна рыбы', nameEn:'A Ton of Fish', desc:'Поймай рыбы на 1000+ кг', descEn:'Catch 1,000+ kg of fish', icon:'🚢', bonus:{coinPct:0.02}, check:s=>(s.kgTotal||0)>=1000}, // Этап 22
{id:'night_first', name:'Ночной клёв', nameEn:'Night Bite', desc:'Поймай рыбу ночью', descEn:'Catch a fish at night', icon:'🌙', bonus:{coinPct:0.01}, check:s=>s.nightCatch===true},
{id:'rain_variant', name:'Дождливый везунчик', nameEn:'Rainy Lucky Guy', desc:'Поймай особую рыбу в дождь', descEn:'Catch a special fish in the rain', icon:'🌧️', bonus:{coinPct:0.01}, check:s=>(s.wxRainV||0)>0}, // Этап 22: M8 погода
{id:'fog_catch', name:'Туманный улов', nameEn:'Foggy Catch', desc:'Поймай рыбу в тумане', descEn:'Catch a fish in the fog', icon:'🌫️', bonus:{coinPct:0.01}, check:s=>(s.wxFog||0)>0},
{id:'snaps_10', name:'Упёртый', nameEn:'Stubborn', desc:'10 обрывов шнура', descEn:'Snap the line 10 times', icon:'💥', bonus:{coinPct:0.01}, check:s=>s.snaps>=10},
{id:'snaps_50', name:'Хроники обрывов', nameEn:'Snap Chronicles', desc:'50 обрывов шнура', descEn:'Snap the line 50 times', icon:'📜', bonus:{coinPct:0.01}, check:s=>s.snaps>=50},
{id:'streak_7', name:'Семёрка', nameEn:'Seven', desc:'Стрик 7 дней', descEn:'7-day streak', icon:'🔥', bonus:{coinPct:0.01}, check:s=>s.streak&&s.streak.days>=7},
{id:'quests_5', name:'Дневник искателя', nameEn:"Seeker's Diary", desc:'Выполни 5 квестов', descEn:'Complete 5 quests', icon:'🎯', bonus:{coinPct:0.01}, check:s=>s.questsDoneTotal>=5},
{id:'skin_first', name:'Красуйся', nameEn:'Dress Up', desc:'Купи скин лодки или поплавка', descEn:'Buy a boat or bobber skin', icon:'🎨', bonus:{coinPct:0.01}, check:s=>(s.boatSkins||[]).length>1||(s.bobberSkins||[]).length>1}, // Этап 22: M-skins
{id:'skins_all', name:'Стилист', nameEn:'Stylist', desc:'Собери все скины лодки и поплавка', descEn:'Collect all boat and bobber skins', icon:'🎭',bonus:{coinPct:0.02}, check:s=>BOAT_SKINS.every(b=>(s.boatSkins||[]).indexOf(b.id)>=0)&&BOBBER_SKINS.every(b=>(s.bobberSkins||[]).indexOf(b.id)>=0)},
{id:'lake_first', name:'Новые берега', nameEn:'New Shores', desc:'Открой локацию «Озёра»', descEn:'Unlock the Lakes location', icon:'🗺️', bonus:{coinPct:0.02}, check:s=>(s.locations||[]).indexOf(1)>=0}, // Этап 22: локации
{id:'sea_first', name:'Широкая вода', nameEn:'Open Water', desc:'Открой локацию «Море»', descEn:'Unlock the Sea location', icon:'⚓', bonus:{coinPct:0.02}, check:s=>(s.locations||[]).indexOf(2)>=0}, // Этап 36: локации (locations-based — пин §10c(8) achBonus не трогает)
{id:'ocean_first', name:'Глубина', nameEn:'The Deep', desc:'Открой локацию «Океан»', descEn:'Unlock the Ocean location', icon:'🌊', bonus:{coinPct:0.02}, check:s=>(s.locations||[]).indexOf(3)>=0}, // Этап 36: локации
{id:'swamp_first', name:'Тайна болота', nameEn:'Swamp Secret', desc:'Открой локацию «Волшебное болото»', descEn:'Unlock the Magic Swamp location', icon:'🗺️', bonus:{coinPct:0.02}, check:s=>(s.locations||[]).indexOf(4)>=0}, // Этап 45: локация Болото (locations-based — пин §10c(8) achBonus не трогает)
{id:'boss_first', name:'Босс-рыбак', nameEn:'Boss Angler', desc:'Победи рыбу-босса', descEn:'Defeat a boss fish', icon:'🏆', bonus:{coinPct:0.03}, check:s=>(s.bossBeaten===true)||(s.bossCount>0)}, // Этап 37: текст нейтральный — босс в каждой локации
// Этап 37: пер-босс ачивки (без coinPct — пин §10c(8) сумма achBonus().coinPct=0.16 не сдвигается;
// bonus:{} пустой — bonusText() в вкладке «Достижения» требует объект, coinPct там нет)
{id:'lake_boss', name:'Победа над Раком', nameEn:'Crayfish Slayer', desc:'Победи босса Озёр', descEn:'Defeat the Lakes boss', icon:'🗺️', bonus:{}, check:s=>!!(s.bosses&&s.bosses.crayfish)}, // Э42: иконка локации (эмодзи рыб убраны)
{id:'sea_boss', name:'Победа над Рыбой-мечом', nameEn:'Swordfish Slayer', desc:'Победи босса Моря', descEn:'Defeat the Sea boss', icon:'⚓', bonus:{}, check:s=>!!(s.bosses&&s.bosses.swordfish)}, // Э42: иконка локации (эмодзи рыб убраны)
{id:'ocean_boss', name:'Победа над Китом', nameEn:'Whale Slayer', desc:'Победи босса Океана', descEn:'Defeat the Ocean boss', icon:'🌊', bonus:{}, check:s=>!!(s.bosses&&s.bosses.whale)}, // Э42: иконка локации (эмодзи рыб убраны)
{id:'swamp_boss', name:'Победа над Духом болота', nameEn:'Spirit Slayer', desc:'Победи Духа болота', descEn:'Defeat the Swamp Spirit', icon:'🗺️', bonus:{}, check:s=>!!(s.bosses&&s.bosses.spiritfin)}, // Этап 45: босс Болота (без coinPct — пин §10c(8); bonus:{} пустой — bonusText() требует объект)
// Этап 46: аквариум — locations-based проверки (см. checkAchievements: массив ACHIEVEMENTS,
// поле check). БЕЗ coinPct — пин §10c(8) сумма achBonus().coinPct=0.18 не сдвигается;
// bonus:{} пустой (как у пер-босс-ачивок) — bonusText() в «Достижениях» требует объект.
// Проверки терпимы к старому сейву: нет aqFish/aqSlots — ложных выдач нет
{id:'aq_first', name:'Первый жилец', nameEn:'First Tenant', desc:'Посели рыбу в аквариум', descEn:'Move a fish into the aquarium', icon:'🐠', bonus:{}, check:s=>(s.aqFish||[]).length>0},
{id:'aq_full', name:'Аквариум полон', nameEn:'Full Tank', desc:'Заполни все слоты аквариума', descEn:'Fill every aquarium slot', icon:'🐠', bonus:{}, check:s=>(s.aqFish||[]).length>=(s.aqSlots||AQ_BASE_SLOTS)},
];
/* Сумма бонусов по выданным достижениям */
function achBonus(){
const b={coinPct:0,windowS:0,line:0};
for(const a of ACHIEVEMENTS){
if(!save.ach||!save.ach[a.id]) continue;
if(a.bonus.coinPct) b.coinPct+=a.bonus.coinPct;
if(a.bonus.windowS) b.windowS+=a.bonus.windowS;
if(a.bonus.line) b.line+=a.bonus.line;
}
return b;
}
function checkAchievements(){
let changed=false;
for(const a of ACHIEVEMENTS){
if(save.ach[a.id]) continue;
if(a.check(save)){
save.ach[a.id]=true; changed=true;
sfx('coin');
if(a.id==='full_variants'){ save.coins+=5000; updateHUD(); } // M6: разовые 5000 за полную коллекцию вариантов
toast(tt('ach_toast',{name:trName(a)})+(a.id==='full_variants'?' +5000 🪙':''));
}
}
if(changed) persist();
}
function bonusText(b){
if(b.coinPct) return tt('bonus_coin',{v:(b.coinPct*100)});
if(b.windowS) return tt('bonus_window',{v:b.windowS});
if(b.line) return tt('bonus_line',{v:b.line});
return '';
}
/* =========================================================
ХОЛСТ И РАЗМЕРЫ
========================================================= */
const canvas=document.getElementById('game');
const ctx=canvas.getContext('2d');
let W=0,H=0,DPR=1,waterY=0,boatX=0;
let tip={x:0,y:0};
function resize(){
DPR=Math.min(window.devicePixelRatio||1,2);
W=window.innerWidth; H=window.innerHeight;
canvas.width=W*DPR; canvas.height=H*DPR;
canvas.style.width=W+'px'; canvas.style.height=H+'px';
ctx.setTransform(DPR,0,0,DPR,0,0);
waterY=Math.round(H*0.26);
boatX=Math.round(W*0.20);
initClouds(); initSparkles(); initRain(); initScenery(); // Этап 44: силуэты по новой ширине
if(aqOpen) initAquarium(); // Этап 46: банка перераскладывается по новым W/H
}
window.addEventListener('resize',resize);
/* =========================================================
ЗВУК
========================================================= */
let AC=null;
function ac(){
if(!AC){ try{ AC=new (window.AudioContext||window.webkitAudioContext)(); }catch(e){ return null; } }
if(AC&&AC.state==='suspended'){ AC.resume(); }
return AC;
}
function beep(t0,f,dur,type,vol){
const c=ac(); if(!c)return;
const o=c.createOscillator(),g=c.createGain();
o.type=type||'sine'; o.frequency.setValueAtTime(f,t0);
g.gain.setValueAtTime(vol,t0); g.gain.exponentialRampToValueAtTime(0.001,t0+dur);
o.connect(g); g.connect(c.destination); o.start(t0); o.stop(t0+dur+0.02);
}
function noiseBurst(t0,dur,vol,freq){
const c=ac(); if(!c)return;
const n=Math.floor(c.sampleRate*dur);
const buf=c.createBuffer(1,n,c.sampleRate),d=buf.getChannelData(0);
for(let i=0;i<n;i++) d[i]=(Math.random()*2-1)*(1-i/n);
const src=c.createBufferSource(); src.buffer=buf;
const f=c.createBiquadFilter(); f.type='lowpass'; f.frequency.value=freq;
const g=c.createGain(); g.gain.value=vol;
src.connect(f); f.connect(g); g.connect(c.destination); src.start(t0);
}
function sfx(name){
if(save.muted) return;
const c=ac(); if(!c) return;
const t=c.currentTime;
switch(name){
case 'click': beep(t,500,0.06,'triangle',0.15); break;
case 'cast': noiseBurst(t,0.22,0.15,2500); break;
case 'splash': noiseBurst(t,0.3,0.4,700); break;
case 'bite': beep(t,900,0.07,'square',0.2); beep(t+0.1,1200,0.09,'square',0.2); break;
case 'hook': beep(t,600,0.05,'triangle',0.25); beep(t+0.06,900,0.08,'triangle',0.25); break;
case 'tick': beep(t,260,0.03,'triangle',0.07); break;
case 'pull': beep(t,140,0.18,'sawtooth',0.12); break;
case 'snap': noiseBurst(t,0.25,0.5,3000); beep(t,180,0.2,'sawtooth',0.2); break;
case 'escape': beep(t,500,0.12,'sine',0.2); beep(t+0.12,350,0.16,'sine',0.18); break;
case 'coin': beep(t,1200,0.08,'sine',0.2); beep(t+0.09,1600,0.12,'sine',0.2); break;
case 'catch': [523,659,784,1047].forEach((f,i)=>beep(t+i*0.11,f,0.14,'triangle',0.22)); break;
case 'no': beep(t,220,0.12,'square',0.15); break;
}
}
function vib(p){ try{ if(navigator.vibrate) navigator.vibrate(p); }catch(e){} }
/* =========================================================
СОСТОЯНИЕ ИГРЫ
========================================================= */
let state='MENU'; // MENU IDLE CHARGE CAST WAIT APPROACH BITE REEL SNAP CARD
let uiOpen=true;
let pauseOpen=false; // Y6: оверлей паузы (треб. 6.3) открыт — гард в YG.onResume
let time=0;
let charge=0, power=0;
let castT=0, castFrom={x:0,y:0}, castTo={x:0,y:0};
let bobber={x:0,y:0};
let castDist=0; // дальность заброса 0..0.97 (визуал: глубина крючка оснастки)
let hookPos={x:0,y:0}; // позиция крючка (визуал; пригодится lane поклёвки/вываживания)
let biteTimer=0, biteT=0, biteFish=null, rippleTimer=0;
let reel=null;
let snapT=0, shake=0;
let stateBoss=false, bossPhase=0; // M4: босс-бой (Этап 37: в каждой локации, 3 фазы)
/* M8: погода — активное событие и таймеры (в реальном времени, как день/ночь) */
let weather=null; // 'rain'/'fog'/null — активное погодное событие (естественное состояние)
let weatherT=0; // M8: остаток секунд активного события
let weatherGap=WEATHER_GAP_MIN+Math.random()*(WEATHER_GAP_MAX-WEATHER_GAP_MIN); // M8: сек до следующего события
let ambient=[]; // фоновые рыбы
let scenery=[]; // Этап 44: береговые силуэты на горизонте (по локации)
/* Этап 45: декор «Волшебного болота» — ОТДЕЛЬНЫЕ от scenery слои поверх воды.
Кувшинки/лилии и лягушки рисуются после воды (drawSwampDecor), поэтому dusk()
береговых силуэтов их не гасит; пересобираются в initScenery() вместе со scenery. */
let pads=[]; // кувшинки: {x,y,r,ph,bob,notch,lily,lilyColor}
let frogs=[]; // лягушки: {x,y,s,state,t,dur,jh,dir,x0,y0,x1,y1}
let clouds=[], sparkles=[];
let rain=[]; // M8: частицы дождя (визуал)
let particles=[], ripples=[];
let bobberTrail=[]; // визуал: след поплавка при забросе
/* M5: уровни снастей (1..4) и эффективные статы: базовые + эффекты апгрейдов */
function rodLvOf(i){ const v=save.rodLv&&save.rodLv[i]>=1?save.rodLv[i]:1; return Math.max(1,Math.min(4,v)); }
function lureLvOf(i){ const v=save.lureLv&&save.lureLv[i]>=1?save.lureLv[i]:1; return Math.max(1,Math.min(4,v)); }
function effRod(i){
const b=RODS[i], lv=rodLvOf(i);
const e=Object.assign({},b,{light:lv>=2}); // M5: Lv2 — фонарик (ночная рыбалка)
if(lv>=2) e.line*=1.15; // Lv2: леска крепче
if(lv>=3){ e.drag*=1.15; e.reel*=1.10; } // Lv3: тормоз и катушка
if(lv>=4){ e.line*=1.15; e.size+=0.10; } // Lv4: леска ещё + крупные
return e;
}
function effLure(i){
const b=LURES[i], lv=lureLvOf(i);
const e=Object.assign({},b);
if(lv>=2) e.bite*=1.15; // Lv2: клёв чаще
if(lv>=3) e.rare+=1.0; // Lv3: редкие
if(lv>=4) e.size+=0.15; // Lv4: крупные
return e;
}
const rod=()=>effRod(save.rod);
const lure=()=>effLure(save.lure);
/* M5: фонарик — не приманка, а апгрейд 1 любого удилища (2+ ур.) */
const hasLight=()=>rodLvOf(save.rod)>=2;
const rand=(a,b)=>a+Math.random()*(b-a);
/* Этап 43: фоновые рыбы — масштаб и оттенок по локации (индекс = save.location, clamp по
той же границе, что LOCATIONS в drawWater): Река — как было (8–22), Озёра — зеленоватые,
Море — синеватые и покрупнее, Океан — редкие крупные тёмно-синие. Чисто визуал.
Этап 45: Болото — мелкая тёмно-зелёная стайка + palette цветов свечения (initFish раскладывает
цвета по рыбам, ночную ауру рисует drawAmbientFish).
Вызовы: старт (СТАРТ), смена локации в shopClick (buylocation/selectlocation) */
const AMBIENT_FISH=[
{n:9,sMin:8, sMax:22,hMin:130,hMax:220},
{n:9,sMin:10,sMax:26,hMin:120,hMax:200},
{n:8,sMin:12,sMax:32,hMin:160,hMax:230},
{n:6,sMin:18,sMax:44,hMin:180,hMax:250},
{n:7,sMin:12,sMax:30,hMin:120,hMax:170,palette:['#5ff2ff','#57ffa8','#d17bff','#ffe07a']}
];
function initFish(){
const f=AMBIENT_FISH[Math.max(0,Math.min(AMBIENT_FISH.length-1,save.location|0))];
ambient=[];
for(let i=0;i<f.n;i++){
ambient.push({
x:Math.random()*W, depth:rand(0.12,0.92), size:rand(f.sMin,f.sMax),
speed:rand(10,32), dir:Math.random()<0.5?1:-1, hue:rand(f.hMin,f.hMax), ph:rand(0,6.28),
// Фаза свечения: только у Болота (есть palette) — стабильный цвет из палитры по индексу рыбы;
// у локаций 0..3 поле = null и нигде не читается
glow:f.palette?f.palette[i%f.palette.length]:null
});
}
}
/* Этап 44: береговые силуэты на горизонте — визуальное различение локаций:
Река — береговая полоса НА ВСЮ ШИРИНУ (трава, 12–17 px, тёмная кромка у воды) + хвойный
лес, посаженный НА ЕЁ КРАЮ (дальний ряд светлее, ближний темнее), Озёра — холмы +
лиственные деревья, Море — парусник (медленно дрейфует) + чайки (летят, машут крыльями),
Океан — открытый горизонт БЕЗ берега (вода и небо). Элементы: {k:'bank',h,c?,c2?} /
{k:'pine',layer,by?,c?} (by — база ствола: у реки — верх кромки берега; c — цвет) /
{k:'hill'} / {k:'tree',c?} / {k:'ship',dir,v} / {k:'gull',y,v,ph} /
{k:'reed',h,lean,ph} (камыш) / {k:'snag',h,lean} (кривое дерево) — болотная гамма.
Этап 45: Болото дополнительно собирает отдельные слои pads[]/frogs[] (кувшинки/лилии,
лягушки) — рисуются поверх воды в drawSwampDecor(), прыжки/покачивание — updateDecor(dt).
Вызовы initScenery(): resize(), старт (в resize), смена локации в shopClick —
новую точку смены локации без initScenery() не добавлять. Дрейф парусника/чаек —
updateDecor(dt). Отрисовка drawScenery() — между drawSky() и drawWater() (базу
силуэтов закрывает вода). Чисто визуал */
function initScenery(){
const i=Math.max(0,Math.min(LOCATIONS.length-1,save.location|0));
scenery=[];
pads=[]; // Этап 45: болотный декор пересобирается вместе с силуэтами (смена локации/ресайз)
frogs=[];
if(i===0){ // Река — береговая полоса на всю ширину + лес в два ряда, посаженный на её кромку
const bankH=12+((Math.random()*6)|0); // 12–17 px
scenery.push({k:'bank',h:bankH});
for(let k=0;k<16;k++) scenery.push({k:'pine',layer:0,x:rand(-12,W+12),h:rand(16,34),by:waterY-bankH});
for(let k=0;k<12;k++) scenery.push({k:'pine',layer:1,x:rand(-12,W+12),h:rand(12,26),by:waterY-bankH});
}else if(i===1){ // Озёра — холмы (за деревьями) + лиственные деревья
const nh=2+((Math.random()*2)|0);
for(let k=0;k<nh;k++) scenery.push({k:'hill',x:rand(W*0.1,W*0.9),rx:rand(W*0.22,W*0.42),ry:rand(16,34)});
for(let k=0;k<10;k++) scenery.push({k:'tree',x:rand(-8,W+8),h:rand(8,18)});
}else if(i===2){ // Море — парусник + чайки (берега нет — открытый горизонт)
scenery.push({k:'ship',x:rand(W*0.25,W*0.8),v:rand(2.5,5),s:rand(0.8,1.15),dir:Math.random()<0.5?1:-1});
for(let k=0;k<3;k++) scenery.push({k:'gull',x:rand(0,W),y:rand(26,72),v:rand(14,26),ph:rand(0,6.28)});
}else if(i===4){ // Этап 45: Болото — мшистый задний берег: камыш + кривые деревья + туи
const bankH=10+((Math.random()*5)|0); // 10–14 px
scenery.push({k:'bank',h:bankH,c:'#3d5130',c2:'#28381f'});
for(let k=0;k<14;k++) scenery.push({k:'reed',x:rand(-12,W+12),h:rand(18,42),lean:rand(2,6),ph:rand(0,6.28)});
for(let k=0;k<6;k++) scenery.push({k:'snag',x:rand(-8,W+8),h:rand(14,30),lean:rand(-0.5,0.5)});
for(let k=0;k<8;k++) scenery.push({k:'pine',layer:0,x:rand(-12,W+12),h:rand(14,26),by:waterY-bankH,c:'#33503a'});
for(let k=0;k<6;k++) scenery.push({k:'pine',layer:1,x:rand(-12,W+12),h:rand(10,20),by:waterY-bankH,c:'#22392a'});
// Кувшинки (8–14): кувшинка = эллипс с вырезом-прорезью; на части — лилия (3 цвета)
const np=8+((Math.random()*7)|0);
for(let k=0;k<np;k++){
pads.push({
x:rand(10,W-10),
y:waterY+rand(18,Math.max(30,H-waterY-30)), // на воде, ниже линии воды
r:rand(9,17),
ph:rand(0,6.28), bob:0,
notch:rand(0,6.28), // угол выреза-прорези
lily:Math.random()<0.45,
lilyColor:['#ffe07a','#f2efe2','#ff9ec4'][(Math.random()*3)|0] // жёлтый/белый/розовый
});
}
// Лягушки (3–5): стартуют на кувшинках, прыгают по параболе на соседние (см. updateDecor).
// Размер s — заметно крупнее (9–14: читаемы на телефоне), вся отрисовка в drawSwampDecor масштабируется от s
const nf=3+((Math.random()*3)|0);
for(let k=0;k<nf;k++){
const p=pads.length?pads[(Math.random()*pads.length)|0]:null;
frogs.push({
x:p?p.x:rand(20,W-20), y:p?p.y-2:waterY+rand(30,120),
s:rand(9,14),
state:'sit', t:rand(0,3), dur:0.5, jh:20, dir:1,
x0:0,y0:0,x1:0,y1:0
});
}
}
// i===3 — Океан: берега нет вообще (открытая вода)
}
function initClouds(){
clouds=[];
for(let i=0;i<4;i++){
clouds.push({x:Math.random()*W,y:rand(14,Math.max(20,waterY-50)),s:rand(0.6,1.2),v:rand(4,9)});
}
}
function initSparkles(){
sparkles=[];
for(let i=0;i<24;i++){
sparkles.push({x:Math.random()*W,y:waterY+rand(10,H-waterY-10),ph:rand(0,6.28)});
}
initStars();
}
/* Визуал: звёзды для ночного неба (только отрисовка, на механику не влияют) */
let stars=[];
function initStars(){
stars=[];
for(let i=0;i<46;i++){
stars.push({x:Math.random()*W,y:Math.random()*Math.max(10,waterY-14),r:rand(0.6,1.7),ph:rand(0,6.28),sp:rand(0.8,2.2)});
}
}
/* M8: частицы дождя (визуал) — ~40 косых полосок; обновление по dt в updateDecor, пересчёт по высоте */
function initRain(){
rain=[];
for(let i=0;i<40;i++){
rain.push({x:rand(-40,W+40), y:rand(-H,H), v:rand(520,760)});
}
}
/* =========================================================
УТИЛИТЫ
========================================================= */
const $=id=>document.getElementById(id);
let toastTO=null;
function toast(msg){
const t=$('toast'); t.textContent=msg; t.style.opacity=1;
clearTimeout(toastTO); toastTO=setTimeout(()=>t.style.opacity=0,1600);
}
function updateHUD(){ $('coins').textContent='🪙 '+save.coins; $('btnMute').textContent=(save.muted?'🔇':'🔊')+' '+t('gm_sound'); }
function splash(x,y,n,spread){
for(let i=0;i<n;i++){
particles.push({
x:x,y:y, vx:rand(-1,1)*(spread||90), vy:rand(-160,-40),
g:320, life:rand(0.35,0.7), max:0.7, size:rand(1.5,3.5),
color:Math.random()<0.5?'255,255,255':'160,215,255'
});
}
}
function addRipple(x,y,max){ ripples.push({x:x,y:y,r:4,max:max||36,a:0.6}); }
/* =========================================================
ЛОГИКА
========================================================= */
function pickSpecies(dist){
const l=lure();
// M3: ночь/вечер — ночная таблица, иначе дневная (жёсткий переключатель)
// Этап 5: локация — Река (8 видов) или Озёра (все 16, этап 33); логика ролла по весам без изменений
// Этап 35: локации — у каждой СВОИ виды (без пересечений); Этап 36: +Океан; Этап 45: +Болото →
// пять локаций: Река 8 / Озёра 10 / Море 5 / Океан 9 / Болото 5.
// Этап 45: табличный доступ по save.location вместо вложенного тернарника (роллы 0..3 идентичны);
// guard: вне диапазона 0..4 (битый сейв/тест) — Река, как было раньше по умолчанию
const night=(dayPhase==='night'||dayPhase==='evening');
const li=save.location|0;
const idx=(li>=0&&li<SPECIES_BY_LOCATION.length)?li:0;
const tbl=night?SPECIES_BY_LOCATION_NIGHT[idx]:SPECIES_BY_LOCATION[idx];
let total=0;
const ws=tbl.map(s=>{
let w=s.weight;
if(dist<s.minDist) w*=0.06;
w*=1+l.rare*s.rarity;
total+=w; return w;
});
let r=Math.random()*total;
for(let i=0;i<tbl.length;i++){ r-=ws[i]; if(r<=0) return tbl[i]; }
return tbl[0];
}
function biteWindow(sp){
if(sp.boss) return rand(0.25,0.5); // M4: босс клюёт реже — окно подсечки короче
return Math.max(0.35, 0.8 - sp.rarity*0.08 + save.rod*0.04 + achBonus().windowS);
}
/* Спринт 4: шанс клёва на таймер — не каждый заброс даёт рыбу (дальше = чаще, ночь тише) */
function biteChance(){
const dist=Math.min(1,(castTo.x-(boatX+30))/Math.max(1,W-boatX-70));
let p=0.65+0.25*dist;
if(dayPhase==='night') p*=0.85;
if(weatherActive()==='fog') p*=WEATHER.fog.biteMult; // M8: туман — клёв чаще ×1,5
return p;
}
/* Ролл веса рыбы: сдвиг в крупную сторону зависит от удочки и приманки */
function rollWeight(sp){
const B=Math.min(0.95, rod().size + lure().size);
const s=Math.pow(Math.random(), 2/(1+2*B)); // B=0 → смещено к мелкой, B↑ → ближе к равномерной
return sp.wMin + s*(sp.wMax-sp.wMin);
}
function startBite(){
const dist=Math.min(1,(castTo.x-(boatX+30))/Math.max(1,W-boatX-70));
let sp=pickSpecies(dist);
// M4: осётр → босс (30% шанс или тест-форс) — клонируем, чтобы не трогать таблицу SPECIES
// Этап 37: босс в каждой локации — босс = вид BOSS_BY_LOCATION[save.location]
const bossId=BOSS_BY_LOCATION[save.location|0];
if(bossId===sp.id && (forceBoss || Math.random()<0.3)) sp=Object.assign({},sp,{boss:true});
const fromLeft=Math.random()<0.5;
const d=Math.max(0.25,sp.minDist)*0.8+0.1;
biteFish={
x:fromLeft?-30:W+30, y:waterY+d*(H-waterY-40),
sx:fromLeft?-30:W+30, sy:waterY+d*(H-waterY-40),
dir:fromLeft?1:-1, sp:sp, t:0, mode:'in'
};
state='APPROACH';
}
function hook(){
const sp=biteFish.sp;
biteFish=null;
sfx('hook'); vib(30);
state='REEL';
stateBoss=!!sp.boss; bossPhase=sp.boss?1:0; // M4: старт босс-боя — фаза 1
let w=sp.boss?20+Math.random()*30:rollWeight(sp); // Этап 37: любой босс весит 20–50 кг (без ролла wMin..wMax вида)
const ov=w/rod().maxW; // 1.0 = рыба ровно на пределе удочки
const str=sp.boss?0.95:sp.strength; // M4: босс сильнее (осётр 0.85 → 0.95)
const aggr=Math.min(1, 0.35*str + 0.75*Math.min(1.25,ov)/1.25); // «злость» рыбы
const d0=45+7*w; // чем крупнее, тем дальше от лодки (T2b: буфер для «сдавать в рывок»)
const line=rod().line+achBonus().line; // прочность с бонусом достижений
// Спринт 4: выносливость — крупная рыба выносливее, босс свежий
const st0=sp.boss?1:0.65+0.35*Math.min(1,w/sp.wMax);
reel={d:d0, d0:d0, tension:0, escape:0, holding:false, phase:'calm', phaseT:rand(0.8,1.5), sp:sp, w:w, ov:ov, aggr:aggr, tick:0, line:line, boss:!!sp.boss, st:st0, tired:false};
}
/* Спринт 4: сила рыбы (перетягивание с drag) — глобальная, автотесты вызывают */
function fishForce(r,ag,pull){
const base=pull?(16+34*r.st)*(0.5+0.9*ag)*(0.7+0.5*Math.min(1.5,r.ov)):5+7*ag;
return r.tired?0.45*base:base;
}
function doCast(){
const p=Math.max(0.12,power);
const dist=Math.min(0.97,(0.10+0.80*p)*lure().cast);
castDist=dist; // запоминаем дальность (визуал: глубина крючка оснастки)
castFrom={x:tip.x,y:tip.y};
castTo={x:boatX+30+dist*Math.max(1,W-boatX-70), y:waterY-2};
castT=0; state='CAST'; sfx('cast');
save.casts++;
newDayCheck();
questHit('casts',()=>true); // condFn обязателен: без него questHit падает с TypeError
checkAchievements();
persist();
}
function recall(){
if(state==='WAIT'||state==='APPROACH'||state==='BITE'){
state='IDLE'; biteFish=null; sfx('click');
}
}
function updateReel(dt){
const r=rod(), sp=reel.sp, ag=reel.aggr, ov=reel.ov;
reel.phaseT-=dt;
if(reel.phaseT<=0){
if(reel.phase==='calm'){
// Спринт 4: рывок длиннее у свежей рыбы, короче у уставшей
reel.phase='pull'; reel.phaseT=(0.8+2.2*reel.st)*(0.6+0.7*ag)*rand(0.8,1.25);
sfx('pull'); vib(40);
}else{
// Спринт 4: пауза растёт по мере усталости
reel.phase='calm'; reel.phaseT=Math.max(0.35,(1.3-0.8*reel.st)*rand(0.8,1.3)); vib(15);
}
}
const pulling=reel.phase==='pull';
// Спринт 4 T2b: выносливость — медленная дуга: свежая тянет сильно, уставшая слабо (Monte-Carlo)
const drain=(pulling?0.016+0.02*ag:0.002)+(reel.holding?0.006:0.001)+(pulling&&!reel.holding?0.01:0);
reel.st=Math.max(0,reel.st-drain*dt);
if(reel.st<=0.12) reel.tired=true; // устала: рывки слабее, втягивается быстрее
const F=fishForce(reel,ag,pulling);
if(reel.holding){
// Спринт 4: перетягивание через drag — рывок сильнее тормоза: леска скользит
const over=F-r.drag;
reel.tension+=(10+Math.max(0,over)*1.1+22*ag*(pulling?1:0.35))*dt;
if(over>0) reel.d+=over*0.22*dt;
else reel.d-=(11+3*r.reel)*(1+0.6*(1-reel.st))*dt; // втягивание, усталую быстрее (до ×1.6)
reel.tick-=dt;
if(reel.tick<=0){ sfx('tick'); reel.tick=0.12; }
if(Math.random()<0.3) splash(bobber.x,waterY,1,40); // брызги у поплавка (он едет за рыбой)
}else{
reel.tension-=38*dt;
if(pulling) reel.d+=F*0.32*dt; // свободный бег: рыба реально отходит (стоимость отпускания — дистанция)
}
reel.tension=Math.max(0,reel.tension);
// escape T2b: отпускать в РЫВОК безопасно (бар не растёт) — «сдался рыбе»;
// а вот зависнуть без удержания в затишье = рыба уходит (пассив = сход)
if(reel.holding){
reel.escape=Math.max(0,reel.escape-0.5*dt);
}else if(!pulling){
reel.escape=Math.min(1,reel.escape+(0.2+ag*0.4)*dt);
}
if(reel.escape>=1){
fishEscaped(sp);
return;
}
if(reel.tension>=reel.line){
state='SNAP'; snapT=1.0; shake=0.5;
save.snaps++; persist(); // счётчик обрывов (достижения)
sfx('snap'); vib(200);
stateBoss=false; bossPhase=0; // M4: настоящий обрыв — босс-бой окончен
toast((ov>1.15?t('snap_big'):t('snap'))+' '+trName(sp)+' '+reel.w.toFixed(1)+' '+t('kg')); // Э42: без эмодзи вида (убраны из SPECIES)
checkAchievements();
}else if(reel.d<=0){
if(reel.boss && bossPhase<3){
// M4: босс-обрыв фаз 1-2 — без штрафа (без save.snaps++), дальше фаза глубже
state='SNAP'; snapT=0.6; shake=0.5;
sfx('snap'); vib(120);
toast(tt('boss_phase',{p:bossPhase}));
}else{
catchFish(sp);
}
}else if(reel.d>=reel.d0*1.45){
// M4: босс тоже может уйти — босс-бой окончен
fishEscaped(sp);
}
}
function fishEscaped(sp){
stateBoss=false; bossPhase=0;
toast(t('escaped')+' '+trName(sp)+' '+reel.w.toFixed(1)+' '+t('kg')); // Э42: без эмодзи вида (убраны из SPECIES)
state='WAIT'; biteTimer=rand(5,10)/lure().bite;
sfx('escape');
}
function catchFish(sp){
const w=reel.w;
const boss=!!reel.boss; // M4: пойман босс (Этап 37: в каждой локации)
const variant=boss?VARIANTS[0]:rollVariant(); // M6: вариант улова (босс — всегда normal, без сложения с ×5)
const shiny=variant.id==='gold'; // M6: семантика «shiny» = gold-вариант (квест, счётчик sn)
const b=achBonus();
const mult=variant.mult*(boss?5:1); // M4: босс = ×5 к цене
const val=Math.max(1,Math.round(w*sp.price*mult*(0.95+Math.random()*0.1)*(1+b.coinPct)));
save.coins+=val; save.earned+=val;
const rec=Object.assign({n:0,best:0,cp:0,sv:0,qz:0,sn:0,em:0,dm:0,rb:0},save.caught[sp.id]||{});
const isRecord=w>rec.best;
rec.n++; rec.best=Math.max(rec.best,w);
const vk=VARIANT_KEY[variant.id]; if(vk) rec[vk]=(rec[vk]||0)+1;
save.caught[sp.id]=rec;
if(boss){ // M4: босс побеждён
save.bossBeaten=true; save.bossCount++;
if(!save.bosses||typeof save.bosses!=='object') save.bosses={}; // Этап 37: защита in-memory старого сейва
save.bosses[sp.id]=true; // Этап 37: пер-босс трекинг (ачивки lake_boss/sea_boss/ocean_boss)
toast(tt('boss_defeated',{v:val}));
}
newDayCheck();
questHit('catch_species',q=>sp.id===q.param);
questHit('catch_weight',q=>w>=q.param);
questHit('catch_rarity',q=>sp.rarity>=q.param);
questHit('catch_shiny',q=>shiny); // M6: «shiny-рыба» = gold-вариант
questHit('earn_coins',()=>true,val); // прогресс — монеты улова (с бонусами)
// Этап 22: счётчики достижений — общий вес, ночной улов, уловы в дождь/туман
save.kgTotal=(save.kgTotal||0)+w;
if(dayPhase==='night') save.nightCatch=true;
const wx=weatherActive();
if(wx==='rain'&&variant.id!=='normal') save.wxRainV=(save.wxRainV||0)+1; // босс всегда normal — не заходит
if(wx==='fog') save.wxFog=(save.wxFog||0)+1;
checkAchievements();
persist(); updateHUD();
sfx('catch'); vib([40,60,40]);
splash(castTo.x,waterY,boss?40:26,boss?180:140);
// вариант — ярче: больше и разноцветнее золотых частиц; босс — частицы крупнее
for(let i=0;i<(boss?44:(variant.id!=='normal'?28:10));i++){
particles.push({x:castTo.x,y:waterY,vx:rand(-80,80),vy:rand(-240,-80),g:300,
life:rand(0.5,0.9),max:0.9,size:boss?rand(3,7):rand(2,4),
color:(variant.id!=='normal'&&i%3===0)?'255,246,190':'255,215,94'});
}
reel=null; stateBoss=false; bossPhase=0; state='CARD';
showCard(sp,w,val,isRecord,variant,boss);
}
function updateGame(dt){
// фоновые рыбы
for(const f of ambient){
f.x+=f.dir*f.speed*dt;
if(f.dir>0&&f.x>W+40){ f.x=-40; f.depth=rand(0.12,0.92); }
if(f.dir<0&&f.x<-40){ f.x=W+40; f.depth=rand(0.12,0.92); }
}
switch(state){
case 'CHARGE':
charge+=dt;
power=1-Math.abs(1-((charge/0.9)%2));
break;
case 'CAST':{
castT+=dt/0.65;
if(castT>=1){
castT=1;
bobber={x:castTo.x,y:waterY-2};
state='WAIT';
biteTimer=rand(4,10)/lure().bite;
nightNoLightToast=false; // новый заброс — тост о фонарике может показаться снова
splash(castTo.x,waterY,14,100);
addRipple(castTo.x,waterY,40);
sfx('splash'); vib(10);
}else{
const t=castT;
bobber.x=castFrom.x+(castTo.x-castFrom.x)*t;
bobber.y=castFrom.y+(castTo.y-castFrom.y)*t-150*Math.sin(Math.PI*t);
}
break;
}
case 'WAIT':{
bobber.y=waterY-3+Math.sin(time*2)*1.5;
rippleTimer-=dt;
if(rippleTimer<=0){ addRipple(bobber.x,waterY+3,22); rippleTimer=1.6; }
if(biteFish&&biteFish.mode==='out') updateBiteFishOut(dt);
biteTimer-=dt;
if(biteTimer<=0&&!biteFish){
if(dayPhase==='night'&&!hasLight()){
// M5: ночью без фонарика клёва нет — таймер тикает вхолостую
// (фонарик — апгрейд 1 удилища: save.rodLv[save.rod]>=2)
if(!nightNoLightToast){ toast(t('night_light')); nightNoLightToast=true; }
}else if(forceBite||Math.random()<biteChance()){
startBite();
}else{
// Спринт 4: пустой клёв — поплавок молчит, попробуем ещё раз
biteTimer=rand(2.5,5)/lure().bite;
}
}
break;
}
case 'APPROACH':
updateBiteFishIn(dt);
bobber.y=waterY-3+Math.sin(time*2)*1.5+Math.sin(time*14)*1.5*Math.min(1,biteFish?biteFish.t:0); // предвестники: дрожание, сильнее по мере приближения рыбы
if(biteFish){
if(biteFish.t>=1){
state='BITE';
biteT=biteWindow(biteFish.sp);
sfx('bite'); vib(60);
addRipple(castTo.x,waterY+3,30);
}
}
break;
case 'BITE':
biteT-=dt;
bobber.y=waterY+9+Math.sin(time*22)*3; // топление: глубже и резче (синхронно с дёрганием рыбы у крючка)
if(Math.random()<0.2) addRipple(bobber.x,waterY+3,26);
if(biteT<=0){
if(biteFish) biteFish.mode='out';
state='WAIT'; biteTimer=rand(5,11)/lure().bite;
sfx('escape'); toast(t('escaped_bite'));
}
break;
case 'REEL':
updateReel(dt);
if(state==='REEL'&&reel){
const fp=fishReelPos();
let tx,ty;
if(reel.holding){
// натянуто: поплавок на пересечении прямой tip→fish с поверхностью (tip–поплавок–рыба коллинеарны, fp.y>waterY>tip.y)
const k=(waterY-tip.y)/(fp.y-tip.y);
tx=tip.x+(fp.x-tip.x)*k; ty=waterY-2;
}else{
// отпускание: провис, поплавок едет за рыбой с отставанием (покачивание на поверхности)
tx=castTo.x+(fp.x-castTo.x)*0.55; ty=waterY-2+Math.sin(time*9)*2;
}
const sm=Math.min(1,dt*8); // сглаживание, чтобы не телепортировалось
bobber.x+=(tx-bobber.x)*sm;
bobber.y+=(ty-bobber.y)*sm;
if(Math.random()<0.25) addRipple(bobber.x,waterY+3,24);
}
break;
case 'SNAP':
snapT-=dt;
if(snapT<=0){
if(stateBoss && bossPhase<3){
// M4: босс-обрыв — не штраф: автоматом следующая (глубже) фаза, без ввода
reel.d=(bossPhase===1?0.6:1.0)*reel.d0; // фаза2: d=0.6·d0, фаза3: d=1.0·d0
reel.tension=0; // фазовый обрыв сбрасывает нагрузку, иначе «беспошлинный» обрыв ведёт к настоящему
bossPhase++;
state='REEL';
}else{
state='IDLE';
}
}
break;
}
}
function updateBiteFishIn(dt){
biteFish.t+=dt/0.45;
const t=Math.min(1,biteFish.t);
const e=t*t*(3-2*t);
// рыба плывёт диагональю от поверхности вниз к крючку/наживке (hookPos — кадр прошлого, паттерн как раньше)
biteFish.x=biteFish.sx+(hookPos.x-biteFish.sx)*e;
biteFish.y=biteFish.sy+(hookPos.y-biteFish.sy)*e;
biteFish.dir=hookPos.x>biteFish.sx?1:-1;
}
function updateBiteFishOut(dt){
biteFish.t+=dt/0.6;
const dir=biteFish.x<W/2?-1:1;
biteFish.x+=dir*380*dt;
biteFish.y+=40*dt;
if(biteFish.t>=1||biteFish.x<-50||biteFish.x>W+50) biteFish=null;
}
function updateDecor(dt){
for(const c of clouds){ c.x+=c.v*dt; if(c.x>W+80) c.x=-80; }
// Этап 44: дрейф силуэтов — парусник медленно (по курсу), чайки быстрее (вправо)
for(const o of scenery){
if(o.k==='ship'){ o.x+=o.dir*o.v*dt; if(o.dir>0&&o.x>W+40) o.x=-40; if(o.dir<0&&o.x<-40) o.x=W+40; }
else if(o.k==='gull'){ o.x+=o.v*dt; if(o.x>W+30) o.x=-30; }
}
// Этап 45: Болото — покачивание кувшинок (sin, амплитуда 1–2 px) и прыжки лягушек.
// Лягушка сидит (таймер 3–7 с) → параболический прыжок (t 0..1) на соседнюю кувшинку → садится.
for(const p of pads) p.bob=Math.sin(time*1.1+p.ph)*1.6;
for(const fr of frogs){
if(fr.state==='jump'){
fr.t+=dt/fr.dur;
if(fr.t>=1){ // приземлилась
fr.x=fr.x1; fr.y=fr.y1; fr.state='sit'; fr.t=rand(3,7);
}else{
const k=fr.t;
fr.x=fr.x0+(fr.x1-fr.x0)*k;
fr.y=fr.y0+(fr.y1-fr.y0)*k-Math.sin(Math.PI*k)*fr.jh; // парабола dx/dy
}
}else{
fr.t-=dt;
if(fr.t<=0){ // выбор цели: ближайшая ДРУГАЯ кувшинка, иначе случайная точка воды
let tgt=null,best=1e9;
for(const p of pads){
const d=(p.x-fr.x)*(p.x-fr.x)+(p.y-fr.y)*(p.y-fr.y);
if(d>16&&d<best){ best=d; tgt=p; }
}
fr.x0=fr.x; fr.y0=fr.y;
if(tgt&&Math.random()<0.85){ fr.x1=tgt.x; fr.y1=tgt.y-2; }
else{
fr.x1=Math.max(20,Math.min(W-20,fr.x+rand(-90,90)));
fr.y1=Math.min(H-24,Math.max(waterY+24,fr.y+rand(-24,36)));
}
fr.dur=rand(0.4,0.7); fr.jh=rand(16,30); fr.t=0; fr.state='jump';
fr.dir=fr.x1>=fr.x0?1:-1;
}
}
}
// M8: дождь — полоски летят вниз-влево, ушедшие за нижний край возвращаются сверху
if(weatherActive()==='rain'){
for(const dr of rain){
dr.y+=dr.v*dt; dr.x-=dr.v*0.3*dt;
if(dr.y>H+20){ dr.y=rand(-120,-20); dr.x=rand(-40,W+40); }
if(dr.x<-60) dr.x=W+40;
}
}
for(let i=particles.length-1;i>=0;i--){
const p=particles[i];
p.life-=dt; p.vy+=p.g*dt; p.x+=p.vx*dt; p.y+=p.vy*dt;
if(p.life<=0||p.y>H) particles.splice(i,1);
}
for(let i=ripples.length-1;i>=0;i--){
const r=ripples[i];
r.r+=45*dt; r.a-=0.9*dt;
if(r.a<=0||r.r>r.max) ripples.splice(i,1);
}
if(shake>0) shake-=dt;
}
function syncDom(){
document.body.classList.toggle('aq-open',aqOpen); // Этап 46: под сценой банки HUD скрыт (CSS body.aq-open #hud)
if(aqOpen){ // Этап 46: под сценой банки игровые HUD-кнопки не показываем
$('btnRecall').classList.add('hidden');
$('btnPause').classList.add('hidden');
return;
}
const showRecall=(state==='WAIT'||state==='APPROACH'||state==='BITE');
$('btnRecall').classList.toggle('hidden',!showRecall);
$('btnPause').classList.toggle('hidden',!(state!=='MENU'&&!uiOpen&&!pauseOpen)); // Y6: пауза — вне меню и без оверлеев
}
/* =========================================================
ОТРИСОВКА
========================================================= */
const UI_FONT="'EmojiEmbedded',system-ui"; // Э28: для canvas-текста (emoji 💥🌀⚡💪💤) — встроенный шрифт первым, как в CSS
function rr(x,y,w,h,r){
ctx.beginPath();
ctx.moveTo(x+r,y);
ctx.arcTo(x+w,y,x+w,y+h,r);
ctx.arcTo(x+w,y+h,x,y+h,r);
ctx.arcTo(x,y+h,x,y,r);
ctx.arcTo(x,y,x+w,y,r);
ctx.closePath();
}
const SKY_PHASES={ // M3: градиенты неба по фазе суток
morning:['#f5a623','#fce38a'],
day:['#7ec8e3','#cfeef7'],
evening:['#e44d26','#f16529'],
night:['#0c1445','#1a237e']
};
/* Визуал: плавные переходы неба/освещения — зона перехода в КОНЦЕ фазы
(ночь темнеет к концу вечера, рассвет — в конце ночи; на стыке t=0 чистое утро) */
const SKY_SEQ=['morning','day','evening','night'];
const SKY_BOUNDS=[0.25,0.5,0.75,1.0]; // переход к следующей фазе завершается на границе
const SKY_ZONE=0.08; // ширина переходной зоны
function hexLerp(a,b,k){
const pa=parseInt(a.slice(1),16),pb=parseInt(b.slice(1),16);
const r=Math.round(((pa>>16)&255)+(((pb>>16)&255)-((pa>>16)&255))*k);
const g=Math.round(((pa>>8)&255)+(((pb>>8)&255)-((pa>>8)&255))*k);
const bl=Math.round((pa&255)+((pb&255)-(pa&255))*k);
return 'rgb('+r+','+g+','+bl+')';
}
function skyColors(){
const t=dayTime;
for(let i=0;i<4;i++){
const b=SKY_BOUNDS[i];
if(t<b||i===3){
const a=SKY_PHASES[SKY_SEQ[i]], c=SKY_PHASES[SKY_SEQ[(i+1)%4]];
const z0=b-SKY_ZONE;
if(t>=z0){ const k=Math.max(0,Math.min(1,(t-z0)/SKY_ZONE)); return [hexLerp(a[0],c[0],k),hexLerp(a[1],c[1],k)]; }
return [a[0],a[1]];
}
}
return [SKY_PHASES.morning[0],SKY_PHASES.morning[1]];
}
/* Числовые величины (ночная доля и т.п.) с теми же переходами */
function phaseLerp(vals){
const t=dayTime;
for(let i=0;i<4;i++){
const b=SKY_BOUNDS[i];
if(t<b||i===3){
const z0=b-SKY_ZONE;
if(t>=z0){ const k=Math.max(0,Math.min(1,(t-z0)/SKY_ZONE)); return vals[i]+(vals[(i+1)%4]-vals[i])*k; }
return vals[i];
}
}
return vals[0];
}
/* Ночная доля освещения 0..1 (1 = глубокая ночь) — вода, звёзды, фонарь */
function nightMix(){ return phaseLerp([0,0,0.4,1]); }
let luminary={x:0,y:0,a:0,night:false}; // визуал: где солнце/луна и её яркость (для дорожки на воде)
function drawSky(){
const cols=skyColors();
const nm=nightMix();
const g=ctx.createLinearGradient(0,0,0,waterY);
g.addColorStop(0,cols[0]); g.addColorStop(1,cols[1]);
ctx.fillStyle=g; ctx.fillRect(0,0,W,waterY);
// звёзды — проступают с ночью, мерцают
if(nm>0.15){
for(const st of stars){
const a=nm*Math.max(0,Math.sin(time*st.sp+st.ph))*0.9;
if(a>0.03){
ctx.fillStyle='rgba(255,255,255,'+a.toFixed(3)+')';
ctx.beginPath(); ctx.arc(st.x,st.y,st.r,0,7); ctx.fill();
}
}
}
// M3: солнце/луна по дуге (визуал: мягкое свечение, гаснут/всходят в сумерках без наложения)
const ang=dayTime*2*Math.PI;
const bx=W/2+Math.cos(ang)*W*0.4, by=H*0.15-Math.sin(ang)*H*0.1;
luminary.x=bx; luminary.y=by; luminary.a=0; luminary.night=false;
if(nm<0.45){
const a=1-nm/0.45; // солнце садится к ночи
luminary.a=a;
ctx.globalAlpha=Math.max(0,a);
const glow=ctx.createRadialGradient(bx,by,6,bx,by,64);
glow.addColorStop(0,'rgba(255,225,130,0.5)'); glow.addColorStop(1,'rgba(255,225,130,0)');
ctx.fillStyle=glow; ctx.fillRect(bx-64,by-64,128,128);
ctx.fillStyle='#ffd700';
ctx.beginPath(); ctx.arc(bx,by,20,0,7); ctx.fill();
ctx.fillStyle='rgba(255,255,255,0.5)';
ctx.beginPath(); ctx.arc(bx-6,by-6,7,0,7); ctx.fill();
ctx.globalAlpha=1;
}else if(nm>0.55){
const a=(nm-0.55)/0.45; // луна всходит в глубине сумерек
luminary.a=a; luminary.night=true;
ctx.globalAlpha=Math.max(0,Math.min(1,a));
const glow=ctx.createRadialGradient(bx,by,8,bx,by,56);
glow.addColorStop(0,'rgba(220,230,255,0.32)'); glow.addColorStop(1,'rgba(220,230,255,0)');
ctx.fillStyle=glow; ctx.fillRect(bx-56,by-56,112,112);
ctx.fillStyle='#f5f5dc';
ctx.beginPath(); ctx.arc(bx,by,18,0,7); ctx.fill();
// кратеры
ctx.fillStyle='rgba(120,130,150,0.35)';
ctx.beginPath(); ctx.arc(bx+6,by+3,3.2,0,7); ctx.fill();
ctx.beginPath(); ctx.arc(bx-4,by+7,2.2,0,7); ctx.fill();
ctx.beginPath(); ctx.arc(bx-2,by-6,2.6,0,7); ctx.fill();
// серп — вырез цветом неба в точке луны
ctx.fillStyle=cols[0];
ctx.beginPath(); ctx.arc(bx+9,by-6,13.5,0,7); ctx.fill();
ctx.globalAlpha=1;
}
// облака (ночью приглушены)
ctx.fillStyle='rgba(255,255,255,'+(0.9-0.68*nm).toFixed(3)+')';
for(const c of clouds){
ctx.save(); ctx.translate(c.x,c.y); ctx.scale(c.s,c.s);
ctx.beginPath();
ctx.arc(0,0,14,0,7); ctx.arc(16,-6,12,0,7); ctx.arc(32,0,15,0,7); ctx.arc(16,6,13,0,7);
ctx.fill(); ctx.restore();
}
}
/* Этап 44: береговые силуэты по локации (см. initScenery). Ночь — силуэты гаснут
к тёмно-синему (hexLerp к #0b1220), парусник/чайки темнеют. Рисуется ПЕРЕД водой:
то, что проваливается под линию воды (низ холмов/киль), перекрывается градиентом */
function drawScenery(){
if(!scenery.length) return;
const nm=nightMix();
const dusk=c=>hexLerp(c,'#0b1220',nm*0.8);
const y0=waterY+1; // базовая линия — чуть под кромкой, чтобы «сесть» на воду
for(const o of scenery){
if(o.k==='bank'){
// Река: береговая полоса на всю ширину (трава + тёмная влажная кромка у воды);
// рисуется ПЕРВОЙ (в scenery стоит первой) — лес сажается на её верхнюю кромку.
// Этап 45: у Болота — своя мшистая гамма (c/c2), у остальных локаций цвета те же
ctx.fillStyle=dusk(o.c||'#4e7a4e');
ctx.fillRect(0,waterY-o.h,W,o.h+3);
ctx.fillStyle=dusk(o.c2||'#39583c');
ctx.fillRect(0,waterY-3,W,6);
}else if(o.k==='pine'){
// сосна: ствол + 3 яруса (дальний ряд — светлее и выше, ближний — темнее);
// база ствола — o.by (у реки — верх кромки берега), по умолчанию линия воды;
// Этап 45: цвет o.c — болотные туи темнее речных сосен
const pb=o.by==null?y0:o.by;
ctx.fillStyle=dusk(o.c||(o.layer?'#24452f':'#3a6a4a'));
ctx.fillRect(o.x-1.2,pb-3,2.4,4);
for(let t=0;t<3;t++){
const by=pb-3-t*o.h*0.26, hw=o.h*0.3*(1-0.3*t), th=o.h*0.42;
ctx.beginPath(); ctx.moveTo(o.x-hw,by); ctx.lineTo(o.x+hw,by); ctx.lineTo(o.x,by-th);
ctx.closePath(); ctx.fill();
}
}else if(o.k==='hill'){
// холм: дуга эллипса над линией воды (низ закроет вода)
ctx.fillStyle=dusk('#4a7a5e');
ctx.beginPath(); ctx.ellipse(o.x,y0+4,o.rx,o.ry,0,Math.PI,0); ctx.fill();
}else if(o.k==='tree'){
// лиственное дерево: ствол + крона из трёх кругов (o.c — опциональный цвет)
ctx.fillStyle=dusk(o.c||'#3c6b4f');
ctx.fillRect(o.x-1,y0-o.h*0.5,2,o.h*0.5+1);
ctx.beginPath();
ctx.arc(o.x,y0-o.h,o.h*0.42,0,7);
ctx.arc(o.x-o.h*0.3,y0-o.h*0.78,o.h*0.34,0,7);
ctx.arc(o.x+o.h*0.3,y0-o.h*0.78,o.h*0.34,0,7);
ctx.fill();
}else if(o.k==='reed'){
// Этап 45: камыш — пучок тонких стеблей с метёлками, качается на ветру (болотная гамма)
const sway=Math.sin(time*0.9+o.ph)*o.lean;
for(let t=0;t<4;t++){
const off=(t-1.5)*2.2;
const h=o.h*(0.72+0.07*t);
const tx=o.x+off+sway*(0.6+t*0.14), ty=y0-h;
ctx.strokeStyle=dusk('#3f5a2e'); ctx.lineWidth=1.2;
ctx.beginPath(); ctx.moveTo(o.x+off,y0);
ctx.quadraticCurveTo(o.x+off+sway*0.4,y0-h*0.6,tx,ty); ctx.stroke();
ctx.fillStyle=dusk('#7a6a35'); // метёлка
ctx.beginPath(); ctx.ellipse(tx,ty,1.7,4.5,sway*0.06,0,7); ctx.fill();
}
}else if(o.k==='snag'){
// Этап 45: кривое болотное дерево — наклонный ствол + пара голых веток
const ln=o.lean||0;
ctx.save(); // не оставляем lineCap/lineWidth остальным слоям кадра
ctx.strokeStyle=dusk('#5a5340'); ctx.lineWidth=2.4; ctx.lineCap='round';
ctx.beginPath(); ctx.moveTo(o.x,y0);
ctx.quadraticCurveTo(o.x+ln*o.h*0.5,y0-o.h*0.55,o.x+ln*o.h,y0-o.h); ctx.stroke();
ctx.lineWidth=1.6;
ctx.beginPath(); ctx.moveTo(o.x+ln*o.h*0.25,y0-o.h*0.5);
ctx.lineTo(o.x+ln*o.h*0.25-o.h*0.3,y0-o.h*0.62); ctx.stroke();
ctx.beginPath(); ctx.moveTo(o.x+ln*o.h*0.4,y0-o.h*0.72);
ctx.lineTo(o.x+ln*o.h*0.4+o.h*0.26,y0-o.h*0.8); ctx.stroke();
ctx.restore();
}else if(o.k==='ship'){
// парусник: киль/мачта/два паруса (парус светлее силуэта — читается на небе)
ctx.save(); ctx.translate(o.x,0); ctx.scale(o.dir,1);
const s=o.s;
ctx.fillStyle=dusk('#3a4f63');
ctx.beginPath(); ctx.moveTo(-14*s,y0-4*s); ctx.lineTo(14*s,y0-4*s); ctx.lineTo(9*s,y0+1); ctx.lineTo(-9*s,y0+1);
ctx.closePath(); ctx.fill();
ctx.strokeStyle=dusk('#3a4f63'); ctx.lineWidth=1.6;
ctx.beginPath(); ctx.moveTo(0,y0-4*s); ctx.lineTo(0,y0-22*s); ctx.stroke();
ctx.fillStyle='rgba(235,242,250,'+(0.9-0.45*nm).toFixed(3)+')';
ctx.beginPath(); ctx.moveTo(1.5,y0-4.5*s); ctx.lineTo(1.5,y0-20*s); ctx.lineTo(12*s,y0-5.5*s);
ctx.closePath(); ctx.fill();
ctx.beginPath(); ctx.moveTo(-1.5,y0-4.5*s); ctx.lineTo(-1.5,y0-16*s); ctx.lineTo(-10*s,y0-5.5*s);
ctx.closePath(); ctx.fill();
ctx.restore();
}else if(o.k==='gull'){
// чайка: две дуги крыла, взмах синусом; ночью почти гаснет
const a=0.65-0.4*nm;
if(a<0.12) continue;
const y=y0-o.y+Math.sin(time*1.3+o.ph)*3;
const f=Math.sin(time*7+o.ph)*0.5+0.5;
ctx.strokeStyle='rgba(35,45,60,'+a.toFixed(3)+')';
ctx.lineWidth=1.4;
ctx.beginPath();
ctx.moveTo(o.x-6,y);
ctx.quadraticCurveTo(o.x-3,y-3-3*f,o.x,y);
ctx.quadraticCurveTo(o.x+3,y-3-3*f,o.x+6,y);
ctx.stroke();
}
}
}
function drawWater(){
const nm=nightMix();
// градиент воды: день ↔ ночь, плавно (визуал); Этап 5: базовые цвета по локации (LOCATIONS)
const loc=LOCATIONS[Math.max(0,Math.min(LOCATIONS.length-1,save.location|0))]; // clamp — миграция гарантирует диапазон
const w=loc.water;
const top=hexLerp(w[0],w[3],nm), bot=hexLerp(w[2],w[5],nm);
const g=ctx.createLinearGradient(0,waterY,0,H);
g.addColorStop(0,top); g.addColorStop(0.55,hexLerp(w[1],w[4],nm)); g.addColorStop(1,bot);
ctx.fillStyle=g; ctx.fillRect(0,waterY,W,H-waterY);
// дорожка света от солнца/луны — мерцающие блики под светилом
if(luminary.a>0.05){
const depth=Math.min(1,Math.max(0,(luminary.y)/(waterY||1))); // светило низко — дорожка длиннее
const len=(H-waterY)*(0.18+0.3*depth);
const base=(luminary.night?0.12:0.2)*luminary.a;
ctx.fillStyle=luminary.night?'rgba(200,220,255,1)':'rgba(255,235,170,1)';
for(let y=waterY+6;y<waterY+len;y+=7){
const k=(y-waterY)/len;
const wob=Math.sin(time*2.2+y*0.55);
const a=Math.max(0,base*(1-k)*(0.45+0.55*wob));
if(a<0.02) continue;
ctx.globalAlpha=a;
const wdt=14+k*30+Math.sin(time*1.7+y)*4;
ctx.fillRect(luminary.x-wdt/2,y,wdt,2);
}
ctx.globalAlpha=1;
}
// блики
for(const s of sparkles){
const a=Math.max(0,Math.sin(time*1.5+s.ph))*0.25*(1-nm*0.5);
if(a>0.02){ ctx.fillStyle='rgba(255,255,255,'+a.toFixed(3)+')'; ctx.fillRect(s.x,s.y,3,1.5); }
}
// волна на поверхности + вторая, глубинная (визуал); Этап 43: характер по локации (LOCATIONS.waves);
// ревизия Этапа 44: amp/amp2=0 — линия не рисуется (Река — спокойная вода без волн)
const wv=loc.waves;
if(wv.amp>0){
ctx.strokeStyle='rgba(255,255,255,'+(0.55-nm*0.25).toFixed(3)+')'; ctx.lineWidth=2;
ctx.beginPath();
for(let x=0;x<=W;x+=8){
const y=waterY+Math.sin(x*wv.freq+time*wv.speed)*wv.amp;
if(x===0) ctx.moveTo(x,y); else ctx.lineTo(x,y);
}
ctx.stroke();
}
if(wv.amp2>0){
ctx.strokeStyle='rgba(255,255,255,'+(0.2-nm*0.1).toFixed(3)+')'; ctx.lineWidth=1.5;
ctx.beginPath();
for(let x=0;x<=W;x+=10){
const y=waterY+7+Math.sin(x*wv.freq2-time*wv.speed2+1.7)*wv.amp2;
if(x===0) ctx.moveTo(x,y); else ctx.lineTo(x,y);
}
ctx.stroke();
}
}
/* Фаза свечения «Волшебного болота»: аура-свечение (радиальный градиент + 'lighter').
rgba-строки цветов кэшируются по hex — строки не собираются каждый кадр (мобильные аллокации).
Профиль градиента крутой (свет собран у тела рыбы), интенсивность масштабирует альфу через
globalAlpha. Аура рисуется ПОЗАДИ силуэта, поэтому контур рыбы остаётся чётким. */
const GLOW_RGBA={}; // hex -> {in:'rgba(...,0.55)',mid:'rgba(...,0.30)',out:'rgba(...,0)'}
function glowColors(hex){
let c=GLOW_RGBA[hex];
if(!c){
const h=hex.replace('#','');
const r=parseInt(h.substr(0,2),16),g=parseInt(h.substr(2,2),16),b=parseInt(h.substr(4,2),16);
c=GLOW_RGBA[hex]={
in:'rgba('+r+','+g+','+b+',0.55)',
mid:'rgba('+r+','+g+','+b+',0.30)',
out:'rgba('+r+','+g+','+b+',0)'
};
}
return c;
}
/* Плавный ramp ночного свечения: 0 при nightMix()<=0.35 (тот же старт, что у фонаря лодки),
затем smoothstep до 1 при nightMix()>=0.9 — вечером разгорается, к утру гаснет. */
function glowIntensity(){
const nm=nightMix();
if(nm<=0.35) return 0;
const k=Math.min(1,(nm-0.35)/0.55);
return k*k*(3-2*k); // smoothstep 0..1
}
// I — интенсивность 0..1 (glowIntensity): множитель на альфу ВСЕХ стопов профиля (globalAlpha)
function drawGlowAura(x,y,r,hex,I){
if(I<=0.02) return; // почти не видно — не создаём градиент/заливку
const c=glowColors(hex);
ctx.save(); // изолируем 'lighter', globalAlpha и fillStyle — не ломаем остальную отрисовку
ctx.globalCompositeOperation='lighter';
ctx.globalAlpha=I;
const g=ctx.createRadialGradient(x,y,r*0.12,x,y,r);
g.addColorStop(0,c.in); g.addColorStop(0.45,c.mid); g.addColorStop(1,c.out);
ctx.fillStyle=g; ctx.fillRect(x-r,y-r,r*2,r*2);
ctx.restore();
}
// Аура светящегося вида: интенсивность плавно растёт с ночью (glowIntensity), утром гаснет
function fishGlow(sp,x,y,r){
if(sp&&sp.glow) drawGlowAura(x,y,r,sp.glow,glowIntensity());
}
function drawFishShape(x,y,size,dir,hue,alpha,wob){
ctx.save(); ctx.translate(x,y); ctx.scale(dir,1);
const w=wob||0;
ctx.fillStyle='hsla('+hue+',55%,38%,'+alpha+')';
ctx.beginPath(); ctx.ellipse(0,0,size,size*0.45,0,0,7); ctx.fill();
// хвост качается (визуал)
ctx.save(); ctx.translate(-size*0.8,0); ctx.rotate(w);
ctx.beginPath();
ctx.moveTo(0,0); ctx.lineTo(-size*0.65,-size*0.42); ctx.lineTo(-size*0.65,size*0.42);
ctx.closePath(); ctx.fill();
ctx.restore();
// блик на спинке + глаз (визуал: живость, у силуэтов едва видны)
ctx.fillStyle='rgba(255,255,255,'+(alpha*0.22).toFixed(3)+')';
ctx.beginPath(); ctx.ellipse(-size*0.1,-size*0.16,size*0.55,size*0.18,-0.25,0,7); ctx.fill();
ctx.fillStyle='rgba(10,25,40,'+(alpha*0.8).toFixed(3)+')';
ctx.beginPath(); ctx.arc(size*0.6,-size*0.07,size*0.09,0,7); ctx.fill();
ctx.restore();
}
function drawAmbientFish(){
const glowI=save.location===4?glowIntensity():0; // свечение фоновых рыб — только Болото, плавный ramp
for(const f of ambient){
const y=waterY+20+f.depth*(H-waterY-50)+Math.sin(time*2+f.ph)*2;
let a=0.22+0.2*(1-f.depth);
if(f.glow){ // болотная рыба: компактный ореол + непрозрачное тело к ночи — контур чёткий
if(glowI>0.02) drawGlowAura(f.x,y,f.size*1.5,f.glow,glowI); // цвет каждой рыбы из palette
a=a+(1-a)*glowI; // к глубокой ночи силуэт становится полностью непрозрачным
}
drawFishShape(f.x,y,f.size,f.dir,f.hue,a,Math.sin(time*5+f.ph)*0.28);
}
}
/* Этап 45: декор «Волшебного болота» — кувшинки с лилиями и прыгающие лягушки.
Отдельный слой ПОВЕРХ воды (вызов в draw() после drawWater/drawAmbientFish, до drawBoat):
рисуется после воды, поэтому dusk() берега его не гасит. К ночи декор лишь притухает
(hexLerp к тёмно-зелёному), днём — живой, а ночное свечение рыб (B2) остаётся видимым.
Фигуры простые (arc/ellipse/lineTo) без shadowBlur и градиентов на кадр. */
function drawSwampDecor(){
if(!pads.length&&!frogs.length) return;
const nm=nightMix();
const dusk=c=>hexLerp(c,'#0b1a12',nm*0.55);
// --- кувшинки (+ лилии на части из них) ---
for(const p of pads){
const x=p.x, y=p.y+p.bob;
// кувшинка: эллипс с радиальным вырезом-прорезью (Pac-Man-образный клин к центру)
ctx.fillStyle=dusk('#2f6b3a');
ctx.strokeStyle=dusk('#1d4527'); ctx.lineWidth=1;
ctx.beginPath();
ctx.moveTo(x,y);
ctx.arc(x,y,p.r,p.notch+0.24,p.notch-0.24+Math.PI*2);
ctx.closePath(); ctx.fill(); ctx.stroke();
// блик на листе (визуал)
ctx.fillStyle=dusk('#3f7f4a');
ctx.beginPath(); ctx.ellipse(x-p.r*0.28,y-p.r*0.22,p.r*0.42,p.r*0.2,-0.3,0,7); ctx.fill();
// лилия: 6 лепестков-эллипсов одного из трёх цветов (жёлтый/белый/розовый)
if(p.lily){
const lx=x+p.r*0.28, ly=y-p.r*0.26;
ctx.fillStyle=dusk(p.lilyColor);
for(let k=0;k<6;k++){
const a=k/6*Math.PI*2+p.ph*0.2;
ctx.beginPath();
ctx.ellipse(lx+Math.cos(a)*3.2,ly+Math.sin(a)*2.4,3.4,1.9,a,0,7);
ctx.fill();
}
}
}
// --- лягушки (тело + задние лапы, тёмно-зелёные; прыгают по параболе из updateDecor) ---
for(const fr of frogs){
const s=fr.s, dir=fr.dir||1;
ctx.save(); ctx.translate(fr.x,fr.y); ctx.scale(dir,1);
ctx.fillStyle=dusk('#2f5a2a');
// задние лапы
ctx.beginPath(); ctx.ellipse(-s*0.55,s*0.55,s*0.72,s*0.34,-0.4,0,7); ctx.fill();
ctx.beginPath(); ctx.ellipse(s*0.1,s*0.72,s*0.78,s*0.3,0.35,0,7); ctx.fill();
// тело
ctx.beginPath(); ctx.ellipse(0,0,s,s*0.72,0,0,7); ctx.fill();
// голова + глаз
ctx.fillStyle=dusk('#3f7a38');
ctx.beginPath(); ctx.ellipse(s*0.68,-s*0.28,s*0.46,s*0.4,0,0,7); ctx.fill();
ctx.fillStyle='rgba(10,25,10,0.9)';
ctx.beginPath(); ctx.arc(s*0.84,-s*0.4,s*0.13,0,7); ctx.fill();
// передние лапы
ctx.fillStyle=dusk('#2f5a2a');
ctx.beginPath(); ctx.ellipse(s*0.5,s*0.5,s*0.5,s*0.2,0.5,0,7); ctx.fill();
ctx.restore();
}
}
function drawBoat(){
const bx=boatX, by=waterY+Math.sin(time*1.5)*1.5;
// блик-отражение под лодкой (визуал)
ctx.fillStyle='rgba(0,20,40,0.28)';
ctx.beginPath(); ctx.ellipse(bx,by+17,34,4.5,0,0,7); ctx.fill();
// лодка (Этап 4: скины — градиент корпуса и кромка по save.boatSkin)
const skin=Math.max(0,Math.min(2,save.boatSkin|0));
const bcols=[['#c9773c','#96521f','#8a4519'],['#c0392b','#7b241c','#6e221a'],['#5d6d7e','#34495e','#2c3a47']][skin];
const bg=ctx.createLinearGradient(bx-32,by,bx+32,by+15);
bg.addColorStop(0,bcols[0]); bg.addColorStop(1,bcols[1]);
ctx.fillStyle=bg;
ctx.beginPath();
ctx.moveTo(bx-32,by); ctx.lineTo(bx+32,by); ctx.lineTo(bx+21,by+15); ctx.lineTo(bx-21,by+15);
ctx.closePath(); ctx.fill();
ctx.fillStyle=bcols[2]; ctx.fillRect(bx-32,by,64,4);
// «Лодка-домик»: каюта на корме корпуса (над линией борта) с ярко-жёлтым окном (визуал)
if(skin===2){
ctx.fillStyle='#2c3a47';
ctx.fillRect(bx-31,by-11,16,11);
ctx.fillStyle='rgba(255,220,100,0.9)';
ctx.fillRect(bx-28,by-9,9,5);
}
// рыбак
ctx.fillStyle='#34495e';
ctx.fillRect(bx-9,by-18,12,12);
ctx.fillStyle='#e8b88a';
ctx.beginPath(); ctx.arc(bx-3,by-24,6.5,0,7); ctx.fill();
ctx.fillStyle='#c0392b';
ctx.beginPath(); ctx.arc(bx-3,by-27,6.5,Math.PI,0); ctx.fill();
// удочка
const swing=state==='CHARGE'?-power*0.35:0;
ctx.strokeStyle='#5d4023'; ctx.lineWidth=3; ctx.lineCap='round';
ctx.beginPath(); ctx.moveTo(bx+2,by-14);
const tx=bx+34+swing*20, ty=by-38-swing*14;
ctx.lineTo(tx,ty); ctx.stroke();
tip={x:tx,y:ty};
// флажок
ctx.strokeStyle='#5d4023'; ctx.lineWidth=2;
ctx.beginPath(); ctx.moveTo(bx-24,by); ctx.lineTo(bx-24,by-22); ctx.stroke();
ctx.fillStyle='#f5a623';
const fl=Math.sin(time*4)*1.5; // флажок колышется (визуал)
ctx.beginPath(); ctx.moveTo(bx-24,by-22); ctx.lineTo(bx-12,by-18+fl); ctx.lineTo(bx-24,by-14); ctx.closePath(); ctx.fill();
// фонарь на носу ночью (визуал: тёплое пятно света)
if(nightMix()>0.35){
const a=(nightMix()-0.35)/0.65;
const lx=bx-24,ly=by-20;
const lg=ctx.createRadialGradient(lx,ly,2,lx,ly,46);
lg.addColorStop(0,'rgba(255,214,120,'+(0.4*a).toFixed(3)+')');
lg.addColorStop(1,'rgba(255,214,120,0)');
ctx.fillStyle=lg; ctx.fillRect(lx-46,ly-46,92,92);
ctx.fillStyle='rgba(255,224,150,'+(0.9*a).toFixed(3)+')';
ctx.beginPath(); ctx.arc(lx,ly,2.5,0,7); ctx.fill();
}
}
function drawLineTo(x,y,sag){
ctx.strokeStyle='rgba(230,240,255,0.8)'; ctx.lineWidth=1.2;
ctx.beginPath(); ctx.moveTo(tip.x,tip.y);
ctx.quadraticCurveTo((tip.x+x)/2,(tip.y+y)/2+sag,x,y);
ctx.stroke();
}
function drawBobber(sink){
const x=bobber.x,y=bobber.y;
// тёмная кромка воды под поплавком (визуал)
ctx.fillStyle='rgba(0,20,40,0.25)';
ctx.beginPath(); ctx.ellipse(x,y+6,7,1.8,0,0,7); ctx.fill();
// Этап 4: скины — поплавок по save.bobberSkin (0 обычный, 1 красный, 2 неоновый)
const skin=Math.max(0,Math.min(2,save.bobberSkin|0));
const bot=['#fff','#e74c3c','#aaff00'][skin];
const top=['#e74c3c','#e74c3c','#aaff00'][skin];
if(skin===2){ ctx.save(); ctx.shadowColor='rgba(170,255,0,0.8)'; ctx.shadowBlur=10; } // неоновое свечение — обязательно save/restore, чтобы не перетечь на другие элементы
ctx.fillStyle=bot;
ctx.beginPath(); ctx.arc(x,y,5.5,0,7); ctx.fill();
ctx.fillStyle=top;
ctx.beginPath(); ctx.arc(x,y,5.5,Math.PI,0); ctx.fill();
// блик (визуал)
ctx.fillStyle='rgba(255,255,255,0.85)';
ctx.beginPath(); ctx.arc(x-1.8,y-1.8,1.4,0,7); ctx.fill();
ctx.strokeStyle=skin===2?'#77cc00':'rgba(255,255,255,0.9)'; ctx.lineWidth=1.5;
ctx.beginPath(); ctx.arc(x,y,5.5,0,7); ctx.stroke();
if(skin===2) ctx.restore();
// топление (визуал, lane 2): в BITE нижняя половина уходит под воду — полупрозрачная кромка воды поверх низа поплавка
// рисуем ВНЕ save/restore неона, чтобы свечение не перетекло на слой воды
if(sink){
ctx.fillStyle='rgba(10,30,60,0.35)';
ctx.beginPath(); ctx.arc(x,y,5.6,0,Math.PI); ctx.closePath(); ctx.fill(); // нижняя половина под водой
ctx.strokeStyle='rgba(150,200,235,0.55)'; ctx.lineWidth=1; // тонкая светлая линия уровня воды
ctx.beginPath(); ctx.moveTo(x-5.6,y); ctx.lineTo(x+5.6,y); ctx.stroke();
}
}
function hookY(){
// глубина крючка на середине воды: доля глубины 0.42..0.54 по дальности заброса (визуал)
return waterY+(H-waterY)*(0.42+castDist*0.12);
}
function drawRig(){
// Живой оснастки (lane 1): поводок от поплавка вниз, крючок и наживка.
// Только визуал — механика/баланс не трогается. hookPos экспортируем для следующих шагов.
const hy=hookY(); // глубина крючка (середина воды, доля 0.42..0.54 по дальности)
const hx=bobber.x+Math.sin(time*2.2)*3; // лёгкое покачивание в противофазе к поплавку
hookPos={x:hx,y:hy};
// вертикальный бледный поводок (бледнее надводной лески 0.8 → 0.35)
ctx.strokeStyle='rgba(230,240,255,0.35)'; ctx.lineWidth=1;
ctx.beginPath(); ctx.moveTo(bobber.x,bobber.y); ctx.lineTo(hx,hy); ctx.stroke();
// крючок: дуга + жало (серый ~5px)
ctx.strokeStyle='#cfd6dd'; ctx.lineWidth=1.4; ctx.lineCap='round';
ctx.beginPath(); ctx.arc(hx,hy+4,4.5,Math.PI*0.1,Math.PI*1.6); ctx.stroke();
const eb=Math.PI*1.6, ex=hx+4.5*Math.cos(eb), ey=(hy+4)+4.5*Math.sin(eb);
ctx.beginPath(); ctx.moveTo(ex,ey); ctx.lineTo(ex+2.5,ey-2); ctx.stroke(); // жало
}
function drawLineAndBobber(){
// след поплавка: копим в забросе, чистим в остальных состояниях (визуал)
if(state==='CAST'){ bobberTrail.push({x:bobber.x,y:bobber.y}); if(bobberTrail.length>14) bobberTrail.shift(); }
else if(bobberTrail.length) bobberTrail.length=0;
if(state==='IDLE'||state==='CHARGE'||state==='MENU'||state==='CARD'){
// болтается грузило
const a=state==='CHARGE'?Math.sin(time*10)*power*4:Math.sin(time*1.2)*1.5;
ctx.strokeStyle='rgba(230,240,255,0.8)'; ctx.lineWidth=1.2;
ctx.beginPath(); ctx.moveTo(tip.x,tip.y); ctx.lineTo(tip.x+a,tip.y+22); ctx.stroke();
ctx.fillStyle='#e74c3c';
ctx.beginPath(); ctx.arc(tip.x+a,tip.y+25,3.5,0,7); ctx.fill();
return;
}
if(state==='SNAP'){
ctx.strokeStyle='rgba(230,240,255,0.8)'; ctx.lineWidth=1.2;
ctx.beginPath(); ctx.moveTo(tip.x,tip.y);
ctx.lineTo(tip.x+10+Math.sin(time*20)*6,tip.y+26); ctx.stroke();
ctx.globalAlpha=Math.min(1,snapT*2);
ctx.shadowColor='rgba(255,120,60,0.9)'; ctx.shadowBlur=14; // вспышка (визуал)
ctx.font='34px '+UI_FONT; ctx.textAlign='center';
ctx.fillText('💥',castTo.x,waterY-20);
ctx.shadowBlur=0;
ctx.globalAlpha=1;
return;
}
if(state==='CAST'){
// след: затухающие точки по траектории (визуал)
for(let i=0;i<bobberTrail.length;i++){
const tp=bobberTrail[i];
ctx.fillStyle='rgba(255,255,255,'+(i/bobberTrail.length*0.35).toFixed(3)+')';
ctx.beginPath(); ctx.arc(tp.x,tp.y,1.6,0,7); ctx.fill();
}
drawLineTo(bobber.x,bobber.y,14); drawBobber(); return;
}
let sag;
if(state==='REEL'){
sag=reel.holding?0:(reel.phase==='pull'?16:12); // holding — натянуто (прямая tip→поплавок); отпускание — провис (рывок больше)
}else{
sag=18+Math.sin(time*2)*3;
}
drawLineTo(bobber.x,bobber.y,sag);
drawBobber(state==='BITE'?1:0); // топление: в BITE нижняя половина под водой, в WAIT/APPROACH/REEL — нет
if(state==='REEL'&&reel){ // подводный сегмент лески bobber→рыба (бледнее надводной), чтобы леска шла к рыбе
const fp=fishReelPos();
ctx.strokeStyle='rgba(230,240,255,0.35)'; ctx.lineWidth=1;
ctx.beginPath(); ctx.moveTo(bobber.x,bobber.y); ctx.lineTo(fp.x,fp.y); ctx.stroke();
}
if(state==='WAIT'||state==='APPROACH'||state==='BITE') drawRig(); // живая оснастка (в REEL крючок «в рыбе» — не рисуем)
if(state==='BITE'){
const yy=bobber.y-24+Math.sin(time*12)*2;
ctx.font='800 28px '+UI_FONT; ctx.textAlign='center';
ctx.lineWidth=4; ctx.strokeStyle='#fff';
ctx.strokeText('!',bobber.x,yy);
ctx.fillStyle='#ff3b30';
ctx.fillText('!',bobber.x,yy);
}
}
function fishReelPos(){
// lane 3: позиция пойманной рыбы в REEL по прогрессу вываживания — по диагонали от глубины крючка (середина воды) к лодке у поверхности (d0>0 гарантировано)
const p=Math.max(0,Math.min(1,1-reel.d/reel.d0));
const pe=p*p*(3-2*p); // лёгкое ease — плавный подход к лодке
const k=reel.tired?0.35:1; // уставшая рыба вялая — амплитуда покачивания ниже
const hy=hookY(); // старт пути — точка крючка (середина воды)
const x=castTo.x+(boatX+24-castTo.x)*pe + Math.sin(time*7*k)*4*k; // сохраняем покачивание
const y=hy+(waterY+10-hy)*pe + Math.sin(time*5*k)*3*k; // диагональ: из глубины к лодке у поверхности
return {x:x,y:y};
}
/* Размер силуэта пойманной рыбы — от АБСОЛЮТНОГО веса (кг), не от wMin/wMax вида.
УЗЛОВАЯ ТАБЛИЦА (длина силуэта = 2.45·fs: тело 2·fs + хвост), узлы по степеням двойки:
0.1 кг → 12 px · 0.2 → 16 · 0.4 → 20 · 0.8 → 24 · 1.6 → 36 · 3.2 → 48 ·
6.4 → 64 (= лодка) · 12.8 → 80 · 25.6 → 96 · 51.2 → 160 · 100 → 256 px = 4× лодки 64 px.
Интерполяция линейна по log2(веса) — узлы степени двойки, кривая монотонна, узлы бьёт точно.
Веса <0.1 кг клампятся в 12 px, >100 кг — в 256 px. */
const FISH_SIZE_KNOTS=[[0,12],[1,16],[2,20],[3,24],[4,36],[5,48],[6,64],[7,80],[8,96],[9,160],[Math.log2(1000),256]]; // [log2(w/0.1), длина px]
function fishSizeFromW(w){
const t=Math.log2(Math.max(0.1,Math.min(100,w))/0.1);
for(let i=1;i<FISH_SIZE_KNOTS.length;i++){
const t0=FISH_SIZE_KNOTS[i-1][0],l0=FISH_SIZE_KNOTS[i-1][1],t1=FISH_SIZE_KNOTS[i][0],l1=FISH_SIZE_KNOTS[i][1];
if(t<=t1) return (l0+(l1-l0)*(t-t0)/(t1-t0))/2.45;
}
return 256/2.45;
}
function drawHookedFish(){
if(state!=='REEL'||!reel) return;
const sp=reel.sp;
const k=reel.tired?0.35:1; // Спринт 4: уставшая рыба вялая — медленнее и амплитуда ниже
const fp=fishReelPos();
const x=fp.x, y=fp.y;
const dir=Math.sin(time*2.5*k)>0?1:-1;
const fs=fishSizeFromW(reel.w); // размер = абсолютный вес (редкость и босс-×1.5 убраны: крупнее вес → крупнее рыба)
if(reel.boss){ // босс: золотая аура (визуал)
const gg=ctx.createRadialGradient(x,y,4,x,y,fs*2.6);
gg.addColorStop(0,'rgba(255,215,94,0.35)'); gg.addColorStop(1,'rgba(255,215,94,0)');
ctx.fillStyle=gg; ctx.fillRect(x-fs*2.6,y-fs*2.6,fs*5.2,fs*5.2);
}
fishGlow(sp,x,y,fs*1.5); // Фаза свечения: компактный ореол вида ночью (в save/restore — не ломает боссовую)
drawFishShape(x,y,fs,dir,sp.hue,sp.glow?1:0.75,Math.sin(time*9*k)*0.3*k); // светящийся вид — непрозрачный силуэт (крупнее у тяжёлых)
}
function drawParticles(){
for(const p of particles){
ctx.fillStyle='rgba('+p.color+','+(Math.max(0,p.life/p.max)).toFixed(3)+')';
ctx.beginPath(); ctx.arc(p.x,p.y,p.size,0,7); ctx.fill();
}
}
function drawRipples(){
for(const r of ripples){
ctx.strokeStyle='rgba(255,255,255,'+Math.max(0,r.a).toFixed(3)+')';
ctx.lineWidth=1.5;
ctx.beginPath(); ctx.ellipse(r.x,r.y,r.r,r.r*0.3,0,0,7); ctx.stroke();
}
}
// Э42: рыбка-маркер на канвасе (вместо эмодзи-рыбки — рыбные глифы убраны из субсета):
// тело-эллипс + треугольный хвост + глазок; s — «радиус» тела
function drawFishMark(x,y,s){
ctx.save();
ctx.fillStyle='rgba(230,244,252,0.95)';
ctx.beginPath(); ctx.ellipse(x-s*0.15,y,s,s*0.55,0,0,Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.moveTo(x+s*0.55,y); ctx.lineTo(x+s*1.25,y-s*0.55); ctx.lineTo(x+s*1.25,y+s*0.55); ctx.closePath(); ctx.fill();
ctx.fillStyle='#17436b';
ctx.beginPath(); ctx.arc(x-s*0.55,y-s*0.12,Math.max(1,s*0.14),0,Math.PI*2); ctx.fill();
ctx.restore();
}
function drawMeters(){
if(state!=='REEL'||!reel) return;
const r=rod(), sp=reel.sp;
const pulling=reel.phase==='pull';
// M4: босс-бой — золотая полоса фазы над метрами (визуал: пульс + точки фаз)
if(reel.boss){
const pulse=1+Math.sin(time*5)*0.08;
ctx.textAlign='center';
ctx.font='800 '+(16*pulse).toFixed(1)+'px '+UI_FONT;
ctx.fillStyle='#ffd75e';
ctx.shadowColor='rgba(255,180,40,0.8)'; ctx.shadowBlur=10;
ctx.fillText(tt('boss_phase_draw',{p:bossPhase}),W/2,84);
ctx.shadowBlur=0;
for(let i=1;i<=3;i++){
ctx.beginPath();
ctx.arc(W/2-20+i*20,94,4,0,7);
ctx.fillStyle=i<=bossPhase?'#ffd75e':'rgba(255,255,255,0.25)';
ctx.fill();
}
}
// --- нагрузка (вертикально справа) ---
const bw=14, bh=H*0.36, bx=W-30, by=H*0.46-bh/2;
ctx.fillStyle='rgba(0,0,0,0.35)'; rr(bx,by,bw,bh,7); ctx.fill();
const ratio=Math.min(1,reel.tension/reel.line);
const col=ratio<0.5?'#2ecc71':ratio<0.8?'#f1c40f':'#e74c3c';
if(ratio>0.01){
ctx.fillStyle=col;
if(ratio>0.8){ ctx.shadowColor='rgba(231,76,60,0.85)'; ctx.shadowBlur=12; } // перегруз: тревожное свечение
const fh=bh*ratio;
rr(bx+2,by+bh-2-fh,bw-4,Math.max(2,fh-2),5); ctx.fill();
ctx.shadowBlur=0;
}
ctx.strokeStyle='rgba(255,255,255,0.6)'; ctx.lineWidth=1.5;
ctx.beginPath(); ctx.moveTo(bx-3,by); ctx.lineTo(bx+bw+3,by); ctx.stroke();
// Спринт 4: черта drag — выше неё при удержании леска скользит
const dgy=by+bh*(1-Math.min(1,r.drag/reel.line));
ctx.strokeStyle='rgba(255,255,255,0.9)'; ctx.lineWidth=2;
ctx.beginPath(); ctx.moveTo(bx-2,dgy); ctx.lineTo(bx+bw+2,dgy); ctx.stroke();
const Fm=fishForce(reel,reel.aggr,pulling);
const slipping=reel.holding&&Fm-r.drag>0;
ctx.font='16px '+UI_FONT; ctx.textAlign='center';
ctx.fillText(slipping?'🌀':pulling?'⚡':(reel.holding?'💪':'·'),bx+bw/2,by-8);
// --- дистанция (снизу) ---
const dw=W-96, dx=22, dy=H-52, dh=13;
ctx.fillStyle='rgba(0,0,0,0.35)'; rr(dx,dy,dw,dh,7); ctx.fill();
// rem = доля оставшейся дистанции: полоска «сгорает» к лодке, рыбка на её конце
const rem=Math.max(0,Math.min(1,reel.d/reel.d0));
if(rem>0.01){
const dg=ctx.createLinearGradient(dx,0,dx+dw,0);
dg.addColorStop(0,'#2e86c8'); dg.addColorStop(1,'#3fa7e8');
ctx.fillStyle=dg;
rr(dx+2,dy+2,Math.max(2,(dw-4)*rem),dh-4,5); ctx.fill();
}
// escape: красная заливка поверх синей, слева направо (рыба ускользает)
if(reel.escape>0.01){
const ew=(dw-4)*reel.escape;
ctx.fillStyle='rgba(231,76,60,0.7)';
rr(dx+2,dy+2,ew,dh-4,5); ctx.fill();
}
// рыбка-маркер на конце полоски (Э42: рисуется path-рыбкой, эмодзи убраны)
const fx=dx+2+(dw-4)*rem;
drawFishMark(fx,dy-6+Math.sin(time*10)*1.5,8);
ctx.font='18px '+UI_FONT; ctx.textAlign='center';
drawFishMark(dx+12,dy+dh+18,6.5);
ctx.fillText('⚡',dx+32,dy+dh+18);
// Спринт 4: выносливость — тонкая полоска под дистанцией (затухает к нулю)
// T2b: начинается после иконок дистанции/выносливости (dx+44), чтобы не перекрывать их
const sdh=5, sdy=dy+dh+3;
ctx.fillStyle='rgba(0,0,0,0.3)'; rr(dx+44,sdy,dw-44,sdh,2.5); ctx.fill();
if(reel.st>0.01){
ctx.fillStyle=reel.tired?'#7d5ba6':'#b07cc6';
rr(dx+45.5,sdy+1.5,Math.max(1.5,(dw-47)*reel.st),sdh-3,2); ctx.fill();
}
if(reel.tired){
ctx.font='13px '+UI_FONT; ctx.textAlign='left';
ctx.fillText('💤',dx+dw+6,sdy+sdh-1);
}
// статус
ctx.font='700 15px '+UI_FONT; ctx.textAlign='center';
if(pulling){
ctx.fillStyle='#ff6b5e';
ctx.shadowColor='rgba(255,60,40,'+(0.5+0.4*Math.sin(time*10)).toFixed(3)+')'; ctx.shadowBlur=8;
ctx.fillText(t('reel_pull'),W/2,dy-30);
ctx.shadowBlur=0;
}else if(reel.tired){
ctx.fillStyle='#c9a0f5';
ctx.shadowColor='rgba(120,60,180,0.6)'; ctx.shadowBlur=6;
ctx.fillText(t('reel_tired'),W/2,dy-30);
ctx.shadowBlur=0;
}else{
ctx.fillStyle='#7ef0a8';
ctx.shadowColor='rgba(0,0,0,0.6)'; ctx.shadowBlur=4;
ctx.fillText(t('reel_hold'),W/2,dy-30);
ctx.shadowBlur=0;
}
}
function drawPowerBar(){
if(state!=='CHARGE') return;
const w=W*0.7,h=16,x=(W-w)/2,y=H-64;
ctx.fillStyle='rgba(0,0,0,0.4)'; rr(x,y,w,h,8); ctx.fill();
const g=ctx.createLinearGradient(x,0,x+w,0);
g.addColorStop(0,'#2ecc71'); g.addColorStop(0.6,'#f1c40f'); g.addColorStop(1,'#e74c3c');
ctx.fillStyle=g;
if(power>0.01){
const fw=Math.max(2,(w-4)*power);
rr(x+2,y+2,fw,h-4,6); ctx.fill();
// бегунок на конце заливки (визуал)
ctx.fillStyle='rgba(255,255,255,0.9)';
ctx.beginPath(); ctx.arc(x+2+fw-3,y+h/2,4,0,7); ctx.fill();
}
ctx.font='700 14px '+UI_FONT; ctx.textAlign='center';
ctx.fillStyle='#fff';
ctx.shadowColor='rgba(0,0,0,0.65)'; ctx.shadowBlur=4;
ctx.fillText(t('cast_release'),W/2,y-10);
ctx.shadowBlur=0;
}
function drawHint(){
if(state!=='IDLE'||save.casts>=5) return;
const a=0.55+Math.sin(time*3)*0.25;
ctx.globalAlpha=a;
ctx.font='700 17px '+UI_FONT; ctx.textAlign='center';
ctx.fillStyle='#fff';
ctx.shadowColor='rgba(0,0,0,0.65)'; ctx.shadowBlur=5;
ctx.fillText(t('menu_charge_touch'),W/2,H-70); // Y5: i18n (текст тот же, что в меню — тач-ветка)
ctx.shadowBlur=0;
ctx.globalAlpha=1;
}
/* M8: погода — слой в КОНЦЕ draw-цикла: дождь — косые полоски поверх воды и неба,
туман — полупрозрачный слой поверх сцены (+2 полосы глубже для объёма).
HUD — DOM, canvas-слой его не скрывает; поплавок/кнопки под 0.28 тумана остаются читаемыми */
function drawWeather(){
const id=weatherActive();
if(!id) return;
if(id==='rain'){
ctx.strokeStyle='rgba(180,200,220,0.5)';
ctx.lineWidth=1.5;
ctx.beginPath();
for(const dr of rain){
ctx.moveTo(dr.x,dr.y);
ctx.lineTo(dr.x-6,dr.y+20);
}
ctx.stroke();
}else{
ctx.fillStyle='rgba(200,215,225,0.28)';
ctx.fillRect(-8,-8,W+16,H+16); // +8px по периметру: слой рисуется внутри shake-транса — без просвета фона при обрыве
// две полосы с разной прозрачностью и медленным дрейфом — глубина
const b1=((time*10)%(H+120))-120;
const b2=H-((time*6)%(H+160));
ctx.fillStyle='rgba(226,236,244,0.10)';
ctx.fillRect(0,b1,W,48);
ctx.fillStyle='rgba(226,236,244,0.07)';
ctx.fillRect(0,b2,W,70);
}
}
function draw(){
ctx.clearRect(0,0,W,H);
ctx.save();
if(shake>0){
ctx.translate(rand(-1,1)*shake*8,rand(-1,1)*shake*8);
}
if(aqOpen){ drawAquarium(); ctx.restore(); return; } // Этап 46: сцена аквариума вместо озера
drawSky();
drawScenery(); // Этап 44: береговые силуэты (перед водой — базу закрывает градиент)
drawWater();
drawAmbientFish();
drawSwampDecor(); // Этап 45: кувшинки/лилии/лягушки Болота — поверх воды, до лодки
if(biteFish&&biteFish.mode!=='out'){
const bs=12+biteFish.sp.rarity*2.5; // размер силуэта зацепившейся рыбы
if(state==='BITE'){
// топление (lane 2): рыба дёргается у крючка, синхронно с фазами топления поплавка (time*22)
const bx=hookPos.x+Math.sin(time*25)*7, by=hookPos.y+Math.cos(time*22)*5;
fishGlow(biteFish.sp,bx,by,bs*1.5); // Фаза свечения: компактный ореол вида ночью
drawFishShape(bx,by,bs, Math.sin(time*25)>0?1:-1, biteFish.sp.hue, biteFish.sp.glow?1:0.8, Math.sin(time*8)*0.25);
}else{
// APPROACH: подплывает к поплавку как раньше
fishGlow(biteFish.sp,biteFish.x,biteFish.y,bs*1.5);
drawFishShape(biteFish.x,biteFish.y,bs,biteFish.dir,biteFish.sp.hue,biteFish.sp.glow?1:0.8,Math.sin(time*8)*0.25);
}
}
else if(biteFish&&biteFish.mode==='out'){ // рыба ушла — светящийся силуэт гаснет, но аура ещё видна
const bs=12+biteFish.sp.rarity*2.5;
fishGlow(biteFish.sp,biteFish.x,biteFish.y,bs*1.5);
drawFishShape(biteFish.x,biteFish.y,bs,biteFish.dir,biteFish.sp.hue,biteFish.sp.glow?0.85:0.5,Math.sin(time*8)*0.25);
}
drawBoat();
drawLineAndBobber();
drawHookedFish();
drawRipples();
drawParticles();
drawMeters();
drawPowerBar();
drawHint();
drawWeather(); // M8: погодный слой — в конце draw-цикла
ctx.restore();
}
/* =========================================================
АКВАРИУМ (Этап 46)
Декоративная сцена коллекции ЭКЗЕМПЛЯРОВ (вид+вариант+вес), не вода:
в LOCATIONS не добавляется, роллы/боссы/баланс не трогаются. Сцена рисуется
в draw() ВМЕСТО озера при aqOpen; DOM-бар #aqBar и поповер #aqPop — поверх
canvas. Вход — из хаба «Меню» (#btnGmAquarium). Экземпляры копятся кнопкой
«В аквариум» на карточке улова (модель C — бесплатная копия, catchFish не
меняется). Рантайм-раскладка aqFishRt НЕ персистится (индекс = save.aqFish).
========================================================= */
let aqOpen=false; // сцена активна (draw() рисует банку вместо озера)
let aqFishRt=[]; // рантайм: {x,y,dir,ph,spd,vy,tail} по индексу save.aqFish — НЕ в сейве
let aqSel=-1; // выбранный экземпляр (-1 — ничего)
let aqFromMenu=false; // вход был из хаба «Меню» (аналог shopFromMenu): «Назад» вернёт в #gamemenu
let aqTank={x:0,y:0,w:0,h:0,sandY:0}; // геометрия банки (пересобирается в initAquarium)
let aqPlants=[], aqBubbles=[]; // декор сцены (растения/пузыри) — тоже не в сейве
let aqFishEls=[]; // DOM-элементы жильцов (#aqFishLayer) — те же SVG-портреты, что на карточке; индекс = save.aqFish
/* Вариантное оформление жильца — ровно как на карточке улова: перекраска тела/плавников
(--f1/--f3, наследуется сквозь <use> в спрайт) для copper/gold/quartz/emerald/diamond,
плюс класс .var<id> по варианту — CSS в #aqFishLayer добавляет цветной drop-shadow
(silver — grayscale+brightness, rainbow — вращение оттенка). normal — без эффекта,
как и на карточке. */
const AQ_VARIANT_RECOLOR={
copper:{f1:'hsl(25,55%,55%)',f3:'hsl(25,50%,35%)'},
gold:{f1:'#ffd75e',f3:'hsl(45,70%,40%)'},
quartz:{f1:'hsl(270,25%,72%)',f3:'hsl(270,25%,45%)'},
emerald:{f1:'hsl(150,55%,42%)',f3:'hsl(150,50%,28%)'},
diamond:{f1:'hsl(200,60%,78%)',f3:'hsl(200,50%,45%)'}
};
/* CSS-классы оформления варианта в банке — те же имена, что у карточки (var<id>),
кроме normal (на карточке normal тоже без оформления) */
const AQ_VARIANT_CLASS=['copper','silver','quartz','gold','emerald','diamond','rainbow'];
function aqApplyVariant(el,variant){
const r=AQ_VARIANT_RECOLOR[variant.id];
if(r){ el.style.setProperty('--f1',r.f1); el.style.setProperty('--f3',r.f3); }
else{ el.style.removeProperty('--f1'); el.style.removeProperty('--f3'); }
for(const id of AQ_VARIANT_CLASS) el.classList.toggle('var'+id,variant.id===id);
}
/* Размер силуэта в банке: от веса к пределу вида (w/wMax) и редкости; кламп — чтобы влезал */
function aqFishSize(sp,w){
const rel=sp.wMax>0?Math.min(1,Math.max(0,w/sp.wMax)):0.4;
const s=8+rel*22+sp.rarity*1.6;
return Math.max(7,Math.min(Math.min(W,H)*0.11,s));
}
/* Позиция жильца в DOM: та же система координат, что у canvas (W/H — CSS-px), элемент
центрируется на (x,y) и зеркалится по направлению. Вызывается каждый кадр из updateAquarium. */
function aqPlaceFish(el,rt){
if(!el||!rt) return;
el.style.transform='translate('+rt.x.toFixed(1)+'px,'+rt.y.toFixed(1)+'px) translate(-50%,-50%)'
+' rotate('+(Math.sin(rt.tail||0)*4).toFixed(2)+'deg) scale('+rt.dir+',1)';
}
/* DOM-элемент жильца: <div class=aqfish><svg viewBox=0 0 160 100><use href="#fish-<id>"/></svg></div>.
Те же SVG-портреты и вариантное оформление, что на карточке улова (Э42 + ревизия). Размер —
aqFishSize (прежняя шкала): длина 2·size, высота по соотношению 160:100. */
function aqBuildFishEl(i){
const f=save.aqFish[i];
const sp=SPECIES.find(s=>s.id===f.id); if(!sp) return null;
const variant=VARIANTS.find(v=>v.id===f.v)||VARIANTS[0];
const size=aqFishSize(sp,f.w);
const el=document.createElement('div');
el.className='aqfish'; el.dataset.i=i;
el.style.width=(size*2).toFixed(1)+'px';
el.style.height=(size*1.25).toFixed(1)+'px';
const svg=document.createElementNS('http://www.w3.org/2000/svg','svg');
svg.setAttribute('viewBox','0 0 160 100');
const use=document.createElementNS('http://www.w3.org/2000/svg','use');
use.setAttribute('href','#fish-'+sp.id);
svg.appendChild(use); el.appendChild(svg);
aqApplyVariant(el,variant);
aqPlaceFish(el,aqFishRt[i]);
return el;
}
/* Раскладка банки и экземпляров по текущим W/H; вызывается при входе и на resize */
function initAquarium(){
aqTank.x=14; aqTank.y=14;
aqTank.w=Math.max(160,W-28); aqTank.h=Math.max(160,H-28);
aqTank.sandY=aqTank.y+aqTank.h*0.80;
const topPad=aqTank.y+54; // запас под DOM-бар #aqBar (пилюли лежат поверх верхней кромки банки)
const n=save.aqFish.length;
aqFishRt=[];
// Сетка по ширине/высоте — рыбы стартуют вразброс, не друг на друге
const cols=Math.max(1,Math.ceil(Math.sqrt(n)));
const rows=Math.max(1,Math.ceil(n/cols));
for(let i=0;i<n;i++){
const cr=Math.floor(i/cols), cc=i%cols;
const cw=(aqTank.w-70)/cols, ch=Math.max(24,(aqTank.sandY-topPad-16)/rows);
aqFishRt.push({
x:aqTank.x+40+cc*cw+rand(0,Math.max(1,cw*0.5)),
y:topPad+cr*ch+rand(0,Math.max(1,ch*0.6)),
dir:Math.random()<0.5?1:-1, ph:rand(0,6.28), tail:rand(0,6.28),
spd:rand(12,26), vy:rand(5,12)
});
}
// Рыбы-жильцы — DOM-слой тех же SVG-портретов (по индексу save.aqFish); слой обрезан по банке
const layer=$('aqFishLayer');
layer.innerHTML='';
layer.style.clipPath='inset('+aqTank.y+'px '+(W-aqTank.x-aqTank.w).toFixed(1)+'px '
+(H-aqTank.y-aqTank.h).toFixed(1)+'px '+aqTank.x+'px round 18px)';
aqFishEls=[];
for(let i=0;i<n;i++){
const el=aqBuildFishEl(i); // индекс массива = индекс save.aqFish (даже если вид битый → null)
aqFishEls[i]=el;
if(el) layer.appendChild(el);
}
aqPlants=[];
const np=5+((Math.random()*4)|0);
for(let k=0;k<np;k++) aqPlants.push({
x:rand(aqTank.x+18,aqTank.x+aqTank.w-18),
h:rand(28,Math.min(110,aqTank.h*0.5)), w:rand(3,7), ph:rand(0,6.28),
c:['#2f7a4a','#3f8f57','#256b41'][(Math.random()*3)|0]
});
aqBubbles=[];
for(let k=0;k<16;k++) aqBubbles.push({
x:rand(aqTank.x+14,aqTank.x+aqTank.w-14), y:rand(aqTank.y+20,aqTank.sandY),
r:rand(1.5,3.5), v:rand(16,34), ph:rand(0,6.28)
});
aqSel=-1;
renderAqBar();
}
/* Плавание: движение по горизонтали, разворот у стенок, вертикальный дрейф, анимация хвоста */
function updateAquarium(dt){
const left=aqTank.x+14, right=aqTank.x+aqTank.w-14;
const top=aqTank.y+52, bot=aqTank.sandY-8; // top — ниже DOM-бара #aqBar
for(const f of aqFishRt){
f.x+=f.dir*f.spd*dt;
f.y+=Math.sin(time*0.8+f.ph)*f.vy*dt;
f.tail+=dt*7;
if(f.dir>0&&f.x>right) f.dir=-1;
if(f.dir<0&&f.x<left) f.dir=1;
if(f.y<top) f.y=top;
if(f.y>bot) f.y=bot;
}
for(let i=0;i<aqFishRt.length;i++) aqPlaceFish(aqFishEls[i],aqFishRt[i]); // синхронизируем DOM-портреты
for(const b of aqBubbles){
b.y-=b.v*dt; b.ph+=dt*2;
if(b.y<aqTank.y+10){ b.y=aqTank.sandY-4; b.x=rand(left,right); }
}
}
/* Отрисовка банки: вода/стекло (hexLerp к nightMix — общий цикл суток), грунт, растения,
пузыри, аура болотных и рамка выбора. Сами рыбы-жильцы — DOM-слой #aqFishLayer
(SVG-портреты, как на карточке улова) поверх canvas. */
function drawAquarium(){
const nm=nightMix();
const x=aqTank.x, y=aqTank.y, w=aqTank.w, h=aqTank.h;
// фон комнаты за стеклом
ctx.fillStyle='#050b14'; ctx.fillRect(0,0,W,H);
ctx.save();
rr(x,y,w,h,18); ctx.clip();
// вода: день ↔ ночь
const g=ctx.createLinearGradient(0,y,0,y+h);
g.addColorStop(0,hexLerp('#a9e4ff','#123a5c',nm));
g.addColorStop(1,hexLerp('#2f8fc4','#06172b',nm));
ctx.fillStyle=g; ctx.fillRect(x,y,w,h);
// косые лучи света (визуал)
ctx.globalAlpha=0.05+0.06*(1-nm); ctx.fillStyle='#ffffff';
for(let k=0;k<4;k++){
const bx0=x+w*(0.08+k*0.24);
ctx.beginPath();
ctx.moveTo(bx0,y); ctx.lineTo(bx0+w*0.05,y);
ctx.lineTo(bx0-w*0.03,y+h); ctx.lineTo(bx0-w*0.08,y+h);
ctx.closePath(); ctx.fill();
}
ctx.globalAlpha=1;
// грунт (волнистая кромка песка)
ctx.fillStyle=hexLerp('#e8d6a6','#4b4233',nm*0.75);
ctx.beginPath();
ctx.moveTo(x,aqTank.sandY);
for(let px=x;px<=x+w;px+=24) ctx.lineTo(px,aqTank.sandY+Math.sin(px*0.05)*4);
ctx.lineTo(x+w,y+h); ctx.lineTo(x,y+h); ctx.closePath(); ctx.fill();
ctx.strokeStyle=hexLerp('#b9a06b','#33291d',nm*0.75); ctx.lineWidth=2;
ctx.beginPath();
for(let px=x;px<=x+w;px+=24){ const py=aqTank.sandY+Math.sin(px*0.05)*4; if(px===x)ctx.moveTo(px,py); else ctx.lineTo(px,py); }
ctx.stroke();
// растения (пучок из 3 тростинок, качается; лист у средней — читаемость на телефоне)
for(const pl of aqPlants){
const col=hexLerp(pl.c,'#0a1a12',nm*0.45);
ctx.strokeStyle=col; ctx.fillStyle=col; ctx.lineCap='round';
for(let b=-1;b<=1;b++){
const sway=Math.sin(time*1.1+pl.ph+b*0.6)*pl.h*0.08;
const bx=pl.x+b*pl.w*1.4;
ctx.lineWidth=pl.w*(b===0?1:0.7);
ctx.beginPath();
ctx.moveTo(bx,aqTank.sandY+4);
ctx.quadraticCurveTo(bx+pl.w*1.5,aqTank.sandY-pl.h*0.55,bx+sway,aqTank.sandY-pl.h*(b===0?1:0.78));
ctx.stroke();
}
ctx.beginPath();
ctx.ellipse(pl.x+pl.w*2.4,aqTank.sandY-pl.h*0.52,pl.w*1.7,pl.w*0.8,-0.5,0,7);
ctx.fill();
}
// пузыри
ctx.strokeStyle='rgba(255,255,255,'+(0.25+0.25*(1-nm)).toFixed(3)+')'; ctx.lineWidth=1.2;
for(const b of aqBubbles){
ctx.beginPath(); ctx.arc(b.x+Math.sin(b.ph)*3,b.y,b.r,0,7); ctx.stroke();
}
// рыбы-жильцы: сами портреты — DOM-слой #aqFishLayer (те же SVG-спрайты и вариантное
// оформление, что на карточке улова); на canvas — только ореол свечения болотных
// (уходит ЗА портрет) и рамка выбора
for(let i=0;i<aqFishRt.length;i++){
const f=save.aqFish[i], rt=aqFishRt[i];
if(!f||!rt) continue;
const sp=SPECIES.find(s=>s.id===f.id); if(!sp) continue;
const size=aqFishSize(sp,f.w);
if(sp.glow) drawGlowAura(rt.x,rt.y,size*1.5,sp.glow,glowIntensity()); // болотные — свечение вида
if(aqSel===i){ // рамка выбранной рыбы
ctx.strokeStyle='#c8fff4'; ctx.lineWidth=2;
ctx.beginPath(); ctx.ellipse(rt.x,rt.y,size*1.25,size*0.85,0,0,7); ctx.stroke();
}
}
ctx.restore();
// стекло: рамка + вертикальный блик
ctx.save();
rr(x,y,w,h,18); ctx.clip();
ctx.globalAlpha=0.10; ctx.fillStyle='#fff';
ctx.beginPath();
ctx.moveTo(x+w*0.06,y); ctx.lineTo(x+w*0.16,y);
ctx.lineTo(x+w*0.04,y+h); ctx.lineTo(x-w*0.02,y+h);
ctx.closePath(); ctx.fill();
ctx.restore();
ctx.strokeStyle='rgba(200,255,244,.55)'; ctx.lineWidth=3; rr(x,y,w,h,18); ctx.stroke();
ctx.strokeStyle='rgba(255,255,255,.10)'; ctx.lineWidth=1; rr(x+4,y+4,w-8,h-8,15); ctx.stroke();
}
/* Тап по сцене: ближайшая рыба в радиусе → выделение + поповер; тап в пустоту — снять выбор */
function aqTap(x,y){
let best=-1, bd=Infinity;
for(let i=0;i<aqFishRt.length;i++){
const rt=aqFishRt[i], f=save.aqFish[i];
if(!f) continue;
const sp=SPECIES.find(s=>s.id===f.id); if(!sp) continue;
const r=aqFishSize(sp,f.w)*1.2+10;
const d=(rt.x-x)*(rt.x-x)+(rt.y-y)*(rt.y-y);
if(d<r*r&&d<bd){ bd=d; best=i; }
}
if(best>=0){ aqSel=best; showAqPop(); }
else{ aqSel=-1; hideAqPop(); }
renderAqBar();
}
function showAqPop(){
const f=save.aqFish[aqSel]; if(!f){ hideAqPop(); return; }
const sp=SPECIES.find(s=>s.id===f.id); if(!sp){ hideAqPop(); return; }
const variant=VARIANTS.find(v=>v.id===f.v)||VARIANTS[0];
$('aqPopName').textContent=trName(sp)+(variant.id!=='normal'?' · '+trName(variant):'');
$('aqPopSub').textContent=f.w.toFixed(1)+' '+t('kg')+' · '+trRarity(sp.rarity); // Этап 46: без цены — выпуск бесплатный
$('btnAqRelease').textContent=t('aq_release');
$('aqPop').classList.remove('hidden');
}
function hideAqPop(){ $('aqPop').classList.add('hidden'); }
/* Бар аквариума: счётчик слотов + кнопка покупки (цена = AQ_SLOT_COSTS[aqSlots-AQ_BASE_SLOTS]) */
function renderAqBar(){
$('aqBar').title=t('aq_title');
$('btnAqBack').textContent=t('aq_back');
$('aqCount').textContent=tt('aq_slots',{n:save.aqFish.length,m:save.aqSlots});
const btn=$('btnAqSlot'), maxed=save.aqSlots>=AQ_MAX_SLOTS;
btn.disabled=maxed;
btn.textContent=maxed?t('aq_max'):tt('aq_buy_slot',{n:AQ_SLOT_COSTS[save.aqSlots-AQ_BASE_SLOTS]});
if(aqSel>=0&&aqSel<save.aqFish.length) showAqPop(); else hideAqPop();
}
/* Выпуск выбранного экземпляра из банки (Этап 46, ревизия: чистая косметика — без монет).
Точечно удаляем запись и её рантайм-раскладку ИНДЕКСАМИ, позиции остальных рыб сохраняем
(никакого initAquarium — иначе остальные «телепортируются»). Слот просто освобождается. */
function aqReleaseSel(){
if(aqSel<0||aqSel>=save.aqFish.length) return;
save.aqFish.splice(aqSel,1);
aqFishRt.splice(aqSel,1);
const el=aqFishEls[aqSel]; if(el) el.remove(); // портрет (DOM) уносим точечно — остальных не трогаем
aqFishEls.splice(aqSel,1);
aqSel=-1;
hideAqPop();
persist();
toast(t('aq_released'));
renderAqBar();
}
/* Вход/выход: из хаба «Меню» (аналог fromMenuShop) — гменю прячем, «Назад» возвращает в него */
function openAquarium(){
sfx('click');
aqFromMenu=true;
$('gamemenu').classList.add('hidden');
aqOpen=true; uiOpen=true;
YG.gameplayStop(); // сцена — не геймплей (паттерн лавки/паузы)
initAquarium();
$('aqBar').classList.remove('hidden');
document.body.classList.add('aq-open'); // HUD (#coins/#btnShop/#weather) скрыт под сценой
syncDom(); // под оверлеем-сценой #btnPause/#btnRecall не показываем
}
function closeAquarium(){
sfx('click');
aqOpen=false; aqSel=-1;
$('aqBar').classList.add('hidden');
document.body.classList.remove('aq-open'); // HUD возвращаем
hideAqPop();
if(aqFromMenu){ $('gamemenu').classList.remove('hidden'); uiOpen=true; }
aqFromMenu=false;
}
/* =========================================================
Y1: SDK Яндекс Игр
Обёртка YG — единственная точка SDK. Стаб (file:// без ?sdk=1) —
no-op + счётчики для тестов. SDK подгружается динамически ТОЛЬКО
по http(s) (или file://?sdk=1): при чистом file:// скрипт не
создаётся вообще (иначе 404/ERR_FILE_NOT_FOUND в консоли).
file://?sdk=1 — относительный sdk.js: работает, если лежит рядом
с index.html.
========================================================= */
const YG={
sdk:null, active:false, // ysdk или null (стаб)
paused:false, // глобальная пауза: реклама / скрытая вкладка / платформа
readySent:false,
counts:{ready:0,start:0,stop:0,fsAdv:0,rv:0}, // счётчики вызовов (для тестов)
adWatchdog:null, // Y2: watchdog — SDK не закрыл рекламу (нет onClose/onError) → снять паузу
_sdkReady(){ if(this.sdk){ try{ this.sdk.features.LoadingAPI&&this.sdk.features.LoadingAPI.ready&&this.sdk.features.LoadingAPI.ready(); }catch(e){} } },
init(ysdk){
if(this.active) return; // однократно (апгрейд стаб(null) → реальный SDK позже — разрешён)
this.sdk=ysdk||null; this.active=!!ysdk;
if(ysdk){
try{
const p=()=>this.onPause(), r=()=>this.onResume();
ysdk.on('game_api_pause',p);
ysdk.on('game_api_resume',r);
}catch(e){}
}
if(this.readySent) this._sdkReady(); // SDK доинициализировался после ready()
},
ready(){ this.readySent=true; this.counts.ready++; this._sdkReady(); },
gameplayStart(){ this.counts.start++; if(this.sdk){ try{ this.sdk.features.GameplayAPI&&this.sdk.features.GameplayAPI.start&&this.sdk.features.GameplayAPI.start(); }catch(e){} } },
gameplayStop(){ this.counts.stop++; if(this.sdk){ try{ this.sdk.features.GameplayAPI&&this.sdk.features.GameplayAPI.stop&&this.sdk.features.GameplayAPI.stop(); }catch(e){} } },
onPause(){
if(this.paused) return;
this.paused=true;
this.gameplayStop();
if(AC){ try{ AC.suspend(); }catch(e){} }
},
onResume(){
if(!this.paused) return;
// резюме не снимается, пока открыта реклама — снимет endAd()
// (пути сюда: visibilitychange / game_api_resume, а оверлей рекламы ещё открыт)
if(adShowing) return;
// Y6: резюме не снимается, пока открыт оверлей паузы — снимут только
// closePause()/кнопка «В меню» (пути сюда: blur/focus/visibilitychange
// во время открытой паузы)
if(pauseOpen) return;
this.paused=false;
last=performance.now();
if(state!=='MENU'&&!uiOpen) this.gameplayStart();
},
// Y2: реклама — ТОЛЬКО через SDK Яндекса (треб. 4.1). Стаб: fullscreen —
// no-op (вернёт false — реклама не показана), rewarded — симуляция успешного
// просмотра (локальная разработка, all-в-одном-клике флоу).
// Y2: watchdog — если SDK не доставит onClose/onError (баг SDK, убитый iframe),
// паузу снимаем сами, иначе игра замрзнет навсегда (треб. 1.14)
_startAdWatchdog(){
clearTimeout(this.adWatchdog);
this.adWatchdog=setTimeout(()=>{ if(adShowing){ console.warn('Yandex SDK: реклама не закрылась (90 с) — снимаю паузу'); endAd(); } },90000);
},
showFullscreen(cb){
this.counts.fsAdv++;
if(this.sdk){
try{ this.sdk.adv.showFullscreenAdv({callbacks:cb}); }catch(e){ return false; } // сбой SDK — как стаб: endAd() на месте вызова
this._startAdWatchdog();
return true;
}
return false; // стаб: реклама не показана
},
showRewarded(cb){
this.counts.rv++;
if(this.sdk){
try{ this.sdk.adv.showRewardedVideo({callbacks:cb}); }catch(e){ return false; }
this._startAdWatchdog(); // стаб-ветка ниже — флоу синхронный, watchdog не ставится
return true;
}
// стаб: симуляция успешного просмотра (локальная разработка)
try{ cb.onOpen&&cb.onOpen(); cb.onRewarded&&cb.onRewarded(); cb.onClose&&cb.onClose(true); }catch(e){}
return true;
},
};
// Y2: константы и состояние рекламы. Interstitial — на закрытии карточки улова
// (логическая пауза, треб. 4.4): первый показ не раньше AD_FIRST_MS сессии,
// дальше не чаще AD_COOLDOWN_MS. Rewarded «монеты ×2» — на самой карточке
// (треб. 4.5.1/4.5.2). Реклама не персистится (как погода).
const AD_FIRST_MS=120000; // первый interstitial не раньше 2 мин сессии
const AD_COOLDOWN_MS=180000; // между interstitial'ами — 3 мин
let sessionStartTs=Date.now(); // старт сессии (момент загрузки страницы)
let lastAdTs=0; // время запуска последнего interstitial
let adShowing=false; // реклама прямо сейчас (interstitial или rewarded)
let rvVal=0, rvDone=false; // rewarded: цена улова для бонуса ×2 и флаг «уже получен»
/* =========================================================
Y5: i18n RU/EN — автоопределение языка (треб. 2.14)
T — таблицы строк: ru — ТЕКУЩИЕ точные тексты, en — перевод.
Резолюция языка: forceLang (тест-форс) → SDK environment.i18n.lang
→ ru. Стаб (file:// без SDK) — ВСЕГДА ru. navigator.language
не используем осознанно (headless en-US сломал бы RU-тесты).
Блок стоит ДО IIFE-загрузки SDK: стаб-ветка сразу вызывает
resolveLang(null) → setLang → t(...), так что T должен быть
инициализирован до этой точки скрипта.
========================================================= */
const T={
ru:{
title:'Рыбалка',
menu_title:'🎣 РЫБАЛКА',
menu_charge_touch:'👆 Держи палец — заряжай бросок',
menu_charge_mouse:'🖱 Зажми кнопку мыши — заряжай бросок',
menu_throw:'<b>отпусти</b> — кидай удочку.<br>Дальше бросок = реже и жирнее рыба!',
menu_bite:'Видишь <b style="color:#ff6b5e">!</b> — быстро жми, чтобы зацепить.<br>При вываживании: тяни, когда рыба отдыхает,<br>отпусти, когда она <b>тянет</b> — иначе лопнет!<br>Не тяни слишком долго — рыба <b>сбегает</b> (красная полоса)!',
menu_weight:'У каждой рыбы свой <b>вес</b>, продажа — по цене за кг.<br>У удочек есть <b>предел веса</b>: перевесишь — шнур лопнет! <br>Лучшие снасти = больше шансов на трофей 🏆',
menu_drag:'Не каждый заброс — с клёвом: если молчит — подожди.<br>У удилищ есть <b>тормоз (drag)</b>: рывок сильнее — леска скользит!',
btn_play:'Играть',
shop_title:'🛒 Лавка рыбака',
sec_rods:'🎣 Удилища',
sec_lures:'🪱 Приманки',
sec_boat:'Лодка',
sec_bobber:'Поплавок',
sec_locations:'Локации',
sec_streak:'Стрик входа',
sec_quests:'Квесты дня',
sec_ach:'🏅 Достижения',
btn_select:'Выбрать',
btn_selected:'✓ Выбрано',
btn_selected_chk:'Выбрано ✓',
btn_selected_f:'Выбрана ✓',
btn_buy:'Купить',
btn_cancel:'Отмена',
btn_reset:'Сбросить',
btn_ok:'Отлично!',
free:'бесплатно',
lvl:'ур.',
kg:'кг',
per_kg:'🪙/кг',
price_label:'Цена: ',
locked_desc:'Откроется: улучши «{name}» до 4 ур.',
gate_toast:'Сначала улучши «{name}» до 4 ур.',
stats:'Бросков: {casts} · Заработано: {earned} 🪙',
not_caught:'не пойман',
max_w:'макс {w} кг',
card_sub:'{w} кг · {price} 🪙/кг',
card_record:'🏆 РЕКОРД!',
card_boss:'🏆 БОСС!',
card_boss_suffix:'-БОСС',
rv_btn:'📺 Реклама: ×2 за этот улов',
rv_done:'✓ Бонус получен',
boss_defeated:'🏆 БОСС ПОБЕЖДЁН! +{v} 🪙',
streak_day:'Стрик: день {d} — +{c} 🪙',
streak_line:'День {d} · далее +{n} 🪙',
quest_done:'Квест выполнен: {text} +{r} 🪙',
quests_done_today:'Квесты на сегодня закончились — заходи завтра!',
shield:'Щит спас стрик!',
bonus_coin:'+{v}% к монетам',
bonus_window:'+{v} с к подсечке',
bonus_line:'+{v} к прочности',
ach_toast:'🏅 Достижение: {name}',
bought:'Куплено: {name}!',
upgraded:'Улучшено: {name} → {lv} ур.!',
not_enough:'Не хватает монет 🪙',
welcome_lakes:'Добро пожаловать на Озёра!',
welcome_sea:'Добро пожаловать в Море!',
welcome_ocean:'Добро пожаловать в Океан!',
welcome_swamp:'Добро пожаловать в Волшебное болото! Здесь всё светится...',
location_set:'Локация: {name}',
progress_reset:'Прогресс сброшен 🔄',
night_light:'Ночью нужен фонарик — улучши удилище (первый апгрейд)',
escaped_bite:'Ушла… жми быстрее!',
snap_big:'💥 Рыба крупнее предела удочки!',
snap:'💥 Линия лопнула!',
escaped:'Сбежала!',
boss_phase:'⚠️ Босс рвёт леску! Фаза {p}/3',
boss_phase_draw:'⚠️ БОСС-РЫБА · ФАЗА {p}/3',
reel_pull:'⚡ РЫБА ТЯНЕТ — ОТПУСТИ! ⚡',
reel_tired:'💤 Рыба устала — втягивай!',
reel_hold:'Держи палец — втягивай!',
cast_release:'Отпусти — бросок!',
wx_clear:'☀️ Погода прояснила',
wx_rain:'Дождь: шанс особых рыб ×3',
wx_fog:'Туман: клёв чаще ×1,5',
sec_short:'с',
reset_title:'🗑️ Сбросить прогресс?',
reset_text:'Монеты, снасти и коллекция будут стёрты.<br>Это действие <b>нельзя отменить</b>.',
reset_title_attr:'Сбросить прогресс',
tab_tackle:'Лавка',
tab_journal:'Дневник',
tab_rewards:'Награды',
tab_ach:'Достижения',
tab_skins:'Скины',
tab_locations:'Локации',
pause_title:'⏸ Пауза',
pause_resume:'Продолжить',
pause_menu:'В меню',
btn_pause_title:'Пауза',
gm_title:'🎣 Меню',
gm_shop:'🛒 Магазин',
gm_ach:'🏅 Достижения',
gm_journal:'📔 Дневник',
gm_howto:'📖 Инструкции',
gm_sound:'Звук',
gm_reset:'🗑️ Очистить',
gm_aquarium:'🐠 Аквариум',
aq_title:'🐠 Аквариум',
aq_keep:'🐠 В аквариум',
aq_back:'← Назад',
aq_slots:'{n}/{m}',
aq_buy_slot:'+ Слот ({n} 🪙)',
aq_max:'+ Слот: максимум',
aq_release:'Выпустить',
aq_no_room:'Нет места',
aq_kept:'🐠 Рыба поселилась в аквариуме!',
aq_released:'Рыба выпущена'
},
en:{
title:'Fishing',
menu_title:'🎣 FISHING',
menu_charge_touch:'👆 Hold your finger — charge the cast',
menu_charge_mouse:'🖱 Hold the mouse button — charge the cast',
menu_throw:'<b>let go</b> — cast your rod.<br>Farther cast = rarer and bigger fish!',
menu_bite:'See the <b style="color:#ff6b5e">!</b> — hit it fast to hook the fish.<br>While reeling in: pull when the fish rests,<br>let go when it <b>pulls</b> — or the line snaps!<br>Do not hold too long — the fish <b>escapes</b> (red bar)!',
menu_weight:'Every fish has its own <b>weight</b>, sold by price per kg.<br>Rods have a <b>weight limit</b>: overload — the line snaps! <br>Better gear = better trophy chances 🏆',
menu_drag:'Not every cast gets a bite: if it is silent — wait.<br>Rods have a <b>drag</b>: a stronger jerk — the line slips!',
btn_play:'Play',
shop_title:"🛒 Angler's Shop",
sec_rods:'🎣 Rods',
sec_lures:'🪱 Lures',
sec_boat:'Boat',
sec_bobber:'Bobber',
sec_locations:'Locations',
sec_streak:'Daily Streak',
sec_quests:'Daily Quests',
sec_ach:'🏅 Achievements',
btn_select:'Select',
btn_selected:'✓ Selected',
btn_selected_chk:'Selected ✓',
btn_selected_f:'Selected ✓',
btn_buy:'Buy',
btn_cancel:'Cancel',
btn_reset:'Reset',
btn_ok:'Nice!',
free:'Free',
lvl:'lvl',
kg:'kg',
per_kg:'🪙/kg',
price_label:'Price: ',
locked_desc:'Unlocks: upgrade "{name}" to level 4',
gate_toast:'Upgrade "{name}" to level 4 first',
stats:'Casts: {casts} · Earned: {earned} 🪙',
not_caught:'not caught',
max_w:'max {w} kg',
card_sub:'{w} kg · {price} 🪙/kg',
card_record:'🏆 RECORD!',
card_boss:'🏆 BOSS!',
card_boss_suffix:'-BOSS',
rv_btn:'📺 Ad: ×2 for this catch',
rv_done:'✓ Bonus received',
boss_defeated:'🏆 BOSS DEFEATED! +{v} 🪙',
streak_day:'Streak: day {d} — +{c} 🪙',
streak_line:'Day {d} · next +{n} 🪙',
quest_done:'Quest done: {text} +{r} 🪙',
quests_done_today:"Today's quests are done — come back tomorrow!",
shield:'The shield saved your streak!',
bonus_coin:'+{v}% to coins',
bonus_window:'+{v}s hookset window',
bonus_line:'+{v} line strength',
ach_toast:'🏅 Achievement: {name}',
bought:'Bought: {name}!',
upgraded:'Upgraded: {name} → lvl {lv}!',
not_enough:'Not enough coins 🪙',
welcome_lakes:'Welcome to the Lakes!',
welcome_sea:'Welcome to the Sea!',
welcome_ocean:'Welcome to the Ocean!',
welcome_swamp:'Welcome to the Magic Swamp! Everything glows here...',
location_set:'Location: {name}',
progress_reset:'Progress reset 🔄',
night_light:'Night needs a flashlight — upgrade your rod (first upgrade)',
escaped_bite:'It got away… hit it faster!',
snap_big:'💥 Fish bigger than the rod limit!',
snap:'💥 Line snapped!',
escaped:'Escaped!',
boss_phase:'⚠️ The boss is tearing the line! Phase {p}/3',
boss_phase_draw:'⚠️ BOSS FISH · PHASE {p}/3',
reel_pull:'⚡ FISH PULLS — LET GO! ⚡',
reel_tired:'💤 Fish is tired — reel in!',
reel_hold:'Hold — reel in!',
cast_release:'Release — cast!',
wx_clear:'☀️ Weather cleared',
wx_rain:'Rain: special fish chance ×3',
wx_fog:'Fog: bites more often ×1.5',
sec_short:'s',
reset_title:'🗑️ Reset progress?',
reset_text:'Coins, gear and the collection will be erased.<br>This action <b>cannot be undone</b>.',
reset_title_attr:'Reset progress',
tab_tackle:'Shop',
tab_journal:'Journal',
tab_rewards:'Rewards',
tab_ach:'Achievements',
tab_skins:'Skins',
tab_locations:'Locations',
pause_title:'⏸ Pause',
pause_resume:'Resume',
pause_menu:'To menu',
btn_pause_title:'Pause',
gm_title:'🎣 Menu',
gm_shop:'🛒 Shop',
gm_ach:'🏅 Achievements',
gm_journal:'📔 Journal',
gm_howto:'📖 How to play',
gm_sound:'Sound',
gm_reset:'🗑️ Reset',
gm_aquarium:'🐠 Aquarium',
aq_title:'🐠 Aquarium',
aq_keep:'🐠 To aquarium',
aq_back:'← Back',
aq_slots:'{n}/{m}',
aq_buy_slot:'+ Slot ({n} 🪙)',
aq_max:'+ Slot: max',
aq_release:'Release',
aq_no_room:'No room',
aq_kept:'🐠 The fish moved into the aquarium!',
aq_released:'Fish released'
}
};
let LANG='ru';
let forceLang=null; // Y5: тест-глобал: 'ru'|'en'|null (приоритет в resolveLang)
function t(key){ const m=T[LANG]||{}; return (key in m)?m[key]:((T.ru&&T.ru[key]!==undefined)?T.ru[key]:key); }
function tt(key,o){ let s=t(key); if(o){for(const k in o)s=s.split('{'+k+'}').join(o[k]);} return s; }
function trName(x){ return (LANG==='en'&&x.nameEn)?x.nameEn:x.name; }
function trDesc(x){ return (LANG==='en'&&x.descEn)?x.descEn:x.desc; }
function trRarity(i){ return (LANG==='en')?RARITY_NAMES_EN[i]:RARITY_NAMES[i]; }
/* Y5: подпись следующего уровня снасти в лавке (RU/EN по языку) */
function upgNextText(type,lv){ return (LANG==='en')?((type==='rod'?ROD_UPG_NEXT_EN:LURE_UPG_NEXT_EN)[lv]):((type==='rod'?ROD_UPG_NEXT:LURE_UPG_NEXT)[lv]); }
/* Y5: безопасные проверки видимости оверлеев (для setLang) */
function shopOpenNow(){ const s=$('shop'); return !!(s&&!s.classList.contains('hidden')); }
function cardOpenNow(){ const c=$('card'); return !!(c&&!c.classList.contains('hidden')); }
const IS_TOUCH=('ontouchstart' in window)||(navigator.maxTouchPoints>0); // Y5: device-aware текст меню
/* Y5: меню — заполняется из T (ид btnPlay и класс big сохраняются; узлы НЕ пересоздаются —
обработчики слушателей живут) */
function renderMenu(){
$('menuTitle').textContent=t('menu_title');
$('menuP1').innerHTML=(IS_TOUCH?t('menu_charge_touch'):t('menu_charge_mouse'))+'<br>'+t('menu_throw');
$('menuP2').innerHTML=t('menu_bite');
$('menuP3').innerHTML=t('menu_weight');
$('menuP4').innerHTML=t('menu_drag');
$('btnPlay').textContent=t('btn_play');
}
/* Y5: статичные подписи вне shopBody (лавка/вкладки/сброс/карточка) — из T */
function renderStaticUI(){
$('shopTitle').textContent=t('shop_title');
$('coins').title=t('shop_title'); // клик по монетам → лавка
$('btnReset').title=t('reset_title_attr');
$('tabTackle').title=t('tab_tackle');
$('tabJournal').title=t('tab_journal');
$('tabRewards').title=t('tab_rewards');
$('tabAch').title=t('tab_ach');
$('tabSkins').title=t('tab_skins');
$('tabLocations').title=t('tab_locations');
$('resetTitle').textContent=t('reset_title');
$('resetText').innerHTML=t('reset_text');
$('btnResetNo').textContent=t('btn_cancel');
$('btnResetYes').textContent=t('btn_reset');
$('pauseTitle').textContent=t('pause_title');
$('btnPauseResume').textContent=t('pause_resume');
$('btnPauseMenu').textContent=t('pause_menu');
$('btnPause').title=t('btn_pause_title');
$('gmTitle').textContent=t('gm_title');
$('btnGmResume').textContent=t('pause_resume');
$('btnGmShop').textContent=t('gm_shop');
$('btnGmAch').textContent=t('gm_ach');
$('btnGmJournal').textContent=t('gm_journal');
$('btnGmHowto').textContent=t('gm_howto');
$('btnGmAquarium').textContent=t('gm_aquarium'); // Этап 46: вход в аквариум
$('btnReset').textContent=t('gm_reset');
$('btnMute').textContent=(save.muted?'🔇':'🔊')+' '+t('gm_sound');
$('cardRecord').textContent=t('card_record');
$('cardBoss').textContent=t('card_boss');
$('btnCardOk').textContent=t('btn_ok');
$('btnRv').textContent=t('rv_btn');
syncKeepBtn(); // Этап 46: подпись/доступность «В аквариум»
}
/* Y5: пере-подпись открытых элементов карточки улова при смене языка */
function refreshCardNow(){
$('cardRecord').textContent=t('card_record');
$('cardBoss').textContent=t('card_boss');
$('btnCardOk').textContent=t('btn_ok');
$('btnRv').textContent=rvDone?t('rv_done'):t('rv_btn');
syncKeepBtn(); // Этап 46
}
function setLang(lang){
LANG=(lang==='en')?'en':'ru';
document.documentElement.lang=LANG;
document.title=t('title');
renderMenu();
renderStaticUI();
updateHUD(); // 🔊/🔇 «Звук» — подпись от языка
if(shopOpenNow())renderShop();
if(cardOpenNow())refreshCardNow();
if(aqOpen)renderAqBar(); // Этап 46: язык открытой сцены аквариума
}
/* Y5: резолюция языка — ДВЕ ветки вызова (стаб IIFE / finish(ysdk)):
forceLang → SDK i18n.lang → ru (дефолт, в т.ч. стаб) */
function resolveLang(ysdk){
let lg=forceLang;
if(!lg&&ysdk&&ysdk.environment&&ysdk.environment.i18n&&ysdk.environment.i18n.lang)lg=ysdk.environment.i18n.lang;
setLang(lg||'ru');
}
// Y1: динамическая загрузка SDK (документированный Яндексом вариант):
// http(s) — /sdk.js, file://?sdk=1 — sdk.js рядом с index.html. Первый сбой
// — один ретраи, второй — стаб + warn. 3-секундный таймер не финализирует.
(function(){
const sdkEnabled=location.protocol!=='file:'||/[?&]sdk=1/.test(location.search);
if(!sdkEnabled){ YG.init(null); resolveLang(null); return; } // file:// без ?sdk=1 — сразу стаб (язык всегда ru, кроме forceLang), скрипт не создаётся
const finish=(ysdk)=>{ YG.init(ysdk||null); resolveLang(ysdk); }; // единая точка (защита в init: идемпотентно); Y5: язык из SDK i18n.lang
let attempt=0, settled=false, slowWarned=false;
const load=()=>{
attempt++;
const s=document.createElement('script');
s.src=location.protocol==='file:'?'sdk.js':'/sdk.js'; // file:// — относительный путь
s.async=true;
s.onload=()=>{
settled=true;
if(window.YaGames){ try{ YaGames.init().then(y=>finish(y)).catch(()=>finish(null)); }catch(e){ finish(null); } }
else finish(null);
};
s.onerror=()=>{
if(attempt<2){ load(); return; } // первый сбой — один ретраи (новый скрипт с тем же src)
settled=true;
finish(null);
console.warn('Yandex SDK: загрузка не удалась, игра в стаб-режиме');
};
document.head.appendChild(s);
};
load();
setTimeout(()=>{ // страховка: не финализирует, только одноразовый warn
if(!settled&&!slowWarned){ slowWarned=true; console.warn('Yandex SDK: загрузка дольше 3 с'); }
},3000);
})();
// Y1/Y6: потеря фокуса (треб. 1.3) — пауза звука/гейма при скрытой вкладке
// (visibilitychange) И при потере фокуса окна (blur, десктоп/мобил);
// возврат — visible/focus → YG.onResume (гарды adShowing/pauseOpen внутри)
document.addEventListener('visibilitychange',()=>{ if(document.hidden) YG.onPause(); else YG.onResume(); });
window.addEventListener('blur',()=>YG.onPause());
window.addEventListener('focus',()=>YG.onResume());
/* =========================================================
ЦИКЛ
========================================================= */
let last=performance.now();
function frame(now){
if(!YG.readySent) YG.ready(); // Y1: LoadingAPI.ready() (треб. 1.19.2) — первый кадр = меню видно
const dt=Math.min(0.05,(now-last)/1000); last=now;
if(!YG.paused){ // Y1: под паузой (реклама/скрытая вкладка/платформа) замораживается всё игровое время
time+=dt;
setDayTime(dayTime+dt/DAY_LENGTH); // M3: день/ночь идут в реальном времени
updateWeather(dt); // M8: погода — в реальном времени (как день/ночь)
updateWeatherHud(); // M8: пилюля погоды в HUD (иконка + остаток секунд)
updateDecor(dt);
if(aqOpen) updateAquarium(dt); // Этап 46: жизнь банки (плавание/пузыри) поверх uiOpen-паузы
if(!uiOpen) updateGame(dt);
// Y6: синхронизация HUD-кнопок — каждый кадр вне паузы (а не только в updateGame):
// переходы в MENU/под оверлеи (btnResetYes/openShop/btnReset) не гонят updateGame,
// и без этого #btnPause оставался бы «залипшим» видимым за оверлеем
syncDom();
}
draw();
requestAnimationFrame(frame);
}
/* =========================================================
ВВОД
========================================================= */
function onDown(px,py){
ac();
if(aqOpen){ aqTap(px,py); return; } // Этап 46: в аквариуме тап выбирает рыбу (сцена вместо озера)
if(uiOpen) return;
if(state==='IDLE'){ state='CHARGE'; charge=0; power=0; }
else if(state==='APPROACH'||state==='BITE'){ hook(); }
else if(state==='REEL'&&reel){ reel.holding=true; }
}
function onUp(){
if(state==='CHARGE') doCast();
else if(state==='REEL'&&reel) reel.holding=false;
}
canvas.addEventListener('pointerdown',e=>{ e.preventDefault(); onDown(e.clientX,e.clientY); });
window.addEventListener('pointerup',onUp);
window.addEventListener('pointercancel',onUp);
document.addEventListener('contextmenu',e=>e.preventDefault());
/* =========================================================
UI: МАГАЗИН / ДНЕВНИК / КАРТОЧКА
========================================================= */
let shopTab='tackle';
let shopFromMenu=false; // лавка открыта из «Меню» — × вернёт в меню, а не в игру
function syncTabs(){
$('tabTackle').classList.toggle('active',shopTab==='tackle');
$('tabJournal').classList.toggle('active',shopTab==='journal');
$('tabRewards').classList.toggle('active',shopTab==='rewards');
$('tabAch').classList.toggle('active',shopTab==='ach');
$('tabSkins').classList.toggle('active',shopTab==='skins');
$('tabLocations').classList.toggle('active',shopTab==='locations');
}
function openShop(tab){ // tab не задан — открыть на последней вкладке (HUD 🛒); задан — принудительно (монеты/«Меню»)
if(tab!==undefined) shopTab=tab;
syncTabs();
uiOpen=true; $('shop').classList.remove('hidden'); renderShop(); sfx('click'); YG.gameplayStop();
} // Y1: геймплейная разметка
function closeShop(){
$('shop').classList.add('hidden'); sfx('click');
if(shopFromMenu){ shopFromMenu=false; $('gamemenu').classList.remove('hidden'); uiOpen=true; } // шаг назад — в «Меню» (без gameplayStart)
else{ uiOpen=false; YG.gameplayStart(); }
} // Y1: геймплейная разметка
function setTab(t){ shopTab=t; syncTabs(); renderShop(); }
function lvPips(lv){ let s=''; for(let k=1;k<=4;k++) s+=k<=lv?'●':'○'; return s; }
function itemRow(item,i,type,owned,cur){
const list=type==='rod'?RODS:LURES;
const lvMap=type==='rod'?(save.rodLv||{}):(save.lureLv||{});
const lv=owned?Math.max(1,Math.min(4,lvMap[i]>=1?lvMap[i]:1)):1;
// M5: гейтинг — снасть №i открывается, когда снасть №(i−1) того же типа на 4 ур.
const prevLv=i>0?((lvMap[list[i-1].id]>=1)?lvMap[list[i-1].id]:1):0;
const locked=!owned&&i>0&&prevLv<4;
let btns='';
if(owned){
btns+=cur?'<button class="buybtn current" disabled>'+t('btn_selected')+'</button>'
:'<button class="buybtn owned" data-act="use" data-type="'+type+'" data-i="'+i+'">'+t('btn_select')+'</button>';
if(lv<4) btns+='<button class="buybtn upgbt" data-act="upgrade" data-type="'+type+'" data-i="'+i+'">⬆ '+upgNextText(type,lv-1)+' · '+upgCost(type,i,lv)+' 🪙</button>';
}else if(!locked){
btns='<button class="buybtn" data-act="buy" data-type="'+type+'" data-i="'+i+'">'+item.cost+' 🪙</button>';
}
let desc=trDesc(item); // Y5: i18n
if(locked) desc=tt('locked_desc',{name:trName(list[i-1])});
const lvTag=owned?' <span class="lvtag">'+lvPips(lv)+' '+lv+' '+t('lvl')+'</span>':'';
return '<div class="item'+(locked?' locked':'')+'"><div><div class="itemname">'+(item.icon||'')+' '+trName(item)+lvTag+'</div>'+
'<div class="itemdesc">'+desc+'</div></div>'+
(btns?'<div class="rowbtns">'+btns+'</div>':'<div class="rowbtns"><button class="buybtn" disabled>🔒</button></div>')+'</div>';
}
/* Этап 4: строка скина (стиль как у снастей, но проще): название, цена, одна кнопка
(«Купить N 🪙» / «Выбрать» / «Выбрано ✓») */
function skinRow(list,i,owned,equipped,group){
const s=list[i];
const price=s.cost>0?s.cost+' 🪙':t('free');
let btn;
if(equipped){
btn='<button class="buybtn current" disabled>'+t('btn_selected_chk')+'</button>';
}else if(owned){
btn='<button class="buybtn owned" data-act="select" data-type="'+group+'" data-i="'+i+'">'+t('btn_select')+'</button>';
}else{
btn='<button class="buybtn" data-act="buyskin" data-type="'+group+'" data-i="'+i+'">'+t('btn_buy')+' '+price+'</button>';
}
return '<div class="item"><div><div class="itemname">'+trName(s)+'</div>'+
'<div class="itemdesc">'+t('price_label')+price+'</div></div>'+
'<div class="rowbtns">'+btn+'</div></div>';
}
/* Этап 5: строка локации (по образцу skinRow): название, подпись, цена, одна кнопка
(«Купить N 🪙» / «Выбрать» / «Выбрана ✓») */
function locationRow(loc,i,owned,selected){
const price=loc.cost>0?loc.cost+' 🪙':t('free');
let btn;
if(selected){
btn='<button class="buybtn current" disabled>'+t('btn_selected_f')+'</button>';
}else if(owned){
btn='<button class="buybtn owned" data-act="selectlocation" data-i="'+i+'">'+t('btn_select')+'</button>';
}else{
btn='<button class="buybtn" data-act="buylocation" data-i="'+i+'">'+t('btn_buy')+' '+price+'</button>';
}
return '<div class="item"><div><div class="itemname">'+trName(loc)+'</div>'+
'<div class="itemdesc">'+trDesc(loc)+' · '+price+'</div></div>'+
'<div class="rowbtns">'+btn+'</div></div>';
}
function renderShop(){
const body=$('shopBody');
if(shopTab==='tackle'){
let h='<div class="section-title">'+t('sec_rods')+'</div>';
RODS.forEach((r,i)=>{ h+=itemRow(r,i,'rod',save.rods.indexOf(i)>=0,save.rod===i); });
h+='<div class="section-title">'+t('sec_lures')+'</div>';
LURES.forEach((l,i)=>{ h+=itemRow(l,i,'lure',save.lures.indexOf(i)>=0,save.lure===i); });
body.innerHTML=h;
}else if(shopTab==='journal'){
let h='<div class="statsline">'+tt('stats',{casts:save.casts,earned:save.earned})+'</div>';
const vc=v=>v>0?String(v):'—'; // M6: не пойманный вариант — «—»
const spKeys=VARIANTS.filter(v=>v.id!=='normal').map(v=>VARIANT_KEY[v.id]);
for(const s of SPECIES){
const c=Object.assign({n:0,best:0,cp:0,sv:0,qz:0,sn:0,em:0,dm:0,rb:0},save.caught[s.id]||{});
if(c.n>0){
const nm=c.n-spKeys.reduce((a,k)=>a+(c[k]||0),0);
// M6: мини-счётчики вариантов (обычная + все особые по лестнице VARIANTS);
// Y5: i18n — подписи вариантов из VARIANTS через trName
// Фаза свечения: светящийся вид — маркер-класс + drop-shadow цвета glow на портрете
const glowAttr=s.glow?' class="glow-fish" style="filter:drop-shadow(0 0 6px '+s.glow+')"':'';
h+='<div class="jrow"><span class="je"><svg'+glowAttr+' viewBox="0 0 160 100"><use href="#fish-'+s.id+'"/></svg></span><span class="jn"><b>'+trName(s)+'</b>'+
'<span class="jv jv-n" title="'+trName(VARIANTS[0])+'">'+vc(nm)+'</span>'+
spKeys.map(k=>{const v=VARIANTS.find(x=>VARIANT_KEY[x.id]===k);
return '<span class="jv jv-'+k+'" title="'+trName(v)+'">'+vc(c[k])+'</span>';}).join('')+'<br>'+
'<span style="font-size:11px;color:'+RARITY_COLORS[s.rarity]+'">'+trRarity(s.rarity)+' · '+s.price+' '+t('per_kg')+'</span></span>'+
'<span class="jc">×'+c.n+'<br>'+tt('max_w',{w:c.best.toFixed(1)})+'</span></div>';
}else{
h+='<div class="jrow unknown"><span class="je">❓</span><span class="jn">???</span>'+
'<span class="jc">'+t('not_caught')+'</span></div>';
}
}
body.innerHTML=h;
}else if(shopTab==='rewards'){
// 🎯 Награды: стрик входа + квесты дня
const st=save.streak;
const idx=st.days>0?streakIdx(st.days):0;
let h='<div class="section-title">'+t('sec_streak')+'</div><div class="streak">';
for(let i=1;i<=7;i++){
h+='<span class="st-dot'+(st.days>0&&i<=idx?' on':'')+(st.days>0&&i===idx?' cur':'')+'">'+STREAK_REWARDS[i-1]+'</span>';
}
h+='</div>';
h+='<div class="statsline">'+tt('streak_line',{d:(st.days||0),n:nextStreakReward(st.days||1)})+'</div>';
h+='<div class="section-title">'+t('sec_quests')+'</div>';
const ql=(save.quests&&save.quests.list)||[];
if(ql.length){
for(const q of ql){
h+='<div class="jrow'+(q.done?' qdone':'')+'"><span class="je">'+questIcon(q)+'</span>'+
'<span class="jn"><b>'+questText(q)+'</b></span>'+
'<span class="jc">'+(q.done?'✓ +'+q.reward+' 🪙':q.prog+'/'+q.goal)+'</span></div>';
}
}else{
h+='<div class="statsline">'+t('quests_done_today')+'</div>';
}
body.innerHTML=h;
}else if(shopTab==='ach'){
// 🏅 Достижения: выданные — яркие, остальные приглушённые
let h='<div class="section-title">'+t('sec_ach')+'</div>';
for(const a of ACHIEVEMENTS){
const got=!(!(save.ach&&save.ach[a.id]));
h+='<div class="jrow'+(got?' got':' locked')+'"><span class="je">'+a.icon+'</span>'+
'<span class="jn"><b>'+trName(a)+'</b><br><span class="adesc">'+trDesc(a)+' · '+bonusText(a.bonus)+'</span></span>'+
(got?'<span class="jc">✓</span>':'')+'</div>';
}
body.innerHTML=h;
}else if(shopTab==='skins'){
// Этап 4: скины — лодка и поплавок (чистая косметика, coin-sink)
let h='<div class="section-title">'+t('sec_boat')+'</div>';
BOAT_SKINS.forEach((s,i)=>{ h+=skinRow(BOAT_SKINS,i,save.boatSkins.indexOf(i)>=0,save.boatSkin===i,'boat'); });
h+='<div class="section-title">'+t('sec_bobber')+'</div>';
BOBBER_SKINS.forEach((s,i)=>{ h+=skinRow(BOBBER_SKINS,i,save.bobberSkins.indexOf(i)>=0,save.bobberSkin===i,'bobber'); });
body.innerHTML=h;
}else if(shopTab==='locations'){
// Этап 5: локации — Река (родная вода) и Озёра (65536 🪙, новые рыбы)
let h='<div class="section-title">'+t('sec_locations')+'</div>';
LOCATIONS.forEach((loc,i)=>{ h+=locationRow(loc,i,save.locations.indexOf(i)>=0,save.location===i); });
body.innerHTML=h;
}
}
function shopClick(e){
const b=e.target.closest('[data-act]');
if(!b) return;
const type=b.dataset.type, i=+b.dataset.i;
if(b.dataset.act==='buylocation'||b.dataset.act==='selectlocation'){
// Этап 5: локации — покупка (монеты списать, push, АВТО-ПЕРЕХОД) и выбор
const loc=LOCATIONS[i];
if(!loc) return;
if(loc.id!==i) return; // защита: индекс массива не обязан совпадать с id при будущей вставке записей
if(b.dataset.act==='buylocation'){
if(save.locations.indexOf(i)>=0){ sfx('no'); return; } // уже куплено — защита
if(save.coins<loc.cost){ sfx('no'); toast(t('not_enough')); return; }
save.coins-=loc.cost; save.locations.push(i);
save.location=i; // авто-переход на купленную локацию
initFish(); // Этап 43: фоновые рыбы по новой воде
initScenery(); // Этап 44: силуэты новой воды
persist(); updateHUD(); sfx('coin'); renderShop();
// Этап 35/36/45: у каждой локации свой тост (id 0 «Река» не покупается — общий «Куплено»)
let buyToast;
if(loc.id===1) buyToast=t('welcome_lakes');
else if(loc.id===2) buyToast=t('welcome_sea');
else if(loc.id===3) buyToast=t('welcome_ocean');
else if(loc.id===4) buyToast=t('welcome_swamp');
else buyToast=tt('bought',{name:trName(loc)});
toast(buyToast);
}else{
if(save.locations.indexOf(i)<0){ sfx('no'); return; } // не куплено — выбрать нельзя (паттерн защиты buy)
save.location=i;
initFish(); // Этап 43: фоновые рыбы по новой воде
initScenery(); // Этап 44: силуэты новой воды
persist(); updateHUD(); sfx('click'); renderShop();
toast(tt('location_set',{name:trName(loc)}));
}
return;
}
if(type==='boat'||type==='bobber'){
// Этап 4: скины — покупка (монеты списать, авто-экипировка) и выбор
const slist=type==='boat'?BOAT_SKINS:BOBBER_SKINS;
const owned=type==='boat'?save.boatSkins:save.bobberSkins;
const item=slist[i];
if(b.dataset.act==='buyskin'){
if(!item||owned.indexOf(i)>=0){ sfx('no'); return; } // уже куплено — защита
if(save.coins<item.cost){ sfx('no'); toast(t('not_enough')); return; }
save.coins-=item.cost; owned.push(i);
if(type==='boat') save.boatSkin=i; else save.bobberSkin=i; // авто-экипировка
persist(); updateHUD(); sfx('coin'); renderShop();
toast(tt('bought',{name:trName(item)}));
}else{
if(!item||owned.indexOf(i)<0){ sfx('no'); return; } // не куплено — выбрать нельзя (паттерн защиты buy)
if(type==='boat') save.boatSkin=i; else save.bobberSkin=i;
persist(); updateHUD(); sfx('click'); renderShop();
}
return;
}
const list=type==='rod'?RODS:LURES;
const owned=type==='rod'?save.rods:save.lures;
if(!save.rodLv||typeof save.rodLv!=='object') save.rodLv={};
if(!save.lureLv||typeof save.lureLv!=='object') save.lureLv={};
const lvMap=type==='rod'?save.rodLv:save.lureLv;
const item=list[i];
if(b.dataset.act==='buy'){
if(i>0&&lvMap[list[i-1].id]<4){ sfx('no'); toast(tt('gate_toast',{name:trName(list[i-1])})); return; } // M5: гейтинг
if(save.coins<item.cost){ sfx('no'); toast(t('not_enough')); return; }
save.coins-=item.cost; owned.push(i); lvMap[i]=1; // куплено — уровень 1
if(type==='rod') save.rod=i; else save.lure=i;
persist(); updateHUD(); sfx('coin'); renderShop();
toast(tt('bought',{name:trName(item)}));
checkAchievements();
}else if(b.dataset.act==='upgrade'){
if(owned.indexOf(i)<0){ sfx('no'); return; } // M5: не куплено — апгрейд недоступен (паттерн защиты buy)
const lv=(lvMap[i]>=1)?lvMap[i]:1;
if(lv>=4) return;
const cost=upgCost(type,i,lv);
if(save.coins<cost){ sfx('no'); toast(t('not_enough')); return; }
save.coins-=cost; lvMap[i]=lv+1; // M5: апгрейд — уровень++
persist(); updateHUD(); sfx('coin'); renderShop();
toast(tt('upgraded',{name:trName(item),lv:lv+1}));
checkAchievements();
}else{
if(type==='rod') save.rod=i; else save.lure=i;
persist(); updateHUD(); sfx('click'); renderShop();
}
}
let cardSpec=null; // Этап 46: экземпляр открытой карточки {sp,w,variant} — для кнопки «В аквариум»
function syncKeepBtn(){ // Этап 46: доступность «В аквариум» — нет свободного слота → disabled + «Нет места»
const full=save.aqFish.length>=save.aqSlots;
$('btnKeep').disabled=full;
$('btnKeep').textContent=full?t('aq_no_room'):t('aq_keep');
$('btnKeep').title=full?t('aq_no_room'):t('aq_keep');
}
function showCard(sp,w,val,rec,variant,boss){
cardSpec={sp:sp,w:w,variant:variant}; // Этап 46: копия экземпляра для аквариума (модель C; catchFish не трогаем)
syncKeepBtn();
$('cardRarity').textContent=trRarity(sp.rarity);
$('cardRarity').style.color=RARITY_COLORS[sp.rarity];
document.getElementById('cardFishUse').setAttribute('href','#fish-'+sp.id); // SVG-портрет вида (спрайт; эмодзи рыб убраны — этап 42)
$('cardName').textContent=trName(sp)+(boss?t('card_boss_suffix'):''); // M4: имя босса; Y5: i18n
$('cardSub').textContent=tt('card_sub',{w:w.toFixed(1),price:sp.price});
$('cardVal').textContent='+'+val+' 🪙';
$('cardRecord').classList.toggle('hidden',!rec);
// M6: бейдж варианта + оформление карточки (по лестнице VARIANTS; normal — без оформления)
for(const v of VARIANTS){ if(v.id==='normal') continue;
$('card').classList.toggle(v.css+'-card',variant.id===v.id);
}
$('cardVariant').classList.toggle('hidden',variant.id==='normal');
$('cardVariant').textContent=trName(variant).toUpperCase();
// M4: босс: бейдж 🏆 + золотое оформление карточки
$('cardBoss').classList.toggle('hidden',!boss);
$('card').classList.toggle('boss-card',!!boss);
// Фаза свечения: светящийся вид (glow) в normal-варианте — цветной drop-shadow портрета;
// CSS-переменная --glow читается правилом #card.glow-card .bigemoji. Свечение — оформление
// вида (не зависит от времени суток), вариант normal — чтобы не спорить с фильтрами вариантов
const glowOn=!!sp.glow&&variant.id==='normal';
$('card').classList.toggle('glow-card',glowOn);
if(sp.glow) $('card').style.setProperty('--glow',sp.glow);
// Y2: rewarded «монеты ×2» — бонус = полная цена улова (rvVal), сброс флага
rvVal=val; rvDone=false;
$('btnRv').classList.remove('hidden');
$('btnRv').disabled=false;
$('btnRv').textContent=t('rv_btn');
$('card').classList.remove('hidden');
}
/* =========================================================
КНОПКИ
========================================================= */
$('btnPlay').addEventListener('click',()=>{
ac(); sfx('click');
$('menu').classList.add('hidden');
$('gamemenu').classList.add('hidden'); // инструкции могли быть открыты из «Меню»
shopFromMenu=false;
uiOpen=false; state='IDLE';
if(!YG.paused) YG.gameplayStart(); // Y1: геймплейная разметка (треб. 1.19.3; под паузой резюме поднимет сам)
});
$('btnShop').addEventListener('click',()=>openShop()); // без аргумента — последняя вкладка
$('coins').addEventListener('click',()=>openShop('tackle')); // монеты → лавка, вкладка «Лавка»
$('btnCloseShop').addEventListener('click',closeShop);
$('tabTackle').addEventListener('click',()=>{ setTab('tackle'); sfx('click'); });
$('tabJournal').addEventListener('click',()=>{ setTab('journal'); sfx('click'); });
$('tabRewards').addEventListener('click',()=>{ setTab('rewards'); sfx('click'); });
$('tabAch').addEventListener('click',()=>{ setTab('ach'); sfx('click'); });
$('tabSkins').addEventListener('click',()=>{ setTab('skins'); sfx('click'); }); // Этап 4: вкладка скинов
$('tabLocations').addEventListener('click',()=>{ setTab('locations'); sfx('click'); }); // Этап 5: вкладка локаций
$('shopBody').addEventListener('click',shopClick);
$('btnMute').addEventListener('click',()=>{
save.muted=!save.muted; persist(); updateHUD(); sfx('click');
});
$('btnRecall').addEventListener('click',()=>{ recall(); });
// Y6: кнопка паузы (треб. Яндекс Игр 6.3) — оверлей «Продолжить/В меню».
// Общие гарды: паузу можно открыть только в игре (не MENU, без оверлеев и рекламы);
// снять её может только closePause()/«В меню» (YG.onResume под гардом pauseOpen).
function openPause(){
if(state==='MENU'||uiOpen||adShowing||pauseOpen) return;
sfx('click');
pauseOpen=true; uiOpen=true;
YG.onPause();
$('pause').classList.remove('hidden');
$('btnPause').classList.add('hidden'); // под паузой updateGame/syncDom не гонятся — прячем руками
}
function closePause(){
if(!pauseOpen) return;
sfx('click');
pauseOpen=false; uiOpen=(state==='MENU');
$('pause').classList.add('hidden');
YG.onResume();
}
$('btnPause').addEventListener('click',openPause);
$('btnPauseResume').addEventListener('click',closePause);
/* Y6/этап «Меню»: «В меню» из паузы открывает хаб #gamemenu — state НЕ меняем,
сессия (заброс/вываживание) заморожена под оверлеем до «Продолжить».
YG.onResume: state!=='MENU', но uiOpen=true — gameplayStart не вызовет
(onPause при открытии паузы уже сделал gameplayStop — счётчики сбалансированы). */
$('btnPauseMenu').addEventListener('click',()=>{
sfx('click');
pauseOpen=false;
$('pause').classList.add('hidden');
$('gamemenu').classList.remove('hidden');
uiOpen=true;
YG.onResume();
});
/* Кнопки хаба «Меню» */
$('btnGmResume').addEventListener('click',()=>{
sfx('click');
$('gamemenu').classList.add('hidden');
uiOpen=false;
YG.gameplayStart(); // Y1: возврат к геймплею (сессия сохранена — продолжается с того же места)
});
function fromMenuShop(tab){ // лавка из «Меню»: гменю прячем (#gamemenu в DOM позже #shop — иначе перекрыл бы лавку), × вернёт назад
shopFromMenu=true;
$('gamemenu').classList.add('hidden');
openShop(tab);
}
$('btnGmShop').addEventListener('click',()=>fromMenuShop('tackle'));
$('btnGmAch').addEventListener('click',()=>fromMenuShop('ach'));
$('btnGmJournal').addEventListener('click',()=>fromMenuShop('journal'));
/* Этап 46: вход в аквариум из «Меню» + бар сцены (назад/слот/продажа из банки) */
$('btnGmAquarium').addEventListener('click',()=>openAquarium());
$('btnAqBack').addEventListener('click',()=>closeAquarium());
$('btnAqSlot').addEventListener('click',()=>{
if(save.aqSlots>=AQ_MAX_SLOTS){ sfx('no'); return; } // максимум — кнопка disabled, защита
const cost=AQ_SLOT_COSTS[save.aqSlots-AQ_BASE_SLOTS];
if(!Number.isFinite(cost)){ sfx('no'); return; } // Этап 46: битый aqSlots (индекс вне лестницы) — не даём NaN
if(save.coins<cost){ sfx('no'); toast(t('not_enough')); return; } // паттерн shopClick
save.coins-=cost; save.aqSlots++;
persist(); updateHUD(); sfx('coin');
renderAqBar(); checkAchievements();
});
$('btnAqRelease').addEventListener('click',()=>aqReleaseSel());
$('btnGmHowto').addEventListener('click',()=>{
sfx('click');
$('gamemenu').classList.add('hidden');
$('menu').classList.remove('hidden'); // начальные инструкции; «Играть» вернёт в игру
});
$('btnReset').addEventListener('click',()=>{
sfx('click');
$('gamemenu').classList.add('hidden'); // #resetDlg в DOM раньше #gamemenu — иначе оказался бы под ним
uiOpen=true; // подтверждение поверх (единственный путь к сбросу — из «Меню»)
$('resetDlg').classList.remove('hidden');
// gameplayStop не нужен: в «Меню» попадаем только из паузы — onPause уже остановил геймплей
});
$('btnResetNo').addEventListener('click',()=>{
sfx('click');
$('resetDlg').classList.add('hidden');
$('gamemenu').classList.remove('hidden'); // отмена → возврат в «Меню»
uiOpen=true;
});
$('btnResetYes').addEventListener('click',()=>{
const muted=save.muted; // настройка звука — не игровые данные, оставляем
save=freshSave(muted);
persist();
refreshQuests(); // квесты дня; стрик начнётся с первой загрузки (чистый старт, coins=0)
reel=null; biteFish=null;
stateBoss=false; bossPhase=0; // M4: босс-бой сброшен
forceBite=false; forceBoss=false; forceVariant=null; forceWeather=null; // T2b: тест-форсы тоже сбрасываем (симметрия); M8: погода
weather=null; weatherT=0; weatherGap=weatherGapNext(); // M8: чистый старт — новый погодный таймер
$('resetDlg').classList.add('hidden');
$('shop').classList.add('hidden');
$('card').classList.add('hidden');
$('gamemenu').classList.add('hidden');
shopFromMenu=false;
state='MENU'; uiOpen=true;
$('menu').classList.remove('hidden');
updateHUD(); renderShop();
sfx('no'); vib(50);
toast(t('progress_reset'));
});
// Y2: interstitial на закрытии карточки улова (треб. 4.4 — логическая пауза).
// Гейт: первый показ не раньше AD_FIRST_MS сессии, затем — через AD_COOLDOWN_MS.
// persist() — ПЕРЕД паузой (треб. 4.2: процесс сохранён до перехода на рекламу);
// YG.onPause() — звук и весь игровой процесс (треб. 4.7). В стабе showFullscreen
// вернёт false — endAd() сразу, игра не зависает в adShowing=true.
function tryInterstitial(){
if(adShowing) return;
const now=Date.now();
if(now-sessionStartTs < AD_FIRST_MS) return;
if(now-lastAdTs < AD_COOLDOWN_MS) return;
lastAdTs=now; // отмечаем ПОПЫТКУ, а не показ: при троттлинге платформы (wasShown=false) кулдаун сгорает — это осознанно, ограничивает частоту вызовов (retry-storm)
adShowing=true;
persist(); // 4.2: процесс сохранён до перехода на рекламу
YG.onPause(); // 4.7: звук + весь игровой процесс на паузе
const ok=YG.showFullscreen({
onOpen(){},
onClose(){ endAd(); }, // onClose(wasShown) приходит и при троттлинге
onError(){ endAd(); }
});
if(!ok) endAd(); // стаб: не показана — сразу резюм
}
function endAd(){ clearTimeout(YG.adWatchdog); YG.adWatchdog=null; adShowing=false; YG.onResume(); } // Y2: реклама закрыта/ошибка — снимаем паузу и останавливаем watchdog
$('btnCardOk').addEventListener('click',()=>{
sfx('click');
$('card').classList.add('hidden');
state='IDLE';
tryInterstitial(); // Y2: после возврата в IDLE; гейт может пропустить (внутри return)
});
/* Этап 46: «В аквариум» (модель C — бесплатная КОПИЯ экземпляра). Монеты/квесты/ачивки улова
уже начислены catchFish; здесь только push в save.aqFish + persist. Карточка закрывается
как при «Отлично!» — интерстишел тем же гейтом. */
$('btnKeep').addEventListener('click',()=>{
if(!cardSpec) return;
if(save.aqFish.length>=save.aqSlots){ sfx('no'); toast(t('aq_no_room')); return; }
sfx('click');
save.aqFish.push({id:cardSpec.sp.id,v:cardSpec.variant.id,w:cardSpec.w});
persist();
checkAchievements();
toast(t('aq_kept'));
cardSpec=null;
$('card').classList.add('hidden');
state='IDLE';
tryInterstitial();
});
// Y2: rewarded «монеты ×2» (треб. 4.5.1 — что показываем и что даём, написано на
// кнопке и в title; 4.5.2 — чистый бонус, игру не блокирует: карточка остаётся,
// закрыть можно в любой момент). В стабе onRewarded срабатывает синхронно — весь
// флоу в одном клике; в реальном SDK onRewarded придёт после просмотра, onClose —
// позже (endAd именно через onClose, не раньше).
$('btnRv').addEventListener('click',()=>{
if(rvDone||adShowing) return;
sfx('click');
adShowing=true;
YG.onPause(); // 4.7
const ok=YG.showRewarded({
onOpen(){},
onRewarded(){
rvDone=true;
save.coins+=rvVal; // бонус = полная цена улова ещё раз
persist(); updateHUD();
$('cardVal').textContent='+'+(rvVal*2)+' 🪙';
$('btnRv').disabled=true;
$('btnRv').textContent=t('rv_done');
sfx('coin'); vib(30);
},
onClose(){ endAd(); },
onError(){ endAd(); }
});
if(!ok) endAd();
});
/* Закрытие по тапу мимо диалога: тап «в молоко» — на подложку оверлея (e.target === сам
оверлей, а не .panel) — закрывает его тем же действием, что и кнопка закрытия.
pointerdown — отзывчиво, как и весь ввод игры. #menu не диалог (экран старта) — вне списка. */
function backdropClose(el,fn){ el.addEventListener('pointerdown',e=>{ if(e.target===el) fn(); }); }
backdropClose($('shop'),closeShop); // лавка — как ×
backdropClose($('resetDlg'),()=>$('btnResetNo').click()); // сброс — как «Отмена» (в «Меню»)
backdropClose($('pause'),closePause); // пауза — как «Продолжить»
backdropClose($('gamemenu'),()=>$('btnGmResume').click()); // хаб — как «Продолжить» (сессия с места)
backdropClose($('card'),()=>$('btnCardOk').click()); // карточка — как «Отлично!» (вкл. интерстишел)
/* =========================================================
СТАРТ
========================================================= */
loadSave();
resize();
initFish();
updateHUD();
renderMenu(); // Y5: i18n — меню из T (device-aware; стаб-ветка уже вызывала через resolveLang, это страховка)
renderStaticUI(); // Y5: i18n — статичные подписи (лавка/вкладки/сброс/карточка)
// Э28: встроенный emoji-шрифт — дождаться @font-face перед первым кадром (канвас-emoji 💥🌀⚡💪💤
// рисуются им); data: URI грузится быстро, timeout-фолбэк 1.5 с — игра не «висит» даже на file://
const fontsGate=(document.fonts&&document.fonts.load)
?Promise.race([document.fonts.load('16px "EmojiEmbedded"').catch(()=>{}),new Promise(r=>setTimeout(r,1500))])
:Promise.resolve();
fontsGate.then(()=>requestAnimationFrame(frame));
</script>
</body>
</html>