Визуал: полировка интерфейса, воды, неба и солнца/луны по фазам дня, анимаций рыбы и поплавка, карточки улова, лавки и дневника, тостов, адаптивности
This commit is contained in:
+302
-63
@@ -16,56 +16,86 @@ button{font-family:inherit;cursor:pointer}
|
||||
/* ---------- 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}
|
||||
#coins{background:rgba(6,25,45,.55);padding:8px 14px;border-radius:999px;color:#ffe9a8;font-weight:700;
|
||||
font-size:17px;border:1px solid rgba(255,255,255,.15)}
|
||||
#coins{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)}
|
||||
.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,.15);background:rgba(6,25,45,.55);color:#fff}
|
||||
.hudbtns button:active{background:rgba(30,60,95,.8)}
|
||||
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:rgba(6,25,45,.65);color:#fff;
|
||||
border:1px solid rgba(255,255,255,.2);z-index:10}
|
||||
#btnRecall:active{background:rgba(30,60,95,.85)}
|
||||
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:rgba(6,25,45,.9);color:#fff;padding:10px 18px;border-radius:999px;font-size:15px;font-weight:600;
|
||||
background:linear-gradient(rgba(9,32,58,.94),rgba(5,20,38,.92));color:#fff;padding:11px 20px;border-radius:999px;font-size:15px;font-weight:600;
|
||||
z-index:30;opacity:0;transition:opacity .25s;pointer-events:none;white-space:nowrap;
|
||||
border:1px solid rgba(255,255,255,.15)}
|
||||
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);
|
||||
max-width:92vw;overflow:hidden;text-overflow:ellipsis}
|
||||
|
||||
/* ---------- Overlays ---------- */
|
||||
.overlay{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;
|
||||
background:rgba(6,20,38,.6);backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px);z-index:20;padding:16px}
|
||||
.panel{position:relative;background:linear-gradient(#14406b,#0d2f4e);border:2px solid #2e6da3;border-radius:20px;
|
||||
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 10px 40px rgba(0,0,0,.5)}
|
||||
.title{font-size:34px;font-weight:800;letter-spacing:1px;margin-bottom:10px}
|
||||
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(#ffd76e,#f5a623);border:none;border-radius:14px;box-shadow:0 4px 0 #b97708}
|
||||
.big:active{transform:translateY(2px);box-shadow:0 2px 0 #b97708}
|
||||
.panelhead{font-size:22px;font-weight:800;margin-bottom:4px}
|
||||
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}
|
||||
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}
|
||||
.tabs button.active{background:#f5a623;color:#04263d}
|
||||
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,.08);border-radius:12px;padding:10px 12px;margin-bottom:8px;text-align:left}
|
||||
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}
|
||||
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.current{background:#2ecc71;color:#04263d}
|
||||
.buybtn:disabled{opacity:.45}
|
||||
.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}
|
||||
.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-radius:10px;margin-bottom:6px;font-size:14px;text-align:left}
|
||||
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}
|
||||
.jrow .jn{flex:1}
|
||||
.jrow .jc{color:#9fc3e2;font-weight:600;font-size:13px}
|
||||
@@ -78,17 +108,20 @@ button{font-family:inherit;cursor:pointer}
|
||||
.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)}
|
||||
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}
|
||||
#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))}
|
||||
@keyframes bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-9px)}}
|
||||
#card .rarity{font-weight:800;font-size:13px;letter-spacing:1.5px;text-transform:uppercase}
|
||||
#card .cname{font-size:24px;font-weight:800;margin:2px 0 6px}
|
||||
#card .cval{font-size:20px;font-weight:800;color:#ffe9a8;margin-bottom:6px}
|
||||
#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}
|
||||
.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)}}
|
||||
.shiny-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}
|
||||
@@ -103,8 +136,32 @@ button{font-family:inherit;cursor:pointer}
|
||||
|
||||
.twobtns{display:flex;gap:10px;margin-top:16px}
|
||||
.twobtns .big{margin-top:0;width:auto;flex:1}
|
||||
.big.danger{background:linear-gradient(#ff8a7a,#e74c3c);box-shadow:0 4px 0 #96281b;color:#fff}
|
||||
.big.danger:active{box-shadow:0 2px 0 #96281b}
|
||||
.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 .cname{font-size:21px}
|
||||
.hudbtns button{width:42px;height:42px;font-size:19px;border-radius:12px}
|
||||
#coins{font-size:15px;padding:7px 12px}
|
||||
.tabs button{padding:10px 6px;font-size:13.5px}
|
||||
.jrow .je{font-size:20px;width: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}
|
||||
.big{margin-top:10px;padding:11px;font-size:16px}
|
||||
.tabs button{padding:8px}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -555,6 +612,7 @@ let stateBoss=false, bossPhase=0; // M4: босс-бой (Осётр-босс, 3
|
||||
let ambient=[]; // фоновые рыбы
|
||||
let clouds=[], sparkles=[];
|
||||
let particles=[], ripples=[];
|
||||
let bobberTrail=[]; // визуал: след поплавка при забросе
|
||||
|
||||
const rod=()=>RODS[save.rod];
|
||||
const lure=()=>LURES[save.lure];
|
||||
@@ -580,6 +638,15 @@ function initSparkles(){
|
||||
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)});
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
@@ -909,30 +976,100 @@ const SKY_PHASES={ // M3: градиенты неба по фазе суток
|
||||
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=SKY_PHASES[dayPhase];
|
||||
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);
|
||||
// M3: солнце/луна по дуге
|
||||
// звёзды — проступают с ночью, мерцают
|
||||
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;
|
||||
if(dayPhase!=='night'){
|
||||
ctx.fillStyle='rgba(255,230,120,0.35)';
|
||||
ctx.beginPath(); ctx.arc(bx,by,34,0,7); ctx.fill();
|
||||
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();
|
||||
}else{
|
||||
// луна — полумесяц (тень вырезается цветом неба)
|
||||
ctx.fillStyle='rgba(245,245,220,0.22)';
|
||||
ctx.beginPath(); ctx.arc(bx,by,28,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+8,by-5,15,0,7); ctx.fill();
|
||||
ctx.beginPath(); ctx.arc(bx+9,by-6,13.5,0,7); ctx.fill();
|
||||
ctx.globalAlpha=1;
|
||||
}
|
||||
// облака (ночью приглушены)
|
||||
ctx.fillStyle='rgba(255,255,255,'+(dayPhase==='night'?0.25:dayPhase==='evening'?0.6:0.9)+')';
|
||||
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();
|
||||
@@ -941,45 +1078,83 @@ function drawSky(){
|
||||
}
|
||||
}
|
||||
function drawWater(){
|
||||
const nm=nightMix();
|
||||
// градиент воды: день ↔ ночь, плавно (визуал)
|
||||
const top=hexLerp('#2f9bd8','#0d2246',nm), bot=hexLerp('#0a2f52','#041226',nm);
|
||||
const g=ctx.createLinearGradient(0,waterY,0,H);
|
||||
g.addColorStop(0,'#2f9bd8'); g.addColorStop(1,'#0a2f52');
|
||||
g.addColorStop(0,top); g.addColorStop(0.55,hexLerp('#1f6fb0','#091c3a',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;
|
||||
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); }
|
||||
}
|
||||
// волна на поверхности
|
||||
ctx.strokeStyle='rgba(255,255,255,0.55)'; ctx.lineWidth=2;
|
||||
// волна на поверхности + вторая, глубинная (визуал)
|
||||
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*0.05+time*2)*2.5;
|
||||
if(x===0) ctx.moveTo(x,y); else ctx.lineTo(x,y);
|
||||
}
|
||||
ctx.stroke();
|
||||
// M3: тёмный оверлей ночью (opacity 0→0.3, зависит от фазы)
|
||||
const nd={morning:0.05,day:0,evening:0.16,night:0.3}[dayPhase];
|
||||
if(nd>0){ ctx.fillStyle='rgba(4,10,38,'+nd+')'; ctx.fillRect(0,waterY,W,H-waterY); }
|
||||
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*0.04-time*1.4+1.7)*3;
|
||||
if(x===0) ctx.moveTo(x,y); else ctx.lineTo(x,y);
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
function drawFishShape(x,y,size,dir,hue,alpha){
|
||||
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(-size*0.8,0); ctx.lineTo(-size*1.45,-size*0.42); ctx.lineTo(-size*1.45,size*0.42);
|
||||
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(){
|
||||
for(const f of ambient){
|
||||
const y=waterY+20+f.depth*(H-waterY-50)+Math.sin(time*2+f.ph)*2;
|
||||
drawFishShape(f.x,y,f.size,f.dir,f.hue,0.22+0.2*(1-f.depth));
|
||||
drawFishShape(f.x,y,f.size,f.dir,f.hue,0.22+0.2*(1-f.depth),Math.sin(time*5+f.ph)*0.28);
|
||||
}
|
||||
}
|
||||
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();
|
||||
// лодка
|
||||
ctx.fillStyle='#b0622d';
|
||||
const bg=ctx.createLinearGradient(bx-32,by,bx+32,by+15);
|
||||
bg.addColorStop(0,'#c9773c'); bg.addColorStop(1,'#96521f');
|
||||
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();
|
||||
@@ -1002,7 +1177,19 @@ function drawBoat(){
|
||||
ctx.strokeStyle='#5d4023'; ctx.lineWidth=2;
|
||||
ctx.beginPath(); ctx.moveTo(bx-24,by); ctx.lineTo(bx-24,by-22); ctx.stroke();
|
||||
ctx.fillStyle='#f5a623';
|
||||
ctx.beginPath(); ctx.moveTo(bx-24,by-22); ctx.lineTo(bx-12,by-18); ctx.lineTo(bx-24,by-14); ctx.closePath(); ctx.fill();
|
||||
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;
|
||||
@@ -1012,14 +1199,23 @@ function drawLineTo(x,y,sag){
|
||||
}
|
||||
function drawBobber(){
|
||||
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();
|
||||
ctx.fillStyle='#fff';
|
||||
ctx.beginPath(); ctx.arc(x,y,5.5,0,7); ctx.fill();
|
||||
ctx.fillStyle='#e74c3c';
|
||||
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='rgba(255,255,255,0.9)'; ctx.lineWidth=1.5;
|
||||
ctx.beginPath(); ctx.arc(x,y,5.5,0,7); 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;
|
||||
@@ -1034,12 +1230,22 @@ function drawLineAndBobber(){
|
||||
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 system-ui'; ctx.textAlign='center';
|
||||
ctx.fillText('💥',castTo.x,waterY-20);
|
||||
ctx.shadowBlur=0;
|
||||
ctx.globalAlpha=1;
|
||||
return;
|
||||
}
|
||||
if(state==='CAST'){ drawLineTo(bobber.x,bobber.y,14); drawBobber(); 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;
|
||||
}
|
||||
const sag=state==='REEL'?(reel.phase==='pull'?4:12):18+Math.sin(time*2)*3;
|
||||
drawLineTo(bobber.x,bobber.y,sag);
|
||||
drawBobber();
|
||||
@@ -1059,7 +1265,12 @@ function drawHookedFish(){
|
||||
const y=waterY+26+Math.sin(time*5)*3;
|
||||
const dir=Math.sin(time*2.5)>0?1:-1;
|
||||
const fs=8+(reel.w/sp.wMax)*10+sp.rarity;
|
||||
drawFishShape(x,y,reel.boss?fs*1.5:fs,dir,sp.hue,0.75); // силуэт крупнее у тяжёлых, босс ×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);
|
||||
}
|
||||
drawFishShape(x,y,reel.boss?fs*1.5:fs,dir,sp.hue,0.75,Math.sin(time*9)*0.3); // силуэт крупнее у тяжёлых, босс ×1.5
|
||||
}
|
||||
function drawParticles(){
|
||||
for(const p of particles){
|
||||
@@ -1078,11 +1289,21 @@ function drawMeters(){
|
||||
if(state!=='REEL'||!reel) return;
|
||||
const r=rod(), sp=reel.sp;
|
||||
const pulling=reel.phase==='pull';
|
||||
// M4: босс-бой — золотая полоса фазы над метрами
|
||||
// M4: босс-бой — золотая полоса фазы над метрами (визуал: пульс + точки фаз)
|
||||
if(reel.boss){
|
||||
ctx.font='800 16px system-ui'; ctx.textAlign='center';
|
||||
const pulse=1+Math.sin(time*5)*0.08;
|
||||
ctx.textAlign='center';
|
||||
ctx.font='800 '+(16*pulse).toFixed(1)+'px system-ui';
|
||||
ctx.fillStyle='#ffd75e';
|
||||
ctx.shadowColor='rgba(255,180,40,0.8)'; ctx.shadowBlur=10;
|
||||
ctx.fillText('⚠️ БОСС-РЫБА · ФАЗА '+bossPhase+'/3',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;
|
||||
@@ -1091,8 +1312,10 @@ function drawMeters(){
|
||||
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();
|
||||
@@ -1104,7 +1327,9 @@ function drawMeters(){
|
||||
// rem = доля оставшейся дистанции: полоска «сгорает» к лодке, рыбка на её конце
|
||||
const rem=Math.max(0,Math.min(1,reel.d/reel.d0));
|
||||
if(rem>0.01){
|
||||
ctx.fillStyle='#3fa7e8';
|
||||
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();
|
||||
}
|
||||
// рыбка-маркер на конце полоски
|
||||
@@ -1117,10 +1342,14 @@ function drawMeters(){
|
||||
ctx.font='700 15px system-ui'; 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('⚡ РЫБА ТЯНЕТ — ОТПУСТИ! ⚡',W/2,dy-30);
|
||||
ctx.shadowBlur=0;
|
||||
}else{
|
||||
ctx.fillStyle='#7ef0a8';
|
||||
ctx.shadowColor='rgba(0,0,0,0.6)'; ctx.shadowBlur=4;
|
||||
ctx.fillText('Держи палец — втягивай!',W/2,dy-30);
|
||||
ctx.shadowBlur=0;
|
||||
}
|
||||
}
|
||||
function drawPowerBar(){
|
||||
@@ -1130,10 +1359,18 @@ function drawPowerBar(){
|
||||
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){ rr(x+2,y+2,Math.max(2,(w-4)*power),h-4,6); ctx.fill(); }
|
||||
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 system-ui'; ctx.textAlign='center';
|
||||
ctx.fillStyle='#fff';
|
||||
ctx.shadowColor='rgba(0,0,0,0.65)'; ctx.shadowBlur=4;
|
||||
ctx.fillText('Отпусти — бросок!',W/2,y-10);
|
||||
ctx.shadowBlur=0;
|
||||
}
|
||||
function drawHint(){
|
||||
if(state!=='IDLE'||save.casts>=5) return;
|
||||
@@ -1141,7 +1378,9 @@ function drawHint(){
|
||||
ctx.globalAlpha=a;
|
||||
ctx.font='700 17px system-ui'; ctx.textAlign='center';
|
||||
ctx.fillStyle='#fff';
|
||||
ctx.shadowColor='rgba(0,0,0,0.65)'; ctx.shadowBlur=5;
|
||||
ctx.fillText('👆 Держи палец — заряжай бросок',W/2,H-70);
|
||||
ctx.shadowBlur=0;
|
||||
ctx.globalAlpha=1;
|
||||
}
|
||||
function draw(){
|
||||
@@ -1153,8 +1392,8 @@ function draw(){
|
||||
drawSky();
|
||||
drawWater();
|
||||
drawAmbientFish();
|
||||
if(biteFish&&biteFish.mode!=='out') drawFishShape(biteFish.x,biteFish.y,12+biteFish.sp.rarity*2.5,biteFish.dir,biteFish.sp.hue,0.8);
|
||||
else if(biteFish&&biteFish.mode==='out') drawFishShape(biteFish.x,biteFish.y,12+biteFish.sp.rarity*2.5,biteFish.dir,biteFish.sp.hue,0.5);
|
||||
if(biteFish&&biteFish.mode!=='out') drawFishShape(biteFish.x,biteFish.y,12+biteFish.sp.rarity*2.5,biteFish.dir,biteFish.sp.hue,0.8,Math.sin(time*8)*0.25);
|
||||
else if(biteFish&&biteFish.mode==='out') drawFishShape(biteFish.x,biteFish.y,12+biteFish.sp.rarity*2.5,biteFish.dir,biteFish.sp.hue,0.5,Math.sin(time*8)*0.25);
|
||||
drawBoat();
|
||||
drawLineAndBobber();
|
||||
drawHookedFish();
|
||||
|
||||
Reference in New Issue
Block a user