| <!doctype html> |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> |
| <title>QUANTPOE</title> |
| <meta name="description" content="Quantum Path of Exile. Twin-stick ARPG where the loot drops in superposition — it collapses to a rarity (Born rule) only when you grab it. Slay, collect, ascend. For Marci."> |
| <link rel="preconnect" href="https://fonts.googleapis.com"> |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
| <link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@500;700;900&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet"> |
| <style> |
| :root{--bg:#0a0706;--ink:#e8ddc8;--dim:#8a7a63;--blood:#c0261f;--gold:#e6c05a;--cyan:#39e6ff;--magic:#5aa0ff;--rare:#f5d84a;--uniq:#e08a2a;--mono:'JetBrains Mono',monospace;--disp:'Cinzel',serif} |
| *{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent;user-select:none} |
| html,body{height:100%;overflow:hidden;background:#000;color:var(--ink);font-family:var(--mono)} |
| #stage{position:fixed;inset:0} |
| canvas{position:absolute;inset:0;width:100%;height:100%;cursor:crosshair} |
| #hud{position:absolute;inset:0;pointer-events:none;z-index:5} |
| .top{position:absolute;top:0;left:0;right:0;display:flex;justify-content:space-between;padding:12px 16px;font-family:var(--mono);font-size:12px} |
| .stat{color:var(--dim)}.stat b{color:var(--gold);font-family:var(--disp);font-weight:700} |
| .top .r{text-align:right} |
| #lvl b{color:var(--cyan)} |
| /* orbs */ |
| #orbs{position:absolute;bottom:14px;left:16px;display:flex;gap:14px;align-items:flex-end} |
| .orb{width:60px;height:60px;border-radius:50%;position:relative;border:2px solid #2a1c16;overflow:hidden;box-shadow:inset 0 0 12px #000,0 0 8px #000} |
| .orb .fill{position:absolute;left:0;right:0;bottom:0;transition:height .15s} |
| #hp .fill{background:linear-gradient(180deg,#e0453a,#8f120c)} |
| #hp .lab{position:absolute;inset:0;display:grid;place-items:center;font-size:11px;font-weight:700;color:#fff;text-shadow:0 1px 2px #000} |
| #xpbar{position:absolute;bottom:0;left:0;right:0;height:5px;background:#160f0c} |
| #xpbar .fill{height:100%;background:linear-gradient(90deg,#39e6ff,#5aa0ff);width:0;box-shadow:0 0 8px #39e6ff} |
| #feed{position:absolute;bottom:84px;left:16px;display:flex;flex-direction:column;gap:2px;font-family:var(--mono);font-size:12px} |
| #feed div{opacity:0;animation:rise 2.6s ease-out forwards} |
| @keyframes rise{0%{opacity:0;transform:translateY(6px)}12%{opacity:1}80%{opacity:1}100%{opacity:0;transform:translateY(-6px)}} |
| #nova{position:absolute;right:18px;bottom:20px;width:74px;height:74px;border-radius:50%;pointer-events:auto;display:grid;place-items:center;font-family:var(--disp);font-size:12px;font-weight:700;color:var(--cyan);background:rgba(57,230,255,.1);border:2px solid rgba(57,230,255,.4)} |
| #nova.cd{opacity:.35}#nova:active{background:rgba(57,230,255,.3)} |
| @media(hover:hover){#nova{display:none}} |
| /* overlays */ |
| .screen{position:absolute;inset:0;z-index:10;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:24px;background:radial-gradient(120% 90% at 50% 30%,rgba(192,38,31,.12),transparent 60%),rgba(10,7,6,.9);pointer-events:auto} |
| .screen.hide{display:none} |
| h1{font-family:var(--disp);font-weight:900;font-size:clamp(46px,13vw,110px);line-height:.9;letter-spacing:.03em;background:linear-gradient(180deg,#f5d84a,#c0261f 70%,#6a0f0a);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;filter:drop-shadow(0 3px 30px rgba(192,38,31,.4))} |
| .sub{font-family:var(--mono);font-size:10px;letter-spacing:.34em;color:var(--dim);text-transform:uppercase;margin-top:8px} |
| .ded{font-family:var(--disp);font-style:italic;font-size:17px;color:var(--gold);margin-top:12px} |
| .tag{color:var(--dim);max-width:30rem;margin:20px 0 4px;font-size:13px;line-height:1.6}.tag b{color:var(--ink)}.tag .c{color:var(--cyan)} |
| .keys{font-family:var(--mono);font-size:11px;color:var(--dim);margin-top:16px;line-height:1.7}.keys b{color:var(--ink)} |
| button.go{font-family:var(--disp);font-weight:700;font-size:22px;letter-spacing:.05em;cursor:pointer;color:#160500;background:linear-gradient(180deg,#f5d84a,#d89a1e);border:0;border-radius:8px;padding:12px 40px;margin-top:22px;box-shadow:0 5px 26px rgba(230,192,90,.3)} |
| button.go:active{transform:translateY(2px)} |
| .fine{margin-top:20px;font-family:var(--mono);font-size:9px;letter-spacing:.16em;color:#5a3f2f;text-transform:uppercase}.fine .f{color:var(--blood)} |
| #go-score{font-family:var(--disp);font-weight:900;font-size:clamp(30px,7vw,52px);color:var(--gold);margin:6px 0} |
| </style> |
| </head> |
| <body> |
| <div id="stage"> |
| <canvas id="c"></canvas> |
| <div id="hud"> |
| <div class="top"> |
| <div class="stat" id="lvl">exile lvl <b>1</b></div> |
| <div class="stat" style="text-align:center">slain <b id="slain">0</b></div> |
| <div class="stat r">⟠ <b id="curr">0</b> <span style="color:var(--dim)">chaos</span></div> |
| </div> |
| <div id="feed"></div> |
| <div id="orbs"><div class="orb" id="hp"><div class="fill" style="height:100%"></div><div class="lab" id="hp-lab">100</div></div></div> |
| <div id="nova">NOVA</div> |
| <div id="xpbar"><div class="fill" id="xp-fill"></div></div> |
| </div> |
|
|
| <div class="screen" id="start"> |
| <h1>QUANTPOE</h1> |
| <div class="sub">quantum · path of exile</div> |
| <div class="ded">for Marci</div> |
| <p class="tag">An arena of the Atlas. Loot drops in <span class="c">superposition</span> — shimmering through every rarity at once — and only <b>collapses</b> to what it truly is when you <b>grab it</b>. That's the Born rule: the rarer the roll, the smaller its amplitude. Slay, collect, ascend.</p> |
| <p class="keys"><b>move</b> WASD / arrows · <b>aim & fire</b> mouse / hold · <b>Collapse Nova</b> Space<br>touch: left side to move · right side to fire · NOVA button</p> |
| <button class="go" id="start-btn">ENTER THE ATLAS</button> |
| <p class="fine">🜂 loot is a measurement · <span class="f">ahogy a dolgok vannak</span></p> |
| </div> |
|
|
| <div class="screen hide" id="over"> |
| <div class="sub">you have died, exile</div> |
| <div id="go-score">0</div> |
| <p class="tag" style="margin-top:0">reached <b id="go-lvl">lvl 1</b> · slew <b id="go-slain">0</b> · hoarded <b id="go-curr">0</b> chaos</p> |
| <button class="go" id="again-btn">RISE AGAIN</button> |
| <p class="fine">PeetPedro · entropy-om · <span class="f">for Marci</span></p> |
| </div> |
| </div> |
|
|
| <script> |
| (function(){ |
| "use strict"; |
| var cv=document.getElementById('c'), ctx=cv.getContext('2d'); |
| var W=0,H=0,DPR=Math.min(2,devicePixelRatio||1); |
| var RARITY=[ |
| {name:'common',col:'#c8bfa8',p:0.60,val:1,orb:'Scroll of Wisdom'}, |
| {name:'magic', col:'#5aa0ff',p:0.24,val:4,orb:'Orb of Alteration'}, |
| {name:'rare', col:'#f5d84a',p:0.13,val:12,orb:'Chaos Orb'}, |
| {name:'unique',col:'#e08a2a',p:0.03,val:60,orb:'Exalted Orb'} |
| ]; |
| var player, mobs, bolts, loots, parts, keys={}, aim={x:0,y:0,down:false}, moveStick=null; |
| var running=false, slain=0, curr=0, wave=0, spawnT=0, novaCd=0, shake=0, t=0; |
| var best=+(localStorage.getItem('qpoe_best')||0); |
| |
| |
| var actx=null; |
| function A(){ if(!actx){try{actx=new(window.AudioContext||window.webkitAudioContext)();}catch(e){}} if(actx&&actx.state==='suspended')actx.resume(); } |
| function beep(f,d,v,type){ if(!actx)return; var n=actx.currentTime,o=actx.createOscillator(),g=actx.createGain(); |
| o.type=type||'square'; o.frequency.setValueAtTime(f,n); g.gain.setValueAtTime(v||.08,n); g.gain.exponentialRampToValueAtTime(.0005,n+(d||.1)); |
| o.connect(g);g.connect(actx.destination);o.start(n);o.stop(n+(d||.1)+.02); } |
| |
| function resize(){ W=innerWidth;H=innerHeight; cv.width=W*DPR;cv.height=H*DPR; ctx.setTransform(DPR,0,0,DPR,0,0); } |
| addEventListener('resize',resize); |
| |
| function start(){ |
| running=true; slain=0; curr=0; wave=0; spawnT=0; novaCd=0; shake=0; |
| player={x:W/2,y:H/2,r:15,hp:100,maxHp:100,lvl:1,xp:0,xpNext:8,dmg:14,spd:230,fireCd:0}; |
| mobs=[];bolts=[];loots=[];parts=[]; |
| aim.x=W/2;aim.y=H*0.3; |
| document.getElementById('start').classList.add('hide'); document.getElementById('over').classList.add('hide'); |
| document.getElementById('feed').innerHTML=''; |
| hud(); |
| for(var i=0;i<4;i++) spawnMob(); |
| } |
| function over(){ running=false; |
| if(slain>best){best=slain;localStorage.setItem('qpoe_best',best);} |
| document.getElementById('go-score').textContent=slain+' slain'; |
| document.getElementById('go-lvl').textContent='lvl '+player.lvl; |
| document.getElementById('go-slain').textContent=slain; |
| document.getElementById('go-curr').textContent=curr; |
| document.getElementById('over').classList.remove('hide'); |
| } |
| function feed(txt,col){ var f=document.getElementById('feed'); var d=document.createElement('div'); |
| d.style.color=col||'#c8bfa8'; d.textContent=txt; f.appendChild(d); setTimeout(function(){d.remove();},2600); } |
| function hud(){ |
| document.getElementById('lvl').innerHTML='exile lvl <b>'+player.lvl+'</b>'; |
| document.getElementById('slain').textContent=slain; |
| document.getElementById('curr').textContent=curr; |
| var hpf=Math.max(0,player.hp/player.maxHp); |
| document.querySelector('#hp .fill').style.height=(hpf*100)+'%'; |
| document.getElementById('hp-lab').textContent=Math.max(0,Math.round(player.hp)); |
| document.getElementById('xp-fill').style.width=(player.xp/player.xpNext*100)+'%'; |
| document.getElementById('nova').classList.toggle('cd', novaCd>0); |
| } |
| |
| function spawnMob(){ |
| var edge=Math.floor(Math.random()*4), x,y; |
| if(edge===0){x=Math.random()*W;y=-20;} else if(edge===1){x=W+20;y=Math.random()*H;} |
| else if(edge===2){x=Math.random()*W;y=H+20;} else {x=-20;y=Math.random()*H;} |
| var tier=Math.random()< (0.08+wave*0.01)? (Math.random()<0.25?2:1):0; |
| var hpm=[1,2.4,5][tier], sc=[1,1.25,1.6][tier]; |
| mobs.push({x:x,y:y,r:12*sc,hp:14*hpm*(1+wave*0.08),maxHp:14*hpm*(1+wave*0.08), |
| spd:60+Math.random()*40+wave*3, tier:tier, hit:0, dmg:7+wave*0.6}); |
| } |
| function dropLoot(x,y){ |
| if(Math.random()<0.62) loots.push({x:x,y:y,r:9,phase:Math.random()*6.28,born:t,collapsed:false}); |
| } |
| function collapseLoot(l){ |
| var r=Math.random(),acc=0,pick=RARITY[0]; |
| for(var i=0;i<RARITY.length;i++){ acc+=RARITY[i].p; if(r<=acc){pick=RARITY[i];break;} } |
| curr+=pick.val; feed('+'+pick.val+' · '+pick.orb, pick.col); |
| beep(pick.name==='unique'?900:pick.name==='rare'?620:440,0.14,0.1,'triangle'); |
| if(pick.name==='unique'){ shake=Math.max(shake,10); } |
| for(var p=0;p<8;p++){ var a=Math.random()*6.28,s=1+Math.random()*3; |
| parts.push({x:l.x,y:l.y,vx:Math.cos(a)*s,vy:Math.sin(a)*s,life:1,col:pick.col}); } |
| hud(); |
| } |
| function gainXp(n){ player.xp+=n; if(player.xp>=player.xpNext){ player.xp-=player.xpNext; player.lvl++; |
| player.xpNext=Math.round(player.xpNext*1.35); player.maxHp+=18; player.hp=player.maxHp; player.dmg+=4; |
| feed('LEVEL '+player.lvl+' — ascended', '#39e6ff'); beep(700,0.2,0.12,'sine'); shake=Math.max(shake,6); } hud(); } |
| |
| function fire(){ |
| if(player.fireCd>0) return; player.fireCd=0.16; |
| var dx=aim.x-player.x, dy=aim.y-player.y, d=Math.hypot(dx,dy)||1; |
| var crit=Math.random()<0.18; |
| bolts.push({x:player.x,y:player.y,vx:dx/d*620,vy:dy/d*620,life:1.1,dmg:player.dmg*(crit?2.6:1),crit:crit}); |
| beep(crit?520:300,0.06,0.05,'square'); |
| } |
| function nova(){ if(novaCd>0)return; novaCd=4.5; shake=Math.max(shake,14); beep(120,0.4,0.14,'sawtooth'); |
| parts.push({ring:true,x:player.x,y:player.y,r:0,life:1}); |
| for(var i=mobs.length-1;i>=0;i--){ var m=mobs[i]; if(Math.hypot(m.x-player.x,m.y-player.y)<220){ |
| m.hp-=player.dmg*3.2; m.hit=1; if(m.hp<=0) killMob(i); } } |
| hud(); |
| } |
| function killMob(i){ var m=mobs[i]; slain++; gainXp([1,2,4][m.tier]); dropLoot(m.x,m.y); |
| for(var p=0;p<7;p++){var a=Math.random()*6.28,s=1+Math.random()*2.6; |
| parts.push({x:m.x,y:m.y,vx:Math.cos(a)*s,vy:Math.sin(a)*s,life:1,col:m.tier?'#f5d84a':'#c0261f'});} |
| mobs.splice(i,1); beep(180,0.08,0.05,'square'); } |
| |
| |
| function toWorld(e){ return {x:e.clientX,y:e.clientY}; } |
| addEventListener('keydown',function(e){ var k=e.key.length===1?e.key.toLowerCase():e.key; keys[k]=true; |
| if(k===' '){e.preventDefault();A();nova();} }); |
| addEventListener('keyup',function(e){ keys[e.key.length===1?e.key.toLowerCase():e.key]=false; }); |
| cv.addEventListener('pointermove',function(e){ if(e.pointerType==='mouse'){aim.x=e.clientX;aim.y=e.clientY;} |
| else if(moveStick&&e.pointerId===moveStick.id){ moveStick.dx=e.clientX-moveStick.ox; moveStick.dy=e.clientY-moveStick.oy; } |
| else if(aim.pid===e.pointerId){ aim.x=e.clientX;aim.y=e.clientY; } }); |
| cv.addEventListener('pointerdown',function(e){ A(); if(!running)return; |
| if(e.pointerType==='mouse'){ aim.down=true; aim.x=e.clientX;aim.y=e.clientY; return; } |
| if(e.clientX<W*0.5){ moveStick={id:e.pointerId,ox:e.clientX,oy:e.clientY,dx:0,dy:0}; } |
| else { aim.pid=e.pointerId; aim.down=true; aim.x=e.clientX;aim.y=e.clientY; } }); |
| function up(e){ if(e.pointerType==='mouse'){aim.down=false;return;} |
| if(moveStick&&e.pointerId===moveStick.id) moveStick=null; |
| if(aim.pid===e.pointerId){aim.down=false;aim.pid=null;} } |
| cv.addEventListener('pointerup',up); cv.addEventListener('pointercancel',up); |
| document.getElementById('nova').addEventListener('pointerdown',function(e){e.preventDefault();A();nova();}); |
| document.getElementById('start-btn').addEventListener('click',function(){A();start();}); |
| document.getElementById('again-btn').addEventListener('click',function(){A();start();}); |
| |
| function update(dt){ t+=dt; |
| |
| var mx=(keys.a||keys.arrowleft?-1:0)+(keys.d||keys.arrowright?1:0); |
| var my=(keys.w||keys.arrowup?-1:0)+(keys.s||keys.arrowdown?1:0); |
| if(moveStick){ var ml=Math.hypot(moveStick.dx,moveStick.dy); if(ml>6){mx=moveStick.dx/ml;my=moveStick.dy/ml;} } |
| var ml2=Math.hypot(mx,my); if(ml2>0){ player.x+=mx/ml2*player.spd*dt; player.y+=my/ml2*player.spd*dt; } |
| player.x=Math.max(player.r,Math.min(W-player.r,player.x)); player.y=Math.max(player.r,Math.min(H-player.r,player.y)); |
| if(moveStick&&!aim.down){ if(ml2>0){aim.x=player.x+mx*40;aim.y=player.y+my*40;} } |
| |
| player.fireCd-=dt; if(aim.down) fire(); |
| if(novaCd>0){novaCd-=dt; if(novaCd<=0)hud();} |
| |
| spawnT+=dt; wave+=dt*0.05; var cap=6+Math.floor(wave*1.5); |
| if(spawnT> Math.max(0.5,1.6-wave*0.05) && mobs.length<cap){ spawnT=0; spawnMob(); } |
| |
| for(var i=mobs.length-1;i>=0;i--){ var m=mobs[i]; if(m.hit>0)m.hit-=dt*3; |
| var dx=player.x-m.x,dy=player.y-m.y,d=Math.hypot(dx,dy)||1; m.x+=dx/d*m.spd*dt; m.y+=dy/d*m.spd*dt; |
| if(d<m.r+player.r){ player.hp-=m.dmg*dt*2.2; shake=Math.max(shake,3); if(player.hp<=0){hud();over();return;} } } |
| if(Math.random()<0.3) hud(); |
| |
| for(var b=bolts.length-1;b>=0;b--){ var o=bolts[b]; o.x+=o.vx*dt;o.y+=o.vy*dt;o.life-=dt; |
| if(o.life<=0||o.x<-20||o.x>W+20||o.y<-20||o.y>H+20){bolts.splice(b,1);continue;} |
| for(var k=mobs.length-1;k>=0;k--){ var m2=mobs[k]; if(Math.hypot(m2.x-o.x,m2.y-o.y)<m2.r+4){ |
| m2.hp-=o.dmg; m2.hit=1; for(var p=0;p<(o.crit?6:3);p++){var a=Math.random()*6.28;parts.push({x:o.x,y:o.y,vx:Math.cos(a)*2,vy:Math.sin(a)*2,life:.6,col:o.crit?'#39e6ff':'#e6c05a'});} |
| bolts.splice(b,1); if(m2.hp<=0)killMob(k); break; } } } |
| |
| for(var l=loots.length-1;l>=0;l--){ var lo=loots[l]; lo.phase+=dt*4; |
| if(Math.hypot(lo.x-player.x,lo.y-player.y)<player.r+lo.r+6){ collapseLoot(lo); loots.splice(l,1); } |
| else if(t-lo.born>14){ loots.splice(l,1); } } |
| |
| for(var q=parts.length-1;q>=0;q--){ var pr=parts[q]; |
| if(pr.ring){ pr.r+=dt*700; pr.life-=dt*1.6; if(pr.life<=0)parts.splice(q,1); } |
| else { pr.x+=pr.vx;pr.y+=pr.vy;pr.vx*=.92;pr.vy*=.92;pr.life-=dt*1.8; if(pr.life<=0)parts.splice(q,1); } } |
| if(shake>0)shake=Math.max(0,shake-dt*30); |
| } |
| |
| function draw(){ |
| ctx.fillStyle='#0a0706'; ctx.fillRect(0,0,W,H); |
| |
| ctx.strokeStyle='rgba(80,50,30,0.08)'; ctx.lineWidth=1; |
| for(var gx=0;gx<W;gx+=48){ctx.beginPath();ctx.moveTo(gx,0);ctx.lineTo(gx,H);ctx.stroke();} |
| for(var gy=0;gy<H;gy+=48){ctx.beginPath();ctx.moveTo(0,gy);ctx.lineTo(W,gy);ctx.stroke();} |
| var sx=0,sy=0; if(shake>0){sx=(Math.random()*2-1)*shake;sy=(Math.random()*2-1)*shake;} |
| ctx.save(); ctx.translate(sx,sy); |
| if(!running){ ctx.restore(); return; } |
| |
| for(var l=0;l<loots.length;l++){ var lo=loots[l]; |
| var hue=(lo.phase*40)%360; var pulse=0.6+0.4*Math.sin(lo.phase*2); |
| var g=ctx.createRadialGradient(lo.x,lo.y,0,lo.x,lo.y,lo.r*3); |
| g.addColorStop(0,'hsla('+hue+',90%,70%,'+(0.9*pulse)+')'); g.addColorStop(0.5,'hsla('+hue+',90%,60%,0.3)'); g.addColorStop(1,'rgba(0,0,0,0)'); |
| ctx.fillStyle=g; ctx.beginPath(); ctx.arc(lo.x,lo.y,lo.r*3,0,7); ctx.fill(); |
| ctx.fillStyle='hsl('+hue+',90%,75%)'; ctx.beginPath(); ctx.arc(lo.x,lo.y,lo.r*0.6*pulse,0,7); ctx.fill(); |
| } |
| |
| for(var i=0;i<mobs.length;i++){ var m=mobs[i]; var col=['#7a1512','#3a5aa8','#8a6a12'][m.tier]; |
| ctx.fillStyle=m.hit>0?'#fff':col; ctx.beginPath(); ctx.arc(m.x,m.y,m.r,0,7); ctx.fill(); |
| ctx.strokeStyle=['#c0261f','#5aa0ff','#f5d84a'][m.tier]; ctx.lineWidth=2; ctx.beginPath(); ctx.arc(m.x,m.y,m.r,0,7); ctx.stroke(); |
| |
| ctx.fillStyle='#ffcf3f'; ctx.beginPath();ctx.arc(m.x-m.r*0.3,m.y-m.r*0.15,1.6,0,7);ctx.arc(m.x+m.r*0.3,m.y-m.r*0.15,1.6,0,7);ctx.fill(); |
| if(m.hp<m.maxHp){ ctx.fillStyle='#000';ctx.fillRect(m.x-m.r,m.y-m.r-6,m.r*2,3); ctx.fillStyle='#c0261f';ctx.fillRect(m.x-m.r,m.y-m.r-6,m.r*2*(m.hp/m.maxHp),3); } |
| } |
| |
| for(var b=0;b<bolts.length;b++){ var o=bolts[b]; ctx.strokeStyle=o.crit?'#39e6ff':'#e6c05a'; ctx.lineWidth=o.crit?4:2.5; |
| ctx.beginPath(); ctx.moveTo(o.x,o.y); ctx.lineTo(o.x-o.vx*0.02,o.y-o.vy*0.02); ctx.stroke(); |
| ctx.fillStyle=o.crit?'#bffaff':'#fff2c0'; ctx.beginPath();ctx.arc(o.x,o.y,o.crit?3.5:2.5,0,7);ctx.fill(); } |
| |
| for(var q=0;q<parts.length;q++){ var pr=parts[q]; |
| if(pr.ring){ ctx.strokeStyle='rgba(57,230,255,'+pr.life+')'; ctx.lineWidth=3; ctx.beginPath();ctx.arc(pr.x,pr.y,pr.r,0,7);ctx.stroke(); } |
| else { ctx.globalAlpha=Math.max(0,pr.life); ctx.fillStyle=pr.col; ctx.beginPath();ctx.arc(pr.x,pr.y,2.2,0,7);ctx.fill(); ctx.globalAlpha=1; } } |
| |
| var pg=ctx.createRadialGradient(player.x,player.y,0,player.x,player.y,player.r*2.4); |
| pg.addColorStop(0,'rgba(230,192,90,0.25)');pg.addColorStop(1,'rgba(0,0,0,0)'); ctx.fillStyle=pg; |
| ctx.beginPath();ctx.arc(player.x,player.y,player.r*2.4,0,7);ctx.fill(); |
| ctx.fillStyle='#1a1410'; ctx.beginPath();ctx.arc(player.x,player.y,player.r,0,7);ctx.fill(); |
| ctx.strokeStyle='#e6c05a';ctx.lineWidth=2.5;ctx.beginPath();ctx.arc(player.x,player.y,player.r,0,7);ctx.stroke(); |
| |
| var adx=aim.x-player.x,ady=aim.y-player.y,ad=Math.hypot(adx,ady)||1; |
| ctx.strokeStyle='rgba(57,230,255,.5)';ctx.lineWidth=2;ctx.beginPath();ctx.moveTo(player.x+adx/ad*player.r,player.y+ady/ad*player.r);ctx.lineTo(player.x+adx/ad*(player.r+14),player.y+ady/ad*(player.r+14));ctx.stroke(); |
| ctx.restore(); |
| |
| var vg=ctx.createRadialGradient(W/2,H/2,H*0.4,W/2,H/2,H*0.85); |
| vg.addColorStop(0,'rgba(0,0,0,0)');vg.addColorStop(1,'rgba(0,0,0,0.55)'); ctx.fillStyle=vg;ctx.fillRect(0,0,W,H); |
| } |
| |
| var last=performance.now(); |
| function frame(now){ var dt=Math.min(0.05,(now-last)/1000); last=now; if(running)update(dt); draw(); requestAnimationFrame(frame); } |
| resize(); requestAnimationFrame(frame); |
| window.__qpoe={ start:start, state:function(){return{running:running,slain:slain,curr:curr,lvl:player?player.lvl:0,hp:player?player.hp:0,mobs:mobs?mobs.length:0};}, |
| autoplay:function(){ if(!running)start(); aim.down=true; aim.x=mobs[0]?mobs[0].x:W/2; aim.y=mobs[0]?mobs[0].y:H/2; keys.d=true; } }; |
| })(); |
| </script> |
| </body> |
| </html> |
|
|