"use strict"; // ─── Game Loop ───────── function gameLoop(){ if(curScreen!=="game"||!S)return; if(showTut){animId=requestAnimationFrame(gameLoop);return} if(S.won||S.lost)return; var dt=1/60; S.time+=dt;frame++; var mx=0,my=0; if(keys["w"]||keys["arrowup"])my=-1; if(keys["s"]||keys["arrowdown"])my=1; if(keys["a"]||keys["arrowleft"])mx=-1; if(keys["d"]||keys["arrowright"])mx=1; if(touch.active&&(Math.abs(touch.dx)>15||Math.abs(touch.dy)>15)){ if(Math.abs(touch.dx)>Math.abs(touch.dy))mx=touch.dx>0?1:-1; else my=touch.dy>0?1:-1; } if(mx&&my){if(Math.abs(mx)>=Math.abs(my))my=0;else mx=0;} S.player.sprinting=keys.shift&&!S.rollingStone; var speed=S.rollingStone?PUSH_SPEED:(S.player.sprinting?SPRINT_SPEED:WALK_SPEED); if(mx||my){ var len=Math.sqrt(mx*mx+my*my); var nx=S.player.x+(mx/len)*speed; var ny=S.player.y+(my/len)*speed; var cp=closestOnPaths(nx,ny,S.net.edges,S.net.nodes); if(cp.d>PATH_W*0.55){nx=cp.x;ny=cp.y;} nx=clamp(nx,10,S.net.mapW-10); ny=clamp(ny,10,S.net.mapH-10); S.player.x=nx;S.player.y=ny; S.player.dir=Math.atan2(my,mx); S.player.frame+=S.player.sprinting?0.25:0.15; } if(S.rollingStone){ var rs=S.rollingStone; var offDist=22; var sdx=Math.cos(S.player.dir)*offDist; var sdy=Math.sin(S.player.dir)*offDist; var tsx=S.player.x+sdx, tsy=S.player.y+sdy; var scp=closestOnPaths(tsx,tsy,S.net.edges,S.net.nodes); if(scp.d>PATH_W*0.5){tsx=scp.x;tsy=scp.y;} rs.x=lerp(rs.x,tsx,0.15); rs.y=lerp(rs.y,tsy,0.15); if(mx||my){ rs.rollAngle+=(mx||my)*0.08; } } if(actionQ){actionQ=false;handleAction()} S.atPharmacy=dist(S.player,S.net.apo)<65; if(S.phase===2){ S.phase2Timer+=dt; for(var ki=0;ki100&&dist(kp,S.net.apo)>70&&dist(kp,S.net.lab)>70){ S.keims.push({x:kp.x,y:kp.y,size:0.06,id:Date.now()+Math.random(),pulse:0}); } } var totK=0; for(var ki=0;ki=100){ S.lost=true; showGO("Die Kristallkeime haben die Niere kritisch gesch\u00E4digt. Der Stein kann nicht mehr bewegt werden."); return; } if(S.stoneAloneTimer>=S.maxAloneTime){ S.lost=true; showGO("Der Weg zum Gipfel ist von Kristallen dauerhaft blockiert. Der Zystinstein sitzt fest!"); return; } if(S.rollingStone&&S.rollingStone.isCystine&&dist(S.rollingStone,S.net.peak)<50){ S.won=true; S.score+=Math.max(0,Math.floor(1000-S.phase2Timer*5+(100-S.kidneyDmg)*5)); sfxWin(); setTimeout(function(){showTrans(curLvl)},700); return; } ["water","bicarb","thiola"].forEach(function(m){ S.meds[m]=Math.max(0,S.meds[m]-dt*(0.3+curLvl*0.15)); }); } for(var pi=sprayParticles.length-1;pi>=0;pi--){ var p=sprayParticles[pi]; p.life-=dt; p.x+=p.vx*dt;p.y+=p.vy*dt; p.vx*=0.95;p.vy*=0.95; if(p.life<=0)sprayParticles.splice(pi,1); } if(S.msgTimer>0){S.msgTimer-=dt;if(S.msgTimer<=0)S.msg="";} var tcx=S.player.x-CW/2,tcy=S.player.y-CH/2; S.camera.x=lerp(S.camera.x,clamp(tcx,0,S.net.mapW-CW),0.1); S.camera.y=lerp(S.camera.y,clamp(tcy,0,S.net.mapH-CH),0.1); render(); if(frame%5===0)updateHUD(); animId=requestAnimationFrame(gameLoop); } // ─── Action ──────────── function handleAction(){ if(!S)return; var P=S.player; if(S.phase===1){ if(!S.rollingStone){ var best=null,bd=Infinity; S.stones.forEach(function(st){ if(st.scanned||st.rolling)return; var d=dist(P,st); if(d<55&&d0){ var hit=false; for(var ki=0;ki0.02}); } } } function setMsg(m,d,al){S.msg=m;S.msgTimer=d;S.msgAl=!!al;} // ─── HUD ─────────────── function updateHUD(){ if(!S)return; $("hud-lv").textContent="Lvl "+(curLvl+1); $("hud-sc").textContent="\u2B50 "+S.score; $("hud-ph").textContent=S.phase===1?("\uD83D\uDD0D "+S.stonesScanned+"/"+S.stones.length):"\u26A1 Phase 2"; var kw=$("kid-w"),kf=$("kid-f"); if(S.phase===2){ kw.style.display="flex"; kf.style.width=S.kidneyDmg+"%"; kf.className="bar-f"+(S.kidneyDmg>70?" bar-dn":S.kidneyDmg>40?" bar-wn":" bar-ok"); }else kw.style.display="none"; var te=$("st-tm"); if(S.phase===2&&S.parkedStone&&!S.rollingStone&&S.stoneAloneTimer>0){ var rem=Math.max(0,S.maxAloneTime-S.stoneAloneTimer); te.textContent="\u23F0 "+rem.toFixed(0)+"s"; te.className="st-tm"+(S.stoneAloneTimer>S.maxAloneTime*0.6?" st-d":" st-w"); te.style.display="inline"; }else te.style.display="none"; var bot=$("hud-bot"); if(S.phase===2)bot.classList.add("vis");else bot.classList.remove("vis"); MEDS.forEach(function(m,i){ var b=$("med-"+i),fl=$("mfl-"+i),st=$("mst-"+i); var pct=(S.meds[m.id]/S.medsMax)*100; b.className="med-b"+(S.selMed===i?" sel":""); b.style.borderColor=S.selMed===i?m.color:"#333"; fl.style.width=pct+"%"; fl.style.background=pct<20?"#e53935":m.color; st.textContent=pct<20?"NIEDRIG!":Math.round(pct)+"%"; st.className="med-st"+(pct<20?" med-lo":" med-ok"); }); var mp=$("msg-pop"); if(S.msg){mp.style.display="block";mp.textContent=S.msg;mp.className="msg-pop"+(S.msgAl?" msg-al":"");} else mp.style.display="none"; }