251 lines
11 KiB
JavaScript
251 lines
11 KiB
JavaScript
"use strict";
|
|
|
|
function render(){
|
|
var canvas=$("gc"),ctx=canvas.getContext("2d");
|
|
var cam=S.camera,P=S.player,net=S.net,cfg=S.cfg;
|
|
|
|
ctx.save();ctx.clearRect(0,0,CW,CH);
|
|
|
|
// Sky
|
|
var sg=ctx.createLinearGradient(0,0,0,CH);
|
|
if(S.phase===1){sg.addColorStop(0,cfg.skyA);sg.addColorStop(1,cfg.skyB);}
|
|
else{var u=Math.min(1,S.kidneyDmg/80);sg.addColorStop(0,lerpCol(cfg.skyA,"#5a2020",u));sg.addColorStop(1,lerpCol(cfg.skyB,"#3a1515",u));}
|
|
ctx.fillStyle=sg;ctx.fillRect(0,0,CW,CH);
|
|
|
|
ctx.translate(-cam.x,-cam.y);
|
|
|
|
// Ground
|
|
ctx.fillStyle=cfg.ground;ctx.fillRect(0,0,net.mapW,net.mapH);
|
|
ctx.fillStyle="#00000014";
|
|
for(var gi=0;gi<250;gi++)ctx.fillRect((gi*137.5)%net.mapW,(gi*97.3)%net.mapH,2,2);
|
|
|
|
// ── Paths ──
|
|
ctx.lineCap="round";ctx.lineJoin="round";
|
|
ctx.strokeStyle="#1a150f";ctx.lineWidth=PATH_W+8;
|
|
net.edges.forEach(function(e){var a=net.nodes[e[0]],b=net.nodes[e[1]];ctx.beginPath();ctx.moveTo(a.x,a.y);ctx.lineTo(b.x,b.y);ctx.stroke()});
|
|
ctx.strokeStyle="#8a7a5e";ctx.lineWidth=PATH_W;
|
|
net.edges.forEach(function(e){var a=net.nodes[e[0]],b=net.nodes[e[1]];ctx.beginPath();ctx.moveTo(a.x,a.y);ctx.lineTo(b.x,b.y);ctx.stroke()});
|
|
ctx.strokeStyle="#a0906840";ctx.lineWidth=PATH_W*0.5;
|
|
net.edges.forEach(function(e){var a=net.nodes[e[0]],b=net.nodes[e[1]];ctx.beginPath();ctx.moveTo(a.x,a.y);ctx.lineTo(b.x,b.y);ctx.stroke()});
|
|
|
|
// ── Bushes ──
|
|
var bCols=["#2d6b1e","#3a7a2a","#1e5a14"];
|
|
S.veg.bushes.forEach(function(b){
|
|
ctx.fillStyle=bCols[b.shade%3];
|
|
ctx.beginPath();ctx.arc(b.x,b.y,b.sz,0,Math.PI*2);ctx.fill();
|
|
ctx.fillStyle="#22551540";
|
|
ctx.beginPath();ctx.arc(b.x-b.sz*0.3,b.y-b.sz*0.3,b.sz*0.5,0,Math.PI*2);ctx.fill();
|
|
});
|
|
|
|
// ── Trees ──
|
|
var tCols=["#1e6b1e","#2a7a25","#22651a","#2b7030"];
|
|
S.veg.trees.forEach(function(t){
|
|
ctx.fillStyle="#5a3a1a";ctx.fillRect(t.x-3,t.y,6,t.trunk);
|
|
ctx.fillStyle="#00000020";ctx.beginPath();ctx.ellipse(t.x,t.y+t.trunk+2,t.sz*0.6,t.sz*0.25,0,0,Math.PI*2);ctx.fill();
|
|
ctx.fillStyle=tCols[t.shade%4];
|
|
ctx.beginPath();ctx.arc(t.x,t.y-t.sz*0.2,t.sz,0,Math.PI*2);ctx.fill();
|
|
ctx.fillStyle="#3a9a3018";ctx.beginPath();ctx.arc(t.x-t.sz*0.25,t.y-t.sz*0.5,t.sz*0.45,0,Math.PI*2);ctx.fill();
|
|
});
|
|
|
|
// ── Keims ──
|
|
for(var ki=0;ki<S.keims.length;ki++){
|
|
var k=S.keims[ki];
|
|
var sz=k.size*30;
|
|
var pulse=Math.sin(k.pulse)*0.12+1;
|
|
var r2=sz*pulse;
|
|
var auraA=0.08+k.size*0.25;
|
|
ctx.fillStyle="rgba(220,40,40,"+auraA+")";
|
|
ctx.beginPath();ctx.arc(k.x,k.y,r2*2.5,0,Math.PI*2);ctx.fill();
|
|
ctx.fillStyle="rgba(220,200,50,"+(0.5+k.size*0.4)+")";
|
|
drawCrystal(ctx,k.x,k.y,r2);
|
|
ctx.fillStyle="rgba(255,240,100,"+(0.3+k.size*0.3)+")";
|
|
drawCrystal(ctx,k.x,k.y,r2*0.4);
|
|
if(k.size>0.35){
|
|
ctx.strokeStyle="rgba(255,50,50,"+(k.size*0.6)+")";
|
|
ctx.lineWidth=1.5;ctx.beginPath();ctx.arc(k.x,k.y,r2*2,0,Math.PI*2);ctx.stroke();
|
|
}
|
|
}
|
|
|
|
// ── Spray particles ──
|
|
sprayParticles.forEach(function(p){
|
|
var alpha=p.life*1.5;
|
|
ctx.fillStyle=p.color.replace(")",","+alpha+")").replace("rgb","rgba");
|
|
ctx.beginPath();ctx.arc(p.x,p.y,2+p.life*3,0,Math.PI*2);ctx.fill();
|
|
});
|
|
|
|
// ── Buildings ──
|
|
drawBldg(ctx,net.lab.x,net.lab.y,"\uD83D\uDD2C","Labor","#304050");
|
|
drawBldg(ctx,net.apo.x,net.apo.y,"\uD83D\uDC8A","Apotheke","#2a4a2a");
|
|
|
|
// Peak
|
|
ctx.fillStyle="#9a9a8a";
|
|
ctx.beginPath();ctx.moveTo(net.peak.x-50,net.peak.y+30);ctx.lineTo(net.peak.x-12,net.peak.y-40);ctx.lineTo(net.peak.x+8,net.peak.y-35);ctx.lineTo(net.peak.x+50,net.peak.y+30);ctx.closePath();ctx.fill();
|
|
ctx.fillStyle="#e0e0e0";ctx.beginPath();ctx.moveTo(net.peak.x-18,net.peak.y-22);ctx.lineTo(net.peak.x-12,net.peak.y-40);ctx.lineTo(net.peak.x+8,net.peak.y-35);ctx.lineTo(net.peak.x+14,net.peak.y-20);ctx.closePath();ctx.fill();
|
|
ctx.fillStyle="#fff8";ctx.font="11px sans-serif";ctx.textAlign="center";ctx.fillText("\u26F0\uFE0F Gipfel",net.peak.x,net.peak.y+48);
|
|
|
|
// ── Hints (phase 1) ──
|
|
if(S.phase===1){
|
|
var hAlpha=S.cfg.hintAlpha;
|
|
S.hints.forEach(function(h){
|
|
var sp=Math.sin(frame*0.06+h.phase)*0.5+0.5;
|
|
ctx.fillStyle="rgba(255,240,180,"+(sp*hAlpha)+")";
|
|
ctx.beginPath();ctx.arc(h.x,h.y,3+sp*3,0,Math.PI*2);ctx.fill();
|
|
});
|
|
}
|
|
|
|
// ── Stones ──
|
|
S.stones.forEach(function(st){
|
|
if(st.rolling)return;
|
|
if(st.scanned&&!st.isCystine){
|
|
ctx.globalAlpha=0.25;ctx.fillStyle="#555";drawStone(ctx,st.x,st.y,st.shape,st.rollAngle);ctx.globalAlpha=1;return;
|
|
}
|
|
ctx.fillStyle=st.color;
|
|
drawStone(ctx,st.x,st.y,st.shape,st.rollAngle);
|
|
if(!st.scanned&&S.phase===1){
|
|
ctx.fillStyle="#fff7";ctx.font="11px sans-serif";ctx.textAlign="center";ctx.fillText("?",st.x,st.y-18);
|
|
}
|
|
});
|
|
|
|
// Rolling stone
|
|
if(S.rollingStone){
|
|
var rs=S.rollingStone;
|
|
ctx.fillStyle=rs.color;
|
|
drawStone(ctx,rs.x,rs.y,rs.shape,rs.rollAngle);
|
|
if(frame%3===0){
|
|
ctx.fillStyle="#a0906840";
|
|
ctx.beginPath();ctx.arc(rs.x+rng(-8,8),rs.y+rng(8,14),rng(2,4),0,Math.PI*2);ctx.fill();
|
|
}
|
|
}
|
|
|
|
// Parked stone glow
|
|
if(S.parkedStone){
|
|
var gl=Math.sin(frame*0.08)*0.3+0.5;
|
|
ctx.strokeStyle="rgba(255,200,50,"+gl+")";ctx.lineWidth=2;
|
|
ctx.beginPath();ctx.arc(S.parkedStone.x,S.parkedStone.y,STONE_R+8,0,Math.PI*2);ctx.stroke();
|
|
ctx.fillStyle=S.parkedStone.color;
|
|
drawStone(ctx,S.parkedStone.x,S.parkedStone.y,S.parkedStone.shape,S.parkedStone.rollAngle);
|
|
}
|
|
|
|
// ── Player (Sisyphus) ──
|
|
var bob=Math.sin(P.frame*2)*1.5;
|
|
var sprinting=P.sprinting;
|
|
ctx.fillStyle="#00000028";ctx.beginPath();ctx.ellipse(P.x,P.y+20+bob,10,4,0,0,Math.PI*2);ctx.fill();
|
|
ctx.fillStyle="#d4a060";ctx.beginPath();ctx.arc(P.x,P.y-14+bob,9,0,Math.PI*2);ctx.fill();
|
|
ctx.fillStyle="#5a3a1a";ctx.beginPath();ctx.arc(P.x,P.y-18+bob,6,Math.PI,Math.PI*2);ctx.fill();
|
|
ctx.fillStyle=sprinting?"#7a5535":"#8b6040";
|
|
ctx.fillRect(P.x-6,P.y-5+bob,12,15);
|
|
if(S.rollingStone){
|
|
var armDx=Math.cos(P.dir)*10,armDy=Math.sin(P.dir)*10;
|
|
ctx.strokeStyle="#d4a060";ctx.lineWidth=3;
|
|
ctx.beginPath();ctx.moveTo(P.x,P.y+2+bob);ctx.lineTo(P.x+armDx,P.y+2+bob+armDy);ctx.stroke();
|
|
}
|
|
ctx.fillStyle="#4a3020";ctx.fillRect(P.x-7,P.y+3+bob,14,3);
|
|
ctx.fillStyle="#6a4830";
|
|
var lOff=Math.sin(P.frame*3)*(sprinting?3:1.5);
|
|
ctx.fillRect(P.x-5,P.y+10+bob+lOff,4,8);
|
|
ctx.fillRect(P.x+1,P.y+10+bob-lOff,4,8);
|
|
if(sprinting&&frame%2===0){
|
|
ctx.fillStyle="#a0906830";
|
|
ctx.beginPath();ctx.arc(P.x-Math.cos(P.dir)*12,P.y-Math.sin(P.dir)*12+20,rng(2,4),0,Math.PI*2);ctx.fill();
|
|
}
|
|
|
|
// ── Prompts ──
|
|
ctx.font="bold 11px sans-serif";ctx.textAlign="center";
|
|
if(S.phase===1){
|
|
S.stones.forEach(function(st){
|
|
if(!st.scanned&&!st.rolling&&dist(P,st)<55){
|
|
ctx.fillStyle="#ffffffcc";ctx.fillText("[Leertaste] Aufheben",st.x,st.y-22);
|
|
}
|
|
});
|
|
if(S.rollingStone&&dist(P,net.lab)<65){
|
|
ctx.fillStyle="#00ff88cc";ctx.font="bold 12px sans-serif";
|
|
ctx.fillText("[Leertaste] Scannen!",net.lab.x,net.lab.y-48);
|
|
}
|
|
}
|
|
if(S.phase===2){
|
|
if(S.parkedStone&&!S.rollingStone&&dist(P,S.parkedStone)<55){
|
|
ctx.fillStyle="#ffcc00cc";ctx.fillText("[Leertaste] Aufheben",S.parkedStone.x,S.parkedStone.y-STONE_R-14);
|
|
}
|
|
if(S.rollingStone&&S.rollingStone.isCystine){
|
|
ctx.fillStyle="#88aaffcc";ctx.font="10px sans-serif";ctx.fillText("[Leertaste] Parken",S.rollingStone.x,S.rollingStone.y-STONE_R-14);
|
|
}
|
|
if(dist(P,net.apo)<65){
|
|
ctx.fillStyle="#00ff88cc";ctx.fillText("[Leertaste] Auff\u00FCllen",net.apo.x,net.apo.y-48);
|
|
}
|
|
for(var ki=0;ki<S.keims.length;ki++){
|
|
var k=S.keims[ki];
|
|
if(dist(P,k)<70&&S.meds[MEDS[S.selMed].id]>0){
|
|
ctx.fillStyle="#ffcc44cc";ctx.font="10px sans-serif";
|
|
ctx.fillText("[Leertaste] Spr\u00FChen",k.x,k.y-k.size*30-12);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ── Minimap ──
|
|
drawMinimap(ctx,cam);
|
|
|
|
ctx.restore();
|
|
}
|
|
|
|
function drawStone(ctx,x,y,shape,angle){
|
|
ctx.save();ctx.translate(x,y);ctx.rotate(angle||0);
|
|
ctx.beginPath();
|
|
if(shape===0)ctx.ellipse(0,0,STONE_R,STONE_R*0.72,0,0,Math.PI*2);
|
|
else if(shape===1){ctx.moveTo(-STONE_R,STONE_R*0.6);ctx.lineTo(-STONE_R*0.5,-STONE_R*0.8);ctx.lineTo(STONE_R*0.8,-STONE_R*0.5);ctx.lineTo(STONE_R,STONE_R*0.6);ctx.closePath();}
|
|
else ctx.arc(0,0,STONE_R,0,Math.PI*2);
|
|
ctx.fill();ctx.strokeStyle="#00000030";ctx.lineWidth=1;ctx.stroke();
|
|
ctx.fillStyle="#00000015";ctx.beginPath();ctx.arc(STONE_R*0.2,-STONE_R*0.2,STONE_R*0.3,0,Math.PI*2);ctx.fill();
|
|
ctx.fillStyle="#ffffff10";ctx.beginPath();ctx.arc(-STONE_R*0.3,-STONE_R*0.3,STONE_R*0.25,0,Math.PI*2);ctx.fill();
|
|
ctx.restore();
|
|
}
|
|
|
|
function drawCrystal(ctx,x,y,r){
|
|
ctx.beginPath();
|
|
var spikes=7;
|
|
for(var i=0;i<spikes;i++){
|
|
var a=(i/spikes)*Math.PI*2-Math.PI/2;
|
|
var outerR=r*(0.6+(i%2)*0.4);
|
|
var px=x+Math.cos(a)*outerR,py=y+Math.sin(a)*outerR;
|
|
if(i===0)ctx.moveTo(px,py);else ctx.lineTo(px,py);
|
|
var a2=((i+0.5)/spikes)*Math.PI*2-Math.PI/2;
|
|
ctx.lineTo(x+Math.cos(a2)*r*0.3,y+Math.sin(a2)*r*0.3);
|
|
}
|
|
ctx.closePath();ctx.fill();
|
|
}
|
|
|
|
function drawBldg(ctx,x,y,emoji,label,col){
|
|
ctx.fillStyle="#00000025";ctx.beginPath();ctx.ellipse(x,y+28,32,8,0,0,Math.PI*2);ctx.fill();
|
|
ctx.fillStyle=col;ctx.fillRect(x-28,y-22,56,44);
|
|
ctx.fillStyle=col+"cc";ctx.beginPath();ctx.moveTo(x-32,y-22);ctx.lineTo(x,y-38);ctx.lineTo(x+32,y-22);ctx.closePath();ctx.fill();
|
|
ctx.fillStyle="#1a1a1a";ctx.fillRect(x-6,y+6,12,16);
|
|
ctx.font="22px sans-serif";ctx.textAlign="center";ctx.fillText(emoji,x,y+2);
|
|
ctx.fillStyle="#ffffffbb";ctx.font="bold 10px sans-serif";ctx.fillText(label,x,y+40);
|
|
}
|
|
|
|
function drawMinimap(ctx,cam){
|
|
var mmW=130,mmH=100,mmX=cam.x+CW-mmW-10,mmY=cam.y+10;
|
|
var sx=mmW/S.net.mapW,sy=mmH/S.net.mapH;
|
|
ctx.fillStyle="rgba(0,0,0,0.55)";ctx.fillRect(mmX-2,mmY-2,mmW+4,mmH+4);
|
|
ctx.fillStyle="rgba(40,60,30,0.8)";ctx.fillRect(mmX,mmY,mmW,mmH);
|
|
ctx.strokeStyle="#9a8a6a50";ctx.lineWidth=1.5;
|
|
S.net.edges.forEach(function(e){
|
|
var a=S.net.nodes[e[0]],b=S.net.nodes[e[1]];
|
|
ctx.beginPath();ctx.moveTo(mmX+a.x*sx,mmY+a.y*sy);ctx.lineTo(mmX+b.x*sx,mmY+b.y*sy);ctx.stroke();
|
|
});
|
|
ctx.fillStyle="#4488ff";ctx.fillRect(mmX+S.net.lab.x*sx-2,mmY+S.net.lab.y*sy-2,5,5);
|
|
ctx.fillStyle="#44dd44";ctx.fillRect(mmX+S.net.apo.x*sx-2,mmY+S.net.apo.y*sy-2,5,5);
|
|
ctx.fillStyle="#ffffff";ctx.fillRect(mmX+S.net.peak.x*sx-2,mmY+S.net.peak.y*sy-2,5,5);
|
|
S.keims.forEach(function(k){
|
|
ctx.fillStyle="rgba(220,200,50,"+(0.4+k.size*0.5)+")";
|
|
ctx.beginPath();ctx.arc(mmX+k.x*sx,mmY+k.y*sy,1.5+k.size*2,0,Math.PI*2);ctx.fill();
|
|
});
|
|
if(S.parkedStone){ctx.fillStyle="#ffcc00";ctx.fillRect(mmX+S.parkedStone.x*sx-2,mmY+S.parkedStone.y*sy-2,4,4);}
|
|
ctx.fillStyle="#ff4444";ctx.beginPath();ctx.arc(mmX+S.player.x*sx,mmY+S.player.y*sy,3,0,Math.PI*2);ctx.fill();
|
|
ctx.strokeStyle="#ffffff35";ctx.lineWidth=1;ctx.strokeRect(mmX+cam.x*sx,mmY+cam.y*sy,CW*sx,CH*sy);
|
|
ctx.fillStyle="#ffffff55";ctx.font="7px sans-serif";ctx.textAlign="left";
|
|
ctx.fillText("Lab",mmX+S.net.lab.x*sx+5,mmY+S.net.lab.y*sy+3);
|
|
ctx.fillText("Apo",mmX+S.net.apo.x*sx+5,mmY+S.net.apo.y*sy+3);
|
|
ctx.fillText("Gipfel",mmX+S.net.peak.x*sx+5,mmY+S.net.peak.y*sy+3);
|
|
}
|