NEON OUTBREAK
by PixelCoder89315 lines12.3 KB
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<title>NEON OUTBREAK</title>
<style>
*{box-sizing:border-box}
body{margin:0;overflow:hidden;background:#07101d;color:white;font-family:Arial,sans-serif;touch-action:none}
canvas{display:block;width:100vw;height:100vh}
#hud{position:fixed;inset:0;pointer-events:none}
.panel{position:absolute;top:12px;left:12px;background:#081525e8;border:1px solid #31516b;border-radius:12px;padding:12px}
#stats{font-size:13px;line-height:1.8}
#healthbar{height:8px;width:190px;background:#253448;margin-top:5px;border-radius:10px;overflow:hidden}
#health{height:100%;width:100%;background:linear-gradient(90deg,#27e6a3,#c5ff75)}
#xpbar{position:absolute;bottom:0;width:100%;height:5px;background:#18263a}
#xp{height:100%;width:0;background:#65f4ff}
#menu{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;text-align:center;background:#07101dcc;pointer-events:auto}
.box{max-width:500px;padding:25px}
h1{font-size:clamp(36px,9vw,65px);margin:0;color:#56f5ff;text-shadow:0 0 25px #37cfff}
button{border:0;border-radius:12px;padding:14px 20px;background:#56f5ff;color:#07101d;font-weight:bold;font-size:15px;margin:5px}
#upgrade{display:none;position:absolute;inset:0;background:#050a15e8;align-items:center;justify-content:center;pointer-events:auto}
#choices{display:flex;gap:10px;max-width:650px;padding:15px}
.choice{background:#142b42;color:white;border:1px solid #56f5ff;flex:1;min-width:0}
#mobile{display:none;position:absolute;inset:0;pointer-events:none}
#stick{position:absolute;bottom:30px;left:25px;width:125px;height:125px;border-radius:50%;background:#8cefff22;border:2px solid #8cefff88;pointer-events:auto}
#knob{position:absolute;left:36px;top:36px;width:49px;height:49px;border-radius:50%;background:#56f5ff99}
#dash{position:absolute;right:25px;bottom:40px;border-radius:50%;width:75px;height:75px;background:#ff4f8677;color:white;pointer-events:auto}
#pause{position:absolute;right:12px;top:12px;pointer-events:auto;background:#142b42;color:white}
@media(max-width:650px){#choices{flex-direction:column}.choice{padding:10px}#mobile{display:block}#healthbar{width:130px}}
</style>
</head>
<body>
<canvas id="game"></canvas>
<div id="hud">
<div class="panel">
<strong style="color:#56f5ff">NEON OUTBREAK</strong>
<div id="stats">DALGA: <span id="wave">1</span><br>SKOR: <span id="score">0</span><br>SEVİYE: <span id="level">1</span></div>
<div id="healthbar"><div id="health"></div></div>
<div id="hptext">100 / 100 CAN</div>
</div>
<button id="pause">Ⅱ</button>
<div id="xpbar"><div id="xp"></div></div>
<div id="mobile">
<div id="stick"><div id="knob"></div></div>
<button id="dash">DASH</button>
</div>
<div id="menu"><div class="box">
<h1>NEON<br>OUTBREAK</h1>
<p>Her yönden gelen düşmanlara karşı hayatta kal!<br>XP topla, güçlen ve dalgaları geç.</p>
<button onclick="startGame()">ARENA'YA GİR</button>
<p>Bilgisayar: WASD + fare<br>Telefon: joystick + otomatik atış</p>
</div></div>
<div id="upgrade"><div class="box">
<h2>SEVİYE ATLADIN!</h2>
<p>Bir geliştirme seç:</p><div id="choices"></div>
</div></div>
</div>
<script>
const canvas=document.getElementById("game"),ctx=canvas.getContext("2d");
let W,H;
function resize(){W=canvas.width=innerWidth;H=canvas.height=innerHeight}
onresize=resize;resize();
let state="menu",player,enemies,bullets,orbs,particles;
let score=0,wave=1,level=1,xp=0,need=10,time=0,spawn=0,shot=0;
let keys={},mouse={x:0,y:0},joy={x:0,y:0,active:false};
let best=Number(localStorage.getItem("neonBest")||0);
const upgrades=[
["⚡ GÜÇLÜ ATIŞ","Hasar %35 artar",()=>player.damage*=1.35],
["🔥 HIZLI TETİK","Daha hızlı atış",()=>player.rate=Math.max(.09,player.rate*.8)],
["➤ HIZ TAKVİYESİ","Hareket hızı artar",()=>player.speed*=1.15],
["❤️ EKSTRA CAN","25 maksimum can kazan",()=>{player.max+=25;player.hp=Math.min(player.max,player.hp+25)}],
["💜 XP MIKNATISI","XP toplama alanı büyür",()=>player.magnet+=90],
["✚ İYİLEŞME","Canın yenilenir",()=>player.hp=Math.min(player.max,player.hp+40)],
["☄ DELİCİ ATIŞ","Atışlar düşmanları deler",()=>player.pierce++]
];
function startGame(){
state="play";score=0;wave=1;level=1;xp=0;need=10;time=0;spawn=0;shot=0;
player={x:W/2,y:H/2,r:13,hp:100,max:100,speed:220,damage:24,rate:.28,magnet:70,pierce:0,cd:0,inv:0};
enemies=[];bullets=[];orbs=[];particles=[];
document.getElementById("menu").style.display="none";
document.getElementById("upgrade").style.display="none";
}
function enemy(){
let side=Math.floor(Math.random()*4),x,y;
if(side===0){x=-20;y=Math.random()*H}
if(side===1){x=W+20;y=Math.random()*H}
if(side===2){x=Math.random()*W;y=-20}
if(side===3){x=Math.random()*W;y=H+20}
let type=Math.random()<.16?"fast":Math.random()<.13?"tank":"normal";
enemies.push({
x,y,type,r:type==="tank"?19:type==="fast"?10:13,
hp:(type==="tank"?100:type==="fast"?23:40)*(1+wave*.12),
speed:(type==="tank"?45:type==="fast"?115:68)*(1+wave*.025),
damage:type==="tank"?17:10
});
}
function shoot(){
if(shot>0||!enemies.length)return;
let target=null,dist=500*500;
for(let e of enemies){
let d=(e.x-player.x)**2+(e.y-player.y)**2;
if(d<dist){dist=d;target=e}
}
if(!target)return;
let a=Math.atan2(target.y-player.y,target.x-player.x);
bullets.push({x:player.x,y:player.y,vx:Math.cos(a)*570,vy:Math.sin(a)*570,r:5,life:1.2,damage:player.damage,pierce:player.pierce});
shot=player.rate;
}
function burst(x,y,color){
for(let i=0;i<8;i++){
let a=Math.random()*Math.PI*2,s=40+Math.random()*150;
particles.push({x,y,vx:Math.cos(a)*s,vy:Math.sin(a)*s,life:.4,color});
}
}
function levelUp(){
level++;xp-=need;need=Math.floor(need*1.35+3);
state="upgrade";
const box=document.getElementById("upgrade"),choices=document.getElementById("choices");
choices.innerHTML="";
box.style.display="flex";
[...upgrades].sort(()=>Math.random()-.5).slice(0,3).forEach(u=>{
let b=document.createElement("button");
b.className="choice";b.innerHTML="<b>"+u[0]+"</b><br><small>"+u[1]+"</small>";
b.onclick=()=>{u[2]();box.style.display="none";state="play"};
choices.appendChild(b);
});
}
function dash(){
if(state!=="play"||player.cd>0)return;
let dx=joy.x,dy=joy.y;
if(!joy.active){
dx=(keys.d||keys.arrowright?1:0)-(keys.a||keys.arrowleft?1:0);
dy=(keys.s||keys.arrowdown?1:0)-(keys.w||keys.arrowup?1:0);
}
let l=Math.hypot(dx,dy)||1;
player.x+=dx/l*110;player.y+=dy/l*110;
player.inv=.3;player.cd=2;
burst(player.x,player.y,"#56f5ff");
}
function update(dt){
if(state!=="play")return;
time+=dt;shot-=dt;player.cd=Math.max(0,player.cd-dt);player.inv=Math.max(0,player.inv-dt);
let dx=(keys.d||keys.arrowright?1:0)-(keys.a||keys.arrowleft?1:0);
let dy=(keys.s||keys.arrowdown?1:0)-(keys.w||keys.arrowup?1:0);
if(joy.active){dx=joy.x;dy=joy.y}
let len=Math.hypot(dx,dy)||1;
player.x+=dx/len*player.speed*dt*(dx||dy?1:0);
player.y+=dy/len*player.speed*dt*(dx||dy?1:0);
player.x=Math.max(12,Math.min(W-12,player.x));
player.y=Math.max(60,Math.min(H-12,player.y));
spawn-=dt;
if(spawn<=0){
for(let i=0;i<1+Math.floor(wave/5);i++)enemy();
spawn=Math.max(.22,1-wave*.035);
}
if(time>wave*20)wave++;
shoot();
for(let b of bullets){
b.x+=b.vx*dt;b.y+=b.vy*dt;b.life-=dt;
for(let e of enemies){
if(e.hp<=0)continue;
if(Math.hypot(b.x-e.x,b.y-e.y)<b.r+e.r){
e.hp-=b.damage;b.life=0;
burst(b.x,b.y,"#56f5ff");
if(e.hp<=0){
score+=e.type==="tank"?30:10;
orbs.push({x:e.x,y:e.y,r:6});
burst(e.x,e.y,"#ff4f86");
}
if(b.pierce>0)b.pierce--;else break;
}
}
}
bullets=bullets.filter(b=>b.life>0);
for(let e of enemies){
let a=Math.atan2(player.y-e.y,player.x-e.x);
e.x+=Math.cos(a)*e.speed*dt;e.y+=Math.sin(a)*e.speed*dt;
if(Math.hypot(player.x-e.x,player.y-e.y)<player.r+e.r&&player.inv<=0){
player.hp-=e.damage*dt*1.5;
if(player.hp<=0){gameOver();return}
}
}
enemies=enemies.filter(e=>e.hp>0);
for(let o of orbs){
let d=Math.hypot(player.x-o.x,player.y-o.y);
if(d<player.magnet+35){
let a=Math.atan2(player.y-o.y,player.x-o.x);
o.x+=Math.cos(a)*220*dt;o.y+=Math.sin(a)*220*dt;
}
if(d<player.r+o.r+5){o.got=true;xp++;if(xp>=need){levelUp();break}}
}
orbs=orbs.filter(o=>!o.got);
for(let p of particles){p.x+=p.vx*dt;p.y+=p.vy*dt;p.life-=dt;p.vx*=.95;p.vy*=.95}
particles=particles.filter(p=>p.life>0);
document.getElementById("wave").textContent=wave;
document.getElementById("score").textContent=score;
document.getElementById("level").textContent=level;
document.getElementById("health").style.width=Math.max(0,player.hp/player.max*100)+"%";
document.getElementById("hptext").textContent=Math.ceil(player.hp)+" / "+player.max+" CAN";
document.getElementById("xp").style.width=xp/need*100+"%";
}
function gameOver(){
state="over";
if(score>best){best=score;localStorage.setItem("neonBest",best)}
let m=document.getElementById("menu");
m.style.display="flex";
m.innerHTML=`<div class="box"><h1>OYUN BİTTİ</h1>
<p>Skor: ${score}<br>Dalga: ${wave}<br>Rekor: ${best}</p>
<button onclick="startGame()">TEKRAR DENE</button></div>`;
}
function draw(){
ctx.clearRect(0,0,W,H);
let g=ctx.createLinearGradient(0,0,W,H);
g.addColorStop(0,"#0a1c2c");g.addColorStop(1,"#101426");
ctx.fillStyle=g;ctx.fillRect(0,0,W,H);
// Neon grid
ctx.strokeStyle="#55f6ff12";ctx.lineWidth=1;
for(let x=0;x<W;x+=40){ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,H);ctx.stroke()}
for(let y=0;y<H;y+=40){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(W,y);ctx.stroke()}
// XP orbs
for(let o of orbs){
ctx.shadowBlur=15;ctx.shadowColor="#bb6cff";
ctx.fillStyle="#d7a5ff";ctx.beginPath();ctx.arc(o.x,o.y,o.r,0,7);ctx.fill();
}
ctx.shadowBlur=0;
// Bullets
for(let b of bullets){
ctx.shadowBlur=16;ctx.shadowColor="#56f5ff";ctx.fillStyle="#dfffff";
ctx.beginPath();ctx.arc(b.x,b.y,b.r,0,7);ctx.fill();
}
ctx.shadowBlur=0;
// Enemies
for(let e of enemies){
let c=e.type==="tank"?"#ffb15e":e.type==="fast"?"#ff416d":"#f04d82";
ctx.shadowBlur=15;ctx.shadowColor=c;ctx.fillStyle=c;
ctx.beginPath();ctx.arc(e.x,e.y,e.r,0,7);ctx.fill();
ctx.shadowBlur=0;
ctx.fillStyle="#37162b";
ctx.beginPath();ctx.arc(e.x-4,e.y-2,2,0,7);ctx.arc(e.x+4,e.y-2,2,0,7);ctx.fill();
}
// Particles
for(let p of particles){
ctx.globalAlpha=Math.max(0,p.life/.4);ctx.fillStyle=p.color;
ctx.fillRect(p.x,p.y,3,3);
}
ctx.globalAlpha=1;
// Player
if(player){
ctx.save();ctx.translate(player.x,player.y);
let target=enemies.reduce((a,e)=>!a||Math.hypot(e.x-player.x,e.y-player.y)<Math.hypot(a.x-player.x,a.y-player.y)?e:a,null);
let angle=target?Math.atan2(target.y-player.y,target.x-player.x):0;
ctx.rotate(angle);ctx.shadowBlur=22;ctx.shadowColor="#56f5ff";
ctx.fillStyle=player.inv>0?"white":"#56f5ff";
ctx.beginPath();ctx.moveTo(17,0);ctx.lineTo(-10,-10);ctx.lineTo(-5,0);ctx.lineTo(-10,10);ctx.closePath();ctx.fill();
ctx.restore();ctx.shadowBlur=0;
}
requestAnimationFrame(draw);
}
let last=performance.now();
function loop(now){let dt=Math.min((now-last)/1000,.033);last=now;update(dt);requestAnimationFrame(loop)}
document.addEventListener("keydown",e=>{keys[e.key.toLowerCase()]=true;if(e.key===" ")dash()});
document.addEventListener("keyup",e=>keys[e.key.toLowerCase()]=false);
canvas.addEventListener("pointermove",e=>{mouse.x=e.clientX;mouse.y=e.clientY});
document.getElementById("dash").addEventListener("pointerdown",dash);
const stick=document.getElementById("stick"),knob=document.getElementById("knob");
stick.addEventListener("pointerdown",e=>{joy.active=true;stick.setPointerCapture(e.pointerId);moveJoy(e)});
stick.addEventListener("pointermove",e=>{if(joy.active)moveJoy(e)});
function moveJoy(e){
let r=stick.getBoundingClientRect(),dx=e.clientX-(r.left+r.width/2),dy=e.clientY-(r.top+r.height/2);
let len=Math.hypot(dx,dy),max=38;
if(len>max){dx=dx/len*max;dy=dy/len*max}
joy.x=dx/max;joy.y=dy/max;knob.style.transform=`translate(${dx}px,${dy}px)`;
}
function stopJoy(){joy.active=false;joy.x=joy.y=0;knob.style.transform="translate(0,0)"}
stick.addEventListener("pointerup",stopJoy);stick.addEventListener("pointercancel",stopJoy);
document.getElementById("pause").onclick=()=>{
if(state==="play"){state="pause";document.getElementById("menu").style.display="flex";document.getElementById("menu").innerHTML='<div class="box"><h1>DURAKLATILDI</h1><button onclick="resumeGame()">DEVAM ET</button></div>'}
};
function resumeGame(){state="play";document.getElementById("menu").style.display="none"}
window.startGame=startGame;window.resumeGame=resumeGame;
player={x:W/2,y:H/2,r:13,hp:100,max:100,speed:220,damage:24,rate:.28,magnet:70,pierce:0,cd:0,inv:0};
enemies=[];bullets=[];orbs=[];particles=[];
draw();requestAnimationFrame(loop);
</script>
</body>
</html>Game Source: NEON OUTBREAK
Creator: PixelCoder89
Libraries: none
Complexity: complex (315 lines, 12.3 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: neon-outbreak-pixelcoder89" to link back to the original. Then publish at arcadelab.ai/publish.