!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Headshot Heroes β Arcade Target Game</title>
<style>
*{box-sizing:border-box} body{margin:0;background:#101426;color:#f4f7ff;font-family:system-ui,Arial;display:flex;justify-content:center;min-height:100vh}
.wrap{width:min(960px,100%);padding:16px}
h1{margin:4px 0 8px;text-align:center;font-size:clamp(24px,5vw,38px);color:#8ee7ff}
.sub{text-align:center;color:#b9c4df;margin:0 0 12px}
.hud{display:flex;gap:8px;justify-content:space-between;flex-wrap:wrap;margin:10px 0}
.pill{background:#202a46;border:1px solid #39496f;border-radius:12px;padding:8px 12px;font-weight:700}
canvas{display:block;width:100%;height:auto;background:linear-gradient(#17213d,#25365b);border:2px solid #52658f;border-radius:16px;touch-action:manipulation;cursor:crosshair}
.controls{display:flex;gap:8px;flex-wrap:wrap;justify-content:center;margin:12px 0}
button{border:0;border-radius:11px;padding:10px 14px;background:#29385c;color:white;font-weight:700;font-size:14px}
button.active{background:#087f9c;outline:2px solid #8ee7ff}
button:disabled{opacity:.45}
.help{text-align:center;color:#bdc9e5;font-size:13px;line-height:1.5}
#message{text-align:center;min-height:24px;color:#ffe08a;font-weight:700}
</style>
</head>
<body>
<div class="wrap">
<h1>π― HEADSHOT HEROES</h1>
<p class="sub">A colourful arcade game β hit the floating robot targets!</p>
<div class="hud">
<div class="pill">Score: <span id="score">0</span></div>
<div class="pill">Time: <span id="time">45</span>s</div>
<div class="pill">Best: <span id="best">0</span></div>
<div class="pill">Hits: <span id="hits">0</span></div>
</div>
<canvas id="game" width="900" height="510" aria-label="Arcade target game"></canvas>
<div class="controls" id="weapons">
<button class="active" data-weapon="pulse">β‘ Pulse Blaster</button>
<button data-weapon="arc">β¨ Arc Bow</button>
<button data-weapon="burst">π« Star Burst</button>
</div>
<div class="controls">
<button id="slow">β³ Slow Time (Q)</button>
<button id="multi">π Multi-shot (E)</button>
<button id="restart">β» Restart</button>
</div>
<div id="message">Tap/click the floating robot heads to score. Choose a launcher and try your abilities!</div>
<p class="help">Desktop: click targets Β· Q = Slow Time Β· E = Multi-shot<br>Mobile: tap targets and use the buttons. This is a fantasy arcade game with harmless robot targets.</p>
</div>
<script>
(() => {
const c=document.getElementById('game'),ctx=c.getContext('2d');
const $=id=>document.getElementById(id);
let targets=[],shots=[],sparks=[],score=0,hits=0,time=45,weapon='pulse',running=true,slow=0,multi=0,last=0,spawn=0;
let best=Number(localStorage.getItem('hh_best')||0); $('best').textContent=best;
const colors=['#ffcc66','#8ee7ff','#c9a2ff','#83e6a3','#ff9eb4'];
function reset(){targets=[];shots=[];sparks=[];score=0;hits=0;time=45;running=true;slow=0;multi=0;spawn=0;last=0;$('message').textContent='Go! Tap the robot targets!';updateHud();requestAnimationFrame(loop)}
function updateHud(){$('score').textContent=score;$('hits').textContent=hits;$('time').textContent=Math.max(0,Math.ceil(time));$('best').textContent=best;$('slow').disabled=slow>0||!running;$('multi').disabled=multi>0||!running}
function addTarget(){let r=25+Math.random()*12;targets.push({x:70+Math.random()*760,y:85+Math.random()*350,r,vx:(Math.random()-.5)*1.5,vy:(Math.random()-.5)*1.2,color:colors[Math.floor(Math.random()*colors.length)],phase:Math.random()*6.28})}
function drawTarget(t){ctx.save();ctx.translate(t.x,t.y);ctx.shadowColor=t.color;ctx.shadowBlur=14;
// Cute robot-head target, not a person
ctx.fillStyle=t.color;ctx.beginPath();ctx.roundRect(-t.r,-t.r*.82,t.r*2,t.r*1.65,13);ctx.fill();
ctx.shadowBlur=0;ctx.fillStyle='#202943';ctx.fillRect(-t.r*.72,-t.r*.25,t.r*.42,t.r*.35);ctx.fillRect(t.r*.3,-t.r*.25,t.r*.42,t.r*.35);
ctx.strokeStyle='#202943';ctx.lineWidth=3;ctx.beginPath();ctx.arc(0,t.r*.28,t.r*.28,0.15*Math.PI,.85*Math.PI);ctx.stroke();
ctx.fillStyle='#f7fbff';ctx.fillRect(-5,-t.r-10,10,10);ctx.fillStyle=t.color;ctx.beginPath();ctx.arc(0,-t.r-13,5,0,Math.PI*2);ctx.fill();
ctx.restore();
}
function draw(){ctx.clearRect(0,0,c.width,c.height);
// stars
for(let i=0;i<45;i++){let x=(i*137)%900,y=(i*79)%510;ctx.fillStyle='rgba(255,255,255,.25)';ctx.fillRect(x,y,2,2)}
targets.forEach(drawTarget);
shots.forEach(s=>{ctx.save();ctx.strokeStyle=s.color;ctx.lineWidth=5;ctx.shadowColor=s.color;ctx.shadowBlur=15;ctx.beginPath();ctx.moveTo(s.x,s.y);ctx.lineTo(s.x-s.vx*2,s.y-s.vy*2);ctx.stroke();ctx.restore()});
sparks.forEach(p=>{ctx.globalAlpha=Math.max(0,p.life/24);ctx.fillStyle=p.color;ctx.beginPath();ctx.arc(p.x,p.y,3,0,Math.PI*2);ctx.fill();ctx.globalAlpha=1});
if(!running){ctx.fillStyle='rgba(5,10,25,.68)';ctx.fillRect(0,0,900,510);ctx.fillStyle='white';ctx.textAlign='center';ctx.font='bold 48px system-ui';ctx.fillText('TIME UP!',450,230);ctx.font='bold 26px system-ui';ctx.fillText('Final score: '+score,450,278);ctx.font='18px system-ui';ctx.fillText('Press Restart to play again',450,320)}
}
function fire(x,y){if(!running)return;let count=multi>0?3:1;for(let i=0;i<count;i++){let ox=(i-(count-1)/2)*13;shots.push({x:x+ox,y:y+25,vx:ox*.06,vy:-7,color:weapon==='arc'?'#a8ffda':weapon==='burst'?'#ffd36b':'#8ee7ff',life:70})}}
function pointer(e){let rect=c.getBoundingClientRect(),x=(e.clientX-rect.left)*c.width/rect.width,y=(e.clientY-rect.top)*c.height/rect.height;
let idx=-1;for(let i=targets.length-1;i>=0;i--){let t=targets[i];if(Math.hypot(x-t.x,y-t.y)<t.r*1.3){idx=i;break}}
if(idx>=0){let t=targets.splice(idx,1)[0];score+=weapon==='arc'?15:weapon==='burst'?12:10;hits++;for(let k=0;k<14;k++)sparks.push({x:t.x,y:t.y,vx:(Math.random()-.5)*6,vy:(Math.random()-.5)*6,life:24,color:t.color});$('message').textContent=['Nice hit! β¨','Bullseye! π―','Combo energy! β‘'][Math.floor(Math.random()*3)];}
else fire(x,y);
if(score>best){best=score;localStorage.setItem('hh_best',best)}updateHud();
}
c.addEventListener('pointerdown',pointer);
document.querySelectorAll('[data-weapon]').forEach(b=>b.addEventListener('click',()=>{weapon=b.dataset.weapon;document.querySelectorAll('[data-weapon]').forEach(x=>x.classList.toggle('active',x===b));$('message').textContent=b.textContent+' selected!'}));
$('slow').onclick=()=>{if(running&&slow<=0){slow=4;$('message').textContent='Time slowed for 4 seconds!'}};
$('multi').onclick=()=>{if(running&&multi<=0){multi=6;$('message').textContent='Multi-shot active for 6 seconds!'}};
$('restart').onclick=reset;
window.addEventListener('keydown',e=>{if(e.key.toLowerCase()==='q')$('slow').click();if(e.key.toLowerCase()==='e')$('multi').click()});
function loop(ts){if(!running){draw();return}let dt=Math.min((ts-last)/1000||0,.04);last=ts;let factor=slow>0?.45:1;time-=dt;if(slow>0)slow-=dt;if(multi>0)multi-=dt;
spawn+=dt;if(spawn>.75){addTarget();spawn=0}
targets.forEach(t=>{t.x+=t.vx*factor;t.y+=t.vy*factor+Math.sin(ts/450+t.phase)*.35;if(t.x<t.r||t.x>900-t.r)t.vx*=-1;if(t.y<t.r+20||t.y>510-t.r)t.vy*=-1});
shots.forEach(s=>{s.x+=s.vx;s.y+=s.vy;s.life--});shots=shots.filter(s=>s.life>0&&s.y>-20);
sparks.forEach(p=>{p.x+=p.vx;p.y+=p.vy;p.life--});sparks=sparks.filter(p=>p.life>0);
if(time<=0){time=0;running=false;$('message').textContent='Great round! Your score: '+score;updateHud()}
updateHud();draw();if(running)requestAnimationFrame(loop);
}
reset();
})();
</script>
</body>
</html>1
1
8KB
8KB
48.0ms
136.0ms
49.0ms