-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
63 lines (63 loc) · 14.6 KB
/
Copy pathindex.html
File metadata and controls
63 lines (63 loc) · 14.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<!doctype html>
<html lang="de">
<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="#07151f">
<title>Depth Charge</title>
<style>
:root{--ink:#dff7f2;--muted:#86aaa9;--cyan:#5de0cf;--orange:#ffb35c;--danger:#ff6f61;--panel:rgba(5,22,31,.84)}
*{box-sizing:border-box}html,body{margin:0;width:100%;height:100%;overflow:hidden;background:#031019;color:var(--ink);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;touch-action:none}body{display:grid;place-items:center}.shell{position:relative;width:100vw;height:100dvh;max-width:1100px;max-height:760px;overflow:hidden;background:linear-gradient(#061923 0 34%,#063249 34% 100%);box-shadow:0 0 80px #000}.shell:after{content:"";position:absolute;inset:34% 0 0;background:repeating-linear-gradient(170deg,transparent 0 42px,rgba(83,212,210,.055) 43px 45px);pointer-events:none}.hud{position:absolute;z-index:5;inset:0 0 auto;display:flex;align-items:center;justify-content:space-between;padding:calc(10px + env(safe-area-inset-top)) 16px 10px;background:linear-gradient(180deg,rgba(1,9,14,.9),transparent);font-size:12px}.brand{font-weight:900;letter-spacing:.13em;color:var(--cyan)}.brand small{display:block;margin-top:4px;color:var(--muted);font-size:9px;letter-spacing:.08em}.readout{display:flex;gap:14px;text-align:right;color:var(--muted)}.readout b{display:block;color:var(--ink);font-size:15px}.arena{position:absolute;inset:0}.surface{position:absolute;top:33%;left:0;right:0;border-top:1px solid rgba(117,235,220,.45);box-shadow:0 0 18px rgba(89,221,205,.22)}.surface:before,.surface:after{content:"";position:absolute;left:0;width:100%;height:18px;background:linear-gradient(175deg,transparent 30%,rgba(122,235,219,.22) 31% 36%,transparent 37% 58%,rgba(122,235,219,.12) 59% 63%,transparent 64%);opacity:.8}.surface:before{top:-13px}.surface:after{top:5px;transform:scaleX(-1);opacity:.45}.sonar{position:absolute;right:18px;top:22%;width:100px;height:100px;border:1px solid rgba(93,224,207,.28);border-radius:50%;opacity:.5;background:radial-gradient(circle,rgba(93,224,207,.12) 0 1px,transparent 2px),repeating-radial-gradient(circle,transparent 0 24px,rgba(93,224,207,.15) 25px 26px)}.sonar:before{content:"";position:absolute;inset:50% 0 auto;border-top:1px solid rgba(93,224,207,.22)}.sonar:after{content:"";position:absolute;inset:0;border-radius:50%;background:conic-gradient(from 225deg,transparent 0 65%,rgba(93,224,207,.3) 74%,transparent 84%);animation:sweep 3s linear infinite}@keyframes sweep{to{transform:rotate(360deg)}}canvas{position:absolute;inset:0;width:100%;height:100%}.panel{position:absolute;z-index:6;inset:0;display:grid;place-items:center;background:rgba(1,10,15,.5);backdrop-filter:blur(3px)}.card{width:min(90%,440px);padding:28px;border:1px solid rgba(93,224,207,.35);background:var(--panel);box-shadow:0 15px 60px #0008}.eyebrow{color:var(--orange);font-size:11px;letter-spacing:.16em}.card h1{margin:9px 0 12px;font-size:clamp(32px,8vw,60px);line-height:.92;letter-spacing:-.06em;color:var(--cyan)}.card p{color:#a9c8c5;line-height:1.55;font-size:13px}.card button,.fire{border:1px solid var(--cyan);background:#0b3944;color:var(--ink);font:inherit;font-weight:800;letter-spacing:.08em;padding:13px 16px;cursor:pointer}.card button{width:100%;margin-top:14px}.card button:active,.fire:active{transform:translateY(1px);background:#12616a}.controls{position:absolute;z-index:7;left:18px;bottom:calc(18px + env(safe-area-inset-bottom));display:flex;align-items:end;gap:18px}.stick{width:108px;height:108px;border:1px solid rgba(160,236,225,.28);border-radius:50%;background:rgba(1,15,22,.3);position:relative}.nub{position:absolute;width:42px;height:42px;left:33px;top:33px;border-radius:50%;background:rgba(93,224,207,.35);border:1px solid var(--cyan);box-shadow:0 0 20px rgba(93,224,207,.3)}.fire{width:92px;height:58px;border-color:var(--orange);background:rgba(91,42,15,.7);color:var(--orange);font-size:12px}.hint{position:absolute;right:18px;bottom:calc(20px + env(safe-area-inset-bottom));z-index:4;color:#8ab2ae;font-size:10px;text-align:right;line-height:1.6}.hidden{display:none!important}@media(max-width:650px){.hint{top:calc(70px + env(safe-area-inset-top));bottom:auto;right:12px;max-width:145px;font-size:9px}}@media(min-width:700px){.controls{opacity:.72}.hint:after{content:' • SPACE: DEPTH CHARGE'}}
.quit{position:absolute;z-index:7;top:54px;right:16px;padding:8px 10px;border:1px solid rgba(255,111,97,.55);border-radius:4px;background:rgba(34,13,17,.78);color:#ffd5ce;font:700 10px ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;letter-spacing:.08em;cursor:pointer}.quit:hover,.quit:focus-visible{border-color:var(--danger);color:#fff;outline:none}
@media(max-width:650px){.quit{top:auto;right:12px;bottom:calc(18px + env(safe-area-inset-bottom))}}
</style>
</head>
<body>
<main class="shell" id="game">
<div class="hud"><div class="brand">DEPTH CHARGE<small>COASTAL PATROL // SECTOR 07</small></div><div class="readout"><span>SCORE<b id="score">000000</b></span><span>HULL<b id="hull">100%</b></span><span>KILLS<b id="kills">0</b></span></div></div>
<div class="arena"><div class="surface"></div><div class="sonar"></div><canvas id="canvas"></canvas></div>
<div class="controls hidden" id="controls"><div class="stick" id="stick"><div class="nub" id="nub"></div></div><button class="fire" id="fire">DROP<br>CHARGE</button></div>
<button class="quit hidden" id="quitBtn" type="button" aria-label="Patrouille beenden">STOP / QUIT</button>
<div class="hint">A/D OR ARROWS TO STEER · SPACE TO ATTACK<br>Three hits sink a contact. Mind the mines.</div>
<section class="panel" id="start"><div class="card"><div class="eyebrow">TACTICAL ARCADE // 01</div><h1>DEPTH<br>CHARGE</h1><p>Steuere das Patrouillenboot, ortest feindliche U-Boote und setze Wasserbomben im richtigen Moment. Drei Treffer pro Ziel. Ein Treffer auf dich wäre weniger charmant.</p><button id="startBtn">START PATROL →</button></div></section>
<section class="panel hidden" id="gameover"><div class="card"><div class="eyebrow">SIGNAL LOST</div><h1>HULL<br>BREACH</h1><p id="result"></p><button id="restartBtn">REDEPLOY →</button></div></section>
<section class="panel hidden" id="pause"><div class="card"><div class="eyebrow">ALL STOP</div><h1>PAUSED</h1><p>Patrol on hold. The sea waits.</p><button id="resumeBtn">RESUME →</button></div></section>
</main>
<script>
const canvas=document.querySelector('#canvas'),ctx=canvas.getContext('2d'),shell=document.querySelector('.shell');
const ui={score:document.querySelector('#score'),hull:document.querySelector('#hull'),killsEl:document.querySelector('#kills'),start:document.querySelector('#start'),startBtn:document.querySelector('#startBtn'),over:document.querySelector('#gameover'),pause:document.querySelector('#pause'),resumeBtn:document.querySelector('#resumeBtn'),restartBtn:document.querySelector('#restartBtn'),result:document.querySelector('#result'),controls:document.querySelector('#controls'),quitBtn:document.querySelector('#quitBtn'),fire:document.querySelector('#fire'),stick:document.querySelector('#stick'),nub:document.querySelector('#nub')};
let W,H,dpr,mode='menu',last=0,score=0,hull=100,kills=0,keys={},input={x:0,y:0},boat,subs=[],charges=[],mines=[],particles=[],spawnTimer=0,sonarPing=0;
const resize=()=>{dpr=Math.min(devicePixelRatio||1,2);W=shell.clientWidth;H=shell.clientHeight;canvas.width=W*dpr;canvas.height=H*dpr;ctx.setTransform(dpr,0,0,dpr,0,0)};
function reset(){score=0;hull=100;kills=0;boat={x:W*.27,y:H*.34,vx:0,vy:0,angle:0};subs=[];charges=[];mines=[];particles=[];spawnTimer=.5;sonarPing=0;invuln=0;spawnSub();ui.score.textContent='000000';ui.hull.textContent='100%';ui.killsEl.textContent='0';}
function start(){resize();reset();mode='play';ui.start.classList.add('hidden');ui.over.classList.add('hidden');ui.controls.classList.remove('hidden');ui.quitBtn.classList.remove('hidden');last=performance.now();requestAnimationFrame(loop)}
function quit(){mode='menu';reset();keys={};resetStick();ui.start.classList.remove('hidden');ui.over.classList.add('hidden');ui.pause.classList.add('hidden');ui.controls.classList.add('hidden');ui.quitBtn.classList.add('hidden');}
function spawnSub(){const limit=Math.min(3,1+Math.floor(kills/2));if(subs.filter(s=>s.active).length>=limit)return;subs.push({x:W+50,y:H*(.54+Math.random()*.18),vx:-(18+Math.random()*12+Math.min(40,kills*6)),phase:Math.random()*7,hits:0,flash:0,mineTimer:5+Math.random()*3,active:true})}
function drop(){if(mode!=='play'||charges.length>6)return;charges.push({x:boat.x+Math.cos(boat.angle)*25,y:boat.y+24,vy:55,life:0});burst(boat.x,boat.y,'#ffb35c',4)}
function burst(x,y,color,n=12){for(let i=0;i<n;i++){let a=Math.random()*Math.PI*2,s=20+Math.random()*80;particles.push({x,y,vx:Math.cos(a)*s,vy:Math.sin(a)*s,life:.4+Math.random()*.6,color})}}
let invuln=0;
function hit(){if(invuln>0)return;invuln=1.2;hull=Math.max(0,hull-20);ui.hull.textContent=hull+'%';burst(boat.x,boat.y,'#ff6f61',20);if(hull<=0){mode='over';ui.controls.classList.add('hidden');ui.quitBtn.classList.add('hidden');ui.result.textContent=`Patrouille beendet. Endstand: ${String(score).padStart(6,'0')} Punkte. Die See behält ihre Geheimnisse — vorerst.`;ui.over.classList.remove('hidden')}}
function update(dt){
const ax=input.x||((keys.ArrowRight||keys.d?1:0)-(keys.ArrowLeft||keys.a?1:0));
boat.vx+=(ax*120-boat.vx)*dt*5;boat.vy=0;boat.x=Math.max(38,Math.min(W-38,boat.x+boat.vx*dt));boat.y=H*.34;boat.angle=Math.max(-.08,Math.min(.08,boat.vx*.0012));
if(invuln>0)invuln-=dt;
spawnTimer-=dt;if(spawnTimer<0){spawnSub();spawnTimer=3}sonarPing+=dt;
subs.forEach(s=>{s.x+=s.vx*dt;s.y+=Math.sin(performance.now()/1200+s.phase)*2*dt;s.mineTimer-=dt;s.flash=Math.max(0,s.flash-dt);if(s.mineTimer<=0&&s.x<W-80){mines.push({x:s.x,y:s.y,vy:-(18+Math.random()*8),r:5,phase:Math.random()*7,rising:true});s.mineTimer=Math.max(3.5,7+Math.random()*5-kills*.4);burst(s.x,s.y,'#8bbab2',5)}if(s.x<-70){s.active=false;subs=subs.filter(x=>x!==s);spawnTimer=2}});
charges.forEach(c=>{c.life+=dt;c.y+=c.vy*dt;c.vy+=65*dt});charges=charges.filter(c=>c.life<3.2&&c.y<H);
mines.forEach(m=>{m.phase+=dt*.8;if(m.rising)m.y+=m.vy*dt;let d=Math.hypot(m.x-boat.x,m.y-boat.y);if(d<27){hit();m.hit=true}});mines=mines.filter(m=>!m.hit&&m.y>H*.30);
charges.forEach(c=>subs.forEach(s=>{if(Math.hypot(c.x-s.x,c.y-s.y)<34){s.hits++;s.flash=.25;burst(s.x,s.y,'#ffb35c',18);c.life=4;if(s.hits>=3){score+=1000;kills++;ui.score.textContent=String(score).padStart(6,'0');ui.killsEl.textContent=kills;s.active=false;subs=subs.filter(x=>x!==s);const target=Math.min(3,1+Math.floor(kills/2));while(subs.filter(x=>x.active).length<target)spawnSub();spawnTimer=1.5;burst(s.x,s.y,'#5de0cf',25)}}}));
particles.forEach(p=>{p.x+=p.vx*dt;p.y+=p.vy*dt;p.vx*=.96;p.vy*=.96;p.life-=dt});particles=particles.filter(p=>p.life>0);
}
function draw(){ctx.clearRect(0,0,W,H);ctx.save();ctx.globalAlpha=.12;for(let y=H*.36;y<H;y+=18){ctx.strokeStyle='#75d6cf';ctx.beginPath();ctx.moveTo(0,y+Math.sin(Date.now()/700+y)*5);ctx.lineTo(W,y+Math.cos(Date.now()/900+y)*5);ctx.stroke()}ctx.restore();
mines.forEach(m=>{ctx.save();ctx.translate(m.x,m.y);ctx.rotate(m.phase);ctx.strokeStyle='#c26a62';ctx.globalAlpha=.75;ctx.beginPath();ctx.arc(0,0,m.r+2,0,7);ctx.stroke();for(let i=0;i<8;i++){ctx.rotate(Math.PI/4);ctx.beginPath();ctx.moveTo(5,0);ctx.lineTo(11,0);ctx.stroke()}ctx.restore()});
subs.forEach(s=>{ctx.save();ctx.translate(s.x,s.y);ctx.fillStyle=s.flash?'#fff0b0':'#344a58';ctx.strokeStyle='#a2bcb4';ctx.lineWidth=2;ctx.beginPath();ctx.ellipse(0,0,32,9,0,0,7);ctx.fill();ctx.stroke();ctx.fillStyle='#55736e';ctx.fillRect(-4,-15,13,7);ctx.strokeStyle='#8ba3a0';ctx.beginPath();ctx.moveTo(1,-15);ctx.lineTo(1,-22);ctx.lineTo(7,-24);ctx.stroke();ctx.fillStyle='#344a58';ctx.beginPath();ctx.moveTo(28,0);ctx.lineTo(42,-8);ctx.lineTo(42,8);ctx.closePath();ctx.fill();ctx.stroke();ctx.restore()});
charges.forEach(c=>{ctx.fillStyle='#ffb35c';ctx.shadowBlur=12;ctx.shadowColor='#ffb35c';ctx.beginPath();ctx.arc(c.x,c.y,5,0,7);ctx.fill();ctx.shadowBlur=0;ctx.strokeStyle='rgba(255,179,92,.4)';ctx.beginPath();ctx.moveTo(c.x,c.y-14);ctx.lineTo(c.x,c.y-5);ctx.stroke()});
ctx.save();ctx.translate(boat.x,boat.y);ctx.rotate(boat.angle);ctx.fillStyle='#d7dfca';ctx.strokeStyle='#274b4b';ctx.lineWidth=2;ctx.beginPath();ctx.moveTo(-36,3);ctx.lineTo(-27,-7);ctx.lineTo(18,-8);ctx.lineTo(35,0);ctx.lineTo(22,9);ctx.lineTo(-27,9);ctx.closePath();ctx.fill();ctx.stroke();ctx.fillStyle='#b2c7b6';ctx.fillRect(-9,-18,20,10);ctx.fillStyle='#6f9790';ctx.fillRect(-6,-16,7,5);ctx.fillStyle='#d7dfca';ctx.fillRect(13,-13,4,8);ctx.fillStyle='#ffb35c';ctx.beginPath();ctx.arc(28,0,3,0,7);ctx.fill();ctx.strokeStyle='rgba(220,250,235,.5)';ctx.beginPath();ctx.moveTo(-38,6);ctx.lineTo(-53,8);ctx.moveTo(-35,9);ctx.lineTo(-47,12);ctx.stroke();ctx.restore();
particles.forEach(p=>{ctx.globalAlpha=Math.max(0,p.life);ctx.fillStyle=p.color;ctx.fillRect(p.x,p.y,3,3)});ctx.globalAlpha=1;
}
function setPause(on){if(on&&mode==='play'){mode='paused';ui.controls.classList.add('hidden');ui.pause.classList.remove('hidden')}else if(!on&&mode==='paused'){mode='play';last=performance.now();requestAnimationFrame(loop);ui.pause.classList.add('hidden');ui.controls.classList.remove('hidden')}}
function loop(t){if(mode!=='play')return;let dt=Math.min(.033,(t-last)/1000);last=t;update(dt);draw();requestAnimationFrame(loop)}
window.addEventListener('resize',resize);window.addEventListener('keydown',e=>{if(['ArrowLeft','ArrowRight','ArrowUp','ArrowDown',' '].includes(e.key))e.preventDefault();if(document.activeElement&&document.activeElement.blur)document.activeElement.blur();keys[e.key]=true;if(e.code==='Space'){e.preventDefault();drop()}else if(e.key==='p'||e.key==='P'||e.key==='Escape'){e.preventDefault();setPause(mode!=='paused')}});window.addEventListener('keyup',e=>keys[e.key]=false);ui.startBtn.onclick=start;ui.restartBtn.onclick=start;ui.quitBtn.onclick=quit;ui.fire.onclick=drop;ui.resumeBtn.onclick=()=>setPause(false);document.addEventListener('visibilitychange',()=>{if(document.hidden)setPause(true)});
let pointerId=null;ui.stick.addEventListener('pointerdown',e=>{pointerId=e.pointerId;ui.stick.setPointerCapture(pointerId);moveStick(e)});ui.stick.addEventListener('pointermove',e=>{if(e.pointerId===pointerId)moveStick(e)});ui.stick.addEventListener('pointerup',resetStick);ui.stick.addEventListener('pointercancel',resetStick);function moveStick(e){let r=ui.stick.getBoundingClientRect(),x=e.clientX-(r.left+r.width/2),m=Math.min(34,Math.abs(x)),direction=x<0?-1:1;input={x:direction*m/34,y:0};ui.nub.style.transform=`translate(${input.x*34}px,0)`}function resetStick(){pointerId=null;input={x:0,y:0};ui.nub.style.transform='translate(0,0)'}
resize();reset();draw();
</script>
</body></html>